From d58bb2aad7f35b991277316c564e299f625c668e Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 21 Oct 2022 15:25:35 +0200 Subject: [PATCH 001/498] Migrating to Javalin 5 - updated Javalin 5 - updated various dependencies - client openapi not yet working, possibly a limitation of current version of generator --- backend/build.gradle | 85 +++--- .../kotlin/dev/dres/api/cli/OpenApiCommand.kt | 3 +- .../kotlin/dev/dres/api/rest/AccessManager.kt | 2 +- .../dres/api/rest/OpenApiEndpointOptions.kt | 6 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 247 ++++++++++-------- .../rest/handler/AbstractPreviewHandler.kt | 11 +- .../dres/api/rest/handler/AuditLogHandler.kt | 16 +- .../rest/handler/BatchSubmissionHandler.kt | 7 +- .../api/rest/handler/CollectionHandler.kt | 38 ++- .../api/rest/handler/CompetitionHandler.kt | 31 ++- .../handler/CompetitionRunAdminHandler.kt | 39 +-- .../CompetitionRunClientInfoHandler.kt | 13 +- .../api/rest/handler/CompetitionRunHandler.kt | 37 +-- .../handler/CompetitionRunScoreHandler.kt | 25 +- .../dres/api/rest/handler/DownloadHandler.kt | 16 +- .../dres/api/rest/handler/GetMediaHandler.kt | 10 +- .../dres/api/rest/handler/JudgementHandler.kt | 17 +- .../dev/dres/api/rest/handler/LogHandler.kt | 11 +- .../dev/dres/api/rest/handler/LoginHandler.kt | 4 +- .../dres/api/rest/handler/LogoutHandler.kt | 8 +- .../dev/dres/api/rest/handler/RestHandler.kt | 2 +- .../dres/api/rest/handler/StatusHandler.kt | 10 +- .../api/rest/handler/SubmissionHandler.kt | 10 +- .../dev/dres/api/rest/handler/UserHandler.kt | 26 +- .../api/rest/types/WebSocketConnection.kt | 9 +- .../api/rest/types/competition/RestTeam.kt | 4 +- .../rest/types/status/ErrorStatusException.kt | 2 +- .../utilities/extensions/ContextExtensions.kt | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 29 files changed, 389 insertions(+), 308 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 81950e92b..d76b06cc6 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -1,7 +1,7 @@ import org.apache.tools.ant.taskdefs.condition.Os buildscript { - ext.kotlinVersion = '1.4.31' + ext.kotlinVersion = '1.7.20' repositories { mavenCentral() } @@ -15,10 +15,11 @@ buildscript { apply plugin: 'application' apply plugin: 'kotlin' apply plugin: 'kotlinx-serialization' +apply plugin: 'kotlin-kapt' apply plugin: 'idea' mainClassName = 'dev.dres.DRES' -sourceCompatibility = 1.8 +sourceCompatibility = 11 /* Configuration for frontend classpath files (see dependencies). */ configurations { @@ -33,19 +34,17 @@ repositories { } compileKotlin { - kotlinOptions.jvmTarget = "1.8" + kotlinOptions.jvmTarget = "11" kotlinOptions { freeCompilerArgs = ["-Xinline-classes"] } } compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" + kotlinOptions.jvmTarget = "11" } dependencies { - def jettyVersion = '9.4.44.v20210927' - def alpnApiVersion = '1.1.3.v20160715' - def alpnBootVersion = '8.1.12.v20180117' + def javalin = '5.1.1' def log4jVersion = '2.17.0' ///// Frontend files (produced by sub-project). @@ -55,62 +54,66 @@ dependencies { implementation files("$buildDir/ext") {builtBy 'setupFFMpeg'} ///// MapDB - compile group: 'org.mapdb', name: 'mapdb', version: '3.0.8' + implementation group: 'org.mapdb', name: 'mapdb', version: '3.0.8' ////// Javalin - compile ('io.javalin:javalin-bundle:4.1.0'){ - exclude group: 'ch.qos.logback', module: 'logback-classic' - } - compile group: 'io.swagger.core.v3', name: 'swagger-core', version: '2.1.5' - compile group: 'org.webjars', name: 'swagger-ui', version: '3.24.3' - compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.0' - compile group: 'io.github.classgraph', name: 'classgraph', version: '4.8.34' - compile group: 'org.eclipse.jetty.http2', name: 'http2-server', version: jettyVersion - compile group: 'org.eclipse.jetty', name: 'jetty-alpn-conscrypt-server', version: jettyVersion - compile group: 'org.eclipse.jetty.alpn', name: 'alpn-api', version: alpnApiVersion - runtime group: 'org.mortbay.jetty.alpn', name: 'alpn-boot', version: alpnBootVersion + implementation group: 'io.javalin', name: 'javalin', version: "$javalin" + kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin") + + implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "$javalin" + implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "$javalin" + implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: "$javalin" + + implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.0' + + + ////// bcrypt - compile group: 'org.mindrot', name: 'jbcrypt', version: '0.4' + implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4' ////// JLine 3, Clikt & Picnic for optimal terminal experience :-) - compile group: 'com.github.ajalt', name: 'clikt', version: '2.8.0' - compile group: 'org.jline', name: 'jline-terminal', version: '3.20.0' - compile group: 'org.jline', name: 'jline-terminal-jna', version: '3.20.0' - compile group: 'org.jline', name: 'jline-reader', version: '3.20.0' - compile group: 'org.jline', name: 'jline-builtins', version: '3.20.0' - compile group: 'com.jakewharton.picnic', name: 'picnic', version: '0.5.0' + implementation group: 'com.github.ajalt', name: 'clikt', version: '2.8.0' + implementation group: 'org.jline', name: 'jline-terminal', version: '3.20.0' + implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.20.0' + implementation group: 'org.jline', name: 'jline-reader', version: '3.20.0' + implementation group: 'org.jline', name: 'jline-builtins', version: '3.20.0' + implementation group: 'com.jakewharton.picnic', name: 'picnic', version: '0.5.0' ///// Fuel - compile group: 'com.github.kittinunf.fuel', name: 'fuel', version: '2.3.1' + implementation group: 'com.github.kittinunf.fuel', name: 'fuel', version: '2.3.1' ////// CSV - compile group: 'com.github.doyaaaaaken', name: 'kotlin-csv-jvm', version: '0.7.3' + implementation group: 'com.github.doyaaaaaken', name: 'kotlin-csv-jvm', version: '0.7.3' ////// Jaffree ffmpeg wrapper - compile group: 'com.github.kokorin.jaffree', name: 'jaffree', version: '0.9.3' + implementation group: 'com.github.kokorin.jaffree', name: 'jaffree', version: '0.9.3' ////// Cache - compile group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.8.6' + implementation group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.8.6' ////// Log4J - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4jVersion - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4jVersion - compile group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: log4jVersion - compile group: 'org.apache.logging.log4j', name: 'log4j-jul', version: log4jVersion + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4jVersion + implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4jVersion + implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: log4jVersion + implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: log4jVersion ///// JUnit 5 - testCompile "org.jetbrains.kotlin:kotlin-test" - testCompile "org.jetbrains.kotlin:kotlin-test-junit5" + testImplementation "org.jetbrains.kotlin:kotlin-test" + testImplementation "org.jetbrains.kotlin:kotlin-test-junit5" - testCompile group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.7.0' - testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0' - testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.7.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0' ///// Kotlin - compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlinVersion - compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion + implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlinVersion + implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion +} + +kapt { + correctErrorTypes true } test { diff --git a/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt index 46e5a5579..1fef7c160 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt @@ -7,8 +7,7 @@ import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.file import com.github.kittinunf.fuel.Fuel import dev.dres.api.rest.OpenApiEndpointOptions -import dev.dres.api.rest.RestApi -import io.swagger.util.Json + import java.io.File class OpenApiCommand : CliktCommand(name="openapi",help = "Generates and writes the OpenAPI Specifications") { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index c537b862f..13f1f4fa6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -4,7 +4,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.admin.User import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.http.Handler import java.util.concurrent.ConcurrentHashMap diff --git a/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt b/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt index cc6103284..c4feabd49 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest -import io.javalin.plugin.openapi.annotations.HttpMethod -import okhttp3.internal.toImmutableList +import io.javalin.openapi.HttpMethod + /** * Options to configure an OpenApi Specifications endpoint in use with Javalin OpenApi Plugin. @@ -22,7 +22,7 @@ data class OpenApiEndpointOptions( ) { fun ignored(): List>> { - return _ignored.toImmutableList() + return _ignored } private val _ignored = ignores.map { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 3f374e37d..af0428666 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,6 +1,5 @@ package dev.dres.api.rest -import com.fasterxml.jackson.databind.SerializationFeature import dev.dres.api.rest.handler.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.dbo.DataAccessLayer @@ -9,20 +8,17 @@ import dev.dres.run.RunExecutor import dev.dres.utilities.NamedThreadFactory import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* +import io.javalin.community.ssl.SSLPlugin import io.javalin.http.staticfiles.Location -import io.javalin.plugin.openapi.OpenApiOptions -import io.javalin.plugin.openapi.OpenApiPlugin -import io.javalin.plugin.openapi.jackson.JacksonToJsonMapper -import io.javalin.plugin.openapi.ui.SwaggerOptions -import io.swagger.v3.oas.models.info.Info -import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory +import io.javalin.openapi.plugin.OpenApiConfiguration +import io.javalin.openapi.plugin.OpenApiPlugin +import io.javalin.openapi.plugin.swagger.SwaggerConfiguration +import io.javalin.openapi.plugin.swagger.SwaggerPlugin import org.eclipse.jetty.http.HttpCookie -import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory import org.eclipse.jetty.server.* import org.eclipse.jetty.server.session.DefaultSessionCache import org.eclipse.jetty.server.session.FileSessionDataStore import org.eclipse.jetty.server.session.SessionHandler -import org.eclipse.jetty.util.ssl.SslContextFactory import org.eclipse.jetty.util.thread.QueuedThreadPool import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory @@ -37,7 +33,7 @@ object RestApi { private val logMarker = MarkerFactory.getMarker("REST") private val logger = LoggerFactory.getLogger(this.javaClass) - private val mapper = JacksonToJsonMapper.defaultObjectMapper.enable(SerializationFeature.INDENT_OUTPUT) + //private val mapper = JacksonToJsonMapper.defaultObjectMapper.enable(SerializationFeature.INDENT_OUTPUT) fun getOpenApiPlugin(): OpenApiPlugin { return openApiPlugin @@ -194,24 +190,66 @@ object RestApi { ) javalin = Javalin.create { - it.enableCorsForAllOrigins() - it.server { setupHttpServer(config) } - it.registerPlugin( + it.plugins.enableCors{ cors -> + cors.add{ corsPluginConfig -> + corsPluginConfig.anyHost() + } + } + //it.jetty.server { setupHttpServer(config) } +// it.registerPlugin( +// OpenApiPlugin( +// /* "Internal" DRES openapi (/swagger-ui) */ +// getOpenApiOptionsFor(), +// +// ), +// OpenApiPlugin( +// /* "Public" client endpoint (/swagger-client */ +// getOpenApiOptionsFor(OpenApiEndpointOptions.dresSubmittingClientOptions) +// ) +// ) + + it.plugins.register( OpenApiPlugin( - /* "Internal" DRES openapi (/swagger-ui) */ - getOpenApiOptionsFor(), - /* "Public" client endpoint (/swagger-client */ - getOpenApiOptionsFor(OpenApiEndpointOptions.dresSubmittingClientOptions) + OpenApiConfiguration().apply { + this.info.title = "DRES API" + this.info.version = "1.0" + this.info.description = "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0" + this.documentationPath = "/swagger-docs" + } ) ) - it.defaultContentType = "application/json" - it.prefer405over404 = true - it.sessionHandler { fileSessionHandler(config) } + + it.plugins.register( + SwaggerPlugin( + SwaggerConfiguration().apply { + this.documentationPath = "/swagger-docs" + this.uiPath = "/swagger-ui" + } + ) + ) + + //FIXME client openAPI + + it.http.defaultContentType = "application/json" + it.http.prefer405over404 = true + it.jetty.sessionHandler { fileSessionHandler(config) } it.accessManager(AccessManager::manage) - it.addStaticFiles("html", Location.CLASSPATH) - it.addSinglePageRoot("/vote", "vote/index.html") - it.addSinglePageRoot("/", "html/index.html") - it.enforceSsl = config.enableSsl + it.staticFiles.add("html", Location.CLASSPATH) + it.spaRoot.addFile("/vote", "vote/index.html") + it.spaRoot.addFile("/", "html/index.html") + + if (config.enableSsl) { + val ssl = SSLPlugin { conf -> + conf.keystoreFromPath(config.keystorePath, config.keystorePassword) + conf.http2 = true + conf.secure = true + conf.insecurePort = config.httpPort + conf.securePort = config.httpsPort + conf.sniHostCheck = false + } + it.plugins.register(ssl) + } + }.routes { path("api") { apiRestHandlers.groupBy { it.apiVersion }.forEach { apiGroup -> @@ -250,9 +288,9 @@ object RestApi { }.before { logger.info( logMarker, - "${it.req.method} request to ${it.path()} with params (${ + "${it.req().method} request to ${it.path()} with params (${ it.queryParamMap().map { e -> "${e.key}=${e.value}" }.joinToString() - }) from ${it.req.remoteAddr}" + }) from ${it.req().remoteAddr}" ) if (it.path().startsWith("/api/")) { //do not cache api requests it.header("Cache-Control", "no-store") @@ -272,20 +310,21 @@ object RestApi { } - private fun getOpenApiOptionsFor(options: OpenApiEndpointOptions = OpenApiEndpointOptions.dresDefaultOptions) = - OpenApiOptions( - Info().apply { - title("DRES API") - version("1.0") - description("API for DRES (Distributed Retrieval Evaluation Server), Version 1.0") - } - ).apply { - path(options.oasPath) // endpoint for OpenAPI json - swagger(SwaggerOptions(options.swaggerUi)) // endpoint for swagger-ui - activateAnnotationScanningFor("dev.dres.api.rest.handler") - options.ignored().forEach { it.second.forEach { method -> ignorePath(it.first, method) } } - toJsonMapper(JacksonToJsonMapper(jacksonMapper)) - } + +// private fun getOpenApiOptionsFor(options: OpenApiEndpointOptions = OpenApiEndpointOptions.dresDefaultOptions) = +// OpenApiOptions( +// Info().apply { +// title("DRES API") +// version("1.0") +// description("API for DRES (Distributed Retrieval Evaluation Server), Version 1.0") +// } +// ).apply { +// path(options.oasPath) // endpoint for OpenAPI json +// swagger(SwaggerOptions(options.swaggerUi)) // endpoint for swagger-ui +// activateAnnotationScanningFor("dev.dres.api.rest.handler") +// options.ignored().forEach { it.second.forEach { method -> ignorePath(it.first, method) } } +// toJsonMapper(JacksonToJsonMapper(jacksonMapper)) +// } private fun fileSessionHandler(config: Config) = SessionHandler().apply { @@ -312,68 +351,70 @@ object RestApi { get() = pool.readyThreads private fun setupHttpServer(config: Config): Server { - - val httpConfig = HttpConfiguration().apply { - sendServerVersion = false - sendXPoweredBy = false - if (config.enableSsl) { - secureScheme = "https" - securePort = config.httpsPort - } - } - - - - if (config.enableSsl) { - val httpsConfig = HttpConfiguration(httpConfig).apply { - addCustomizer(SecureRequestCustomizer()) - } - - val alpn = ALPNServerConnectionFactory().apply { - defaultProtocol = "http/1.1" - } - - val sslContextFactory = SslContextFactory.Server().apply { - keyStorePath = config.keystorePath - setKeyStorePassword(config.keystorePassword) - //cipherComparator = HTTP2Cipher.COMPARATOR - provider = "Conscrypt" - } - - val ssl = SslConnectionFactory(sslContextFactory, alpn.protocol) - - val http2 = HTTP2ServerConnectionFactory(httpsConfig) - - val fallback = HttpConnectionFactory(httpsConfig) - - return Server(pool).apply { - //HTTP Connector - addConnector( - ServerConnector( - server, - HttpConnectionFactory(httpConfig), - HTTP2ServerConnectionFactory(httpConfig) - ).apply { - port = config.httpPort - }) - // HTTPS Connector - addConnector(ServerConnector(server, ssl, alpn, http2, fallback).apply { - port = config.httpsPort - }) - } - } else { - return Server(pool).apply { - //HTTP Connector - addConnector( - ServerConnector( - server, - HttpConnectionFactory(httpConfig), - HTTP2ServerConnectionFactory(httpConfig) - ).apply { - port = config.httpPort - }) - - } - } + return Server(pool) } +// +// val httpConfig = HttpConfiguration().apply { +// sendServerVersion = false +// sendXPoweredBy = false +// if (config.enableSsl) { +// secureScheme = "https" +// securePort = config.httpsPort +// } +// } +// +// +// +// if (config.enableSsl) { +// val httpsConfig = HttpConfiguration(httpConfig).apply { +// addCustomizer(SecureRequestCustomizer()) +// } +// +// val alpn = ALPNServerConnectionFactory().apply { +// defaultProtocol = "http/1.1" +// } +// +// val sslContextFactory = SslContextFactory.Server().apply { +// keyStorePath = config.keystorePath +// setKeyStorePassword(config.keystorePassword) +// //cipherComparator = HTTP2Cipher.COMPARATOR +// provider = "Conscrypt" +// } +// +// val ssl = SslConnectionFactory(sslContextFactory, alpn.protocol) +// +// val http2 = HTTP2ServerConnectionFactory(httpsConfig) +// +// val fallback = HttpConnectionFactory(httpsConfig) +// +// return Server(pool).apply { +// //HTTP Connector +// addConnector( +// ServerConnector( +// server, +// HttpConnectionFactory(httpConfig), +// HTTP2ServerConnectionFactory(httpConfig) +// ).apply { +// port = config.httpPort +// }) +// // HTTPS Connector +// addConnector(ServerConnector(server, ssl, alpn, http2, fallback).apply { +// port = config.httpsPort +// }) +// } +// } else { +// return Server(pool).apply { +// //HTTP Connector +// addConnector( +// ServerConnector( +// server, +// HttpConnectionFactory(httpConfig), +// HTTP2ServerConnectionFactory(httpConfig) +// ).apply { +// port = config.httpPort +// }) +// +// } +// } +// } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt index 986782501..c48d6656c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt @@ -20,10 +20,7 @@ import dev.dres.utilities.extensions.errorResponse import dev.dres.utilities.extensions.sendFile import dev.dres.utilities.extensions.streamFile import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* import java.io.File import java.io.FileNotFoundException import java.nio.file.Files @@ -144,7 +141,8 @@ class MediaPreviewHandler( "200", [OpenApiContent(type = "image/png")] ), OpenApiResponse("401"), OpenApiResponse("400")], - ignore = true + ignore = true, + methods = [HttpMethod.GET] ) override fun get(ctx: Context) { @@ -190,7 +188,8 @@ class SubmissionPreviewHandler( "200", [OpenApiContent(type = "image/png")] ), OpenApiResponse("401"), OpenApiResponse("400")], - ignore = true + ignore = true, + methods = [HttpMethod.GET] ) override fun get(ctx: Context) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt index 80ab42698..09a4e068a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt @@ -8,12 +8,9 @@ import dev.dres.data.dbo.DAO import dev.dres.data.dbo.NumericDaoIndexer import dev.dres.run.audit.AuditLogEntry import dev.dres.utilities.extensions.toPathParamKey -import io.javalin.core.security.RouteRole import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* +import io.javalin.security.RouteRole abstract class AuditLogHandler(val auditTimes: NumericDaoIndexer) : RestHandler, AccessManagedRestHandler { override val permittedRoles: Set = setOf(RestApiRole.ADMIN) @@ -32,7 +29,8 @@ class GetAuditLogInfoHandler(auditTimes: NumericDaoIndexer) responses = [ OpenApiResponse("200", [OpenApiContent(AuditLogInfo::class)], description = "The audit log info"), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): AuditLogInfo { return AuditLogInfo(size = auditTimes.index.size, latest = auditTimes.index.keys.maxOrNull() ?: 0L) @@ -55,7 +53,8 @@ class ListAuditLogsInRangeHandler(auditTimes: NumericDaoIndexer::class)], description = "The audit logs"), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): Array { var settings = 0 @@ -129,7 +128,8 @@ class ListAuditLogsHandler(auditTimes: NumericDaoIndexer, v // OpenApiResponse("200", [OpenApiContent(AuditLogPage::class)], description = "The audit logs"), OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): Array { val limit = getLimitFromParams(ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt index 49f50c225..3c61dbe1c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt @@ -20,9 +20,10 @@ import dev.dres.run.NonInteractiveRunManager import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import io.javalin.security.RouteRole abstract class BatchSubmissionHandler(internal val collections: DAO, internal val itemIndex: DaoIndexer>, internal val segmentIndex: DaoIndexer) : PostRestHandler, AccessManagedRestHandler { @@ -48,7 +49,7 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D @OpenApi(summary = "Endpoint to accept batch submissions in JSON format", path = "/api/v1/submit/{runId}", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], requestBody = OpenApiRequestBody([OpenApiContent(RunResult::class)]), tags = ["Batch Submission"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt index 45632d8a5..abaeb3cca 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt @@ -16,10 +16,10 @@ import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.basics.media.MediaItem import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.cleanPathString -import io.javalin.core.security.RouteRole import io.javalin.http.BadRequestResponse import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.openapi.* +import io.javalin.security.RouteRole import java.nio.file.FileVisitOption import java.nio.file.Files import java.nio.file.Paths @@ -58,6 +58,7 @@ class ListCollectionHandler(collections: DAO, items: DAO::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) @@ -76,7 +77,7 @@ class AddCollectionHandler(collections: DAO, items: DAO, items: DAO, items: DAO, items: DAO, items: DAO, items: DAO, items: DAO, items: DAO> { @OpenApi( summary = "Lists media items from a given media collection whose name start with the given string.", - path = "/api/v1/collection/{collectionId}/{startsWith}", method = HttpMethod.GET, + path = "/api/v1/collection/{collectionId}/{startsWith}", + methods = [HttpMethod.GET], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID"), OpenApiParam("startsWith", String::class, "Name starts with", required = false) @@ -475,7 +483,8 @@ class RandomMediaItemHandler( @OpenApi( summary = "Selects and returns a random media item from a given media collection.", - path = "/api/v1/collection/{collectionId}/random", method = HttpMethod.GET, + path = "/api/v1/collection/{collectionId}/random", + methods = [HttpMethod.GET], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID") ], @@ -516,7 +525,8 @@ class ListExternalItemHandler(config: Config) : GetRestHandler> { @OpenApi( summary = "Lists items from the external media collection whose name start with the given string.", - path = "/api/v1/external/", method = HttpMethod.GET, + path = "/api/v1/external/", + methods = [HttpMethod.GET], pathParams = [ OpenApiParam("startsWith", String::class, "Name starts with.", required = false) ], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt index 288e39eb4..16ad10b06 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt @@ -16,10 +16,10 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.Team import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.errorResponse -import io.javalin.core.security.RouteRole import io.javalin.http.BadRequestResponse import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.openapi.* +import io.javalin.security.RouteRole import java.io.IOException import java.nio.file.Files @@ -66,7 +66,8 @@ class GetTeamLogoHandler(val config: Config) : AbstractCompetitionRunRestHandler tags = ["Competition Run", "Media"], pathParams = [OpenApiParam("logoId", String::class, "The ID of the logo.")], responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], - ignore = true + ignore = true, + methods = [HttpMethod.GET] ) override fun get(ctx: Context) { @@ -107,7 +108,8 @@ class ListCompetitionHandler(competitions: DAO) : Compet responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = competitions.map { CompetitionOverview.of(it) } @@ -126,7 +128,8 @@ class GetCompetitionHandler(competitions: DAO) : Competi OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = RestCompetitionDescription.fromCompetition(competitionFromContext(ctx)) @@ -147,7 +150,8 @@ class ListTeamHandler(competitions: DAO) : CompetitionHa OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = competitionFromContext(ctx).teams.map { RestTeam(it) } @@ -171,7 +175,8 @@ class ListDetailedTeamHandler(competitions: DAO) : Compe OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = competitionFromContext(ctx).teams.map{ RestDetailedTeam.of(it) } @@ -191,7 +196,8 @@ class ListTaskHandler(competitions: DAO) : CompetitionHa OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = competitionFromContext(ctx).tasks.map { RestTaskDescription.fromTask(it) } @@ -201,7 +207,8 @@ class ListTaskHandler(competitions: DAO) : CompetitionHa class CreateCompetitionHandler(competitions: DAO) : CompetitionHandler(competitions), PostRestHandler { @OpenApi( summary = "Creates a new competition.", - path = "/api/v1/competition", method = HttpMethod.POST, + path = "/api/v1/competition", + methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(CompetitionCreate::class)]), tags = ["Competition"], responses = [ @@ -229,7 +236,8 @@ class CreateCompetitionHandler(competitions: DAO) : Comp class UpdateCompetitionHandler(competitions: DAO, val config: Config, val mediaItems: DAO) : CompetitionHandler(competitions), PatchRestHandler { @OpenApi( summary = "Updates an existing competition.", - path = "/api/v1/competition", method = HttpMethod.PATCH, + path = "/api/v1/competition", + methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(RestCompetitionDescription::class)]), tags = ["Competition"], responses = [ @@ -269,7 +277,8 @@ class UpdateCompetitionHandler(competitions: DAO, val co class DeleteCompetitionHandler(competitions: DAO) : CompetitionHandler(competitions), DeleteRestHandler { @OpenApi( summary = "Deletes the competition with the given competition ID.", - path = "/api/v1/competition/{competitionId}", method = HttpMethod.DELETE, + path = "/api/v1/competition/{competitionId}", + methods = [HttpMethod.DELETE], pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], tags = ["Competition"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 850057834..65e12ba1e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -29,10 +29,11 @@ import dev.dres.run.audit.LogEventSource import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole import io.javalin.http.BadRequestResponse import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import io.javalin.security.RouteRole import org.slf4j.LoggerFactory import java.io.File @@ -111,7 +112,7 @@ class CreateCompetitionRunAdminHandler( @OpenApi( summary = "Creates a new competition run from an existing competition", path = "/api/v1/run/admin/create", - method = HttpMethod.POST, + methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(CompetitionStartMessage::class)]), tags = ["Competition Run Admin"], responses = [ @@ -123,7 +124,7 @@ class CreateCompetitionRunAdminHandler( override fun doPost(ctx: Context): SuccessStatus { val competitionStartMessage = try { - ctx.bodyAsClass() + ctx.bodyAsClass(CompetitionStartMessage::class.java) } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } @@ -205,7 +206,7 @@ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(s @OpenApi( summary = "Starts a competition run.", path = "/api/v1/run/admin/{runId}/start", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -247,7 +248,7 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle @OpenApi( summary = "Moves to and selects the next task. This is a method for admins.", path = "/api/v1/run/admin/{runId}/task/next", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -308,7 +309,7 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand @OpenApi( summary = "Moves to and selects the specified task. This is a method for admins.", path = "/api/v1/run/admin/{runId}/task/switch/{idx}", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [ OpenApiParam("runId", String::class, "Competition run ID"), OpenApiParam("idx", Int::class, "Index of the task to switch to.") @@ -367,7 +368,7 @@ class PreviousTaskCompetitionRunAdminHandler : @OpenApi( summary = "Moves to and selects the previous task. This is a method for admins.", path = "/api/v1/run/admin/{runId}/task/previous", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -418,7 +419,7 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl @OpenApi( summary = "Starts the currently active task as a new task run. This is a method for admins.", path = "/api/v1/run/admin/{runId}/task/start", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -466,7 +467,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl @OpenApi( summary = "Aborts the currently running task run. This is a method for admins.", path = "/api/v1/run/admin/{runId}/task/abort", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -507,7 +508,7 @@ class TerminateCompetitionRunAdminHandler : @OpenApi( summary = "Terminates a competition run. This is a method for admins.", path = "/api/v1/run/admin/{runId}/terminate", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -547,7 +548,7 @@ class AdjustDurationRunAdminHandler : @OpenApi( summary = "Adjusts the duration of a running task run. This is a method for admins.", path = "/api/v1/run/admin/{runId}/adjust/{duration}", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID"), OpenApiParam("duration", Int::class, "Duration to add.") @@ -606,7 +607,7 @@ class ListPastTasksPerTaskRunAdminHandler : @OpenApi( summary = "Lists all past tasks for a given run", path = "/api/v1/run/admin/{runId}/task/past/list", - method = HttpMethod.GET, + methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID") ], @@ -649,7 +650,7 @@ class ListSubmissionsPerTaskRunAdminHandler : @OpenApi( summary = "Lists all submissions for a given task and run.", path = "/api/v1/run/admin/{runId}/submission/list/{taskId}", - method = HttpMethod.GET, + methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID"), OpenApiParam("taskId", String::class, "Task ID") @@ -709,7 +710,7 @@ class OverwriteSubmissionStatusRunAdminHandler : @OpenApi( summary = "Lists all submissions for a given task and run", path = "/api/v1/run/admin/{runId}/submission/override", - method = HttpMethod.PATCH, + methods = [HttpMethod.PATCH], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID") ], @@ -780,7 +781,7 @@ class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf( @OpenApi( summary = "Lists all registered viewers for a competition run. This is a method for admins.", path = "/api/v1/run/admin/{runId}/viewer/list", - method = HttpMethod.GET, + methods = [HttpMethod.GET], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], tags = ["Competition Run Admin"], responses = [ @@ -810,7 +811,7 @@ class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf( @OpenApi( summary = "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", path = "/api/v1/run/admin/{runId}/viewer/list/{viewerId}/force", - method = HttpMethod.POST, + methods = [HttpMethod.POST], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID"), OpenApiParam("viewerId", String::class, "Viewer ID") @@ -852,7 +853,7 @@ class OverviewRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(Res @OpenApi( summary = "Provides a complete overview of a run.", path = "/api/v1/run/admin/{runId}/overview", - method = HttpMethod.GET, + methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID"), ], @@ -882,7 +883,7 @@ class UpdateRunPropertiesAdminHandler : AbstractCompetitionRunAdminRestHandler(s @OpenApi( summary = "Changes the properties of a run", path = "/api/v1/run/admin/{runId}/properties", - method = HttpMethod.PATCH, + methods = [HttpMethod.PATCH], pathParams = [ OpenApiParam("runId", String::class, "Competition Run ID"), ], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt index 322c47126..3e23d50c2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt @@ -9,12 +9,9 @@ import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.* import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* abstract class AbstractCompetitionRunClientInfoHandler : RestHandler, AccessManagedRestHandler { @@ -76,7 +73,8 @@ class ListCompetitionRunClientInfoHandler : AbstractCompetitionRunClientInfoHand responses = [ OpenApiResponse("200", [OpenApiContent(ClientRunInfoList::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ClientRunInfoList = ClientRunInfoList(getRelevantManagers(ctx).map { ClientRunInfo(it) }) @@ -106,7 +104,8 @@ class CompetitionRunClientCurrentTaskInfoHandler : AbstractCompetitionRunClientI OpenApiResponse("200", [OpenApiContent(ClientTaskInfo::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ClientTaskInfo { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 7518c9d6b..19e0fa0bb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -22,12 +22,9 @@ import dev.dres.run.RunExecutor import dev.dres.run.TaskRunStatus import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* import java.io.FileNotFoundException import java.io.IOException @@ -98,7 +95,8 @@ class ListCompetitionRunInfosHandler : AbstractCompetitionRunRestHandler(), GetR responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List = getRelevantManagers(ctx).map { RunInfo(it) } } @@ -114,7 +112,8 @@ class ListCompetitionRunStatesHandler : AbstractCompetitionRunRestHandler(), Get responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List = getRelevantManagers(ctx).map { @@ -140,7 +139,8 @@ class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRes OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): RunInfo { val runId = runId(ctx) @@ -168,7 +168,8 @@ class GetCompetitionRunStateHandler : AbstractCompetitionRunRestHandler(), GetRe OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): RunState { val runId = runId(ctx) @@ -198,7 +199,8 @@ class CurrentTaskInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandl OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): TaskInfo { @@ -233,7 +235,8 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): TaskHint { val runId = runId(ctx) @@ -288,7 +291,8 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): TaskTarget { val runId = runId(ctx) @@ -344,7 +348,8 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { val runId = runId(ctx) @@ -410,7 +415,8 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { val runId = runId(ctx) @@ -452,7 +458,8 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { val runId = runId(ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 4938fda7f..2a77a0c4f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -13,12 +13,9 @@ import dev.dres.run.score.scoreboard.Score import dev.dres.run.score.scoreboard.ScoreOverview import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* /** * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing @@ -78,7 +75,8 @@ class ListCompetitionScoreHandler : AbstractScoreRestHandler(), GetRestHandler::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() @@ -107,7 +105,8 @@ class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler { val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() @@ -229,7 +230,8 @@ class ListScoreSeriesHandler : AbstractScoreRestHandler(), GetRestHandler { val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() @@ -263,7 +265,8 @@ class TeamGroupScoreHandler : AbstractScoreRestHandler(), GetRestHandler { val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt index da2cdc374..67877a72a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt @@ -9,12 +9,9 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.run.InteractiveAsynchronousCompetition import dev.dres.data.model.run.interfaces.Competition import dev.dres.utilities.extensions.UID -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* /** * A [AccessManagedRestHandler] implementation that provides certain data structures as downloadable files. @@ -50,7 +47,8 @@ sealed class DownloadHandler : AccessManagedRestHandler { OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): String { /* Obtain run id and run. */ @@ -85,7 +83,8 @@ sealed class DownloadHandler : AccessManagedRestHandler { OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): String { @@ -134,7 +133,8 @@ sealed class DownloadHandler : AccessManagedRestHandler { OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): String { /* Obtain run id and run. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt index 4754c94ed..35d715021 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt @@ -9,9 +9,10 @@ import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.errorResponse import dev.dres.utilities.extensions.streamFile import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiParam +import io.javalin.openapi.OpenApiResponse import java.io.File class GetMediaHandler(private val itemCache: DaoIndexer>, private val collectionCache : DaoIndexer) : GetRestHandler, AccessManagedRestHandler { @@ -31,7 +32,8 @@ class GetMediaHandler(private val itemCache: DaoIndexer ], tags = ["Media"], responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], - ignore = true + ignore = true, + methods = [HttpMethod.GET] ) override fun get(ctx: Context) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt index 15177bd2b..450f4bc02 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt @@ -21,10 +21,10 @@ import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.VoteValidator import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.BadRequestResponse import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.openapi.* abstract class AbstractJudgementHandler : RestHandler, AccessManagedRestHandler { override val permittedRoles: Set = setOf(RestApiRole.JUDGE) @@ -79,7 +79,8 @@ class NextOpenJudgementHandler(val collections: DAO) : Abstract OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): JudgementRequest { val runId = this.runId(ctx) @@ -124,7 +125,7 @@ class PostJudgementHandler : AbstractJudgementHandler(), PostRestHandler>, A OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { @@ -204,7 +206,7 @@ class JudgementVoteHandler : PostRestHandler { @OpenApi( summary = "Returns a Vote.", - path = "/api/v1/run/{runId}/judge/vote", method = HttpMethod.POST, + path = "/api/v1/run/{runId}/judge/vote", methods = [HttpMethod.POST], pathParams = [OpenApiParam("runId", String::class, "Run ID")], requestBody = OpenApiRequestBody([OpenApiContent(JudgementVote::class)]), tags = ["Judgement"], @@ -255,7 +257,8 @@ class NextOpenVoteJudgementHandler(val collections: DAO) : Abst OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): JudgementRequest { val runId = this.runId(ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt index 3e6ad8603..b821e3b20 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt @@ -15,10 +15,11 @@ import dev.dres.run.eventstream.InvalidRequestEvent import dev.dres.run.eventstream.QueryEventLogEvent import dev.dres.run.eventstream.QueryResultLogEvent import dev.dres.utilities.extensions.sessionId -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.BadRequestResponse import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* abstract class LogHandler : PostRestHandler, AccessManagedRestHandler { @@ -47,7 +48,7 @@ class QueryLogHandler : LogHandler() { @OpenApi(summary = "Accepts query logs from participants", path = "/api/v1/log/query", - method = HttpMethod.POST, + methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(QueryEventLog::class)]), tags = ["Log"], queryParams = [ @@ -84,7 +85,7 @@ class ResultLogHandler : LogHandler() { @OpenApi(summary = "Accepts result logs from participants", path = "/api/v1/log/result", - method = HttpMethod.POST, + methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(QueryResultLog::class)]), tags = ["Log"], queryParams = [ @@ -101,7 +102,7 @@ class ResultLogHandler : LogHandler() { val run = getActiveRun(userId, ctx) val queryResultLog = try { - ctx.bodyAsClass() + ctx.bodyAsClass(QueryResultLog::class.java) } catch (e: BadRequestResponse){ EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), run.id, ctx.body())) throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt index d2d6094a0..ded69b604 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt @@ -14,7 +14,7 @@ import dev.dres.run.audit.LogEventSource import dev.dres.utilities.extensions.sessionId import io.javalin.http.BadRequestResponse import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.openapi.* class LoginHandler(private val audit: DAO) : RestHandler, PostRestHandler { @@ -24,7 +24,7 @@ class LoginHandler(private val audit: DAO) : RestHandler, PostRes @OpenApi(summary = "Sets roles for session based on user account and returns a session cookie.", path = "/api/v1/login", - method = HttpMethod.POST, + methods = [HttpMethod.POST], tags = ["User"], requestBody = OpenApiRequestBody([OpenApiContent(LoginRequest::class)]), responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt index 14f3cf05e..a4ae6e3da 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt @@ -9,10 +9,7 @@ import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* class LogoutHandler(private val audit: DAO) : RestHandler, GetRestHandler { @@ -27,7 +24,8 @@ class LogoutHandler(private val audit: DAO) : RestHandler, GetRes responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessStatus { AccessManager.clearUserSession(ctx.sessionId()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt index 71626b680..00f18cf8d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.utilities.extensions.errorResponse -import io.javalin.core.security.RouteRole +import io.javalin.security.RouteRole import io.javalin.http.Context interface RestHandler { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt index e43de60d1..30b10fea5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.handler import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.HttpMethod -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse data class CurrentTime(val timeStamp: Long = System.currentTimeMillis()) @@ -15,7 +15,7 @@ class CurrentTimeHandler : GetRestHandler { @OpenApi(summary = "Returns the current time on the server.", path = "/api/v1/status/time", - method = HttpMethod.GET, + methods = [HttpMethod.GET], tags = ["Status"], responses = [ OpenApiResponse("200", [OpenApiContent(CurrentTime::class)]) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 148ac0887..97dfd2f8d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -31,10 +31,7 @@ import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.OpenApi -import io.javalin.plugin.openapi.annotations.OpenApiContent -import io.javalin.plugin.openapi.annotations.OpenApiParam -import io.javalin.plugin.openapi.annotations.OpenApiResponse +import io.javalin.openapi.* import org.slf4j.LoggerFactory import java.nio.file.Files import java.nio.file.Path @@ -174,7 +171,8 @@ class SubmissionHandler (val collections: DAO, private val item OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) @@ -195,7 +193,7 @@ class SubmissionHandler (val collections: DAO, private val item throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, run.currentTask(rac)?.uid, submission, LogEventSource.REST, ctx.sessionId(), ctx.req.remoteAddr) + AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, run.currentTask(rac)?.uid, submission, LogEventSource.REST, ctx.sessionId(), ctx.req().remoteAddr) if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { //pre-generate preview generatePreview(submission) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt index 3b4a4e818..6b436f32a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt @@ -14,7 +14,8 @@ import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId import dev.dres.utilities.extensions.toSessionId import io.javalin.http.Context -import io.javalin.plugin.openapi.annotations.* +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* data class SessionId(val sessionId: String) @@ -52,7 +53,7 @@ abstract class UserHandler : RestHandler { } protected fun getCreateUserFromBody(ctx: Context): UserRequest { - return ctx.bodyAsClass() + return ctx.bodyAsClass() } } @@ -63,7 +64,8 @@ class ListUsersHandler : UserHandler(), GetRestHandler>, Acces summary = "Lists all available users.", path = "/api/v1/user/list", tags = ["User"], - responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])] + responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = UserManager.list().map(UserDetails.Companion::of) @@ -87,7 +89,8 @@ class UserDetailsHandler : UserHandler(), GetRestHandler, AccessMan OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found"), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = UserDetails.of(getUserFromId(ctx)) @@ -100,7 +103,7 @@ class DeleteUsersHandler : UserHandler(), DeleteRestHandler, Access @OpenApi( summary = "Deletes the specified user. Requires ADMIN privileges", - path = "/api/v1/user/{userId}", method = HttpMethod.DELETE, + path = "/api/v1/user/{userId}", methods = [HttpMethod.DELETE], pathParams = [OpenApiParam("userId", Long::class, "User ID")], tags = ["User"], responses = [ @@ -128,7 +131,7 @@ class CreateUsersHandler : UserHandler(), PostRestHandler, AccessMa @OpenApi( summary = "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - path = "/api/v1/user", method = HttpMethod.POST, + path = "/api/v1/user", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], responses = [ @@ -156,7 +159,7 @@ class UpdateUsersHandler : UserHandler(), PatchRestHandler, AccessM @OpenApi( summary = "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", - path = "/api/v1/user/{userId}", method = HttpMethod.PATCH, + path = "/api/v1/user/{userId}", methods = [HttpMethod.PATCH], pathParams = [OpenApiParam("userId", String::class, "User ID")], requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], @@ -208,7 +211,8 @@ class CurrentUsersHandler : UserHandler(), GetRestHandler, AccessMa responses = [ OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): UserDetails { return UserDetails.create(getFromSessionOrDie(ctx), ctx) @@ -232,7 +236,8 @@ class CurrentUsersSessionIdHandler : UserHandler(), GetRestHandler, A responses = [ OpenApiResponse("200", [OpenApiContent(SessionId::class)]), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SessionId { return ctx.sessionId().toSessionId() @@ -257,7 +262,8 @@ class ActiveSessionsHandler(private val users: DAO) : GetRestHandler::class)]), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] + ], + methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt index a8cab20ca..e7913725a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt @@ -14,7 +14,8 @@ import java.nio.ByteBuffer * @author Ralph Gasser * @version 1.0 */ -inline class WebSocketConnection(val context: WsContext) { +@JvmInline +value class WebSocketConnection(val context: WsContext) { companion object { val jsonMapper = jacksonObjectMapper() @@ -26,7 +27,7 @@ inline class WebSocketConnection(val context: WsContext) { /** ID of the HTTP session that generated this [WebSocketConnection]. */ val httpSessionId - get() = (this.context.session.upgradeRequest.session as Session).id + get() = (this.context.session as Session).id /** Name of the user that generated this [WebSocketConnection]. */ val userName @@ -37,9 +38,9 @@ inline class WebSocketConnection(val context: WsContext) { get() { val xff = this.context.header("X-Forwarded-For") return if (xff != null) { - "$xff via ${this.context.session.remoteAddress.hostString}" + "$xff via ${this.context.host()}" } else { - this.context.session.remoteAddress.hostString + this.context.host() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt index b95d3acbd..be4ed2605 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt @@ -7,8 +7,8 @@ import dev.dres.utilities.extensions.UID import java.awt.image.BufferedImage import java.io.ByteArrayInputStream import java.nio.file.Files +import java.util.* import javax.imageio.ImageIO -import javax.xml.bind.DatatypeConverter data class RestTeam(val uid: String? = null, @@ -41,7 +41,7 @@ data class RestTeam(val uid: String? = null, fun storeLogo(config: Config, data: String, logoId: UID = UID()): UID { /* Parse image data. */ val base64Image: String = data.substringAfter(",") - val imageBytes = DatatypeConverter.parseBase64Binary(base64Image) + val imageBytes = Base64.getDecoder().decode(base64Image) val image = ByteArrayInputStream(imageBytes).use { val original = ImageIO.read(it) if (original.width <= config.logoMaxSize && original.height <= config.logoMaxSize) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt index 8a87f20c0..569c70add 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt @@ -11,7 +11,7 @@ data class ErrorStatusException(val statusCode: Int, val status: String, private init { if(!doNotLog){ - logger.info("ErrorStatusException with code $statusCode and message '$status' thrown by ${stackTrace.first()} for request from ${ctx.req.remoteAddr}") + logger.info("ErrorStatusException with code $statusCode and message '$status' thrown by ${stackTrace.first()} for request from ${ctx.req().remoteAddr}") } } diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 2e3e40054..0caa5f753 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -26,7 +26,7 @@ fun Context.streamFile(file: File) { } val mimeType = MimeTypeHelper.mimeType(file) this.contentType(mimeType) //needs to be set, probably a bug in Javalin - this.seekableStream(file.inputStream(), mimeType) + this.writeSeekableStream(file.inputStream(), mimeType) } fun Context.streamFile(path: Path) { @@ -36,7 +36,7 @@ fun Context.streamFile(path: Path) { } val mimeType = MimeTypeHelper.mimeType(path.toFile()) this.contentType(mimeType) - this.seekableStream(Files.newInputStream(path, StandardOpenOption.READ), mimeType) + this.writeSeekableStream(Files.newInputStream(path, StandardOpenOption.READ), mimeType) } fun Context.sendFile(file: File) { @@ -49,4 +49,4 @@ fun Context.sendFile(file: File) { this.result(file.inputStream()) } -fun Context.sessionId(): String = this.queryParam("session") ?: this.req.session.id \ No newline at end of file +fun Context.sessionId(): String = this.queryParam("session") ?: this.req().session.id \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e0..ae04661ee 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 0e01405d632908e7663f4413cea2964a3fa3ee79 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 21 Oct 2022 17:15:26 +0200 Subject: [PATCH 002/498] Added client OpenAPI endpoints --- backend/build.gradle | 5 +- .../dev/dres/api/rest/ClientOpenApiPlugin.kt | 4 ++ .../dev/dres/api/rest/ClientSwaggerPlugin.kt | 4 ++ .../dres/api/rest/OpenApiEndpointOptions.kt | 68 ------------------- 4 files changed, 11 insertions(+), 70 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt diff --git a/backend/build.gradle b/backend/build.gradle index d76b06cc6..b34742641 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -31,6 +31,7 @@ configurations { repositories { mavenCentral() + maven { url 'https://maven.reposilite.com/snapshots' } //javalin openapi snapshot } compileKotlin { @@ -60,8 +61,8 @@ dependencies { implementation group: 'io.javalin', name: 'javalin', version: "$javalin" kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin") - implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "$javalin" - implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "$javalin" + implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "5.1.2-SNAPSHOT" + implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "5.1.2-SNAPSHOT" implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: "$javalin" implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.0' diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt new file mode 100644 index 000000000..42b96776d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt @@ -0,0 +1,4 @@ +package dev.dres.api.rest + +class ClientOpenApiPlugin { +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt new file mode 100644 index 000000000..08bac6368 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt @@ -0,0 +1,4 @@ +package dev.dres.api.rest + +class ClientSwaggerPlugin { +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt b/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt deleted file mode 100644 index c4feabd49..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/OpenApiEndpointOptions.kt +++ /dev/null @@ -1,68 +0,0 @@ -package dev.dres.api.rest - -import io.javalin.openapi.HttpMethod - - -/** - * Options to configure an OpenApi Specifications endpoint in use with Javalin OpenApi Plugin. - */ -data class OpenApiEndpointOptions( - /** - * The path of the OpenApi Specifications JSON - */ - val oasPath: String, - /** - * The path of the swagger UI - */ - val swaggerUi: String, - /** - * A list of paths to ignore. Defaults to an empty list - */ - private val ignores: List = listOf() -) { - - fun ignored(): List>> { - return _ignored - } - - private val _ignored = ignores.map { - /* Small routine to distinguish between "internal" (prefixed with /api/) and "public" endpoints */ - if(!it.startsWith("#")){ - "/api/v1$it" to HttpMethod.values().map { it } //FIXME deal with version number - }else{ - it.substring(it.indexOf("#")+1) to HttpMethod.values().map { it } - } - }.toMutableList() - - fun withIgnores(ignores: List>>) : OpenApiEndpointOptions { - this._ignored.addAll(ignores) - return this - } - - companion object { - val commonIgnores = mutableListOf( - "/external/*", - "/collection*", "/collection/*", - "/competition*", "/competition/*", - "/run*", "/run/*", - "/audit*", "/audit/*", - "/mediaItem*", "/mediaItem/*", - "/score*", "/score/*" - ) - - val dresDefaultOptions = OpenApiEndpointOptions("/swagger-docs", "/swagger-ui") - - val dresSubmittingClientOptions = OpenApiEndpointOptions( - "/client-oas", - "/swagger-client", - ignores= commonIgnores + - listOf( - "/user/list", - "/user/session/*" - ) - ).withIgnores(listOf( - "/api/v1/user" to HttpMethod.values().map { it }.filter { it.ordinal != HttpMethod.GET.ordinal }, - "/api/v1/user/{userId}" to HttpMethod.values().map{it} - )) - } -} From 7d4c984143832308a518d24e5a04804e9a024915 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 21 Oct 2022 19:35:12 +0200 Subject: [PATCH 003/498] Finishing up OpenAPI migration --- .../kotlin/dev/dres/api/cli/OpenApiCommand.kt | 11 +- .../dev/dres/api/rest/ClientOpenApiPlugin.kt | 32 ++++- .../dev/dres/api/rest/ClientSwaggerPlugin.kt | 10 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 118 ++---------------- 4 files changed, 55 insertions(+), 116 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt index 1fef7c160..2795f5dec 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/OpenApiCommand.kt @@ -6,7 +6,6 @@ import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.file import com.github.kittinunf.fuel.Fuel -import dev.dres.api.rest.OpenApiEndpointOptions import java.io.File @@ -22,15 +21,15 @@ class OpenApiCommand : CliktCommand(name="openapi",help = "Generates and writes override fun run() { // Do we download the client lib as well? if(!internalOnly){ - downloadOas(OpenApiEndpointOptions.dresSubmittingClientOptions, output) + downloadOas("swagger-docs", output) } - downloadOas(OpenApiEndpointOptions.dresDefaultOptions, output) + downloadOas("client-oas", output) } - private fun downloadOas(oas: OpenApiEndpointOptions, dir: File){ - val src = "http://localhost:8080"+oas.oasPath + private fun downloadOas(path: String, dir: File){ + val src = "http://localhost:8080/$path" vprintln("Downloading from $src") - val fileName = oas.oasPath.substring(1)+".json" // Remove "/" from name and add ".json" + val fileName = "${path}.json" Fuel.download(src) .fileDestination { _, _ -> dir.resolve(fileName) } .progress{readBytes, totalBytes -> diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt index 42b96776d..9f54fc324 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt @@ -1,4 +1,34 @@ package dev.dres.api.rest -class ClientOpenApiPlugin { +import com.fasterxml.jackson.databind.node.ObjectNode +import io.javalin.openapi.plugin.OpenApiConfiguration +import io.javalin.openapi.plugin.OpenApiPlugin + +class ClientOpenApiPlugin : OpenApiPlugin(OpenApiConfiguration().apply { + this.info.title = "DRES API (client)" + this.info.version = "1.0" + this.info.description = "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0" + this.documentationPath = "/client-oas" + this.documentProcessor = {doc -> + + val blacklist = setOf( + "/external/", + "/collection", + "/competition", + "/run", + "/audit", + "/mediaItem", + "/score", + "/user/list", + "/user/session/" + ) + + val relevantRoutes = doc["paths"].fields().asSequence().filter { blacklist.none { b -> it.key.contains(b) } }.map { it.key }.toList() + + (doc["paths"] as ObjectNode).retain(relevantRoutes) + + doc.toPrettyString() + + } +}) { } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt index 08bac6368..3ecb4df37 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt @@ -1,4 +1,10 @@ package dev.dres.api.rest -class ClientSwaggerPlugin { -} \ No newline at end of file +import io.javalin.openapi.plugin.swagger.SwaggerConfiguration +import io.javalin.openapi.plugin.swagger.SwaggerPlugin + +class ClientSwaggerPlugin : SwaggerPlugin( + SwaggerConfiguration().apply { + this.documentationPath = "/client-oas" + this.uiPath = "/swagger-client" + }) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index af0428666..cede2585f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -28,17 +28,9 @@ object RestApi { private var javalin: Javalin? = null - private lateinit var openApiPlugin: OpenApiPlugin - private val logMarker = MarkerFactory.getMarker("REST") private val logger = LoggerFactory.getLogger(this.javaClass) - //private val mapper = JacksonToJsonMapper.defaultObjectMapper.enable(SerializationFeature.INDENT_OUTPUT) - - fun getOpenApiPlugin(): OpenApiPlugin { - return openApiPlugin - } - fun init(config: Config, dataAccessLayer: DataAccessLayer) { val runExecutor = RunExecutor @@ -190,23 +182,11 @@ object RestApi { ) javalin = Javalin.create { - it.plugins.enableCors{ cors -> - cors.add{ corsPluginConfig -> + it.plugins.enableCors { cors -> + cors.add { corsPluginConfig -> corsPluginConfig.anyHost() } } - //it.jetty.server { setupHttpServer(config) } -// it.registerPlugin( -// OpenApiPlugin( -// /* "Internal" DRES openapi (/swagger-ui) */ -// getOpenApiOptionsFor(), -// -// ), -// OpenApiPlugin( -// /* "Public" client endpoint (/swagger-client */ -// getOpenApiOptionsFor(OpenApiEndpointOptions.dresSubmittingClientOptions) -// ) -// ) it.plugins.register( OpenApiPlugin( @@ -219,6 +199,10 @@ object RestApi { ) ) + it.plugins.register( + ClientOpenApiPlugin() + ) + it.plugins.register( SwaggerPlugin( SwaggerConfiguration().apply { @@ -228,10 +212,11 @@ object RestApi { ) ) - //FIXME client openAPI + it.plugins.register(ClientSwaggerPlugin()) it.http.defaultContentType = "application/json" it.http.prefer405over404 = true + it.jetty.server { setupHttpServer() } it.jetty.sessionHandler { fileSessionHandler(config) } it.accessManager(AccessManager::manage) it.staticFiles.add("html", Location.CLASSPATH) @@ -298,7 +283,7 @@ object RestApi { }.error(401) { it.json(ErrorStatus("Unauthorized request!")) }.exception(Exception::class.java) { e, ctx -> - ctx.status(500).json(ErrorStatus("Internal server error!")) + ctx.status(500).json(ErrorStatus("Internal server error: ${e.localizedMessage}")) logger.error("Exception during handling of request to ${ctx.path()}", e) } .start(config.httpPort) @@ -309,24 +294,6 @@ object RestApi { javalin = null } - - -// private fun getOpenApiOptionsFor(options: OpenApiEndpointOptions = OpenApiEndpointOptions.dresDefaultOptions) = -// OpenApiOptions( -// Info().apply { -// title("DRES API") -// version("1.0") -// description("API for DRES (Distributed Retrieval Evaluation Server), Version 1.0") -// } -// ).apply { -// path(options.oasPath) // endpoint for OpenAPI json -// swagger(SwaggerOptions(options.swaggerUi)) // endpoint for swagger-ui -// activateAnnotationScanningFor("dev.dres.api.rest.handler") -// options.ignored().forEach { it.second.forEach { method -> ignorePath(it.first, method) } } -// toJsonMapper(JacksonToJsonMapper(jacksonMapper)) -// } - - private fun fileSessionHandler(config: Config) = SessionHandler().apply { sessionCache = DefaultSessionCache(this).apply { sessionDataStore = FileSessionDataStore().apply { @@ -350,71 +317,8 @@ object RestApi { val readyThreadCount: Int get() = pool.readyThreads - private fun setupHttpServer(config: Config): Server { + private fun setupHttpServer(): Server { return Server(pool) } -// -// val httpConfig = HttpConfiguration().apply { -// sendServerVersion = false -// sendXPoweredBy = false -// if (config.enableSsl) { -// secureScheme = "https" -// securePort = config.httpsPort -// } -// } -// -// -// -// if (config.enableSsl) { -// val httpsConfig = HttpConfiguration(httpConfig).apply { -// addCustomizer(SecureRequestCustomizer()) -// } -// -// val alpn = ALPNServerConnectionFactory().apply { -// defaultProtocol = "http/1.1" -// } -// -// val sslContextFactory = SslContextFactory.Server().apply { -// keyStorePath = config.keystorePath -// setKeyStorePassword(config.keystorePassword) -// //cipherComparator = HTTP2Cipher.COMPARATOR -// provider = "Conscrypt" -// } -// -// val ssl = SslConnectionFactory(sslContextFactory, alpn.protocol) -// -// val http2 = HTTP2ServerConnectionFactory(httpsConfig) -// -// val fallback = HttpConnectionFactory(httpsConfig) -// -// return Server(pool).apply { -// //HTTP Connector -// addConnector( -// ServerConnector( -// server, -// HttpConnectionFactory(httpConfig), -// HTTP2ServerConnectionFactory(httpConfig) -// ).apply { -// port = config.httpPort -// }) -// // HTTPS Connector -// addConnector(ServerConnector(server, ssl, alpn, http2, fallback).apply { -// port = config.httpsPort -// }) -// } -// } else { -// return Server(pool).apply { -// //HTTP Connector -// addConnector( -// ServerConnector( -// server, -// HttpConnectionFactory(httpConfig), -// HTTP2ServerConnectionFactory(httpConfig) -// ).apply { -// port = config.httpPort -// }) -// -// } -// } -// } + } From e6f704581a65e0b129136ba2a6ae07036447c903 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 25 Oct 2022 15:40:14 +0200 Subject: [PATCH 004/498] Dependency updates --- backend/build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index b34742641..673aec1c6 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -45,8 +45,8 @@ compileTestKotlin { } dependencies { - def javalin = '5.1.1' - def log4jVersion = '2.17.0' + def javalin = '5.1.2' + def log4jVersion = '2.17.1' ///// Frontend files (produced by sub-project). implementation frontendClasspath(project(path: ":frontend", configuration: 'frontendFiles')) @@ -58,14 +58,14 @@ dependencies { implementation group: 'org.mapdb', name: 'mapdb', version: '3.0.8' ////// Javalin - implementation group: 'io.javalin', name: 'javalin', version: "$javalin" + implementation group: 'io.javalin', name: 'javalin', version: javalin kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin") implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "5.1.2-SNAPSHOT" implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "5.1.2-SNAPSHOT" - implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: "$javalin" + implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: javalin - implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.0' + implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.4' From 82325b16023089043f2263e971331867ed820669 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 25 Oct 2022 15:49:04 +0200 Subject: [PATCH 005/498] Disabled secondary swagger UI, needs different solution --- .../src/main/kotlin/dev/dres/api/rest/RestApi.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 493011a4a..778324dea 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -205,14 +205,14 @@ object RestApi { ClientOpenApiPlugin() ) - it.plugins.register( - SwaggerPlugin( - SwaggerConfiguration().apply { - this.documentationPath = "/swagger-docs" - this.uiPath = "/swagger-ui" - } - ) - ) +// it.plugins.register( +// SwaggerPlugin( +// SwaggerConfiguration().apply { +// this.documentationPath = "/swagger-docs" +// this.uiPath = "/swagger-ui" +// } +// ) +// ) it.plugins.register(ClientSwaggerPlugin()) From e607b0ca432596d26b22ba07934b8543069f1b0a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 25 Oct 2022 16:01:34 +0200 Subject: [PATCH 006/498] Re-generated OpenAPI spec there are some differences now, needs to be investigated --- backend/build.gradle | 2 +- build.gradle | 1 + doc/oas-client.json | 2041 +++++++++++++---- doc/oas.json | 5160 ++++++++++++++++++++---------------------- 4 files changed, 4166 insertions(+), 3038 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 673aec1c6..2b379cd9e 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -59,7 +59,7 @@ dependencies { ////// Javalin implementation group: 'io.javalin', name: 'javalin', version: javalin - kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin") + kapt("io.javalin.community.openapi:openapi-annotation-processor:5.1.2-SNAPSHOT") implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "5.1.2-SNAPSHOT" implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "5.1.2-SNAPSHOT" diff --git a/build.gradle b/build.gradle index 6929d027a..6660cc6dd 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ openApiGenerate { generateApiTests = false // No tests please generateModelTests = false // No tests please validateSpec = false // No validation please (as in command above) + skipValidateSpec = true generatorName = 'typescript-angular' inputSpec = oasFile diff --git a/doc/oas-client.json b/doc/oas-client.json index f7a244c5f..53b4010aa 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1,28 +1,52 @@ { - "openapi" : "3.0.1", + "openapi" : "3.0.3", "info" : { - "title" : "DRES API", + "title" : "DRES API (client)", + "summary" : null, "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", + "termsOfService" : null, + "contact" : null, + "license" : null, "version" : "1.0" }, "paths" : { - "/api/v1/login" : { + "/api/v1/status/info/admin" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an extensive overview of the server properties.", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresAdminInfo" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/user" : { "post" : { "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV1Login", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "parameters" : [ ], "requestBody" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/LoginRequest" + "$ref" : "#/components/schemas/UserRequest" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -32,7 +56,7 @@ } }, "400" : { - "description" : "Bad Request", + "description" : "If the username is already taken", "content" : { "application/json" : { "schema" : { @@ -41,8 +65,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "500" : { "content" : { "application/json" : { "schema" : { @@ -51,35 +74,25 @@ } } } - } - } - }, - "/api/v1/logout" : { + }, + "deprecated" : false, + "security" : [ ] + }, "get" : { "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV1Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "schema" : { - "type" : "string" - } - } ], + "summary" : "Get information about the current user.", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, - "400" : { - "description" : "Bad Request", + "500" : { "content" : { "application/json" : { "schema" : { @@ -88,123 +101,57 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/user" : { + "/api/v1/status/time" : { "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV1User", + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserDetails" - } - } - } - }, - "500" : { - "description" : "Server Error", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/CurrentTime" } } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/submit" : { + "/api/v1/user/session" : { "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV1Submit", + "tags" : [ "User" ], + "summary" : "Get current sessionId", "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g. video).", - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g. video).", - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g. video).", - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { "name" : "session", "in" : "query", "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + "$ref" : "#/components/schemas/SessionId" } } } }, - "400" : { - "description" : "Bad Request", + "500" : { "content" : { "application/json" : { "schema" : { @@ -212,19 +159,40 @@ } } } - }, - "401" : { - "description" : "Unauthorized", + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "number", + "format" : "int64" + } + } ], + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found", "content" : { "application/json" : { "schema" : { @@ -233,8 +201,7 @@ } } }, - "412" : { - "description" : "Precondition Failed", + "500" : { "content" : { "application/json" : { "schema" : { @@ -243,19 +210,20 @@ } } } - } - } - }, - "/api/v1/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV1LogQuery", + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } @@ -264,24 +232,23 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" + "$ref" : "#/components/schemas/UserRequest" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -290,8 +257,16 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { "content" : { "application/json" : { "schema" : { @@ -300,45 +275,36 @@ } } } - } - } - }, - "/api/v1/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants", - "operationId" : "postApiV1LogResult", + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - } - }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, - "400" : { - "description" : "Bad Request", + "404" : { + "description" : "If the user could not be found", "content" : { "application/json" : { "schema" : { @@ -347,8 +313,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "500" : { "content" : { "application/json" : { "schema" : { @@ -357,55 +322,94 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/status/time" : { + "/api/v1/status/info" : { "get" : { "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV1StatusTime", + "summary" : "Returns an overview of the server properties.", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/CurrentTime" + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/client/run/info/list" : { - "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Lists an overview of all competition runs visible to the current client", - "operationId" : "getApiV1ClientRunInfoList", + "/api/v1/submit/{runId}" : { + "post" : { + "tags" : [ "Batch Submission" ], + "summary" : "Endpoint to accept batch submissions in JSON format", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunResult" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ClientRunInfoList" + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -414,43 +418,37 @@ } } } - } - } - }, - "/api/v1/client/run/info/currentTask/{runId}" : { - "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Returns an overview of the currently active task for a run", - "operationId" : "getApiV1ClientRunInfoCurrenttaskWithRunid", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "schema" : { - "type" : "string" - } - } ], + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ClientTaskInfo" + "$ref" : "#/components/schemas/UserDetails" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -459,8 +457,7 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -469,36 +466,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/download/run/{runId}" : { + "/api/v1/logout" : { "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire competition run structure.", - "operationId" : "getApiV1DownloadRunWithRunid", + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition run ID", - "required" : true, + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -506,9 +504,48 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -517,8 +554,7 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -527,46 +563,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/download/run/{runId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given competition run.", - "operationId" : "getApiV1DownloadRunWithRunidScores", + "/api/v1/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition run ID", + "name" : "session", + "in" : "query", + "description" : "Session Token", "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/csv" : { - "schema" : { - "type" : "string" - } + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" } } }, - "400" : { - "description" : "Bad Request", + "required" : false + }, + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -575,8 +612,7 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -585,36 +621,108 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/download/competition/{competitionId}" : { + "/api/v1/submit" : { "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire competition description structure.", - "operationId" : "getApiV1DownloadCompetitionWithCompetitionid", + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", "parameters" : [ { - "name" : "competitionId", - "in" : "path", - "description" : "Competition ID", - "required" : true, + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -624,7 +732,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -634,7 +741,15 @@ } }, "404" : { - "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { "content" : { "application/json" : { "schema" : { @@ -643,26 +758,33 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } } }, "components" : { "schemas" : { - "LoginRequest" : { - "required" : [ "password", "username" ], + "SuccessStatus" : { "type" : "object", "properties" : { - "username" : { + "description" : { "type" : "string" - }, - "password" : { + } + }, + "required" : [ "description" ] + }, + "ErrorStatus" : { + "type" : "object", + "properties" : { + "description" : { "type" : "string" } - } + }, + "required" : [ "description" ] }, "UserDetails" : { - "required" : [ "id", "role", "username" ], "type" : "object", "properties" : { "id" : { @@ -672,92 +794,1162 @@ "type" : "string" }, "role" : { - "type" : "string", - "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + "$ref" : "#/components/schemas/Role" }, "sessionId" : { "type" : "string" } - } + }, + "required" : [ "id", "username", "role" ] }, - "ErrorStatus" : { - "required" : [ "description", "status" ], + "RestMediaCollection" : { "type" : "object", "properties" : { - "description" : { + "id" : { + "type" : "string" + }, + "name" : { "type" : "string" }, - "status" : { - "type" : "boolean" - } - } - }, - "SuccessStatus" : { - "required" : [ "description", "status" ], - "type" : "object", - "properties" : { "description" : { "type" : "string" }, - "status" : { - "type" : "boolean" + "basePath" : { + "type" : "string" } - } + }, + "required" : [ "id", "name" ] }, - "SuccessfulSubmissionsStatus" : { - "required" : [ "description", "status", "submission" ], + "RestMediaItem" : { "type" : "object", "properties" : { - "submission" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + "id" : { + "type" : "string" }, - "description" : { + "name" : { "type" : "string" }, - "status" : { - "type" : "boolean" - } - } - }, - "QueryEvent" : { - "required" : [ "category", "timestamp", "type", "value" ], + "type" : { + "$ref" : "#/components/schemas/RestMediaItemType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "number", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + } + }, + "required" : [ "id", "name", "type", "collectionId", "location" ] + }, + "DresAdminInfo" : { + "type" : "object", + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "number", + "format" : "int64" + }, + "uptime" : { + "type" : "number", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "number", + "format" : "int64" + }, + "totalMemory" : { + "type" : "number", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime", "os", "jvm", "args", "cores", "freeMemory", "totalMemory", "load", "availableSeverThreads" ] + }, + "ClientTaskInfo" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "remainingTime" : { + "type" : "number", + "format" : "int64" + }, + "running" : { + "type" : "boolean" + } + }, + "required" : [ "id", "name", "taskGroup", "remainingTime", "running" ] + }, + "CompetitionCreate" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "CompetitionStartMessage" : { + "type" : "object", + "properties" : { + "competitionId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/RunType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "competitionId", "name", "type", "properties" ] + }, + "UserRequest" : { + "type" : "object", + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/Role" + } + }, + "required" : [ "username" ] + }, + "TaskHint" : { + "type" : "object", + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "TaskInfo" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "id", "name", "taskGroup", "taskType", "duration" ] + }, + "ScoreOverview" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Score" + } + } + }, + "required" : [ "name", "scores" ] + }, + "TaskTarget" : { + "type" : "object", + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "CurrentTime" : { + "type" : "object", + "properties" : { + "timeStamp" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "SessionId" : { + "type" : "object", + "properties" : { + "sessionId" : { + "type" : "string" + } + }, + "required" : [ "sessionId" ] + }, + "AuditLogInfo" : { + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "timestamp", "size", "latest" ] + }, + "RestCompetitionDescription" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescription" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "RunInfo" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskInfo" + } + }, + "competitionId" : { + "type" : "string" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "type" : { + "$ref" : "#/components/schemas/RunType" + } + }, + "required" : [ "id", "name", "teams", "tasks", "competitionId", "properties", "type" ] + }, + "RunState" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "runStatus" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "taskRunStatus" : { + "$ref" : "#/components/schemas/RestTaskRunStatus" + }, + "currentTask" : { + "$ref" : "#/components/schemas/TaskInfo" + }, + "timeLeft" : { + "type" : "number", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "id", "runStatus", "taskRunStatus", "timeLeft", "timeElapsed" ] + }, + "SubmissionInfo" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/SubmissionStatus" + }, + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "item" : { + "$ref" : "#/components/schemas/RestMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "number", + "format" : "int64" + }, + "end" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "teamId", "memberId", "status", "timestamp" ] + }, + "DresInfo" : { + "type" : "object", + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "number", + "format" : "int64" + }, + "uptime" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "RunResult" : { + "type" : "object", + "properties" : { + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskResult" + } + }, + "timeStamp" : { + "type" : "integer", + "format" : "int32" + }, + "serverTimeStamp$backend" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] + }, + "JudgementValidatorStatus" : { + "type" : "object", + "properties" : { + "validator" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validator", "pending", "open" ] + }, + "JudgementVote" : { + "type" : "object", + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/SubmissionStatus" + } + }, + "required" : [ "verdict" ] + }, + "RestAuditLogEntry" : { + "type" : "object", + "properties" : { + "type" : { + "$ref" : "#/components/schemas/AuditLogEntryType" + }, + "timestamp" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "type", "timestamp" ] + }, + "CompetitionOverview" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "id", "name", "description", "taskCount", "teamCount" ] + }, + "ClientRunInfoList" : { + "type" : "object", + "properties" : { + "runs" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ClientRunInfo" + } + } + }, + "required" : [ "runs" ] + }, + "RestDetailedTeam" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "logoId" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDetails" + } + } + }, + "required" : [ "name", "color", "logoId", "users" ] + }, + "PastTaskInfo" : { + "type" : "object", + "properties" : { + "taskId" : { + "type" : "string" + }, + "descriptionId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "numberOfSubmissions" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "taskId", "descriptionId", "name", "taskGroup", "taskType", "numberOfSubmissions" ] + }, + "ScoreSeries" : { + "type" : "object", + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "TaskRunSubmissionInfo" : { + "type" : "object", + "properties" : { + "taskRunId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } + } + }, + "required" : [ "taskRunId", "submissions" ] + }, + "RestTaskDescription" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "number", + "format" : "int64" + }, + "mediaCollectionId" : { + "type" : "string" + }, + "target" : { + "$ref" : "#/components/schemas/RestTaskDescriptionTarget" + }, + "components" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescriptionComponent" + } + } + }, + "required" : [ "id", "name", "taskGroup", "taskType", "duration", "mediaCollectionId", "target", "components" ] + }, + "RestTeam" : { + "type" : "object", + "properties" : { + "uid" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "logoData" : { + "type" : "string" + }, + "logoId" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "name", "color", "users" ] + }, + "ViewerInfo" : { + "type" : "object", + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "LoginRequest" : { + "type" : "object", + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "JudgementRequest" : { + "type" : "object", + "properties" : { + "token" : { + "type" : "string" + }, + "mediaType" : { + "$ref" : "#/components/schemas/JudgementRequestMediaType" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "string" + }, + "endTime" : { + "type" : "string" + } + }, + "required" : [ "token", "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "AdminRunOverview" : { + "type" : "object", + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "Judgement" : { + "type" : "object", + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/SubmissionStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "QueryEventLog" : { + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResultLog" : { + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "RestFullMediaCollection" : { + "type" : "object", + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/RestMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "properties" : { + "submission" : { + "$ref" : "#/components/schemas/SubmissionStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "submission", "description" ] + }, + "RunProperties" : { + "type" : "object", + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "Role" : { + "type" : "object", + "properties" : { } + }, + "RestMediaItemType" : { + "type" : "object", + "properties" : { } + }, + "RunType" : { + "type" : "object", + "properties" : { } + }, + "ContentElement" : { + "type" : "object", + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "Score" : { + "type" : "object", + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "TaskType" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "taskDuration" : { + "type" : "number", + "format" : "int64" + }, + "targetType" : { + "$ref" : "#/components/schemas/ConfiguredOption" + }, + "components" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOption" + } + }, + "score" : { + "$ref" : "#/components/schemas/ConfiguredOption" + }, + "filter" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOption" + } + }, + "options" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOption" + } + } + }, + "required" : [ "name", "taskDuration", "targetType", "components", "score", "filter", "options" ] + }, + "TaskGroup" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "RestTeamGroup" : { + "type" : "object", + "properties" : { + "uid" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "aggregation" : { + "type" : "string" + } + }, + "required" : [ "name", "teams", "aggregation" ] + }, + "TeamInfo" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" + "uid" : { + "type" : "string" }, - "category" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + "name" : { + "type" : "string" }, - "type" : { + "color" : { "type" : "string" }, - "value" : { + "logoId" : { "type" : "string" } - } + }, + "required" : [ "uid", "name", "color", "logoId" ] }, - "QueryEventLog" : { - "required" : [ "events", "timestamp" ], + "RunManagerStatus" : { + "type" : "object", + "properties" : { } + }, + "RestTaskRunStatus" : { + "type" : "object", + "properties" : { } + }, + "SubmissionStatus" : { + "type" : "object", + "properties" : { } + }, + "TaskResult" : { + "type" : "object", + "properties" : { + "task" : { + "type" : "string" + }, + "resultName" : { + "type" : "string" + }, + "resultType" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ResultElement" + } + } + }, + "required" : [ "task", "resultName", "results" ] + }, + "AuditLogEntryType" : { + "type" : "object", + "properties" : { } + }, + "ClientRunInfo" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/RunManagerStatus" + } + }, + "required" : [ "id", "name", "status" ] + }, + "ScoreSeriesPoint" : { "type" : "object", "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, "timestamp" : { - "type" : "integer", + "type" : "number", "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "RestTaskDescriptionTarget" : { + "type" : "object", + "properties" : { + "type" : { + "$ref" : "#/components/schemas/TargetOption" }, - "events" : { + "mediaItems" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/QueryEvent" + "$ref" : "#/components/schemas/RestTaskDescriptionTargetItem" } } - } + }, + "required" : [ "type", "mediaItems" ] + }, + "RestTaskDescriptionComponent" : { + "type" : "object", + "properties" : { + "type" : { + "$ref" : "#/components/schemas/QueryComponentOption" + }, + "start" : { + "type" : "number", + "format" : "int64" + }, + "end" : { + "type" : "number", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/RestTemporalRange" + } + }, + "required" : [ "type" ] + }, + "JudgementRequestMediaType" : { + "type" : "object", + "properties" : { } + }, + "TeamTaskOverview" : { + "type" : "object", + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskRunOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "QueryEvent" : { + "type" : "object", + "properties" : { + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] }, "QueryResult" : { - "required" : [ "item" ], "type" : "object", "properties" : { "item" : { @@ -779,79 +1971,120 @@ "type" : "integer", "format" : "int32" } - } + }, + "required" : [ "item" ] }, - "QueryResultLog" : { - "required" : [ "events", "resultSetAvailability", "results", "sortType", "timestamp" ], + "ContentType" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { + "mimeType" : { "type" : "string" }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } + "base64" : { + "type" : "boolean" } - } + }, + "required" : [ "mimeType", "base64" ] }, - "CurrentTime" : { - "required" : [ "timeStamp" ], + "ConfiguredOption" : { "type" : "object", "properties" : { - "timeStamp" : { + "option" : { + "$ref" : "#/components/schemas/Option" + }, + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "" : { + "type" : "string" + }, + "asBool" : { + "type" : "boolean" + }, + "asInt" : { "type" : "integer", + "format" : "int32" + }, + "asLong" : { + "type" : "number", "format" : "int64" + }, + "asFloat" : { + "type" : "number", + "format" : "float" + }, + "asDouble" : { + "type" : "number", + "format" : "double" } - } + }, + "required" : [ "option", "parameters" ] }, - "ClientRunInfo" : { - "required" : [ "id", "name", "status" ], + "ResultElement" : { "type" : "object", "properties" : { - "id" : { + "item" : { "type" : "string" }, - "name" : { + "text" : { "type" : "string" }, - "description" : { + "startTimeCode" : { "type" : "string" }, - "status" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + "endTimeCode" : { + "type" : "string" + }, + "index" : { + "type" : "integer", + "format" : "int32" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + }, + "weight" : { + "type" : "number", + "format" : "float" } } }, - "ClientRunInfoList" : { - "required" : [ "runs" ], + "TargetOption" : { + "type" : "object", + "properties" : { } + }, + "RestTaskDescriptionTargetItem" : { "type" : "object", "properties" : { - "runs" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ClientRunInfo" - } + "mediaItem" : { + "type" : "string" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/RestTemporalRange" } - } + }, + "required" : [ "mediaItem" ] }, - "ClientTaskInfo" : { - "required" : [ "id", "name", "remainingTime", "running", "taskGroup" ], + "QueryComponentOption" : { + "type" : "object", + "properties" : { } + }, + "RestTemporalRange" : { + "type" : "object", + "properties" : { + "start" : { + "$ref" : "#/components/schemas/RestTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/RestTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "TaskRunOverview" : { "type" : "object", "properties" : { "id" : { @@ -860,18 +2093,70 @@ "name" : { "type" : "string" }, - "taskGroup" : { + "type" : { "type" : "string" }, - "remainingTime" : { - "type" : "integer", + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "number", "format" : "int64" }, - "running" : { - "type" : "boolean" + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/TaskRunStatus" + }, + "started" : { + "type" : "number", + "format" : "int64" + }, + "ended" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "QueryEventCategory" : { + "type" : "object", + "properties" : { } + }, + "Option" : { + "type" : "object", + "properties" : { + "ordinal" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "ordinal" ] + }, + "RestTemporalPoint" : { + "type" : "object", + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/RestTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "TaskRunStatus" : { + "type" : "object", + "properties" : { } + }, + "RestTemporalUnit" : { + "type" : "object", + "properties" : { } } - } - } + }, + "securitySchemes" : { } + }, + "servers" : [ ], + "security" : null } \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index e84f18d73..0288e5c13 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1,38 +1,41 @@ { - "openapi" : "3.0.1", + "openapi" : "3.0.3", "info" : { "title" : "DRES API", + "summary" : null, "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", + "termsOfService" : null, + "contact" : null, + "license" : null, "version" : "1.0" }, "paths" : { - "/api/v1/login" : { + "/api/v1/run/admin/{runId}/task/abort" : { "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV1Login", - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } + "tags" : [ "Competition Run Admin" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -42,7 +45,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -51,35 +53,30 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/logout" : { + "/api/v1/user/session/active/list" : { "get" : { "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV1Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "schema" : { - "type" : "string" - } - } ], + "summary" : "Get details of all current user sessions", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDetails" + } } } } }, - "400" : { - "description" : "Bad Request", + "500" : { "content" : { "application/json" : { "schema" : { @@ -88,49 +85,46 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "operationId" : "getApiV1UserList", + "/api/v1/collection" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaCollection" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/UserDetails" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } - } - } - } - }, - "/api/v1/user" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV1User", - "responses" : { - "200" : { - "description" : "OK", + }, + "400" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "500" : { - "description" : "Server Error", + "401" : { "content" : { "application/json" : { "schema" : { @@ -139,34 +133,35 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] }, - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV1User", + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "parameters" : [ ], "requestBody" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserRequest" + "$ref" : "#/components/schemas/RestMediaCollection" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "If the username is already taken", "content" : { "application/json" : { "schema" : { @@ -175,8 +170,7 @@ } } }, - "500" : { - "description" : "Server Error", + "401" : { "content" : { "application/json" : { "schema" : { @@ -185,46 +179,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/user/{userId}" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id", - "operationId" : "getApiV1UserWithUserid", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : true, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserDetails" - } + "/api/v1/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a Media Item to the specified Media Collection.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" } } }, - "404" : { - "description" : "If the user could not be found", + "required" : false + }, + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "500" : { - "description" : "Server Error", + "400" : { "content" : { "application/json" : { "schema" : { @@ -233,45 +218,35 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] }, - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV1UserWithUserid", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserDetails" - } + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" } } }, - "404" : { - "description" : "If the user could not be found", + "required" : false + }, + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "500" : { - "description" : "Server Error", + "400" : { "content" : { "application/json" : { "schema" : { @@ -280,43 +255,48 @@ } } } - } - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", - "operationId" : "patchApiV1UserWithUserid", + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/admin/{runId}/adjust/{duration}" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Adjusts the duration of a running task run. This is a method for admins.", "parameters" : [ { - "name" : "userId", + "name" : "runId", "in" : "path", - "description" : "User ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -325,8 +305,7 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -335,8 +314,7 @@ } } }, - "500" : { - "description" : "Server Error", + "404" : { "content" : { "application/json" : { "schema" : { @@ -345,35 +323,66 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/user/session" : { + "/api/v1/status/info/admin" : { "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV1UserSession", + "tags" : [ "Status" ], + "summary" : "Returns an extensive overview of the server properties.", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresAdminInfo" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/client/run/info/currentTask/{runId}" : { + "get" : { + "tags" : [ "Client Run Info" ], + "summary" : "Returns an overview of the currently active task for a run", "parameters" : [ { "name" : "session", "in" : "query", "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SessionId" + "$ref" : "#/components/schemas/ClientTaskInfo" } } } }, - "500" : { - "description" : "Server Error", + "401" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -382,62 +391,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "operationId" : "getApiV1UserSessionActiveList", - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/UserDetails" - } - } + "/api/v1/competition" : { + "post" : { + "tags" : [ "Competition" ], + "summary" : "Creates a new competition.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CompetitionCreate" } } }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/v1/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "operationId" : "getApiV1CollectionList", + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestMediaCollection" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -445,37 +429,8 @@ } } } - } - } - } - }, - "/api/v1/collection/{collectionId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV1CollectionWithCollectionid", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RestFullMediaCollection" - } - } - } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -485,7 +440,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -494,23 +448,26 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] }, - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection", - "operationId" : "deleteApiV1CollectionWithCollectionid", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "required" : true, - "schema" : { - "type" : "string" - } - } ], + "patch" : { + "tags" : [ "Competition" ], + "summary" : "Updates an existing competition.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestCompetitionDescription" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -520,7 +477,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -530,45 +486,6 @@ } }, "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/v1/collection" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a new media collection", - "operationId" : "postApiV1Collection", - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RestMediaCollection" - } - } - } - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -577,8 +494,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { "content" : { "application/json" : { "schema" : { @@ -587,24 +503,28 @@ } } } - } - }, - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV1Collection", + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/admin/create" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Creates a new competition run from an existing competition", + "parameters" : [ ], "requestBody" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "$ref" : "#/components/schemas/CompetitionStartMessage" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -614,7 +534,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -624,7 +543,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -633,36 +551,38 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/mediaItem" : { + "/api/v1/user" : { "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a Media Item to the specified Media Collection.", - "operationId" : "postApiV1Mediaitem", + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "parameters" : [ ], "requestBody" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/UserRequest" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, "400" : { - "description" : "Bad Request", + "description" : "If the username is already taken", "content" : { "application/json" : { "schema" : { @@ -670,35 +590,35 @@ } } } - } - } - }, - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV1Mediaitem", - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + }, + "500" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } } } } }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, - "400" : { - "description" : "Bad Request", + "500" : { "content" : { "application/json" : { "schema" : { @@ -707,36 +627,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/mediaItem/{mediaId}" : { + "/api/v1/run/{runId}/hint/{taskId}" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "getApiV1MediaitemWithMediaid", + "tags" : [ "Competition Run" ], + "summary" : "Returns the task hint for the current task run (i.e. the one that is currently selected).", "parameters" : [ { - "name" : "mediaId", + "name" : "runId", "in" : "path", - "description" : "Media item ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task Description ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/TaskHint" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -745,8 +676,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "403" : { "content" : { "application/json" : { "schema" : { @@ -756,7 +686,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -765,34 +694,37 @@ } } } - } - }, - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "deleteApiV1MediaitemWithMediaid", + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/{runId}/task" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the information for the current task (i.e. the one that is currently selected).", "parameters" : [ { - "name" : "mediaId", + "name" : "runId", "in" : "path", - "description" : "Media item ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/TaskInfo" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -801,8 +733,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "403" : { "content" : { "application/json" : { "schema" : { @@ -812,7 +743,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -821,36 +751,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/collection/{collectionId}/random" : { + "/api/v1/score/run/{runId}/current" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "operationId" : "getApiV1CollectionWithCollectionidRandom", + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns the overviews of all score boards for the current task run, if it is either running or has just ended.", "parameters" : [ { - "name" : "collectionId", + "name" : "runId", "in" : "path", - "description" : "Collection ID", - "required" : true, + "description" : "Competition run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/ScoreOverview" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -860,7 +791,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -870,7 +800,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -879,51 +808,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", - "operationId" : "postApiV1CollectionWithCollectionidResolve", + "/api/v1/run/{runId}/target/{taskId}" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", "parameters" : [ { - "name" : "collectionId", + "name" : "runId", "in" : "path", - "description" : "Collection ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task Description ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestMediaItem" - } + "$ref" : "#/components/schemas/TaskTarget" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -932,8 +857,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "403" : { "content" : { "application/json" : { "schema" : { @@ -943,7 +867,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -952,67 +875,57 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/collection/{collectionId}/{startsWith}" : { + "/api/v1/status/time" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", - "operationId" : "getApiV1CollectionWithCollectionidWithStartswith", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with", - "required" : true, - "schema" : { - "type" : "string" - } - } ], + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/CurrentTime" } } } - }, - "401" : { - "description" : "Unauthorized", + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SessionId" } } } }, - "404" : { - "description" : "Not Found", + "500" : { "content" : { "application/json" : { "schema" : { @@ -1021,39 +934,27 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/external/{startsWith}" : { - "get" : { + "/api/v1/collection/{collectionId}" : { + "delete" : { "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", - "operationId" : "getApiV1ExternalWithStartswith", - "parameters" : [ { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with.", - "required" : true, - "schema" : { - "type" : "string" - } - } ], + "summary" : "Deletes a media collection", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1063,7 +964,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1071,41 +971,45 @@ } } } - }, - "404" : { - "description" : "Not Found", + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/RestFullMediaCollection" } } } - } - } - } - }, - "/api/v1/competition/list" : { - "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "operationId" : "getApiV1CompetitionList", - "responses" : { - "200" : { - "description" : "OK", + }, + "401" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/CompetitionOverview" - } + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "404" : { "content" : { "application/json" : { "schema" : { @@ -1114,26 +1018,28 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/competition" : { - "post" : { + "/api/v1/competition/{competitionId}" : { + "delete" : { "tags" : [ "Competition" ], - "summary" : "Creates a new competition.", - "operationId" : "postApiV1Competition", - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CompetitionCreate" - } - } + "summary" : "Deletes the competition with the given competition ID.", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1143,7 +1049,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1153,7 +1058,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1163,7 +1067,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1172,34 +1075,35 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "Competition" ], - "summary" : "Updates an existing competition.", - "operationId" : "patchApiV1Competition", - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RestCompetitionDescription" - } - } + "summary" : "Loads the detailed definition of a specific competition.", + "parameters" : [ { + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/RestCompetitionDescription" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1209,7 +1113,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1219,7 +1122,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1228,36 +1130,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/competition/{competitionId}" : { - "get" : { - "tags" : [ "Competition" ], - "summary" : "Loads the detailed definition of a specific competition.", - "operationId" : "getApiV1CompetitionWithCompetitionid", + "/api/v1/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", "parameters" : [ { - "name" : "competitionId", + "name" : "mediaId", "in" : "path", - "description" : "Competition ID", - "required" : true, + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestCompetitionDescription" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1267,7 +1170,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1277,7 +1179,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1286,34 +1187,35 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] }, - "delete" : { - "tags" : [ "Competition" ], - "summary" : "Deletes the competition with the given competition ID.", - "operationId" : "deleteApiV1CompetitionWithCompetitionid", + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", "parameters" : [ { - "name" : "competitionId", + "name" : "mediaId", "in" : "path", - "description" : "Competition ID", - "required" : true, + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/RestMediaItem" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1323,7 +1225,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1333,7 +1234,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1342,49 +1242,39 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/competition/{competitionId}/team/list" : { - "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists the Teams of a specific competition.", - "operationId" : "getApiV1CompetitionWithCompetitionidTeamList", + "/api/v1/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", "parameters" : [ { - "name" : "competitionId", + "name" : "userId", "in" : "path", - "description" : "Competition ID", - "required" : true, + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { - "type" : "string" + "type" : "number", + "format" : "int64" } } ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "description" : "If the user could not be found", "content" : { "application/json" : { "schema" : { @@ -1393,8 +1283,7 @@ } } }, - "404" : { - "description" : "Not Found", + "500" : { "content" : { "application/json" : { "schema" : { @@ -1403,39 +1292,45 @@ } } } - } - } - }, - "/api/v1/competition/{competitionId}/team/list/details" : { - "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists the teams with their user details", - "operationId" : "getApiV1CompetitionWithCompetitionidTeamListDetails", + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", "parameters" : [ { - "name" : "competitionId", + "name" : "userId", "in" : "path", - "description" : "Competition ID", - "required" : true, + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestDetailedTeam" - } + "$ref" : "#/components/schemas/UserDetails" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1444,8 +1339,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { "content" : { "application/json" : { "schema" : { @@ -1454,8 +1348,7 @@ } } }, - "404" : { - "description" : "Not Found", + "500" : { "content" : { "application/json" : { "schema" : { @@ -1464,49 +1357,36 @@ } } } - } - } - }, - "/api/v1/competition/{competitionId}/task/list" : { + }, + "deprecated" : false, + "security" : [ ] + }, "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists the Tasks of a specific competition.", - "operationId" : "getApiV1CompetitionWithCompetitionidTaskList", + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id", "parameters" : [ { - "name" : "competitionId", + "name" : "userId", "in" : "path", - "description" : "Competition ID", - "required" : true, + "description" : "User's UID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTaskDescription" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/UserDetails" } } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "description" : "If the user could not be found", "content" : { "application/json" : { "schema" : { @@ -1515,8 +1395,7 @@ } } }, - "404" : { - "description" : "Not Found", + "500" : { "content" : { "application/json" : { "schema" : { @@ -1525,94 +1404,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/submit" : { + "/api/v1/download/run/{runId}" : { "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV1Submit", + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire competition run structure.", "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g. video).", - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g. video).", - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g. video).", - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", + "name" : "runId", + "in" : "path", + "description" : "Competition run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", "content" : { - "application/json" : { + "text/plain" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + "type" : "string" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1622,7 +1444,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1632,17 +1453,6 @@ } }, "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", "content" : { "application/json" : { "schema" : { @@ -1651,45 +1461,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/submit/{runId}" : { - "post" : { - "tags" : [ "Batch Submission" ], - "summary" : "Endpoint to accept batch submissions in JSON format", - "operationId" : "postApiV1SubmitWithRunid", + "/api/v1/download/run/{runId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given competition run.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Competition run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunResult" - } - } - } - }, "responses" : { "200" : { - "description" : "OK", "content" : { - "application/json" : { + "text/plain" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "string" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1699,7 +1501,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1709,7 +1510,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1718,45 +1518,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV1LogQuery", + "/api/v1/download/competition/{competitionId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire competition description structure.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "competitionId", + "in" : "path", + "description" : "Competition ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - } - }, "responses" : { "200" : { - "description" : "OK", "content" : { - "application/json" : { + "text/plain" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "string" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1766,7 +1558,15 @@ } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -1775,35 +1575,38 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/log/result" : { + "/api/v1/run/admin/{runId}/viewer/list/{viewerId}/force" : { "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants", - "operationId" : "postApiV1LogResult", + "tags" : [ "Competition Run Admin" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "schema" : { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { "type" : "string" } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "Viewer ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1813,7 +1616,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1823,7 +1625,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1831,31 +1632,8 @@ } } } - } - } - } - }, - "/api/v1/run/info/list" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Lists an overview of all competition runs visible to the current user", - "operationId" : "getApiV1RunInfoList", - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RunInfo" - } - } - } - } }, - "401" : { - "description" : "Unauthorized", + "404" : { "content" : { "application/json" : { "schema" : { @@ -1864,30 +1642,29 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/state/list" : { + "/api/v1/audit/info" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Lists an overview of all competition runs visible to the current user", - "operationId" : "getApiV1RunStateList", + "tags" : [ "Audit" ], + "summary" : "Gives information about the audit log. Namely size and latest timestamp of known audit logs", + "parameters" : [ ], "responses" : { "200" : { - "description" : "OK", + "description" : "The audit log info", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RunState" - } + "$ref" : "#/components/schemas/AuditLogInfo" } } } }, - "401" : { - "description" : "Unauthorized", + "403" : { + "description" : "Whenever a non-admin user starts the call", "content" : { "application/json" : { "schema" : { @@ -1896,26 +1673,28 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, "/api/v1/run/{runId}/info" : { "get" : { "tags" : [ "Competition Run" ], "summary" : "Returns a specific competition run.", - "operationId" : "getApiV1RunWithRunidInfo", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1925,7 +1704,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1935,7 +1713,6 @@ } }, "403" : { - "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -1945,7 +1722,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1954,26 +1730,28 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, "/api/v1/run/{runId}/state" : { "get" : { "tags" : [ "Competition Run" ], "summary" : "Returns the state of a specific competition run.", - "operationId" : "getApiV1RunWithRunidState", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1983,7 +1761,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1993,7 +1770,6 @@ } }, "403" : { - "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2003,7 +1779,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2012,44 +1787,50 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/hint/{taskId}" : { + "/api/v1/run/{runId}/task/{taskRunId}/submission/list" : { "get" : { "tags" : [ "Competition Run" ], - "summary" : "Returns the task hint for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV1RunWithRunidHintWithTaskid", + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { - "name" : "taskId", + "name" : "taskRunId", "in" : "path", - "description" : "Task Description ID", - "required" : true, + "description" : "Task run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/TaskHint" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } } } } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2059,7 +1840,6 @@ } }, "403" : { - "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2069,7 +1849,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2078,44 +1857,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/target/{taskId}" : { + "/api/v1/score/run/{runId}/history/{taskId}" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV1RunWithRunidTargetWithTaskid", + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task run.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Competition run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { "name" : "taskId", "in" : "path", - "description" : "Task Description ID", - "required" : true, + "description" : "Task run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/TaskTarget" + "$ref" : "#/components/schemas/ScoreOverview" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -2124,8 +1906,7 @@ } } }, - "403" : { - "description" : "Forbidden", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2135,7 +1916,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2144,36 +1924,76 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/task" : { + "/api/v1/status/info" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the information for the current task (i.e. the one that is currently selected).", - "operationId" : "getApiV1RunWithRunidTask", + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/submit/{runId}" : { + "post" : { + "tags" : [ "Batch Submission" ], + "summary" : "Endpoint to accept batch submissions in JSON format", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunResult" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/TaskInfo" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -2182,8 +2002,7 @@ } } }, - "403" : { - "description" : "Forbidden", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2193,7 +2012,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2202,53 +2020,58 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/submission/list" : { + "/api/v1/run/{runId}/judge/status" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the submissions for the current task run, if it is either running or has just ended.", - "operationId" : "getApiV1RunWithRunidSubmissionList", + "tags" : [ "Judgement" ], + "summary" : "Gets the status of all judgement validators.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" + "$ref" : "#/components/schemas/JudgementValidatorStatus" } } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { "content" : { "application/json" : { "schema" : { - "oneOf" : [ { - "$ref" : "#/components/schemas/ErrorStatus" - }, { - "$ref" : "#/components/schemas/ErrorStatus" - } ] + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2257,48 +2080,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.", - "operationId" : "getApiV1RunWithRunidSubmissionListAfterWithTimestamp", + "/api/v1/run/{runId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Minimum Timestamp of returned submissions.", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int64" - } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/JudgementVote" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { "content" : { "application/json" : { "schema" : { @@ -2307,8 +2129,7 @@ } } }, - "403" : { - "description" : "Forbidden", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2318,7 +2139,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2327,67 +2147,54 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/task/{taskRunId}/submission/list" : { + "/api/v1/audit/log/list/limit/{limit}/{page}" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV1RunWithRunidTaskWithTaskrunidSubmissionList", + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query.", "parameters" : [ { - "name" : "runId", + "name" : "limit", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "The maximum number of results. Default: 500", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { - "type" : "string" + "type" : "integer", + "format" : "int32" } }, { - "name" : "taskRunId", + "name" : "page", "in" : "path", - "description" : "Task run ID", - "required" : true, + "description" : "The page index offset, relative to the limit.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { - "type" : "string" + "type" : "integer", + "format" : "int32" } } ], "responses" : { "200" : { - "description" : "OK", + "description" : "The audit logs", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" + "$ref" : "#/components/schemas/RestAuditLogEntry" } } } } }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", + "description" : "Whenever a non-admin user starts the call", "content" : { "application/json" : { "schema" : { @@ -2396,39 +2203,86 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/score/run/{runId}" : { + "/api/v1/audit/log/list/since/{since}/{upto}" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns the score overviews of a specific competition run.", - "operationId" : "getApiV1ScoreRunWithRunid", + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", "parameters" : [ { - "name" : "runId", + "name" : "since", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Timestamp of the earliest audit log to include", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { - "type" : "string" + "type" : "number", + "format" : "int64" + } + }, { + "name" : "upto", + "in" : "path", + "description" : "Timestamp of the latest audit log to include.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "number", + "format" : "int64" } } ], "responses" : { "200" : { - "description" : "OK", + "description" : "The audit logs", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ScoreOverview" + "$ref" : "#/components/schemas/RestAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaCollection" } } } } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2436,9 +2290,31 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/competition/list" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/CompetitionOverview" + } + } + } + } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2447,36 +2323,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/score/run/{runId}/current" : { + "/api/v1/client/run/info/list" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns the overviews of all score boards for the current task run, if it is either running or has just ended.", - "operationId" : "getApiV1ScoreRunWithRunidCurrent", + "tags" : [ "Client Run Info" ], + "summary" : "Lists an overview of all competition runs visible to the current client", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition run ID", + "name" : "session", + "in" : "query", + "description" : "Session Token", "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ScoreOverview" + "$ref" : "#/components/schemas/ClientRunInfoList" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2484,9 +2361,31 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/info/list" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Lists an overview of all competition runs visible to the current user", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RunInfo" + } + } + } + } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2494,9 +2393,31 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/state/list" : { + "get" : { + "tags" : [ "Competition Run" ], + "summary" : "Lists an overview of all competition runs visible to the current user", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RunState" + } + } + } + } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2505,54 +2426,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/score/run/{runId}/history/{taskId}" : { + "/api/v1/score/run/{runId}" : { "get" : { "tags" : [ "Competition Run Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task run.", - "operationId" : "getApiV1ScoreRunWithRunidHistoryWithTaskid", + "summary" : "Returns the score overviews of a specific competition run.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition run ID", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task run ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreOverview" + } } } } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2562,7 +2469,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2571,47 +2477,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/score/run/{runId}/series/{scoreboard}" : { + "/api/v1/competition/{competitionId}/team/list/details" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "operationId" : "getApiV1ScoreRunWithRunidSeriesWithScoreboard", + "tags" : [ "Competition" ], + "summary" : "Lists the teams with their user details", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "ID of the competition run.", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", + "name" : "competitionId", "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, + "description" : "Competition ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ScoreSeries" + "$ref" : "#/components/schemas/RestDetailedTeam" } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2621,7 +2520,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2631,7 +2529,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2640,26 +2537,28 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/score/run/{runId}/scoreboard/list" : { + "/api/v1/external/" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns a list of available scoreboard names for the given run.", - "operationId" : "getApiV1ScoreRunWithRunidScoreboardList", + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", "parameters" : [ { - "name" : "runId", + "name" : "startsWith", "in" : "path", - "description" : "ID of the competition run.", - "required" : true, + "description" : "Name starts with.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -2672,7 +2571,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2682,7 +2580,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2692,7 +2589,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2701,39 +2597,50 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/score/run/{runId}/teamGroup/list" : { + "/api/v1/collection/{collectionId}/{startsWith}" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns team group aggregated values of the current task.", - "operationId" : "getApiV1ScoreRunWithRunidTeamgroupList", + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", "parameters" : [ { - "name" : "runId", + "name" : "collectionId", "in" : "path", - "description" : "ID of the competition run.", - "required" : true, + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ScoreSeries" + "$ref" : "#/components/schemas/RestMediaItem" } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2742,8 +2649,7 @@ } } }, - "403" : { - "description" : "Forbidden", + "401" : { "content" : { "application/json" : { "schema" : { @@ -2753,7 +2659,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2762,36 +2667,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/create" : { - "post" : { + "/api/v1/run/admin/{runId}/task/past/list" : { + "get" : { "tags" : [ "Competition Run Admin" ], - "summary" : "Creates a new competition run from an existing competition", - "operationId" : "postApiV1RunAdminCreate", - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CompetitionStartMessage" - } - } + "summary" : "Lists all past tasks for a given run", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } - }, + } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PastTaskInfo" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2801,45 +2710,6 @@ } }, "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - } - } - }, - "/api/v1/run/admin/{runId}/start" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Starts a competition run.", - "operationId" : "postApiV1RunAdminWithRunidStart", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : true, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2848,8 +2718,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { "content" : { "application/json" : { "schema" : { @@ -2858,36 +2727,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/next" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Moves to and selects the next task. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidTaskNext", + "/api/v1/score/run/{runId}/scoreboard/list" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns a list of available scoreboard names for the given run.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", + "description" : "ID of the competition run.", "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "type" : "string" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2897,7 +2770,15 @@ } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -2906,36 +2787,50 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/previous" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidTaskPrevious", + "/api/v1/score/run/{runId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", + "description" : "ID of the competition run.", "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreSeries" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2945,7 +2840,15 @@ } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -2954,45 +2857,50 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/switch/{idx}" : { - "post" : { + "/api/v1/run/admin/{runId}/submission/list/{taskId}" : { + "get" : { "tags" : [ "Competition Run Admin" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidTaskSwitchWithIdx", + "summary" : "Lists all submissions for a given task and run.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition run ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { - "name" : "idx", + "name" : "taskId", "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, + "description" : "Task ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { - "type" : "integer", - "format" : "int32" + "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TaskRunSubmissionInfo" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3002,7 +2910,15 @@ } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -3011,36 +2927,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/start" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidTaskStart", + "/api/v1/competition/{competitionId}/task/list" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists the Tasks of a specific competition.", "parameters" : [ { - "name" : "runId", + "name" : "competitionId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Competition ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTaskDescription" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3050,7 +2970,15 @@ } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -3059,36 +2987,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/abort" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidTaskAbort", + "/api/v1/competition/{competitionId}/team/list" : { + "get" : { + "tags" : [ "Competition" ], + "summary" : "Lists the Teams of a specific competition.", "parameters" : [ { - "name" : "runId", + "name" : "competitionId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Competition ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestTeam" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3098,7 +3030,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3106,37 +3037,8 @@ } } } - } - } - } - }, - "/api/v1/run/admin/{runId}/terminate" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Terminates a competition run. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidTerminate", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : true, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } }, - "400" : { - "description" : "Bad Request", + "404" : { "content" : { "application/json" : { "schema" : { @@ -3144,56 +3046,64 @@ } } } - }, - "401" : { - "description" : "Unauthorized", + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "parameters" : [ ], + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/UserDetails" + } } } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/adjust/{duration}" : { - "post" : { + "/api/v1/run/admin/{runId}/viewer/list" : { + "get" : { "tags" : [ "Competition Run Admin" ], - "summary" : "Adjusts the duration of a running task run. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidAdjustWithDuration", + "summary" : "Lists all registered viewers for a competition run. This is a method for admins.", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add.", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ViewerInfo" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3203,7 +3113,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3213,7 +3122,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3222,39 +3130,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/viewer/list" : { - "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all registered viewers for a competition run. This is a method for admins.", - "operationId" : "getApiV1RunAdminWithRunidViewerList", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : true, - "schema" : { - "type" : "string" - } - } ], + "/api/v1/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ViewerInfo" - } + "$ref" : "#/components/schemas/UserDetails" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3264,17 +3170,6 @@ } }, "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3283,34 +3178,28 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV1RunAdminWithRunidViewerListWithVieweridForce", + "/api/v1/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "Viewer ID", - "required" : true, + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3320,27 +3209,6 @@ } }, "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3349,47 +3217,46 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/submission/list/{taskId}" : { + "/api/v1/run/{runId}/judge/next" : { "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all submissions for a given task and run.", - "operationId" : "getApiV1RunAdminWithRunidSubmissionListWithTaskid", + "tags" : [ "Judgement" ], + "summary" : "Gets the next open Submission to be judged.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, + "description" : "Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskRunSubmissionInfo" - } + "$ref" : "#/components/schemas/JudgementRequest" + } + } + } + }, + "202" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3399,7 +3266,15 @@ } }, "401" : { - "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { "content" : { "application/json" : { "schema" : { @@ -3409,7 +3284,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3418,45 +3292,46 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/submission/override" : { - "patch" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all submissions for a given task and run", - "operationId" : "patchApiV1RunAdminWithRunidSubmissionOverride", + "/api/v1/run/{runId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open Submission to voted on.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : true, + "description" : "Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } - } - } - }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SubmissionInfo" + "$ref" : "#/components/schemas/JudgementRequest" + } + } + } + }, + "202" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3466,7 +3341,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3476,7 +3350,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3485,39 +3358,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/past/list" : { - "get" : { + "/api/v1/run/admin/{runId}/task/next" : { + "post" : { "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all past tasks for a given run", - "operationId" : "getApiV1RunAdminWithRunidTaskPastList", + "summary" : "Moves to and selects the next task. This is a method for admins.", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/PastTaskInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3527,17 +3398,6 @@ } }, "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3546,26 +3406,28 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, "/api/v1/run/admin/{runId}/overview" : { "get" : { "tags" : [ "Competition Run Admin" ], "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV1RunAdminWithRunidOverview", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3575,7 +3437,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3585,7 +3446,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3595,7 +3455,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3604,19 +3463,22 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/admin/{runId}/properties" : { + "/api/v1/run/admin/{runId}/submission/override" : { "patch" : { "tags" : [ "Competition Run Admin" ], - "summary" : "Changes the properties of a run", - "operationId" : "patchApiV1RunAdminWithRunidProperties", + "summary" : "Lists all submissions for a given task and run", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Competition Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } @@ -3625,24 +3487,23 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RunProperties" + "$ref" : "#/components/schemas/SubmissionInfo" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/SubmissionInfo" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3652,7 +3513,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3662,7 +3522,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3671,36 +3530,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/judge/next" : { - "get" : { + "/api/v1/run/{runId}/judge" : { + "post" : { "tags" : [ "Judgement" ], - "summary" : "Gets the next open Submission to be judged.", - "operationId" : "getApiV1RunWithRunidJudgeNext", + "summary" : "Returns a Judgement.", "parameters" : [ { "name" : "runId", "in" : "path", "description" : "Run ID", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Judgement" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/JudgementRequest" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "202" : { - "description" : "Accepted", + "400" : { "content" : { "application/json" : { "schema" : { @@ -3709,8 +3579,7 @@ } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -3719,8 +3588,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "403" : { "content" : { "application/json" : { "schema" : { @@ -3729,8 +3597,8 @@ } } }, - "403" : { - "description" : "Forbidden", + "408" : { + "description" : "On timeout: Judgement took too long", "content" : { "application/json" : { "schema" : { @@ -3740,7 +3608,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3749,46 +3616,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open Submission to voted on.", - "operationId" : "getApiV1RunWithRunidVoteNext", + "/api/v1/run/admin/{runId}/task/previous" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Run ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/JudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3798,17 +3656,6 @@ } }, "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3817,19 +3664,22 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/judge" : { + "/api/v1/log/query" : { "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Judgement.", - "operationId" : "postApiV1RunWithRunidJudge", + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Run ID", + "name" : "session", + "in" : "query", + "description" : "Session Token", "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } @@ -3838,14 +3688,14 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/Judgement" + "$ref" : "#/components/schemas/QueryEventLog" } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3855,7 +3705,6 @@ } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3865,7 +3714,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3873,9 +3721,38 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + } }, - "403" : { - "description" : "Forbidden", + "400" : { "content" : { "application/json" : { "schema" : { @@ -3884,8 +3761,7 @@ } } }, - "408" : { - "description" : "On timeout: Judgement took too long", + "401" : { "content" : { "application/json" : { "schema" : { @@ -3895,7 +3771,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3904,39 +3779,51 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/judge/status" : { + "/api/v1/run/{runId}/submission/list/after/{timestamp}" : { "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the status of all judgement validators.", - "operationId" : "getApiV1RunWithRunidJudgeStatus", + "tags" : [ "Competition Run" ], + "summary" : "Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Run ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Minimum Timestamp of returned submissions.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "number", + "format" : "int64" + } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/JudgementValidatorStatus" + "$ref" : "#/components/schemas/SubmissionInfo" } } } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -3946,7 +3833,6 @@ } }, "403" : { - "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -3956,7 +3842,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3965,19 +3850,22 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/run/{runId}/judge/vote" : { + "/api/v1/collection/{collectionId}/resolve" : { "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV1RunWithRunidJudgeVote", + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", "parameters" : [ { - "name" : "runId", + "name" : "collectionId", "in" : "path", - "description" : "Run ID", - "required" : true, + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } @@ -3986,24 +3874,29 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/JudgementVote" + "type" : "array", + "items" : { + "type" : "string" + } } } - } + }, + "required" : false }, "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaItem" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -4013,7 +3906,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -4023,7 +3915,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4032,27 +3923,47 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/audit/info" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Gives information about the audit log. Namely size and latest timestamp of known audit logs", - "operationId" : "getApiV1AuditInfo", + "/api/v1/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, "responses" : { "200" : { - "description" : "The audit log info", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/AuditLogInfo" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "403" : { - "description" : "Whenever a non-admin user starts the call", + "400" : { "content" : { "application/json" : { "schema" : { @@ -4060,50 +3971,8 @@ } } } - } - } - } - }, - "/api/v1/audit/log/list/since/{since}/{upto}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query", - "operationId" : "getApiV1AuditLogListSinceWithSinceWithUpto", - "parameters" : [ { - "name" : "since", - "in" : "path", - "description" : "Timestamp of the earliest audit log to include", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "upto", - "in" : "path", - "description" : "Timestamp of the latest audit log to include.", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestAuditLogEntry" - } - } - } - } }, - "403" : { - "description" : "Whenever a non-admin user starts the call", + "401" : { "content" : { "application/json" : { "schema" : { @@ -4112,49 +3981,37 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/audit/log/list/limit/{limit}/{page}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query.", - "operationId" : "getApiV1AuditLogListLimitWithLimitWithPage", + "/api/v1/run/admin/{runId}/start" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Starts a competition run.", "parameters" : [ { - "name" : "limit", - "in" : "path", - "description" : "The maximum number of results. Default: 500", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "page", + "name" : "runId", "in" : "path", - "description" : "The page index offset, relative to the limit.", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { - "type" : "integer", - "format" : "int32" + "type" : "string" } } ], "responses" : { "200" : { - "description" : "The audit logs", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestAuditLogEntry" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "403" : { - "description" : "Whenever a non-admin user starts the call", + "400" : { "content" : { "application/json" : { "schema" : { @@ -4162,56 +4019,56 @@ } } } - } - } - } - }, - "/api/v1/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV1StatusTime", - "responses" : { - "200" : { - "description" : "OK", + }, + "401" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/CurrentTime" + "$ref" : "#/components/schemas/ErrorStatus" } } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/client/run/info/list" : { - "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Lists an overview of all competition runs visible to the current client", - "operationId" : "getApiV1ClientRunInfoList", + "/api/v1/run/admin/{runId}/task/start" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ClientRunInfoList" + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -4220,18 +4077,74 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/client/run/info/currentTask/{runId}" : { + "/api/v1/submit" : { "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Returns an overview of the currently active task for a run", - "operationId" : "getApiV1ClientRunInfoCurrenttaskWithRunid", + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", "parameters" : [ { - "name" : "runId", - "in" : "path", - "required" : true, + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, "schema" : { "type" : "string" } @@ -4239,24 +4152,42 @@ "name" : "session", "in" : "query", "description" : "Session Token", - "required" : true, + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ClientTaskInfo" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -4266,7 +4197,15 @@ } }, "404" : { - "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { "content" : { "application/json" : { "schema" : { @@ -4275,36 +4214,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/download/run/{runId}" : { + "/api/v1/run/{runId}/submission/list" : { "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire competition run structure.", - "operationId" : "getApiV1DownloadRunWithRunid", + "tags" : [ "Competition Run" ], + "summary" : "Returns the submissions for the current task run, if it is either running or has just ended.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition run ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "string" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SubmissionInfo" + } } } } }, - "400" : { - "description" : "Bad Request", + "401" : { "content" : { "application/json" : { "schema" : { @@ -4313,8 +4256,57 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/admin/{runId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { "content" : { "application/json" : { "schema" : { @@ -4323,8 +4315,7 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -4333,36 +4324,40 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/download/run/{runId}/scores" : { + "/api/v1/score/run/{runId}/teamGroup/list" : { "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given competition run.", - "operationId" : "getApiV1DownloadRunWithRunidScores", + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns team group aggregated values of the current task.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition run ID", + "description" : "ID of the competition run.", "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { - "text/csv" : { + "application/json" : { "schema" : { - "type" : "string" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ScoreSeries" + } } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -4371,8 +4366,7 @@ } } }, - "401" : { - "description" : "Unauthorized", + "403" : { "content" : { "application/json" : { "schema" : { @@ -4382,7 +4376,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4391,36 +4384,95 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } }, - "/api/v1/download/competition/{competitionId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire competition description structure.", - "operationId" : "getApiV1DownloadCompetitionWithCompetitionid", + "/api/v1/run/admin/{runId}/terminate" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Terminates a competition run. This is a method for admins.", "parameters" : [ { - "name" : "competitionId", + "name" : "runId", "in" : "path", - "description" : "Competition ID", - "required" : true, + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], "responses" : { "200" : { - "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v1/run/admin/{runId}/properties" : { + "patch" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Changes the properties of a run", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "Competition Run ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { - "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -4430,7 +4482,6 @@ } }, "401" : { - "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -4440,7 +4491,6 @@ } }, "404" : { - "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4449,26 +4499,33 @@ } } } - } + }, + "deprecated" : false, + "security" : [ ] } } }, "components" : { "schemas" : { - "LoginRequest" : { - "required" : [ "password", "username" ], + "SuccessStatus" : { "type" : "object", "properties" : { - "username" : { + "description" : { "type" : "string" - }, - "password" : { + } + }, + "required" : [ "description" ] + }, + "ErrorStatus" : { + "type" : "object", + "properties" : { + "description" : { "type" : "string" } - } + }, + "required" : [ "description" ] }, "UserDetails" : { - "required" : [ "id", "role", "username" ], "type" : "object", "properties" : { "id" : { @@ -4478,65 +4535,108 @@ "type" : "string" }, "role" : { - "type" : "string", - "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + "$ref" : "#/components/schemas/Role" }, "sessionId" : { "type" : "string" } - } + }, + "required" : [ "id", "username", "role" ] }, - "ErrorStatus" : { - "required" : [ "description", "status" ], + "RestMediaCollection" : { "type" : "object", "properties" : { - "description" : { + "id" : { + "type" : "string" + }, + "name" : { "type" : "string" }, - "status" : { - "type" : "boolean" - } - } - }, - "SuccessStatus" : { - "required" : [ "description", "status" ], - "type" : "object", - "properties" : { "description" : { "type" : "string" }, - "status" : { - "type" : "boolean" + "basePath" : { + "type" : "string" } - } + }, + "required" : [ "id", "name" ] }, - "UserRequest" : { - "required" : [ "username" ], + "RestMediaItem" : { "type" : "object", "properties" : { - "username" : { + "id" : { "type" : "string" }, - "password" : { + "name" : { "type" : "string" }, - "role" : { - "type" : "string", - "enum" : [ "ADMIN", "JUDGE", "VIEWER", "PARTICIPANT" ] + "type" : { + "$ref" : "#/components/schemas/RestMediaItemType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "number", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" } - } + }, + "required" : [ "id", "name", "type", "collectionId", "location" ] }, - "SessionId" : { - "required" : [ "sessionId" ], + "DresAdminInfo" : { "type" : "object", "properties" : { - "sessionId" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "number", + "format" : "int64" + }, + "uptime" : { + "type" : "number", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "number", + "format" : "int64" + }, + "totalMemory" : { + "type" : "number", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" } - } + }, + "required" : [ "version", "startTime", "uptime", "os", "jvm", "args", "cores", "freeMemory", "totalMemory", "load", "availableSeverThreads" ] }, - "RestMediaCollection" : { - "required" : [ "id", "name" ], + "ClientTaskInfo" : { "type" : "object", "properties" : { "id" : { @@ -4545,61 +4645,83 @@ "name" : { "type" : "string" }, - "description" : { + "taskGroup" : { "type" : "string" }, - "basePath" : { - "type" : "string" + "remainingTime" : { + "type" : "number", + "format" : "int64" + }, + "running" : { + "type" : "boolean" } - } + }, + "required" : [ "id", "name", "taskGroup", "remainingTime", "running" ] }, - "RestFullMediaCollection" : { - "required" : [ "collection", "items" ], + "CompetitionCreate" : { "type" : "object", "properties" : { - "collection" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "name" : { + "type" : "string" }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestMediaItem" - } + "description" : { + "type" : "string" } - } + }, + "required" : [ "name", "description" ] }, - "RestMediaItem" : { - "required" : [ "collectionId", "id", "location", "name", "type" ], + "CompetitionStartMessage" : { "type" : "object", "properties" : { - "id" : { + "competitionId" : { "type" : "string" }, "name" : { "type" : "string" }, "type" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO" ] + "$ref" : "#/components/schemas/RunType" }, - "collectionId" : { + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "competitionId", "name", "type", "properties" ] + }, + "UserRequest" : { + "type" : "object", + "properties" : { + "username" : { "type" : "string" }, - "location" : { + "password" : { "type" : "string" }, - "durationMs" : { - "type" : "integer", - "format" : "int64" + "role" : { + "$ref" : "#/components/schemas/Role" + } + }, + "required" : [ "username" ] + }, + "TaskHint" : { + "type" : "object", + "properties" : { + "taskId" : { + "type" : "string" }, - "fps" : { - "type" : "number", - "format" : "float" + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ContentElement" + } + }, + "loop" : { + "type" : "boolean" } - } + }, + "required" : [ "taskId", "sequence", "loop" ] }, - "CompetitionOverview" : { - "required" : [ "description", "id", "name", "taskCount", "teamCount" ], + "TaskInfo" : { "type" : "object", "properties" : { "id" : { @@ -4608,113 +4730,90 @@ "name" : { "type" : "string" }, - "description" : { + "taskGroup" : { "type" : "string" }, - "taskCount" : { - "type" : "integer", - "format" : "int32" + "taskType" : { + "type" : "string" }, - "teamCount" : { - "type" : "integer", - "format" : "int32" + "duration" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "id", "name", "taskGroup", "taskType", "duration" ] }, - "CompetitionCreate" : { - "required" : [ "description", "name" ], + "ScoreOverview" : { "type" : "object", "properties" : { "name" : { "type" : "string" }, - "description" : { + "taskGroup" : { "type" : "string" - } - } - }, - "ConfiguredOptionQueryComponentOption" : { - "required" : [ "option", "parameters" ], - "type" : "object", - "properties" : { - "option" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] }, - "parameters" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Score" } } - } + }, + "required" : [ "name", "scores" ] }, - "ConfiguredOptionScoringOption" : { - "required" : [ "option", "parameters" ], + "TaskTarget" : { "type" : "object", "properties" : { - "option" : { - "type" : "string", - "enum" : [ "KIS", "AVS" ] + "taskId" : { + "type" : "string" }, - "parameters" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ContentElement" } } - } + }, + "required" : [ "taskId", "sequence" ] }, - "ConfiguredOptionSimpleOption" : { - "required" : [ "option", "parameters" ], + "CurrentTime" : { "type" : "object", "properties" : { - "option" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "parameters" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } + "timeStamp" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "timeStamp" ] }, - "ConfiguredOptionSubmissionFilterOption" : { - "required" : [ "option", "parameters" ], + "SessionId" : { "type" : "object", "properties" : { - "option" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "parameters" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } + "sessionId" : { + "type" : "string" } - } + }, + "required" : [ "sessionId" ] }, - "ConfiguredOptionTargetOption" : { - "required" : [ "option", "parameters" ], + "AuditLogInfo" : { "type" : "object", "properties" : { - "option" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "MULTIPLE_MEDIA_ITEMS", "JUDGEMENT", "VOTE", "TEXT" ] + "timestamp" : { + "type" : "number", + "format" : "int64" }, - "parameters" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "timestamp", "size", "latest" ] }, "RestCompetitionDescription" : { - "required" : [ "id", "judges", "name", "taskGroups", "taskTypes", "tasks", "teamGroups", "teams" ], "type" : "object", "properties" : { "id" : { @@ -4762,10 +4861,10 @@ "type" : "string" } } - } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] }, - "RestTaskDescription" : { - "required" : [ "components", "duration", "id", "mediaCollectionId", "name", "target", "taskGroup", "taskType" ], + "RunInfo" : { "type" : "object", "properties" : { "id" : { @@ -4774,215 +4873,213 @@ "name" : { "type" : "string" }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { + "description" : { "type" : "string" }, - "duration" : { - "type" : "integer", - "format" : "int64" + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TeamInfo" + } }, - "mediaCollectionId" : { - "type" : "string" - }, - "target" : { - "$ref" : "#/components/schemas/RestTaskDescriptionTarget" - }, - "components" : { + "tasks" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestTaskDescriptionComponent" + "$ref" : "#/components/schemas/TaskInfo" } + }, + "competitionId" : { + "type" : "string" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "type" : { + "$ref" : "#/components/schemas/RunType" } - } + }, + "required" : [ "id", "name", "teams", "tasks", "competitionId", "properties", "type" ] }, - "RestTaskDescriptionComponent" : { - "required" : [ "type" ], + "RunState" : { "type" : "object", "properties" : { - "type" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { + "id" : { "type" : "string" }, - "path" : { - "type" : "string" + "runStatus" : { + "$ref" : "#/components/schemas/RunManagerStatus" }, - "dataType" : { - "type" : "string" + "taskRunStatus" : { + "$ref" : "#/components/schemas/RestTaskRunStatus" }, - "mediaItem" : { - "type" : "string" + "currentTask" : { + "$ref" : "#/components/schemas/TaskInfo" }, - "range" : { - "$ref" : "#/components/schemas/RestTemporalRange" - } - } - }, - "RestTaskDescriptionTarget" : { - "required" : [ "mediaItems", "type" ], - "type" : "object", - "properties" : { - "type" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "MULTIPLE_MEDIA_ITEMS", "JUDGEMENT", "VOTE", "TEXT" ] + "timeLeft" : { + "type" : "number", + "format" : "int64" }, - "mediaItems" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTaskDescriptionTargetItem" - } + "timeElapsed" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "id", "runStatus", "taskRunStatus", "timeLeft", "timeElapsed" ] }, - "RestTaskDescriptionTargetItem" : { - "required" : [ "mediaItem" ], + "SubmissionInfo" : { "type" : "object", "properties" : { - "mediaItem" : { + "id" : { "type" : "string" }, - "temporalRange" : { - "$ref" : "#/components/schemas/RestTemporalRange" - } - } - }, - "RestTeam" : { - "required" : [ "color", "name", "users" ], - "type" : "object", - "properties" : { - "uid" : { + "teamId" : { "type" : "string" }, - "name" : { + "teamName" : { "type" : "string" }, - "color" : { + "memberId" : { "type" : "string" }, - "logoData" : { + "memberName" : { "type" : "string" }, - "logoId" : { + "status" : { + "$ref" : "#/components/schemas/SubmissionStatus" + }, + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "item" : { + "$ref" : "#/components/schemas/RestMediaItem" + }, + "text" : { "type" : "string" }, - "users" : { - "type" : "array", - "items" : { - "type" : "string" - } + "start" : { + "type" : "number", + "format" : "int64" + }, + "end" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "teamId", "memberId", "status", "timestamp" ] }, - "RestTeamGroup" : { - "required" : [ "aggregation", "name", "teams" ], + "DresInfo" : { "type" : "object", "properties" : { - "uid" : { + "version" : { "type" : "string" }, - "name" : { - "type" : "string" + "startTime" : { + "type" : "number", + "format" : "int64" }, - "teams" : { + "uptime" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "RunResult" : { + "type" : "object", + "properties" : { + "tasks" : { "type" : "array", "items" : { - "type" : "string" + "$ref" : "#/components/schemas/TaskResult" } }, - "aggregation" : { - "type" : "string" + "timeStamp" : { + "type" : "integer", + "format" : "int32" + }, + "serverTimeStamp$backend" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] }, - "RestTemporalPoint" : { - "required" : [ "unit", "value" ], + "JudgementValidatorStatus" : { "type" : "object", "properties" : { - "value" : { + "validator" : { "type" : "string" }, - "unit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" } - } + }, + "required" : [ "validator", "pending", "open" ] }, - "RestTemporalRange" : { - "required" : [ "end", "start" ], + "JudgementVote" : { "type" : "object", "properties" : { - "start" : { - "$ref" : "#/components/schemas/RestTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/RestTemporalPoint" + "verdict" : { + "$ref" : "#/components/schemas/SubmissionStatus" } - } + }, + "required" : [ "verdict" ] }, - "TaskGroup" : { - "required" : [ "name", "type" ], + "RestAuditLogEntry" : { "type" : "object", "properties" : { - "name" : { - "type" : "string" - }, "type" : { - "type" : "string" + "$ref" : "#/components/schemas/AuditLogEntryType" + }, + "timestamp" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "type", "timestamp" ] }, - "TaskType" : { - "required" : [ "components", "filter", "name", "options", "score", "targetType", "taskDuration" ], + "CompetitionOverview" : { "type" : "object", "properties" : { - "name" : { + "id" : { "type" : "string" }, - "taskDuration" : { - "type" : "integer", - "format" : "int64" - }, - "targetType" : { - "$ref" : "#/components/schemas/ConfiguredOptionTargetOption" - }, - "components" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ConfiguredOptionQueryComponentOption" - } + "name" : { + "type" : "string" }, - "score" : { - "$ref" : "#/components/schemas/ConfiguredOptionScoringOption" + "description" : { + "type" : "string" }, - "filter" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ConfiguredOptionSubmissionFilterOption" - } + "taskCount" : { + "type" : "integer", + "format" : "int32" }, - "options" : { + "teamCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "id", "name", "description", "taskCount", "teamCount" ] + }, + "ClientRunInfoList" : { + "type" : "object", + "properties" : { + "runs" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ConfiguredOptionSimpleOption" + "$ref" : "#/components/schemas/ClientRunInfo" } } - } + }, + "required" : [ "runs" ] }, "RestDetailedTeam" : { - "required" : [ "color", "logoId", "name", "users" ], "type" : "object", "properties" : { "name" : { @@ -5000,398 +5097,345 @@ "$ref" : "#/components/schemas/UserDetails" } } - } + }, + "required" : [ "name", "color", "logoId", "users" ] }, - "SuccessfulSubmissionsStatus" : { - "required" : [ "description", "status", "submission" ], + "PastTaskInfo" : { "type" : "object", "properties" : { - "submission" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "description" : { + "taskId" : { "type" : "string" }, - "status" : { - "type" : "boolean" - } - } - }, - "ResultElement" : { - "type" : "object", - "properties" : { - "item" : { + "descriptionId" : { "type" : "string" }, - "text" : { + "name" : { "type" : "string" }, - "startTimeCode" : { + "taskGroup" : { "type" : "string" }, - "endTimeCode" : { + "taskType" : { "type" : "string" }, - "index" : { - "type" : "integer", - "format" : "int32" - }, - "rank" : { + "numberOfSubmissions" : { "type" : "integer", "format" : "int32" - }, - "weight" : { - "type" : "number", - "format" : "float" } - } + }, + "required" : [ "taskId", "descriptionId", "name", "taskGroup", "taskType", "numberOfSubmissions" ] }, - "RunResult" : { - "required" : [ "tasks", "timeStamp" ], + "ScoreSeries" : { "type" : "object", "properties" : { - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskResult" - } - }, - "timeStamp" : { - "type" : "integer", - "format" : "int32" - } - } - }, - "TaskResult" : { - "required" : [ "resultName", "results", "task" ], - "type" : "object", - "properties" : { - "task" : { - "type" : "string" - }, - "resultName" : { + "team" : { "type" : "string" }, - "resultType" : { + "name" : { "type" : "string" }, - "results" : { + "points" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ResultElement" + "$ref" : "#/components/schemas/ScoreSeriesPoint" } } - } + }, + "required" : [ "team", "name", "points" ] }, - "QueryEvent" : { - "required" : [ "category", "timestamp", "type", "value" ], + "TaskRunSubmissionInfo" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "type" : { - "type" : "string" - }, - "value" : { + "taskRunId" : { "type" : "string" - } - } - }, - "QueryEventLog" : { - "required" : [ "events", "timestamp" ], - "type" : "object", - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" }, - "events" : { + "submissions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/QueryEvent" + "$ref" : "#/components/schemas/SubmissionInfo" } } - } + }, + "required" : [ "taskRunId", "submissions" ] }, - "QueryResult" : { - "required" : [ "item" ], + "RestTaskDescription" : { "type" : "object", "properties" : { - "item" : { + "id" : { "type" : "string" }, - "segment" : { - "type" : "integer", - "format" : "int32" + "name" : { + "type" : "string" }, - "frame" : { - "type" : "integer", - "format" : "int32" + "taskGroup" : { + "type" : "string" }, - "score" : { - "type" : "number", - "format" : "double" + "taskType" : { + "type" : "string" }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - } - }, - "QueryResultLog" : { - "required" : [ "events", "resultSetAvailability", "results", "sortType", "timestamp" ], - "type" : "object", - "properties" : { - "timestamp" : { - "type" : "integer", + "duration" : { + "type" : "number", "format" : "int64" }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { + "mediaCollectionId" : { "type" : "string" }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } + "target" : { + "$ref" : "#/components/schemas/RestTaskDescriptionTarget" }, - "events" : { + "components" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/QueryEvent" + "$ref" : "#/components/schemas/RestTaskDescriptionComponent" } } - } + }, + "required" : [ "id", "name", "taskGroup", "taskType", "duration", "mediaCollectionId", "target", "components" ] }, - "RunInfo" : { - "required" : [ "competitionId", "id", "name", "properties", "tasks", "teams", "type" ], + "RestTeam" : { "type" : "object", "properties" : { - "id" : { + "uid" : { "type" : "string" }, "name" : { "type" : "string" }, - "description" : { + "color" : { "type" : "string" }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TeamInfo" - } + "logoData" : { + "type" : "string" }, - "tasks" : { + "logoId" : { + "type" : "string" + }, + "users" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/TaskInfo" + "type" : "string" } + } + }, + "required" : [ "name", "color", "users" ] + }, + "ViewerInfo" : { + "type" : "object", + "properties" : { + "viewersId" : { + "type" : "string" }, - "competitionId" : { + "username" : { "type" : "string" }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" + "host" : { + "type" : "string" }, - "type" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS" ] + "ready" : { + "type" : "boolean" } - } + }, + "required" : [ "viewersId", "username", "host", "ready" ] }, - "RunProperties" : { - "required" : [ "allowRepeatedTasks", "limitSubmissionPreviews", "participantCanView", "shuffleTasks" ], + "LoginRequest" : { "type" : "object", "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" + "username" : { + "type" : "string" }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" + "password" : { + "type" : "string" } - } + }, + "required" : [ "username", "password" ] }, - "TaskInfo" : { - "required" : [ "duration", "id", "name", "taskGroup", "taskType" ], + "JudgementRequest" : { "type" : "object", "properties" : { - "id" : { + "token" : { "type" : "string" }, - "name" : { - "type" : "string" + "mediaType" : { + "$ref" : "#/components/schemas/JudgementRequestMediaType" }, - "taskGroup" : { + "validator" : { "type" : "string" }, - "taskType" : { + "collection" : { "type" : "string" }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - } - }, - "TeamInfo" : { - "required" : [ "color", "logoId", "name", "uid" ], - "type" : "object", - "properties" : { - "uid" : { + "item" : { "type" : "string" }, - "name" : { + "taskDescription" : { "type" : "string" }, - "color" : { + "startTime" : { "type" : "string" }, - "logoId" : { + "endTime" : { "type" : "string" } - } + }, + "required" : [ "token", "mediaType", "validator", "collection", "item", "taskDescription" ] }, - "RunState" : { - "required" : [ "id", "runStatus", "taskRunStatus", "timeElapsed", "timeLeft" ], + "AdminRunOverview" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "runStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "taskRunStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] - }, - "currentTask" : { - "$ref" : "#/components/schemas/TaskInfo" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TeamTaskOverview" + } } - } + }, + "required" : [ "state", "teamOverviews" ] }, - "ContentElement" : { - "required" : [ "contentType", "offset" ], + "Judgement" : { "type" : "object", "properties" : { - "contentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + "token" : { + "type" : "string" }, - "content" : { + "validator" : { "type" : "string" }, - "offset" : { - "type" : "integer", - "format" : "int64" + "verdict" : { + "$ref" : "#/components/schemas/SubmissionStatus" } - } + }, + "required" : [ "token", "validator", "verdict" ] }, - "TaskHint" : { - "required" : [ "loop", "sequence", "taskId" ], + "QueryEventLog" : { "type" : "object", "properties" : { - "taskId" : { - "type" : "string" + "timestamp" : { + "type" : "number", + "format" : "int64" }, - "sequence" : { + "events" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ContentElement" + "$ref" : "#/components/schemas/QueryEvent" } }, - "loop" : { - "type" : "boolean" + "serverTimeStamp$backend" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] }, - "TaskTarget" : { - "required" : [ "sequence", "taskId" ], + "QueryResultLog" : { "type" : "object", "properties" : { - "taskId" : { + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "sortType" : { "type" : "string" }, - "sequence" : { + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ContentElement" + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" } + }, + "serverTimeStamp$backend" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, - "SubmissionInfo" : { - "required" : [ "memberId", "status", "teamId", "timestamp" ], + "RestFullMediaCollection" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" + "collection" : { + "$ref" : "#/components/schemas/RestMediaCollection" }, - "teamName" : { - "type" : "string" + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "properties" : { + "submission" : { + "$ref" : "#/components/schemas/SubmissionStatus" }, - "memberId" : { + "description" : { "type" : "string" + } + }, + "required" : [ "submission", "description" ] + }, + "RunProperties" : { + "type" : "object", + "properties" : { + "participantCanView" : { + "type" : "boolean" }, - "memberName" : { - "type" : "string" + "shuffleTasks" : { + "type" : "boolean" }, - "status" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + "allowRepeatedTasks" : { + "type" : "boolean" }, - "timestamp" : { + "limitSubmissionPreviews" : { "type" : "integer", - "format" : "int64" - }, - "item" : { - "$ref" : "#/components/schemas/RestMediaItem" + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "Role" : { + "type" : "object", + "properties" : { } + }, + "RestMediaItemType" : { + "type" : "object", + "properties" : { } + }, + "RunType" : { + "type" : "object", + "properties" : { } + }, + "ContentElement" : { + "type" : "object", + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ContentType" }, - "text" : { + "content" : { "type" : "string" }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", + "offset" : { + "type" : "number", "format" : "int64" } - } + }, + "required" : [ "contentType", "offset" ] }, "Score" : { - "required" : [ "score", "teamId" ], "type" : "object", "properties" : { "teamId" : { @@ -5401,190 +5445,218 @@ "type" : "number", "format" : "double" } - } + }, + "required" : [ "teamId", "score" ] }, - "ScoreOverview" : { - "required" : [ "name", "scores" ], + "TaskType" : { "type" : "object", "properties" : { "name" : { "type" : "string" }, - "taskGroup" : { - "type" : "string" + "taskDuration" : { + "type" : "number", + "format" : "int64" }, - "scores" : { + "targetType" : { + "$ref" : "#/components/schemas/ConfiguredOption" + }, + "components" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/Score" + "$ref" : "#/components/schemas/ConfiguredOption" } - } - } - }, - "ScoreSeries" : { - "required" : [ "name", "points", "team" ], - "type" : "object", - "properties" : { - "team" : { - "type" : "string" }, - "name" : { - "type" : "string" + "score" : { + "$ref" : "#/components/schemas/ConfiguredOption" }, - "points" : { + "filter" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ScoreSeriesPoint" + "$ref" : "#/components/schemas/ConfiguredOption" + } + }, + "options" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ConfiguredOption" } } - } + }, + "required" : [ "name", "taskDuration", "targetType", "components", "score", "filter", "options" ] }, - "ScoreSeriesPoint" : { - "required" : [ "score", "timestamp" ], + "TaskGroup" : { "type" : "object", "properties" : { - "score" : { - "type" : "number", - "format" : "double" + "name" : { + "type" : "string" }, - "timestamp" : { - "type" : "integer", - "format" : "int64" + "type" : { + "type" : "string" } - } + }, + "required" : [ "name", "type" ] }, - "CompetitionStartMessage" : { - "required" : [ "competitionId", "name", "properties", "type" ], + "RestTeamGroup" : { "type" : "object", "properties" : { - "competitionId" : { + "uid" : { "type" : "string" }, "name" : { "type" : "string" }, - "type" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS" ] + "teams" : { + "type" : "array", + "items" : { + "type" : "string" + } }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" + "aggregation" : { + "type" : "string" } - } + }, + "required" : [ "name", "teams", "aggregation" ] }, - "ViewerInfo" : { - "required" : [ "host", "ready", "username", "viewersId" ], + "TeamInfo" : { "type" : "object", "properties" : { - "viewersId" : { + "uid" : { "type" : "string" }, - "username" : { + "name" : { "type" : "string" }, - "host" : { + "color" : { "type" : "string" }, - "ready" : { - "type" : "boolean" + "logoId" : { + "type" : "string" } - } + }, + "required" : [ "uid", "name", "color", "logoId" ] }, - "TaskRunSubmissionInfo" : { - "required" : [ "submissions", "taskRunId" ], + "RunManagerStatus" : { + "type" : "object", + "properties" : { } + }, + "RestTaskRunStatus" : { + "type" : "object", + "properties" : { } + }, + "SubmissionStatus" : { + "type" : "object", + "properties" : { } + }, + "TaskResult" : { "type" : "object", "properties" : { - "taskRunId" : { + "task" : { "type" : "string" }, - "submissions" : { + "resultName" : { + "type" : "string" + }, + "resultType" : { + "type" : "string" + }, + "results" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" + "$ref" : "#/components/schemas/ResultElement" } } - } + }, + "required" : [ "task", "resultName", "results" ] }, - "PastTaskInfo" : { - "required" : [ "descriptionId", "name", "numberOfSubmissions", "taskGroup", "taskId", "taskType" ], + "AuditLogEntryType" : { + "type" : "object", + "properties" : { } + }, + "ClientRunInfo" : { "type" : "object", "properties" : { - "taskId" : { - "type" : "string" - }, - "descriptionId" : { + "id" : { "type" : "string" }, "name" : { "type" : "string" }, - "taskGroup" : { + "description" : { "type" : "string" }, - "taskType" : { - "type" : "string" + "status" : { + "$ref" : "#/components/schemas/RunManagerStatus" + } + }, + "required" : [ "id", "name", "status" ] + }, + "ScoreSeriesPoint" : { + "type" : "object", + "properties" : { + "score" : { + "type" : "number", + "format" : "double" }, - "numberOfSubmissions" : { - "type" : "integer", - "format" : "int32" + "timestamp" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "score", "timestamp" ] }, - "AdminRunOverview" : { - "required" : [ "state", "teamOverviews" ], + "RestTaskDescriptionTarget" : { "type" : "object", "properties" : { - "state" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + "type" : { + "$ref" : "#/components/schemas/TargetOption" }, - "teamOverviews" : { + "mediaItems" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/TeamTaskOverview" + "$ref" : "#/components/schemas/RestTaskDescriptionTargetItem" } } - } + }, + "required" : [ "type", "mediaItems" ] }, - "TaskRunOverview" : { - "required" : [ "duration", "group", "id", "name", "status", "taskId", "type" ], + "RestTaskDescriptionComponent" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" + "type" : { + "$ref" : "#/components/schemas/QueryComponentOption" }, - "name" : { - "type" : "string" + "start" : { + "type" : "number", + "format" : "int64" }, - "type" : { + "end" : { + "type" : "number", + "format" : "int64" + }, + "description" : { "type" : "string" }, - "group" : { + "path" : { "type" : "string" }, - "duration" : { - "type" : "integer", - "format" : "int64" + "dataType" : { + "type" : "string" }, - "taskId" : { + "mediaItem" : { "type" : "string" }, - "status" : { - "type" : "string", - "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" + "range" : { + "$ref" : "#/components/schemas/RestTemporalRange" } - } + }, + "required" : [ "type" ] + }, + "JudgementRequestMediaType" : { + "type" : "object", + "properties" : { } }, "TeamTaskOverview" : { - "required" : [ "tasks", "teamId" ], "type" : "object", "properties" : { "teamId" : { @@ -5596,415 +5668,164 @@ "$ref" : "#/components/schemas/TaskRunOverview" } } - } + }, + "required" : [ "teamId", "tasks" ] }, - "JudgementRequest" : { - "required" : [ "collection", "item", "mediaType", "taskDescription", "token", "validator" ], + "QueryEvent" : { "type" : "object", "properties" : { - "token" : { - "type" : "string" - }, - "mediaType" : { - "type" : "string", - "enum" : [ "TEXT", "VIDEO", "IMAGE" ] - }, - "validator" : { - "type" : "string" - }, - "collection" : { - "type" : "string" - }, - "item" : { - "type" : "string" + "timestamp" : { + "type" : "number", + "format" : "int64" }, - "taskDescription" : { - "type" : "string" + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" }, - "startTime" : { + "type" : { "type" : "string" }, - "endTime" : { + "value" : { "type" : "string" } - } + }, + "required" : [ "timestamp", "category", "type", "value" ] }, - "Judgement" : { - "required" : [ "token", "validator", "verdict" ], + "QueryResult" : { "type" : "object", "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { + "item" : { "type" : "string" }, - "verdict" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - } - } - }, - "JudgementValidatorStatus" : { - "required" : [ "open", "pending", "validator" ], - "type" : "object", - "properties" : { - "validator" : { - "type" : "string" + "segment" : { + "type" : "integer", + "format" : "int32" }, - "pending" : { + "frame" : { "type" : "integer", "format" : "int32" }, - "open" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { "type" : "integer", "format" : "int32" } - } + }, + "required" : [ "item" ] }, - "JudgementVote" : { - "required" : [ "verdict" ], + "ContentType" : { "type" : "object", "properties" : { - "verdict" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + "mimeType" : { + "type" : "string" + }, + "base64" : { + "type" : "boolean" } - } + }, + "required" : [ "mimeType", "base64" ] }, - "AuditLogInfo" : { - "required" : [ "latest", "size", "timestamp" ], + "ConfiguredOption" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" + "option" : { + "$ref" : "#/components/schemas/Option" }, - "size" : { + "parameters" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "" : { + "type" : "string" + }, + "asBool" : { + "type" : "boolean" + }, + "asInt" : { "type" : "integer", "format" : "int32" }, - "latest" : { - "type" : "integer", + "asLong" : { + "type" : "number", "format" : "int64" + }, + "asFloat" : { + "type" : "number", + "format" : "float" + }, + "asDouble" : { + "type" : "number", + "format" : "double" } - } + }, + "required" : [ "option", "parameters" ] }, - "RestAuditLogEntry" : { - "required" : [ "id", "timestamp", "type" ], + "ResultElement" : { "type" : "object", "properties" : { - "type" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + "item" : { + "type" : "string" }, - "id" : { + "text" : { "type" : "string" }, - "timestamp" : { + "startTimeCode" : { + "type" : "string" + }, + "endTimeCode" : { + "type" : "string" + }, + "index" : { "type" : "integer", - "format" : "int64" + "format" : "int32" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + }, + "weight" : { + "type" : "number", + "format" : "float" } } }, - "RestCompetitionEndAuditLogEntry" : { - "required" : [ "api", "competition", "id", "timestamp", "type" ], + "TargetOption" : { "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - } - } - } ] + "properties" : { } }, - "RestCompetitionStartAuditLogEntry" : { - "required" : [ "api", "competition", "id", "timestamp", "type" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - } - } - } ] - }, - "RestJudgementAuditLogEntry" : { - "required" : [ "api", "competition", "id", "timestamp", "token", "type", "validator", "verdict" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "token" : { - "type" : "string" - }, - "verdict" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - } - } - } ] - }, - "RestLoginAuditLogEntry" : { - "required" : [ "api", "id", "session", "timestamp", "type", "user" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "user" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - } - } - } ] - }, - "RestLogoutAuditLogEntry" : { - "required" : [ "api", "id", "session", "timestamp", "type" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "session" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - } - } - } ] - }, - "RestPrepareJudgementAuditLogEntry" : { - "required" : [ "id", "submission", "timestamp", "token", "type", "validator" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "validator" : { - "type" : "string" - }, - "token" : { - "type" : "string" - }, - "submission" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } - } - } ] - }, - "RestSubmissionAuditLogEntry" : { - "required" : [ "address", "api", "competition", "id", "submission", "taskName", "timestamp", "type" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "submission" : { - "$ref" : "#/components/schemas/SubmissionInfo" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - }, - "address" : { - "type" : "string" - } - } - } ] - }, - "RestSubmissionStatusOverwriteAuditLogEntry" : { - "required" : [ "api", "id", "newVerdict", "runId", "session", "submissionId", "timestamp", "type" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "runId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "newVerdict" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "session" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - } - } - } ] - }, - "RestSubmissionValidationAuditLogEntry" : { - "required" : [ "id", "submissionId", "timestamp", "type", "validator", "verdict" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "submissionId" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - } - } - } ] - }, - "RestTaskEndAuditLogEntry" : { - "required" : [ "api", "competition", "id", "taskName", "timestamp", "type" ], - "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - } - } - } ] - }, - "RestTaskModifiedAuditLogEntry" : { - "required" : [ "api", "competition", "id", "modification", "taskName", "timestamp", "type" ], + "RestTaskDescriptionTargetItem" : { "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "modification" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - } + "properties" : { + "mediaItem" : { + "type" : "string" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/RestTemporalRange" } - } ] + }, + "required" : [ "mediaItem" ] }, - "RestTaskStartAuditLogEntry" : { - "required" : [ "api", "competition", "id", "taskName", "timestamp", "type" ], + "QueryComponentOption" : { "type" : "object", - "allOf" : [ { - "$ref" : "#/components/schemas/RestAuditLogEntry" - }, { - "type" : "object", - "properties" : { - "competition" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "api" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "user" : { - "type" : "string" - } - } - } ] + "properties" : { } }, - "CurrentTime" : { - "required" : [ "timeStamp" ], + "RestTemporalRange" : { "type" : "object", "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" + "start" : { + "$ref" : "#/components/schemas/RestTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/RestTemporalPoint" } - } + }, + "required" : [ "start", "end" ] }, - "ClientRunInfo" : { - "required" : [ "id", "name", "status" ], + "TaskRunOverview" : { "type" : "object", "properties" : { "id" : { @@ -6013,49 +5834,70 @@ "name" : { "type" : "string" }, - "description" : { + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "number", + "format" : "int64" + }, + "taskId" : { "type" : "string" }, "status" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + "$ref" : "#/components/schemas/TaskRunStatus" + }, + "started" : { + "type" : "number", + "format" : "int64" + }, + "ended" : { + "type" : "number", + "format" : "int64" } - } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] }, - "ClientRunInfoList" : { - "required" : [ "runs" ], + "QueryEventCategory" : { + "type" : "object", + "properties" : { } + }, + "Option" : { "type" : "object", "properties" : { - "runs" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ClientRunInfo" - } + "ordinal" : { + "type" : "integer", + "format" : "int32" } - } + }, + "required" : [ "ordinal" ] }, - "ClientTaskInfo" : { - "required" : [ "id", "name", "remainingTime", "running", "taskGroup" ], + "RestTemporalPoint" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { + "value" : { "type" : "string" }, - "remainingTime" : { - "type" : "integer", - "format" : "int64" - }, - "running" : { - "type" : "boolean" + "unit" : { + "$ref" : "#/components/schemas/RestTemporalUnit" } - } + }, + "required" : [ "value", "unit" ] + }, + "TaskRunStatus" : { + "type" : "object", + "properties" : { } + }, + "RestTemporalUnit" : { + "type" : "object", + "properties" : { } } - } - } + }, + "securitySchemes" : { } + }, + "servers" : [ ], + "security" : null } \ No newline at end of file From ea3a50191405b08a10531dafe8e55669c95884a1 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 25 Oct 2022 18:14:44 +0200 Subject: [PATCH 007/498] Merged stash into branch (Preparation for storage engine changes. Does not compile) --- backend/build.gradle | 10 +- .../src/main/kotlin/dev/dres/Playground.kt | 163 - .../dev/dres/api/cli/CompetitionCommand.kt | 4 +- .../dres/api/cli/MediaCollectionCommand.kt | 14 +- .../rest/handler/AbstractPreviewHandler.kt | 4 +- .../handler/CompetitionRunAdminHandler.kt | 6 +- .../dres/api/rest/handler/GetMediaHandler.kt | 2 +- .../api/rest/handler/SubmissionHandler.kt | 2 +- .../types/collection/RestMediaCollection.kt | 2 +- .../src/main/kotlin/dev/dres/data/dbo/DAO.kt | 271 - .../kotlin/dev/dres/data/dbo/DaoIndexer.kt | 62 - .../dev/dres/data/dbo/DataAccessLayer.kt | 41 - .../dev/dres/data/dbo/NumericDaoIndexer.kt | 24 - .../main/kotlin/dev/dres/data/model/Entity.kt | 6 - .../kotlin/dev/dres/data/model/admin/User.kt | 4 +- .../model/basics/media/MediaCollection.kt | 23 +- .../dres/data/model/basics/media/MediaItem.kt | 67 +- .../model/basics/media/MediaItemSegment.kt | 38 +- .../competition/CompetitionDescription.kt | 4 +- .../competition/TaskDescriptionTarget.kt | 4 +- .../dev/dres/data/model/competition/Team.kt | 8 +- .../data/model/run/interfaces/Competition.kt | 4 +- .../serializers/MediaCollectionSerializer.kt | 2 +- .../dev/dres/run/audit/AuditLogEntry.kt | 4 +- .../dev/dres/run/updatables/DAOUpdatable.kt | 4 +- frontend/package-lock.json | 25678 ++++++++++++++++ frontend/yarn.lock | 2812 +- 27 files changed, 27276 insertions(+), 1987 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/Playground.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/dbo/DAO.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/dbo/DaoIndexer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/dbo/NumericDaoIndexer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/Entity.kt create mode 100644 frontend/package-lock.json diff --git a/backend/build.gradle b/backend/build.gradle index b34742641..0953736a4 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -46,6 +46,7 @@ compileTestKotlin { dependencies { def javalin = '5.1.1' + def xodusVersion = '2.0.1' def log4jVersion = '2.17.0' ///// Frontend files (produced by sub-project). @@ -54,8 +55,12 @@ dependencies { ///// FFMpeg dependency implementation files("$buildDir/ext") {builtBy 'setupFFMpeg'} - ///// MapDB - implementation group: 'org.mapdb', name: 'mapdb', version: '3.0.8' + ///// Xodus & Xodus DNQ + implementation group: 'org.jetbrains.xodus', name: 'xodus-openAPI', version: xodusVersion + implementation group: 'org.jetbrains.xodus', name: 'xodus-environment', version: xodusVersion + implementation group: 'org.jetbrains.xodus', name: 'xodus-entity-store', version: xodusVersion + implementation group: 'org.jetbrains.xodus', name: 'xodus-vfs', version: xodusVersion + implementation group: 'org.jetbrains.xodus', name: 'dnq', version: '2.0.0' ////// Javalin implementation group: 'io.javalin', name: 'javalin', version: "$javalin" @@ -81,7 +86,6 @@ dependencies { implementation group: 'org.jline', name: 'jline-builtins', version: '3.20.0' implementation group: 'com.jakewharton.picnic', name: 'picnic', version: '0.5.0' - ///// Fuel implementation group: 'com.github.kittinunf.fuel', name: 'fuel', version: '2.3.1' diff --git a/backend/src/main/kotlin/dev/dres/Playground.kt b/backend/src/main/kotlin/dev/dres/Playground.kt deleted file mode 100644 index 412a8a03b..000000000 --- a/backend/src/main/kotlin/dev/dres/Playground.kt +++ /dev/null @@ -1,163 +0,0 @@ -package dev.dres - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.data.dbo.DAO -import dev.dres.data.model.UID -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.StatusAspect -import dev.dres.data.serializers.CompetitionRunSerializer -import dev.dres.data.serializers.CompetitionSerializer -import dev.dres.data.serializers.MediaItemSerializer -import java.io.File -import java.nio.file.Paths - - -object Playground { - - data class StatusConrainer(override var status: SubmissionStatus) : StatusAspect - - @JvmStatic - fun main(args: Array) { - val competitionId = UID("8c57b76e-416f-44ea-a783-7d0e8becf7a3") - - val mapper = jacksonObjectMapper() - - val mediaItems = DAO(Paths.get("C:/Users/Lucaro/Desktop/vbs21/data/mediaItems.db"), MediaItemSerializer) - val competitionSerializer = CompetitionSerializer(mediaItems) - - val runs: DAO = DAO(Paths.get("C:/Users/Lucaro/Desktop/vbs21/data/runs.db"), CompetitionRunSerializer(competitionSerializer)) - - val writer = File("C:/Users/Lucaro/Desktop/vbs21/mediaItems.csv").printWriter() - - mediaItems.sortedBy { it.name }.forEach { - writer.println("${it.id.string},${it.name}") - } - - writer.flush() - writer.close() - - -// val writer = File("C:\\Users\\Lucaro\\Desktop\\vbs21\\run.json").printWriter() -// -// writer.println(mapper.writeValueAsString(runs.filter { it.id == competitionId }.first())) -// -// writer.flush() -// writer.close() - - -// val audit = DAO(Paths.get("C:\\Users\\Lucaro\\Desktop\\vbs21\\data\\auditLog.db"), AuditLogEntrySerializer) -// -// -// -// var flag = false -// -// val logEntries = audit.sortedBy { it.timestamp }.filter { -// flag || if (it is CompetitionStartAuditLogEntry && it.competition == competitionId) { -// flag = true -// true -// } else false -// } -// -// val writer = File("C:\\Users\\Lucaro\\Desktop\\vbs21\\audits.json").printWriter() -// -// logEntries.forEach { -// writer.println(mapper.writeValueAsString(it)) -// } -// -// writer.flush() -// writer.close() - -// val elements = listOf( -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT) -// -// ) -// -// println(InferredAveragePrecisionScorer.infAP(elements)) - - -// -// -// val audit = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/auditLog.db"), AuditLogEntrySerializer) -// -// val mediaItems = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/mediaItems.db"), MediaItemSerializer) -// val competitionSerializer = CompetitionSerializer(mediaItems) -// -// val competitions = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/competitions.db"), competitionSerializer) -// -// -// val competition = competitions[UID("db2ef71a-df42-4fbd-a2ea-2e1c8b393edd")]!! -// -// val targets = competition.tasks.map { -// it.name to (it.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.map { it.name } -// }.toMap() -// -// val teams = competition.teams.map { it.name } -// -//// println(audit.find { it.id.string == "b54fc9dd-5a86-4c92-be7b-7642b35c887b" }!!.timestamp) -//// -//// return -// -// val events = audit.filter { it.timestamp > 1604053744241 }.sortedBy { it.timestamp } -// -// val taskStartTimes = mutableMapOf() -// -// val submissionWriter = File("submissions.csv").printWriter() -// val taskStartWriter = File("taskStart.csv").printWriter() -// -// submissionWriter.println("timestamp,task,time,team,member,session,item,correct") -// taskStartWriter.println("timestamp,task") -// -// for (event in events) { -// -// when(event) { -// -// is TaskStartAuditLogEntry -> { -// -// taskStartTimes[event.taskName] = event.timestamp -// taskStartWriter.println("${event.timestamp},${event.taskName}") -// -// } -// -// -// is SubmissionAuditLogEntry -> { -// -// val taskName = event.taskName -// -// if (!targets.containsKey(taskName)){ -// println("ignoring $taskName") -// continue -// } -// -// val time = event.submission.timestamp - taskStartTimes[taskName]!! -// val item = event.submission.item.name -// val correct = (targets[taskName] ?: -// error("$taskName not found")).contains(item) -// -// -// -// //submissionWriter.println("${event.timestamp},$taskName,$time,${teams[event.submission.teamId]},${event.submission.member.string},${event.user},$item,$correct") -// -// } -// -// else -> { -// println("ignoring $event") -// } -// -// } -// -// } -// -// submissionWriter.flush() -// submissionWriter.close() -// taskStartWriter.flush() -// taskStartWriter.close() -// - - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt index f17b7fa86..cc2fcf2ea 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt @@ -136,7 +136,7 @@ class CompetitionCommand(internal val competitions: DAO, return } - val videoFile = File(File(collection.basePath), item.location) + val videoFile = File(File(collection.path), item.location) if (!videoFile.exists()) { println("ERROR: file ${videoFile.absolutePath} not found for item ${item.name}") @@ -144,7 +144,7 @@ class CompetitionCommand(internal val competitions: DAO, } println("rendering ${it.item} at ${it.temporalRange}") - FFmpegUtil.prepareMediaSegmentTask(it, collection.basePath, this@CompetitionCommand.taskCacheLocation) + FFmpegUtil.prepareMediaSegmentTask(it, collection.path, this@CompetitionCommand.taskCacheLocation) } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 238ec9b87..3a42469ca 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -121,7 +121,7 @@ class MediaCollectionCommand(val collections: DAO, val items: D dirty = true basePath!! }else{ - collection.basePath + collection.path } if(dirty){ @@ -159,7 +159,7 @@ class MediaCollectionCommand(val collections: DAO, val items: D } body { this@MediaCollectionCommand.collections.forEach { - row(it.id.string, it.name, it.description ?: "", it.basePath, this@MediaCollectionCommand.mediaItemCollectionIndex.filter { uid -> it.id.equals(uid) }.size) + row(it.id.string, it.name, it.description ?: "", it.path, this@MediaCollectionCommand.mediaItemCollectionIndex.filter { uid -> it.id.equals(uid) }.size) } } } @@ -262,7 +262,7 @@ class MediaCollectionCommand(val collections: DAO, val items: D val collectionItems = this@MediaCollectionCommand.items.filter { it.collection == collectionId } - val baseFile = File(collection.basePath) + val baseFile = File(collection.path) var counter = 0 @@ -310,20 +310,20 @@ class MediaCollectionCommand(val collections: DAO, val items: D return } - val base = File(collection.basePath) + val base = File(collection.path) if (!base.exists()) { - println("Cannot scan collection, '${collection.basePath}' does not exist.") + println("Cannot scan collection, '${collection.path}' does not exist.") return } if (!base.isDirectory) { - println("Cannot scan collection, '${collection.basePath}' is no directory.") + println("Cannot scan collection, '${collection.path}' is no directory.") return } if (!base.canRead()) { - println("Cannot scan collection, '${collection.basePath}' is not readable.") + println("Cannot scan collection, '${collection.path}' is not readable.") return } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt index c48d6656c..10b53e8cc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt @@ -61,7 +61,7 @@ abstract class AbstractPreviewHandler( val collection = this.collections[item.collection] ?: throw ErrorStatusException(404, "Collection ${item.collection} does not exist.", ctx) - val basePath = File(collection.basePath) + val basePath = File(collection.path) if (item is MediaItem.ImageItem) { @@ -90,7 +90,7 @@ abstract class AbstractPreviewHandler( ctx.sendFile(imgPath.toFile()) } else { //if not, wait for it if necessary - val future = FFmpegUtil.executeFFmpegAsync(Path.of(collection.basePath, item.location), time, imgPath) + val future = FFmpegUtil.executeFFmpegAsync(Path.of(collection.path, item.location), time, imgPath) val waitTime = if (RestApi.readyThreadCount > 500) { 3L diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 65e12ba1e..b23fb9419 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -1,11 +1,9 @@ package dev.dres.api.rest.handler -import com.sun.net.httpserver.Authenticator import dev.dres.api.rest.AccessManager import dev.dres.api.rest.RestApiRole import dev.dres.api.rest.types.collection.RestMediaItem import dev.dres.api.rest.types.competition.CompetitionStartMessage -import dev.dres.api.rest.types.competition.RestCompetitionDescription import dev.dres.api.rest.types.run.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException @@ -152,7 +150,7 @@ class CreateCompetitionRunAdminHandler( val collection = this.collections[item.collection] ?: throw ErrorStatusException(400, "collection ${item.collection} not found", ctx) - val videoFile = File(File(collection.basePath), item.location) + val videoFile = File(File(collection.path), item.location) if (!videoFile.exists()) { logger.error("file ${videoFile.absolutePath} not found for item ${item.name}") @@ -162,7 +160,7 @@ class CreateCompetitionRunAdminHandler( val outputFile = File(cacheLocation, it.cacheItemName()) if (!outputFile.exists()) { logger.warn("Query video file for item ${it.item} not found, rendering to ${outputFile.absolutePath}") - FFmpegUtil.prepareMediaSegmentTask(it, collection.basePath, cacheLocation) + FFmpegUtil.prepareMediaSegmentTask(it, collection.path, cacheLocation) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt index 35d715021..af019176c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt @@ -60,7 +60,7 @@ class GetMediaHandler(private val itemCache: DaoIndexer return } - val basePath = File(collection.basePath) + val basePath = File(collection.path) val itemFile = File(basePath, item.location) try{ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index 97dfd2f8d..3433a2053 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -223,7 +223,7 @@ class SubmissionHandler (val collections: DAO, private val item if (Files.exists(imgPath)){ return } - val mediaItemLocation = Path.of(collection.basePath, submission.item.location) + val mediaItemLocation = Path.of(collection.path, submission.item.location) FFmpegUtil.extractFrame(mediaItemLocation, submission.start, imgPath) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt index 5710858d6..2bb1dce94 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt @@ -21,7 +21,7 @@ data class RestMediaCollection(val id: String = UID.EMPTY.string, val name: Stri * * @param task The [TaskDescription] to convert. */ - fun fromMediaCollection(item: MediaCollection) = RestMediaCollection(item.id.string, item.name, item.description, item.basePath) + fun fromMediaCollection(item: MediaCollection) = RestMediaCollection(item.id.string, item.name, item.description, item.path) } /** diff --git a/backend/src/main/kotlin/dev/dres/data/dbo/DAO.kt b/backend/src/main/kotlin/dev/dres/data/dbo/DAO.kt deleted file mode 100644 index 688c71796..000000000 --- a/backend/src/main/kotlin/dev/dres/data/dbo/DAO.kt +++ /dev/null @@ -1,271 +0,0 @@ -package dev.dres.data.dbo - -import com.github.benmanes.caffeine.cache.Caffeine -import com.github.benmanes.caffeine.cache.LoadingCache -import dev.dres.data.model.Entity -import dev.dres.data.model.UID -import dev.dres.data.serializers.UIDSerializer -import dev.dres.utilities.extensions.optimisticRead -import dev.dres.utilities.extensions.write -import org.mapdb.DB -import org.mapdb.DBMaker -import org.mapdb.Serializer -import java.nio.file.Path -import java.util.concurrent.TimeUnit -import java.util.concurrent.locks.StampedLock - -/** - * A simple data access object [DAO] implementation for the [Entity] objects used by DRES. - * - * @author Ralph Gasser - * @version 1.0 - */ -class DAO(path: Path, private val serializer: Serializer, cacheSize: Long = 100, cacheDuration: Long = 30) : Iterable, AutoCloseable { - - init { - path.parent.toFile().mkdirs() - } - - /** The [DB] object used to store */ - private val db = DBMaker.fileDB(path.toFile()).transactionEnable().fileMmapEnableIfSupported().make() - - /** Internal data structure used to keep track of the data held by this [DAO]. */ - private val data = this.db.hashMap("data", UIDSerializer, this.serializer).counterEnable().createOrOpen() - - /** Stamped lock to mediate read/write operations through this [DAO]. */ - private val lock: StampedLock = StampedLock() - - private val indexers = mutableListOf>() - - /** Internal cache */ - private val cache: LoadingCache = Caffeine.newBuilder() - .maximumSize(cacheSize) - .expireAfterAccess(cacheDuration, TimeUnit.MINUTES) - .build { key -> data[key]} - - /** Name of the entity accessed through this [DAO]. */ - val name = path.fileName.toString().replace(".db","") - - init { - this.db.commit() - } - - /** - * Returns the value [T] for the given ID. - * - * @param id The ID of the entry. - * @return Entry [T] - */ - operator fun get(id: UID) = this.lock.optimisticRead { - cache[id] - } - - /** - * Returns true if value for given key exists and false otherwise. - * - * @param id The key of the entry. - * @return Entry [T] - */ - fun exists(id: UID) = this.lock.optimisticRead { this.data.containsKey(id) } - - /** - * Deletes the value [T] for the given ID. - * - * @param id The ID of the entry that should be deleted - * @return Deleted entry [T] - */ - fun delete(id: UID): T? = this.lock.write { - try { - val deleted = this.data.remove(id) - this.db.commit() - this.cache.invalidate(id) - if (deleted != null){ - this.indexers.forEach { - it.delete(deleted) - } - } - return deleted - } catch (e: Throwable) { - this.db.rollback() - throw e - } - } - - /** - * Deletes the value [T] - * - * @param value The value that should be deleted. - * @return Deleted entry [T] - */ - fun delete(value: T) = this.delete(value.id) - - - /** - * Deletes all values with given ids - */ - fun batchDelete(ids: Iterable) = this.lock.write { - val toDelete = mutableListOf() - try { - for (id in ids){ - val t = data[id] - if (t != null){ - toDelete.add(t) - } - this.data.remove(id) - } - this.db.commit() - this.cache.invalidate(ids) - toDelete.forEach { t -> - this.indexers.forEach { - it.delete(t) - } - } - } catch (e: Throwable) { - this.db.rollback() - throw e - } - } - - /** - * Updates the value for the given ID with the new value [T] - * - * @param id The ID of the value that should be updated - * @param value The new value [T] - */ - fun update(id: UID, value: T) = this.lock.write { - if (this.data.containsKey(id)) { - val old = this.data[id]!! - try { - this.data[id] = value - this.db.commit() - this.cache.put(id, value) - this.indexers.forEach { - it.delete(old) - it.append(value) - } - } catch (e: Throwable) { - this.db.rollback() - throw e - } - } else { - throw IndexOutOfBoundsException("Could not update value with ID $id because such a value doesn't exist.") - } - } - - /** - * Updates the value [T] - * - * @param value The new value [T] - */ - fun update(value: T) = this.update(value.id, value) - - /** - * Appends the given value using this [DAO] - * - * @param value The value [T] that should be appended - * @return ID of the new value. - */ - fun append(value: T): UID = this.lock.write { - val next = UID() - value.id = next - try { - this.data[next] = value - this.db.commit() - this.cache.put(value.id, value) - this.indexers.forEach { - it.append(value) - } - } catch (e: Throwable) { - this.db.rollback() - throw e - } - return next - } - - /** - * Appends the given values using this [DAO] - * - * @param values An iterable of the values [T] that should be appended. - */ - fun batchAppend(values: Iterable) = this.lock.write { - try { - for (value in values) { - val next = UID() - value.id = next - this.data[next] = value - this.cache.put(value.id, value) - this.indexers.forEach { - it.append(value) - } - } - this.db.commit() - this.data.values - } catch (e: Throwable) { - this.db.rollback() - this.indexers.forEach { it.rebuild() } - this.cache.invalidateAll(values) - throw e - } - } - - /** - * Closes this [DAO] - */ - override fun close() { - if (!this.db.isClosed()) { - this.data.close() - this.db.close() - this.cache.invalidateAll() - } - } - - /** - * Returns an iterator over the elements of this object. - */ - override fun iterator(): Iterator = object : Iterator { - private val ids = this@DAO.lock.optimisticRead { - this@DAO.data.keys.toList() - } - private var idx = -1 - - override fun hasNext(): Boolean = this@DAO.lock.optimisticRead { - while (++idx < ids.size) { - if (this@DAO.data.containsKey(ids[idx])) { - return true - } - } - return false - } - - override fun next(): T = this@DAO.lock.optimisticRead { - if (this@DAO.data.containsKey(this.ids[idx])) { - return this@DAO.data[this.ids[idx]]!! - } - throw NoSuchElementException("There is no element with ID ${this.ids[idx]} for DAO '${this@DAO.name}'.") - } - } - - fun filter(predicate: (T) -> Boolean): List = this.lock.optimisticRead { - return this.data.values.filterNotNull().filter(predicate) - } - - fun map(transform: (T) -> R): List = this.lock.optimisticRead { - return this.data.values.filterNotNull().map(transform) - } - - fun find(predicate: (T) -> Boolean): T? = this.lock.optimisticRead { - return this.data.values.find{ it != null && predicate(it) } - } - - fun forEach(action: (T) -> Unit): Unit = this.lock.optimisticRead { - return this.data.values.filterNotNull().forEach(action) - } - - fun groupBy(keySelector: (T) -> K): Map> = this.lock.optimisticRead { - return this.data.values.filterNotNull().groupBy(keySelector) - } - - internal fun addIndexer(daoIndexer: DaoIndexer) { - indexers.add(daoIndexer) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/dbo/DaoIndexer.kt b/backend/src/main/kotlin/dev/dres/data/dbo/DaoIndexer.kt deleted file mode 100644 index 39f9684c4..000000000 --- a/backend/src/main/kotlin/dev/dres/data/dbo/DaoIndexer.kt +++ /dev/null @@ -1,62 +0,0 @@ -package dev.dres.data.dbo - -import dev.dres.data.model.Entity -import dev.dres.data.model.UID -import dev.dres.utilities.extensions.optimisticRead -import dev.dres.utilities.extensions.write -import java.util.concurrent.locks.StampedLock - -/** - * Wrapper for DAO which enables index-based access - */ -open class DaoIndexer internal constructor(internal val dao: DAO, internal val keyTransform: (T) -> K) { - - internal val index: MutableMap> = mutableMapOf() - - internal val lock = StampedLock() - - init { - //load DAO to index - rebuild() - dao.addIndexer(this) - } - - /** - * rebuilds the index - */ - fun rebuild() = lock.write { - val map = dao.groupBy( keyTransform ).mapValues { it.value.map { e -> e.id }.toMutableList() } - index.clear() - index.putAll(map) - } - - operator fun get(key: K): List = lock.optimisticRead { - index[key]?.mapNotNull { dao[it] } ?: emptyList() - } - - fun keys(): Set = lock.optimisticRead { index.keys } - - - internal fun delete(value: T) = lock.write { - index[keyTransform(value)]?.remove(value.id) - } - - internal fun append(value: T) = lock.write { - val key = keyTransform(value) - if (!index.containsKey(key)) { - index[key] = mutableListOf(value.id) - } else { - index[key]!!.add(value.id) - } - } - - fun find(predicate: (K) -> Boolean): List = this.lock.optimisticRead { - val key = this.index.keys.find(predicate) - return if (key == null) emptyList() else this[key] - } - - fun filter(predicate: (K) -> Boolean): List = this.lock.optimisticRead { - return this.index.keys.filter(predicate).flatMap { this[it] } - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt b/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt deleted file mode 100644 index 54b4bedd0..000000000 --- a/backend/src/main/kotlin/dev/dres/data/dbo/DataAccessLayer.kt +++ /dev/null @@ -1,41 +0,0 @@ -package dev.dres.data.dbo - -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.serializers.* -import java.nio.file.Path - -/** - * The data access layer used by DRES - * - * @author Ralph Gasser - * @version 1.0 - */ -class DataAccessLayer(private val basePath: Path) { - /** List of [dev.dres.data.model.admin.User]s managed by this DRES instance. */ - val users = DAO(this.basePath.resolve("users.db"), UserSerializer, cacheDuration = 1440) - - val collections = DAO(this.basePath.resolve("collections.db"), MediaCollectionSerializer) - val collectionNameIndex = DaoIndexer(collections){it.name} - val collectionUidIndex = DaoIndexer(collections){it.id} - - val mediaItems = DAO(this.basePath.resolve("mediaItems.db"), MediaItemSerializer) - val mediaItemCollectionIndex = DaoIndexer(mediaItems) { it.collection } - val mediaItemCollectionNameIndex = DaoIndexer(mediaItems) { it.collection to it.name } - val mediaItemCollectionUidIndex = DaoIndexer(mediaItems){it.collection to it.id} - val mediaItemPathIndex = DaoIndexer(mediaItems){it.location} - - - val mediaSegments = DAO(this.basePath.resolve("mediaSegments.db"), MediaItemSegmentSerializer, cacheSize = 10_000) - val mediaSegmentItemIdIndex = DaoIndexer(mediaSegments){it.mediaItemId} - - private val competitionSerializer = CompetitionSerializer(mediaItems) - - /** List of [dev.dres.data.model.competition.CompetitionDescription]s managed by this DRES instance. */ - val competitions = DAO(this.basePath.resolve("competitions.db"), competitionSerializer) - - /** List of [dev.dres.data.model.run.InteractiveSynchronousCompetition]s managed by this DRES instance. */ - val runs: DAO = DAO(this.basePath.resolve("runs.db"), CompetitionRunSerializer(competitionSerializer)) - - val audit = DAO(this.basePath.resolve("auditLog.db"), AuditLogEntrySerializer, cacheDuration = 1440) - val auditTimes = NumericDaoIndexer(audit){it.timestamp} -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/dbo/NumericDaoIndexer.kt b/backend/src/main/kotlin/dev/dres/data/dbo/NumericDaoIndexer.kt deleted file mode 100644 index dcb1fce0c..000000000 --- a/backend/src/main/kotlin/dev/dres/data/dbo/NumericDaoIndexer.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.dres.data.dbo - -import dev.dres.data.model.Entity -import dev.dres.utilities.extensions.optimisticRead - -class NumericDaoIndexerinternal constructor(dao: DAO, keyTransform: (T) -> K) : DaoIndexer(dao, keyTransform) { - - fun inRange(lower: K, upper: K): List = lock.optimisticRead { - val lowerD = lower.toDouble() - val upperD = upper.toDouble() - index.keys.filter { it.toDouble() >= lowerD && it.toDouble() >= upperD }.mapNotNull { index[it] }.flatten().mapNotNull { dao[it] } - } - - fun atLeast(lower: K): List = lock.optimisticRead { - val lowerD = lower.toDouble() - index.keys.filter { it.toDouble() >= lowerD }.mapNotNull { index[it] }.flatten().mapNotNull { dao[it] } - } - - fun atMost(upper: K): List = lock.optimisticRead { - val upperD = upper.toDouble() - index.keys.filter { it.toDouble() >= upperD }.mapNotNull { index[it] }.flatten().mapNotNull { dao[it] } - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/Entity.kt b/backend/src/main/kotlin/dev/dres/data/model/Entity.kt deleted file mode 100644 index 54c46f535..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/Entity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.dres.data.model - -interface Entity { - /** Primary key of this [Entity]. */ - var id: UID -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index 98f23a9e3..d4f53a004 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -2,7 +2,7 @@ package dev.dres.data.model.admin import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty -import dev.dres.data.model.Entity +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID import org.mindrot.jbcrypt.BCrypt @@ -12,7 +12,7 @@ data class User constructor( override var id: UserId = UID.EMPTY, val username: UserName, val password: HashedPassword, - val role: Role) : Entity { + val role: Role) : PersistentEntity { override fun toString(): String = "User(id=$id, username=${username.name}, role=$role)" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt index c08f0be8f..a34147352 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt @@ -1,7 +1,22 @@ package dev.dres.data.model.basics.media -import dev.dres.data.model.Entity -import dev.dres.data.model.UID -import java.util.* +import dev.dres.data.model.PersistentEntity +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.xdRequiredStringProp +import kotlinx.dnq.xdStringProp + +/** + * A named media collection consisting of media items. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class MediaCollection(entity: Entity): PersistentEntity(entity) { + companion object : XdNaturalEntityType() + val name: String by xdRequiredStringProp(unique = true, trimmed = false) + val path: String by xdRequiredStringProp(unique = true, trimmed = false) + val description: String? by xdStringProp(trimmed = false) +} + -data class MediaCollection(override var id: UID = UID.EMPTY, val name: String, val description: String?, val basePath: String) : Entity \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt index a3ee06b28..fb137a8b7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt @@ -1,47 +1,38 @@ package dev.dres.data.model.basics.media -import dev.dres.data.model.Entity -import dev.dres.data.model.UID +import dev.dres.data.model.PersistentEntity +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* /** * A media item such as a video or an image * * @author Ralph Gasser - * @version 1.0 + * @version 2.0.0 */ -sealed class MediaItem : Entity { - - abstract val collection: UID - abstract val name: String - abstract val location: String - - abstract fun withCollection(collection: UID): MediaItem - - data class ImageItem constructor( - override var id: UID, - override val name: String, - override val location: String, - override val collection: UID): MediaItem() { - override fun withCollection(collection: UID): ImageItem = ImageItem(id, name, location, collection) - - companion object { - val EMPTY = ImageItem(UID.EMPTY, "n/a", "", UID.EMPTY) - } - } - - data class VideoItem constructor( - override var id: UID, - override val name: String, - override val location: String, - override val collection: UID, - override val durationMs: Long, - override val fps: Float - ): MediaItem(), PlayableMediaItem { - override fun withCollection(collection: UID): VideoItem = VideoItem(id, name, location, collection, durationMs, fps) - - companion object { - val EMPTY = VideoItem(UID.EMPTY, "n/a", "", UID.EMPTY, 0, 0f) - } - - } +class MediaItem(entity: Entity) : PersistentEntity(entity) { + + /** */ + companion object : XdNaturalEntityType() + + /** The name of this [MediaItem]. */ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The location of this [MediaItem] on disk. */ + var location by xdRequiredStringProp(unique = false, trimmed = false) + + /** Frame rate of the [MediaItem] in frames per second. Null for type without temporal development. */ + var fps by xdNullableFloatProp() + + /** Duration of the [MediaItem] in milliseconds. Null for type without temporal development. */ + var durationMs by xdNullableLongProp() + + /** The [MediaType] of this [MediaItem]. */ + var type by xdLink1(MediaType) + + /** The [MediaCollection] this [MediaItem] belongs to. */ + var collection by xdLink1(MediaCollection) + + /** List of [MediaItemSegment] that this [MediaItem] contains. */ + val segments by xdLink0_N(MediaItemSegment::item) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt index b4dad25e9..048706c27 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt @@ -1,13 +1,39 @@ package dev.dres.data.model.basics.media -import dev.dres.data.model.Entity +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID +import dev.dres.data.model.basics.time.TemporalPoint import dev.dres.data.model.basics.time.TemporalRange +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.simple.min +import kotlinx.dnq.xdLink1 +import kotlinx.dnq.xdRequiredIntProp +import kotlinx.dnq.xdRequiredStringProp -data class MediaItemSegment(val mediaItemId: UID, val name: String, val range: TemporalRange) +/** + * A segment of a [MediaItem] as mostly used by items that exhibit temporal progression. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The name of this [MediaItemSegment]. */ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [MediaType] of this [MediaItem]. */ + var item by xdLink1(MediaItem) + + /** The start frame number of this [MediaItemSegment]. */ + var startFrame by xdRequiredIntProp() { min(0) } + + /** The end frame number of this [MediaItemSegment]. */ + var endFrame by xdRequiredIntProp() { min(0) } + + /** Returns the [range] of this [MediaItemSegment] as [TemporalRange]. */ + val range: TemporalRange + get() = TemporalRange(TemporalPoint.Frame(this.startFrame, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.endFrame, this.item.fps ?: 1.0f)) -data class MediaItemSegmentList(override var id: UID, val mediaItemId: UID, val segments: MutableList) : Entity { - init { - require(segments.all { it.mediaItemId == mediaItemId } ){"All segments need to belong to the same media item"} - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt index ed310cdf2..eb6090740 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.competition import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.Entity +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID import dev.dres.data.model.admin.UserId import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard @@ -18,7 +18,7 @@ data class CompetitionDescription( val teams: MutableList, val teamGroups: MutableList, val judges: MutableList -) : Entity { +) : PersistentEntity { fun validate() { for (group in this.taskGroups) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt index 693b7cbbe..8f6cc0dd8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt @@ -65,7 +65,7 @@ sealed class TaskDescriptionTarget { override fun textDescription() = "Media Item ${item.name}" override fun toQueryContentElement(config: Config, collections: DAO): List { val collection = collections[this.item.collection]!! - val file = File(File(collection.basePath), this.item.location) + val file = File(File(collection.path), this.item.location) val contentElements = mutableListOf() FileInputStream(file).use { imageInFile -> val fileData = ByteArray(file.length().toInt()) @@ -117,7 +117,7 @@ sealed class TaskDescriptionTarget { val contentElements = mutableListOf() this.items.forEach { item -> val collection = collections[item.collection]!! - val file = File(File(collection.basePath), item.location) + val file = File(File(collection.path), item.location) FileInputStream(file).use { imageInFile -> val fileData = ByteArray(file.length().toInt()) imageInFile.read(fileData) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt index 4c7f59a35..2452fc6ba 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt @@ -12,13 +12,7 @@ import java.nio.file.Paths * @author Ralph Gasser, Loris Sauter, Luca Rossetto * @version 1.2.0 */ -data class Team constructor( - val uid: TeamId, - val name: String, - val color: String, - val logoId: UID, - val users: MutableList) { - +data class Team constructor(val uid: TeamId, val name: String, val color: String, val logoId: UID, val users: MutableList) { companion object { /** * Generates and returns the [Path] to the team logo with the given [logoId]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt index 2d369f0cb..edabe329c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.Entity +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.run.RunProperties @@ -10,7 +10,7 @@ import dev.dres.data.model.run.RunProperties * @author Ralph Gasser * @version 1.0.0 */ -interface Competition: Run, Entity { +interface Competition: Run, PersistentEntity { /** The unique [CompetitionId] that identifies this [Competition]. Used by the persistence layer. */ override var id: CompetitionId diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt index 330c9b1e7..7dc854a7f 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt @@ -12,7 +12,7 @@ object MediaCollectionSerializer: Serializer { out.writeUID(value.id) out.writeUTF(value.name) out.writeUTF(value.description ?: "") - out.writeUTF(value.basePath) + out.writeUTF(value.path) } override fun deserialize(input: DataInput2, available: Int): MediaCollection { diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt index 18b6593bc..ac9cd8a9e 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt @@ -1,6 +1,6 @@ package dev.dres.run.audit -import dev.dres.data.model.Entity +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus @@ -28,7 +28,7 @@ enum class LogEventSource { INTERNAL } -sealed class AuditLogEntry(val type: AuditLogEntryType): Entity{ +sealed class AuditLogEntry(val type: AuditLogEntryType): PersistentEntity{ var timestamp: Long = System.currentTimeMillis() internal set diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt index 1fedee9da..298d7f07d 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt @@ -1,7 +1,7 @@ package dev.dres.run.updatables import dev.dres.data.dbo.DAO -import dev.dres.data.model.Entity +import dev.dres.data.model.PersistentEntity import dev.dres.run.RunManagerStatus /** @@ -11,7 +11,7 @@ import dev.dres.run.RunManagerStatus * @author Ralph Gasser * @version 1.0 */ -class DAOUpdatable(val dao: DAO, val obj: T): StatefulUpdatable { +class DAOUpdatable(val dao: DAO, val obj: T): StatefulUpdatable { companion object { diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 000000000..40c7c52c1 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,25678 @@ +{ + "name": "dres-frontend", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dres-frontend", + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "@angular-slider/ngx-slider": "2.0.x", + "@angular/animations": "13.2.x", + "@angular/cdk": "13.2.x", + "@angular/common": "13.2.x", + "@angular/compiler": "13.2.x", + "@angular/core": "13.2.x", + "@angular/flex-layout": "^13.0.0-beta.38", + "@angular/forms": "13.2.x", + "@angular/material": "13.2.x", + "@angular/platform-browser": "13.2.x", + "@angular/platform-browser-dynamic": "13.2.x", + "@angular/router": "13.2.x", + "angularx-qrcode": "13.0.x", + "apexcharts": "3.33.x", + "ng-apexcharts": "1.7.x", + "ngx-color-picker": "12.0.x", + "rxjs": "6.5.x", + "tslib": "2.0.x", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "13.2.x", + "@angular-eslint/builder": "13.2.0", + "@angular-eslint/eslint-plugin": "13.2.0", + "@angular-eslint/eslint-plugin-template": "13.2.0", + "@angular-eslint/schematics": "13.2.0", + "@angular-eslint/template-parser": "13.2.0", + "@angular/cli": "13.2.x", + "@angular/compiler-cli": "13.2.x", + "@angular/language-service": "13.2.x", + "@types/jasmine": "3.6.x", + "@types/jasminewd2": "2.0.x", + "@types/node": "12.11.x", + "@typescript-eslint/eslint-plugin": "5.17.0", + "@typescript-eslint/parser": "5.17.0", + "eslint": "^8.12.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^7.0.0", + "jasmine-core": "3.8.x", + "jasmine-spec-reporter": "5.0.x", + "karma": "6.3.x", + "karma-chrome-launcher": "3.1.x", + "karma-coverage-istanbul-reporter": "3.0.x", + "karma-jasmine": "4.0.x", + "karma-jasmine-html-reporter": "1.5.x", + "lint-staged": "^12.3.7", + "prettier": "2.6.2", + "prettier-eslint": "^14.0.1", + "protractor": "7.0.x", + "stylelint": "^14.8.2", + "stylelint-config-html": "^1.0.0", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-prettier-scss": "^0.0.1", + "stylelint-config-scss": "^1.0.0-security", + "stylelint-config-standard": "^25.0.0", + "stylelint-config-standard-scss": "^3.0.0", + "ts-node": "8.3.x", + "typescript": "4.5.x" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1302.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "13.2.3", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/build-angular": { + "version": "13.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "1.1.1", + "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/build-webpack": "0.1302.3", + "@angular-devkit/core": "13.2.3", + "@babel/core": "7.16.12", + "@babel/generator": "7.16.8", + "@babel/helper-annotate-as-pure": "7.16.7", + "@babel/plugin-proposal-async-generator-functions": "7.16.8", + "@babel/plugin-transform-async-to-generator": "7.16.8", + "@babel/plugin-transform-runtime": "7.16.10", + "@babel/preset-env": "7.16.11", + "@babel/runtime": "7.16.7", + "@babel/template": "7.16.7", + "@discoveryjs/json-ext": "0.5.6", + "@ngtools/webpack": "13.2.3", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.3", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "15.3.0", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "10.2.1", + "core-js": "3.20.3", + "critters": "0.0.16", + "css-loader": "6.5.1", + "esbuild-wasm": "0.14.14", + "glob": "7.2.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.2", + "less-loader": "10.2.0", + "license-webpack-plugin": "4.0.1", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.5.3", + "minimatch": "3.0.4", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.5", + "postcss-import": "14.0.2", + "postcss-loader": "6.2.1", + "postcss-preset-env": "7.2.3", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.49.0", + "sass-loader": "12.4.0", + "semver": "7.3.5", + "source-map-loader": "3.0.1", + "source-map-support": "0.5.21", + "stylus": "0.56.0", + "stylus-loader": "6.2.0", + "terser": "5.10.0", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.1", + "webpack": "5.67.0", + "webpack-dev-middleware": "5.3.0", + "webpack-dev-server": "4.7.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.14.14" + }, + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0", + "@angular/localize": "^13.0.0", + "@angular/service-worker": "^13.0.0", + "karma": "^6.3.0", + "ng-packagr": "^13.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.4.3 <4.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { + "version": "1.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "sourcemap-codec": "1.4.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { + "version": "7.16.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { + "version": "3.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.3.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1302.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1302.3", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/core": { + "version": "13.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/schematics": { + "version": "13.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "13.2.3", + "jsonc-parser": "3.0.0", + "magic-string": "0.25.7", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-eslint/builder": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-13.2.0.tgz", + "integrity": "sha512-7V/bJwkMX+1OzxNUMxiOGdfdksjBmzCdPfxNEHXrT4la+vQQxg1oaIsgx74OkDqFTBv6NJO4PK8Flm9/QE4CDw==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "13.1.3" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.2.0.tgz", + "integrity": "sha512-ZA9JPERpeSo+G/bmp8GS/WjBbYkPDVzN6IINHz9SVdv//LWE58yymFFjRabHJx46iAEOe8P0CoKduuJWtEvNrQ==", + "dev": true + }, + "node_modules/@angular-eslint/eslint-plugin": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-13.2.0.tgz", + "integrity": "sha512-8qscIBc4montFQ52+XfBk7qR675oXV8mvRpjDh3cTfIZCzV6CSbbbH2iLp/9egnn0Pgy2ZUIsWgcsbK4W3+4bw==", + "dev": true, + "dependencies": { + "@angular-eslint/utils": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-13.2.0.tgz", + "integrity": "sha512-Ej9bNGbpf8oaQGglPVWAQkBSIHhQv0FeJ/vVnB2fhHUoK9BbkGUYpjAFSSRlnMhl6ktPWKCX3yJiW+F4uIUTIw==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0", + "aria-query": "^4.2.2", + "axobject-query": "^2.2.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "node_modules/@angular-eslint/schematics": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", + "integrity": "sha512-wxpfZ1L8Twa9Jfwqis7fY6jZvORqAv1k+9m1P6+6/l8gIm/8RYwD44o9Qm2MGt7YClnVthpCDhvfprcoKq6zgA==", + "dev": true, + "dependencies": { + "@angular-eslint/eslint-plugin": "13.2.0", + "@angular-eslint/eslint-plugin-template": "13.2.0", + "ignore": "5.2.0", + "strip-json-comments": "3.1.1", + "tmp": "0.2.1" + }, + "peerDependencies": { + "@angular/cli": ">= 13.0.0 < 14.0.0" + } + }, + "node_modules/@angular-eslint/schematics/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@angular-eslint/template-parser": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", + "integrity": "sha512-dv+bBFzvA4q/KiEDpO7BDUmB1EpVW0P0sX6OSs2eRzViEuXztkatQ4XOmgzuxP0+E1+zl/mK3F7uBDNXiaah9g==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "eslint-scope": "^5.1.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/utils": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-13.2.0.tgz", + "integrity": "sha512-ywkk+pVLaDLwzNKUcc/xWtJC4Bkm+qRrMOR8ZX3q84E5RTvIvc8IcPFBE4ey3lnGe+nE44OEGuLedCo9vn1Meg==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-slider/ngx-slider": { + "version": "2.0.4", + "license": "MIT", + "dependencies": { + "detect-passive-events": "^2.0.3", + "rxjs": "^6.5.2", + "tslib": "^1.9.0" + }, + "peerDependencies": { + "@angular/common": ">=6.1.0", + "@angular/core": ">=6.1.0", + "@angular/forms": ">=6.1.0" + } + }, + "node_modules/@angular-slider/ngx-slider/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@angular/animations": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "13.2.2" + } + }, + "node_modules/@angular/animations/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/cdk": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^13.0.0 || ^14.0.0-0", + "@angular/core": "^13.0.0 || ^14.0.0-0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cdk/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/cli": { + "version": "13.2.3", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/core": "13.2.3", + "@angular-devkit/schematics": "13.2.3", + "@schematics/angular": "13.2.3", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.3", + "ini": "2.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "npm-package-arg": "8.1.5", + "npm-pick-manifest": "6.1.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "12.0.3", + "resolve": "1.22.0", + "semver": "7.3.5", + "symbol-observable": "4.0.0", + "uuid": "8.3.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "13.2.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/common/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/compiler": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "13.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.8.6", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "13.2.2", + "typescript": ">=4.4.2 <4.6" + } + }, + "node_modules/@angular/compiler-cli/node_modules/tslib": { + "version": "2.3.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular/compiler/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/core": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/core/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/flex-layout": { + "version": "13.0.0-beta.38", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^13.0.0", + "@angular/common": "^13.0.0", + "@angular/core": "^13.0.0", + "@angular/platform-browser": "^13.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/flex-layout/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/forms": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.2.2", + "@angular/core": "13.2.2", + "@angular/platform-browser": "13.2.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/forms/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/language-service": { + "version": "13.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + } + }, + "node_modules/@angular/material": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^13.0.0 || ^14.0.0-0", + "@angular/cdk": "13.2.2", + "@angular/common": "^13.0.0 || ^14.0.0-0", + "@angular/core": "^13.0.0 || ^14.0.0-0", + "@angular/forms": "^13.0.0 || ^14.0.0-0", + "@angular/platform-browser": "^13.0.0 || ^14.0.0-0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/material/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/platform-browser": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "13.2.2", + "@angular/common": "13.2.2", + "@angular/core": "13.2.2" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.2.2", + "@angular/compiler": "13.2.2", + "@angular/core": "13.2.2", + "@angular/platform-browser": "13.2.2" + } + }, + "node_modules/@angular/platform-browser-dynamic/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/platform-browser/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@angular/router": { + "version": "13.2.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.2.2", + "@angular/core": "13.2.2", + "@angular/platform-browser": "13.2.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/router/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.17.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.0.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.17.2", + "@babel/parser": "^7.17.0", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.0", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.16.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.16.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.0.tgz", + "integrity": "sha512-G5FaGZOWORq9zthDjIrjib5XlcddeqLbIiDO3YQsut6j7aGf76xn0umUC/pA6+nApk3hQJF4JzLzg5PCl6ewJg==", + "dev": true, + "dependencies": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.0", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cordobo/qrcode": { + "version": "1.5.0", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^17.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.1.0", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@ngtools/webpack": { + "version": "13.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0", + "typescript": ">=4.4.3 <4.6", + "webpack": "^5.30.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/git": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/@npmcli/git/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "dev": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/@npmcli/run-script": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/@nrwl/cli": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.1.7.tgz", + "integrity": "sha512-HVvqYwefizcoVc4xrTgfIC8nfMA9cx5NiaIbZFDIZv12NsdalpA6a2BmmV8Zck+QQSGEHrhTZyt1AqDhA1t+6A==", + "dev": true, + "dependencies": { + "nx": "14.1.7" + } + }, + "node_modules/@nrwl/cli/node_modules/@nrwl/tao": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.1.7.tgz", + "integrity": "sha512-7jzPqEwElMiTKCZm2iuqC5aPIs+IwocWR6Tl1JhC1eK2PWskHOhdgTQM186lfXwPKWH4NJAE3eZFqtECnGDCJg==", + "dev": true, + "dependencies": { + "nx": "14.1.7" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nrwl/cli/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@nrwl/cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nrwl/cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@nrwl/cli/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/cli/node_modules/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@nrwl/cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/cli/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@nrwl/cli/node_modules/nx": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/nx/-/nx-14.1.7.tgz", + "integrity": "sha512-Q10PVQ70TFg9HY6KQKJ316B42sVtxmFk/VUdC7ZHKxzDm/JeYf6OaDarQOUZaFZp/EL2brtEsrlWSMTWmk3r4w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nrwl/cli": "14.1.7", + "@nrwl/tao": "14.1.7", + "@parcel/watcher": "2.0.4", + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173", + "chalk": "4.1.0", + "chokidar": "^3.5.1", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^7.0.2", + "dotenv": "~10.0.0", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^10.1.0", + "glob": "7.1.4", + "ignore": "^5.0.4", + "jsonc-parser": "3.0.0", + "minimatch": "3.0.4", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "string-width": "^4.2.3", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^3.9.0", + "tslib": "^2.3.0", + "v8-compile-cache": "2.3.0", + "yargs": "^17.4.0", + "yargs-parser": "21.0.1" + }, + "bin": { + "nx": "bin/nx.js" + } + }, + "node_modules/@nrwl/cli/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/cli/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@nrwl/cli/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@nrwl/devkit": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", + "integrity": "sha512-TAAsZJvVc/obeH0rZKY6miVhyM2GHGb8qIWp9MAIdLlXf4VDcNC7rxwb5OrGVSwuTTjqGYBGPUx0yEogOOJthA==", + "dev": true, + "dependencies": { + "@nrwl/tao": "13.1.3", + "ejs": "^3.1.5", + "ignore": "^5.0.4", + "rxjs": "^6.5.4", + "semver": "7.3.4", + "tslib": "^2.0.0" + } + }, + "node_modules/@nrwl/devkit/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", + "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", + "dev": true, + "dependencies": { + "chalk": "4.1.0", + "enquirer": "~2.3.6", + "fs-extra": "^9.1.0", + "jsonc-parser": "3.0.0", + "nx": "13.1.3", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "tmp": "~0.2.1", + "tslib": "^2.0.0", + "yargs-parser": "20.0.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/tao/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nrwl/tao/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@nrwl/tao/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nrwl/tao/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@nrwl/tao/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/tao/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/tao/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@nrwl/tao/node_modules/yargs-parser": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", + "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", + "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@schematics/angular": { + "version": "13.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "13.2.3", + "@angular-devkit/schematics": "13.2.3", + "jsonc-parser": "3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/@swc-node/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.9.0.tgz", + "integrity": "sha512-vRnvsMtL9OxybA/Wun1ZhlDvB6MNs4Zujnina0VKdGk+yI6s87KUhdTcbAY6dQMZhQTLFiC1Lnv/BuwCKcCEug==", + "dev": true, + "dependencies": { + "@swc/core": "^1.2.172" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@swc-node/register": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.5.1.tgz", + "integrity": "sha512-6IL5s4QShKGs08qAeNou3rDA3gbp2WHk6fo0XnJXQn/aC9k6FnVBbj/thGOIEDtgNhC/DKpZT8tCY1LpQnOZFg==", + "dev": true, + "dependencies": { + "@swc-node/core": "^1.9.0", + "@swc-node/sourcemap-support": "^0.2.0", + "colorette": "^2.0.16", + "debug": "^4.3.4", + "pirates": "^4.0.5", + "tslib": "^2.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "typescript": ">= 4.3" + } + }, + "node_modules/@swc-node/register/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@swc-node/register/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@swc-node/sourcemap-support": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.2.0.tgz", + "integrity": "sha512-FNrxdI6XMYfoNt81L8eFKEm1d8P82I1nPwS3MrnBGzZoMWB+seQhQK+iN6M5RreJxXbfZw5lF86LRjHEQeGMqg==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.21" + } + }, + "node_modules/@swc-node/sourcemap-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@swc-node/sourcemap-support/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@swc/core": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.189.tgz", + "integrity": "sha512-S5cKX4ECMSfW78DLFgnlilJZgjrFRYwPslrrwpLl3gpwh+Qo72/Mhn71u7G/5xXW+T/xW5GwPccHfCk+k72uUg==", + "dev": true, + "bin": { + "swcx": "run_swcx.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-android-arm-eabi": "1.2.189", + "@swc/core-android-arm64": "1.2.189", + "@swc/core-darwin-arm64": "1.2.189", + "@swc/core-darwin-x64": "1.2.189", + "@swc/core-freebsd-x64": "1.2.189", + "@swc/core-linux-arm-gnueabihf": "1.2.189", + "@swc/core-linux-arm64-gnu": "1.2.189", + "@swc/core-linux-arm64-musl": "1.2.189", + "@swc/core-linux-x64-gnu": "1.2.189", + "@swc/core-linux-x64-musl": "1.2.189", + "@swc/core-win32-arm64-msvc": "1.2.189", + "@swc/core-win32-ia32-msvc": "1.2.189", + "@swc/core-win32-x64-msvc": "1.2.189" + } + }, + "node_modules/@swc/core-android-arm-eabi": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.189.tgz", + "integrity": "sha512-0kN3Le6QzFFz+Lc6a/tf/RkJXubWwWaHxF4c0bVm4AKIFf4nRlUCEqEkjdVaZvL92rpBMHaEEBuIIz3T8DqTTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-android-arm64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.189.tgz", + "integrity": "sha512-smsb+YkDw2OKwg66Z63E/G4NlFApDbsiOPmZXFZbtZbNBD9v+wnk6WVA//XR1bdUI9VbzNKlMPKJxQTE685QDw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.189.tgz", + "integrity": "sha512-OGjZRkTulKirJMLYbW9etb59lA9ueDXVwYRVD9SrNh8tRMTf0Nq+SUT/C3LVhBBGC4KSdWOzBAYbDTTdsnY++Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.189.tgz", + "integrity": "sha512-BEcxnBHx51514Voe2dn/y1y5H9VNyw7Zpp9+mPekZqx5o/onPD5wZ1ZfAsPrA4UlvM3v16u6ITE/cLawJ/GdAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-freebsd-x64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.189.tgz", + "integrity": "sha512-B6g2NWeh2iw6WPOaM19Uj3VE4se6alT265kWibLUshjcofRfnYT1lNhhkrF1D0EVnpC8I96I/xXNQo4Am9z4zQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.189.tgz", + "integrity": "sha512-6WhPG9pyN5AahQTVQk8MoN1I9Z/Ytfqizuie1wV7mW8FMNmMkiJvBekKtE6ftxu80Hqa34r86WfEwmJKm5MqYw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.189.tgz", + "integrity": "sha512-frJTGnxsDe7h2d7BtnBivOdOJTtabkQIIZmHd4JHqafcoekI6teyveIax17axLyimvWl278yTo3hf7ePagD/eg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.189.tgz", + "integrity": "sha512-27K38LoZpME5lojDJIUNo7zdTDwAKLm0BMQ7HXWcYOyiDAekhSidI+SrBWxCfLzfuClhFu6/VE3E7j32VFJsiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.189.tgz", + "integrity": "sha512-Ha5oJKOyQm9w7+e+WdRm4ypijzEmglWZGtgBR6vV6ViqqHcTBAU4nG87ex7y7AS9p+Cbc6EOSR9X1qIB8KxtbA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.189.tgz", + "integrity": "sha512-/p5yXa9HEzpVEuE4ivkW1IvwyYu6fT+L2OvVEs5oXIba80F0Wjy7InWqaa83gwrdMH+bXV6loG8LzZUZu/lpjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.189.tgz", + "integrity": "sha512-o/1ueM6/sifNjYnO6NMEXB895spVfJs5oQIPxQG9vJ/4zWLw8YmAx+u1xJY+XGyK6gnroHt7yPiS87qWdbeF6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.189.tgz", + "integrity": "sha512-YDwRkzykaf+dw5Z7u189cC/Tttkn2NVV84hrGL3LbVuh7wT5PaDhZs4Yz4unZQSlPV12olmZWgNr/i27h5wlpg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.189.tgz", + "integrity": "sha512-Nge8Z/ZkAp5p5No50yBDpBG7+ZYaVWGSuwtPj6OJe7orzvDCEm9GgcVE6J9GEjbclSWlCH8B8lUe17GaKRZHbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/component-emitter": { + "version": "1.2.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.50", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "3.6.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jasminewd2": { + "version": "2.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jasmine": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.11.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prettier": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", + "integrity": "sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==", + "dev": true + }, + "node_modules/@types/q": { + "version": "0.0.32", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/selenium-webdriver": { + "version": "3.0.19", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", + "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abab": { + "version": "2.0.5", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/angularx-qrcode": { + "version": "13.0.3", + "license": "MIT", + "dependencies": { + "@cordobo/qrcode": "1.5.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": "^13.0.0" + } + }, + "node_modules/angularx-qrcode/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/apexcharts": { + "version": "3.33.1", + "license": "MIT", + "dependencies": { + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "2.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001297", + "fraction.js": "^4.1.2", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-loader": { + "version": "8.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/json5": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/blocking-proxy": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "blocking-proxy": "built/lib/bin.js" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/body-parser": { + "version": "1.19.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour": { + "version": "3.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.19.1", + "dev": true, + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/browserstack": { + "version": "1.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^2.2.1" + } + }, + "node_modules/browserstack/node_modules/agent-base": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/browserstack/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/browserstack/node_modules/https-proxy-agent": { + "version": "2.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/builtins": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001312", + "dev": true, + "license": "CC-BY-4.0", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "dependencies": { + "is-regexp": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/connect": { + "version": "3.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/content-type": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "10.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/array-union": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "12.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.20.3", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.22.6", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.6.tgz", + "integrity": "sha512-u5yG2VL6NKXz9BZHr9RAm6eWD1DTNjG7jJnJgLGR+Im0whdPcPXqwqxd+dcUrZvpvPan5KMgn/3pI+Q/aGqPOA==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/critters": { + "version": "0.0.16", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/critters/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/critters/node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/critters/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-functions-list": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", + "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", + "dev": true, + "engines": { + "node": ">=12.22" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "dev": true, + "license": "CC0-1.0", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.2.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "5.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssdb": { + "version": "5.1.0", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-format": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/del": { + "version": "2.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-it": { + "version": "4.0.1", + "license": "MIT" + }, + "node_modules/detect-node": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-passive-events": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "detect-it": "^4.0.1" + } + }, + "node_modules/di": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.68", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/base64-arraybuffer": "~1.0.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "2.2.0", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/errno": { + "version": "0.1.8", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/esbuild": { + "version": "0.14.14", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.14.14", + "esbuild-darwin-64": "0.14.14", + "esbuild-darwin-arm64": "0.14.14", + "esbuild-freebsd-64": "0.14.14", + "esbuild-freebsd-arm64": "0.14.14", + "esbuild-linux-32": "0.14.14", + "esbuild-linux-64": "0.14.14", + "esbuild-linux-arm": "0.14.14", + "esbuild-linux-arm64": "0.14.14", + "esbuild-linux-mips64le": "0.14.14", + "esbuild-linux-ppc64le": "0.14.14", + "esbuild-linux-s390x": "0.14.14", + "esbuild-netbsd-64": "0.14.14", + "esbuild-openbsd-64": "0.14.14", + "esbuild-sunos-64": "0.14.14", + "esbuild-windows-32": "0.14.14", + "esbuild-windows-64": "0.14.14", + "esbuild-windows-arm64": "0.14.14" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.14", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-wasm": { + "version": "0.14.14", + "dev": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", + "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "dev": true, + "dependencies": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "dependencies": { + "clone-regexp": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express": { + "version": "4.17.2", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/cookie": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.14.8", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gauge": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "dev": true, + "license": "ISC" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "dev": true, + "license": "BSD", + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "peer": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "4.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/immutable": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/rxjs": { + "version": "7.5.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/tslib": { + "version": "2.3.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/ip": { + "version": "1.1.5", + "dev": true, + "license": "MIT" + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/pify": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine": { + "version": "2.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "3.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jasmine-spec-reporter": { + "version": "5.0.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "colors": "1.4.0" + } + }, + "node_modules/jasmine/node_modules/jasmine-core": { + "version": "2.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jasminewd2": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.9.x" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jszip": { + "version": "3.7.1", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/karma": { + "version": "6.3.16", + "dev": true, + "license": "MIT", + "dependencies": { + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "colors": "1.4.0", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.2.0", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/mattlewis92" + } + }, + "node_modules/karma-jasmine": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "jasmine-core": "^3.6.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "karma": "*" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "1.5.4", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jasmine-core": ">=3.5", + "karma": ">=0.9", + "karma-jasmine": ">=1.1" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma-source-map-support/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma-source-map-support/node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/known-css-properties": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", + "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "dev": true + }, + "node_modules/less": { + "version": "4.1.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "10.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "2.3.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lie": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz", + "integrity": "sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/lint-staged/node_modules/supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/listr2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/listr2/node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log4js": { + "version": "6.4.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.3", + "debug": "^4.3.3", + "flatted": "^3.2.4", + "rfdc": "^1.3.0", + "streamroller": "^3.0.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.1", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.5.3", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minipass": { + "version": "3.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "license": "ISC" + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/needle": { + "version": "2.9.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/ng-apexcharts": { + "version": "1.7.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@angular/core": ">=13.0.0", + "apexcharts": "^3.31.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/ngx-color-picker": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=9.0.0", + "@angular/core": ">=9.0.0", + "@angular/forms": ">=9.0.0" + } + }, + "node_modules/ngx-color-picker/node_modules/tslib": { + "version": "2.3.1", + "license": "0BSD" + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.3.0", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-releases": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "4.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-package-arg": { + "version": "8.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-packlist": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-pick-manifest": { + "version": "6.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/npm-registry-fetch": { + "version": "12.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.3.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "10.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.0", + "cacache": "^15.3.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.3.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.4.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.1.1", + "ssri": "^8.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nx": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/nx/-/nx-13.1.3.tgz", + "integrity": "sha512-clM0NQhQKYkqcNz2E3uYRMLwhp2L/9dBhJhQi9XBX4IAyA2gWAomhRIlLm5Xxg3g4h1xwSpP3eJ5t89VikY8Pw==", + "dev": true, + "dependencies": { + "@nrwl/cli": "*" + }, + "bin": { + "nx": "bin/nx.js" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", + "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "12.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/pacote/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "license": "MIT", + "optional": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser/node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/piscina": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/postcss": { + "version": "8.4.5", + "dev": true, + "license": "MIT", + "dependencies": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.2" + }, + "peerDependencies": { + "postcss": "^8.0.2" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.2", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.0.2", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.2" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.4", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.0.5", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.5", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.3", + "dev": true, + "license": "CC0-1.0", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-html": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.4.1.tgz", + "integrity": "sha512-OKihuWxPuBQrQeLNsavP7ytJ9IYNj/ViAXB2v7Qjh56LnfESKrkahKA9si4VfPN8xtz6oqUE6KdL0bTPrHJr6g==", + "dev": true, + "peer": true, + "dependencies": { + "htmlparser2": "^7.1.2", + "postcss": "^8.4.0", + "postcss-safe-parser": "^6.0.0" + }, + "engines": { + "node": "^12 || >=14" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.6", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-import": { + "version": "14.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.1.0", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "dev": true, + "license": "CC0-1.0", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "10.1.2", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.8" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.3", + "dev": true, + "license": "CC0-1.0", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.4", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.2.3", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "autoprefixer": "^10.4.2", + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001299", + "css-blank-pseudo": "^3.0.2", + "css-has-pseudo": "^3.0.3", + "css-prefers-color-scheme": "^6.0.2", + "cssdb": "^5.0.0", + "postcss-attribute-case-insensitive": "^5.0.0", + "postcss-color-functional-notation": "^4.2.1", + "postcss-color-hex-alpha": "^8.0.2", + "postcss-color-rebeccapurple": "^7.0.2", + "postcss-custom-media": "^8.0.0", + "postcss-custom-properties": "^12.1.2", + "postcss-custom-selectors": "^6.0.0", + "postcss-dir-pseudo-class": "^6.0.3", + "postcss-double-position-gradients": "^3.0.4", + "postcss-env-function": "^4.0.4", + "postcss-focus-visible": "^6.0.3", + "postcss-focus-within": "^5.0.3", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.2", + "postcss-image-set-function": "^4.0.4", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.0.3", + "postcss-logical": "^5.0.3", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.2", + "postcss-overflow-shorthand": "^3.0.2", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.3", + "postcss-pseudo-class-any-link": "^7.0.2", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^5.0.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.1", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", + "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-eslint": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-14.1.0.tgz", + "integrity": "sha512-K0TRVaAUXtI5xz1ZaVZfvGMmunDNyIGXFkE845hVl6FzSxzRN9E03YmK3IiapcRFv3w4PyAL25LIPsy2sRz2tw==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.4.2", + "@types/prettier": "^2.6.0", + "@typescript-eslint/parser": "^5.10.0", + "common-tags": "^1.4.0", + "dlv": "^1.1.0", + "eslint": "^8.7.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^2.5.1", + "pretty-format": "^23.0.1", + "require-relative": "^0.8.7", + "typescript": "^4.5.4", + "vue-eslint-parser": "^8.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protractor": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.1.7", + "yargs": "^15.3.1" + }, + "bin": { + "protractor": "bin/protractor", + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=10.13.x" + } + }, + "node_modules/protractor/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/cliui": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/protractor/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/protractor/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/y18n": { + "version": "4.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/protractor/node_modules/yargs": { + "version": "15.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/yargs-parser": { + "version": "18.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/psl": { + "version": "1.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.9.6", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "dev": true, + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/request": { + "version": "2.88.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.5.5", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/rxjs-for-await": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", + "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", + "dev": true, + "peerDependencies": { + "rxjs": "^6.0.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.49.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "12.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/saucelabs": { + "version": "1.5.0", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/saucelabs/node_modules/agent-base": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/saucelabs/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/saucelabs/node_modules/https-proxy-agent": { + "version": "2.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/selenium-webdriver": { + "version": "3.6.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/selenium-webdriver/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.0.30", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/selfsigned": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^1.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-static": { + "version": "1.14.2", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.3.3", + "dev": true, + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.3", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.4.18", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true, + "bin": { + "specificity": "bin/specificity" + } + }, + "node_modules/sshpk": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamroller": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "^4.0.3", + "debug": "^4.1.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, + "node_modules/stylelint": { + "version": "14.8.3", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.3.tgz", + "integrity": "sha512-aLpskXwSgFEBYbFRKA/BfuyYMGuXNtn2t5GqoffNPSezvw97x/vVNWcZNF0+cwt+LBjfvyq9/MRE3OjInGRgNA==", + "dev": true, + "dependencies": { + "balanced-match": "^2.0.0", + "colord": "^2.9.2", + "cosmiconfig": "^7.0.1", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", + "execall": "^2.0.0", + "fast-glob": "^3.2.11", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.0", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.25.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.13", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", + "svg-tags": "^1.0.0", + "table": "^6.8.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-html": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", + "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", + "dev": true, + "engines": { + "node": "^12 || >=14" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-prettier": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", + "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", + "dev": true, + "bin": { + "stylelint-config-prettier": "bin/check.js", + "stylelint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">=11.0.0" + } + }, + "node_modules/stylelint-config-prettier-scss": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier-scss/-/stylelint-config-prettier-scss-0.0.1.tgz", + "integrity": "sha512-lBAYG9xYOh2LeWEPC/64xeUxwOTnQ8nDyBijQoWoJb10/bMGrUwnokpt8jegGck2Vbtxh6XGwH63z5qBcVHreQ==", + "dev": true, + "dependencies": { + "stylelint-config-prettier": ">=9.0.3" + }, + "bin": { + "stylelint-config-prettier-scss": "bin/check.js", + "stylelint-config-prettier-scss-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">=11.0.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-recommended-scss": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", + "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", + "dev": true, + "dependencies": { + "postcss-scss": "^4.0.2", + "stylelint-config-recommended": "^6.0.0", + "stylelint-scss": "^4.0.0" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", + "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-scss": { + "version": "1.0.0-security", + "resolved": "https://registry.npmjs.org/stylelint-config-scss/-/stylelint-config-scss-1.0.0-security.tgz", + "integrity": "sha512-8Pgul2mNpzTeK2KCuyV5RcDC1BgzWzU7dCLVJWuxpkKgmxrMqCvjqgyosaKbAVZy2AiaMU0zfHBt7prg7/NaxA==", + "dev": true + }, + "node_modules/stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^7.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-standard-scss": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz", + "integrity": "sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==", + "dev": true, + "dependencies": { + "stylelint-config-recommended-scss": "^5.0.2", + "stylelint-config-standard": "^24.0.0" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-recommended": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", + "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-standard": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", + "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^6.0.0" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-scss": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", + "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0" + }, + "peerDependencies": { + "stylelint": "^14.5.1" + } + }, + "node_modules/stylelint/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylelint/node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/stylelint/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylus": { + "version": "0.56.0", + "dev": true, + "license": "MIT", + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus-loader": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.7", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "license": "MIT", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.easing.js": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "svg.js": ">=2.3.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.filter.js": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "license": "MIT" + }, + "node_modules/svg.pathmorphing.js": { + "version": "0.1.3", + "license": "MIT", + "dependencies": { + "svg.js": "^2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "license": "MIT", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/table/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/table/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser": { + "version": "5.10.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "acorn": "^8.5.0" + }, + "peerDependenciesMeta": { + "acorn": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/terser/node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/thunky": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-node": { + "version": "8.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + }, + "peerDependencies": { + "typescript": ">=2.0" + } + }, + "node_modules/ts-node/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ts-node/node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.0.3", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.5.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.31", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "dependencies": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/watchpack": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webdriver-js-extender": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager": { + "version": "12.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "bin": { + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/webdriver-manager/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/webdriver-manager/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/webdriver-manager/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack": { + "version": "5.67.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/webpack-dev-server/node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/del": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/is-path-cwd": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/ws": { + "version": "8.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.4.23", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/zone.js": { + "version": "0.11.4", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.1.1", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.0" + } + }, + "@angular-devkit/architect": { + "version": "0.1302.3", + "dev": true, + "requires": { + "@angular-devkit/core": "13.2.3", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "13.2.3", + "dev": true, + "requires": { + "@ampproject/remapping": "1.1.1", + "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/build-webpack": "0.1302.3", + "@angular-devkit/core": "13.2.3", + "@babel/core": "7.16.12", + "@babel/generator": "7.16.8", + "@babel/helper-annotate-as-pure": "7.16.7", + "@babel/plugin-proposal-async-generator-functions": "7.16.8", + "@babel/plugin-transform-async-to-generator": "7.16.8", + "@babel/plugin-transform-runtime": "7.16.10", + "@babel/preset-env": "7.16.11", + "@babel/runtime": "7.16.7", + "@babel/template": "7.16.7", + "@discoveryjs/json-ext": "0.5.6", + "@ngtools/webpack": "13.2.3", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.3", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "15.3.0", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "10.2.1", + "core-js": "3.20.3", + "critters": "0.0.16", + "css-loader": "6.5.1", + "esbuild": "0.14.14", + "esbuild-wasm": "0.14.14", + "glob": "7.2.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.2", + "less-loader": "10.2.0", + "license-webpack-plugin": "4.0.1", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.5.3", + "minimatch": "3.0.4", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.5", + "postcss-import": "14.0.2", + "postcss-loader": "6.2.1", + "postcss-preset-env": "7.2.3", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.49.0", + "sass-loader": "12.4.0", + "semver": "7.3.5", + "source-map-loader": "3.0.1", + "source-map-support": "0.5.21", + "stylus": "0.56.0", + "stylus-loader": "6.2.0", + "terser": "5.10.0", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.1", + "webpack": "5.67.0", + "webpack-dev-middleware": "5.3.0", + "webpack-dev-server": "4.7.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "dependencies": { + "@ampproject/remapping": { + "version": "1.1.1", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "sourcemap-codec": "1.4.8" + } + }, + "@babel/core": { + "version": "7.16.12", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "minimatch": { + "version": "3.0.4", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "rxjs": { + "version": "6.6.7", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "dev": true + } + } + }, + "source-map": { + "version": "0.5.7", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "dev": true + } + } + }, + "tslib": { + "version": "2.3.1", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1302.3", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1302.3", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "13.2.3", + "dev": true, + "requires": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "dev": true + } + } + }, + "@angular-devkit/schematics": { + "version": "13.2.3", + "dev": true, + "requires": { + "@angular-devkit/core": "13.2.3", + "jsonc-parser": "3.0.0", + "magic-string": "0.25.7", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "dev": true + } + } + }, + "@angular-eslint/builder": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-13.2.0.tgz", + "integrity": "sha512-7V/bJwkMX+1OzxNUMxiOGdfdksjBmzCdPfxNEHXrT4la+vQQxg1oaIsgx74OkDqFTBv6NJO4PK8Flm9/QE4CDw==", + "dev": true, + "requires": { + "@nrwl/devkit": "13.1.3" + } + }, + "@angular-eslint/bundled-angular-compiler": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.2.0.tgz", + "integrity": "sha512-ZA9JPERpeSo+G/bmp8GS/WjBbYkPDVzN6IINHz9SVdv//LWE58yymFFjRabHJx46iAEOe8P0CoKduuJWtEvNrQ==", + "dev": true + }, + "@angular-eslint/eslint-plugin": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-13.2.0.tgz", + "integrity": "sha512-8qscIBc4montFQ52+XfBk7qR675oXV8mvRpjDh3cTfIZCzV6CSbbbH2iLp/9egnn0Pgy2ZUIsWgcsbK4W3+4bw==", + "dev": true, + "requires": { + "@angular-eslint/utils": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0" + } + }, + "@angular-eslint/eslint-plugin-template": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-13.2.0.tgz", + "integrity": "sha512-Ej9bNGbpf8oaQGglPVWAQkBSIHhQv0FeJ/vVnB2fhHUoK9BbkGUYpjAFSSRlnMhl6ktPWKCX3yJiW+F4uIUTIw==", + "dev": true, + "requires": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0", + "aria-query": "^4.2.2", + "axobject-query": "^2.2.0" + }, + "dependencies": { + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + } + } + }, + "@angular-eslint/schematics": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", + "integrity": "sha512-wxpfZ1L8Twa9Jfwqis7fY6jZvORqAv1k+9m1P6+6/l8gIm/8RYwD44o9Qm2MGt7YClnVthpCDhvfprcoKq6zgA==", + "dev": true, + "requires": { + "@angular-eslint/eslint-plugin": "13.2.0", + "@angular-eslint/eslint-plugin-template": "13.2.0", + "ignore": "5.2.0", + "strip-json-comments": "3.1.1", + "tmp": "0.2.1" + }, + "dependencies": { + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + } + } + }, + "@angular-eslint/template-parser": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", + "integrity": "sha512-dv+bBFzvA4q/KiEDpO7BDUmB1EpVW0P0sX6OSs2eRzViEuXztkatQ4XOmgzuxP0+E1+zl/mK3F7uBDNXiaah9g==", + "dev": true, + "requires": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "eslint-scope": "^5.1.0" + } + }, + "@angular-eslint/utils": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-13.2.0.tgz", + "integrity": "sha512-ywkk+pVLaDLwzNKUcc/xWtJC4Bkm+qRrMOR8ZX3q84E5RTvIvc8IcPFBE4ey3lnGe+nE44OEGuLedCo9vn1Meg==", + "dev": true, + "requires": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0" + } + }, + "@angular-slider/ngx-slider": { + "version": "2.0.4", + "requires": { + "detect-passive-events": "^2.0.3", + "rxjs": "^6.5.2", + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@angular/animations": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/cdk": { + "version": "13.2.2", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/cli": { + "version": "13.2.3", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/core": "13.2.3", + "@angular-devkit/schematics": "13.2.3", + "@schematics/angular": "13.2.3", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.3", + "ini": "2.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "npm-package-arg": "8.1.5", + "npm-pick-manifest": "6.1.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "12.0.3", + "resolve": "1.22.0", + "semver": "7.3.5", + "symbol-observable": "4.0.0", + "uuid": "8.3.2" + } + }, + "@angular/common": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/compiler": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/compiler-cli": { + "version": "13.2.2", + "dev": true, + "requires": { + "@babel/core": "^7.8.6", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "dev": true + } + } + }, + "@angular/core": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/flex-layout": { + "version": "13.0.0-beta.38", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/forms": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/language-service": { + "version": "13.2.2", + "dev": true + }, + "@angular/material": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/platform-browser": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/platform-browser-dynamic": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@angular/router": { + "version": "13.2.2", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "@assemblyscript/loader": { + "version": "0.10.1", + "dev": true + }, + "@babel/code-frame": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/compat-data": { + "version": "7.17.0", + "dev": true + }, + "@babel/core": { + "version": "7.17.2", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.0.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.17.2", + "@babel/parser": "^7.17.0", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.17.0", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.17.1", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + } + }, + "@babel/helpers": { + "version": "7.17.2", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0" + } + }, + "@babel/highlight": { + "version": "7.16.10", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.17.0", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.16.10", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/preset-env": { + "version": "7.16.11", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.16.7", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.0.tgz", + "integrity": "sha512-G5FaGZOWORq9zthDjIrjib5XlcddeqLbIiDO3YQsut6j7aGf76xn0umUC/pA6+nApk3hQJF4JzLzg5PCl6ewJg==", + "dev": true, + "requires": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.16.7", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.17.0", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.0", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.17.0", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "@cordobo/qrcode": { + "version": "1.5.0", + "requires": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^17.3.1" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.1.0", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.6", + "dev": true + }, + "@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@gar/promisify": { + "version": "1.1.2", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@ngtools/webpack": { + "version": "13.2.3", + "dev": true, + "requires": {} + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "1.1.1", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "dev": true + }, + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "dev": true + } + } + }, + "@npmcli/node-gyp": { + "version": "1.0.3", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "2.0.0", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "@nrwl/cli": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.1.7.tgz", + "integrity": "sha512-HVvqYwefizcoVc4xrTgfIC8nfMA9cx5NiaIbZFDIZv12NsdalpA6a2BmmV8Zck+QQSGEHrhTZyt1AqDhA1t+6A==", + "dev": true, + "requires": { + "nx": "14.1.7" + }, + "dependencies": { + "@nrwl/tao": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.1.7.tgz", + "integrity": "sha512-7jzPqEwElMiTKCZm2iuqC5aPIs+IwocWR6Tl1JhC1eK2PWskHOhdgTQM186lfXwPKWH4NJAE3eZFqtECnGDCJg==", + "dev": true, + "requires": { + "nx": "14.1.7" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "nx": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/nx/-/nx-14.1.7.tgz", + "integrity": "sha512-Q10PVQ70TFg9HY6KQKJ316B42sVtxmFk/VUdC7ZHKxzDm/JeYf6OaDarQOUZaFZp/EL2brtEsrlWSMTWmk3r4w==", + "dev": true, + "requires": { + "@nrwl/cli": "14.1.7", + "@nrwl/tao": "14.1.7", + "@parcel/watcher": "2.0.4", + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173", + "chalk": "4.1.0", + "chokidar": "^3.5.1", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^7.0.2", + "dotenv": "~10.0.0", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^10.1.0", + "glob": "7.1.4", + "ignore": "^5.0.4", + "jsonc-parser": "3.0.0", + "minimatch": "3.0.4", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "string-width": "^4.2.3", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^3.9.0", + "tslib": "^2.3.0", + "v8-compile-cache": "2.3.0", + "yargs": "^17.4.0", + "yargs-parser": "21.0.1" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + } + } + }, + "@nrwl/devkit": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", + "integrity": "sha512-TAAsZJvVc/obeH0rZKY6miVhyM2GHGb8qIWp9MAIdLlXf4VDcNC7rxwb5OrGVSwuTTjqGYBGPUx0yEogOOJthA==", + "dev": true, + "requires": { + "@nrwl/tao": "13.1.3", + "ejs": "^3.1.5", + "ignore": "^5.0.4", + "rxjs": "^6.5.4", + "semver": "7.3.4", + "tslib": "^2.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@nrwl/tao": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", + "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", + "dev": true, + "requires": { + "chalk": "4.1.0", + "enquirer": "~2.3.6", + "fs-extra": "^9.1.0", + "jsonc-parser": "3.0.0", + "nx": "13.1.3", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "tmp": "~0.2.1", + "tslib": "^2.0.0", + "yargs-parser": "20.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "yargs-parser": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", + "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", + "dev": true + } + } + }, + "@parcel/watcher": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", + "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", + "dev": true, + "requires": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + } + }, + "@schematics/angular": { + "version": "13.2.3", + "dev": true, + "requires": { + "@angular-devkit/core": "13.2.3", + "@angular-devkit/schematics": "13.2.3", + "jsonc-parser": "3.0.0" + } + }, + "@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "dev": true + }, + "@swc-node/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.9.0.tgz", + "integrity": "sha512-vRnvsMtL9OxybA/Wun1ZhlDvB6MNs4Zujnina0VKdGk+yI6s87KUhdTcbAY6dQMZhQTLFiC1Lnv/BuwCKcCEug==", + "dev": true, + "requires": { + "@swc/core": "^1.2.172" + } + }, + "@swc-node/register": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.5.1.tgz", + "integrity": "sha512-6IL5s4QShKGs08qAeNou3rDA3gbp2WHk6fo0XnJXQn/aC9k6FnVBbj/thGOIEDtgNhC/DKpZT8tCY1LpQnOZFg==", + "dev": true, + "requires": { + "@swc-node/core": "^1.9.0", + "@swc-node/sourcemap-support": "^0.2.0", + "colorette": "^2.0.16", + "debug": "^4.3.4", + "pirates": "^4.0.5", + "tslib": "^2.4.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + } + } + }, + "@swc-node/sourcemap-support": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.2.0.tgz", + "integrity": "sha512-FNrxdI6XMYfoNt81L8eFKEm1d8P82I1nPwS3MrnBGzZoMWB+seQhQK+iN6M5RreJxXbfZw5lF86LRjHEQeGMqg==", + "dev": true, + "requires": { + "source-map-support": "^0.5.21" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "@swc/core": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.189.tgz", + "integrity": "sha512-S5cKX4ECMSfW78DLFgnlilJZgjrFRYwPslrrwpLl3gpwh+Qo72/Mhn71u7G/5xXW+T/xW5GwPccHfCk+k72uUg==", + "dev": true, + "requires": { + "@swc/core-android-arm-eabi": "1.2.189", + "@swc/core-android-arm64": "1.2.189", + "@swc/core-darwin-arm64": "1.2.189", + "@swc/core-darwin-x64": "1.2.189", + "@swc/core-freebsd-x64": "1.2.189", + "@swc/core-linux-arm-gnueabihf": "1.2.189", + "@swc/core-linux-arm64-gnu": "1.2.189", + "@swc/core-linux-arm64-musl": "1.2.189", + "@swc/core-linux-x64-gnu": "1.2.189", + "@swc/core-linux-x64-musl": "1.2.189", + "@swc/core-win32-arm64-msvc": "1.2.189", + "@swc/core-win32-ia32-msvc": "1.2.189", + "@swc/core-win32-x64-msvc": "1.2.189" + } + }, + "@swc/core-android-arm-eabi": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.189.tgz", + "integrity": "sha512-0kN3Le6QzFFz+Lc6a/tf/RkJXubWwWaHxF4c0bVm4AKIFf4nRlUCEqEkjdVaZvL92rpBMHaEEBuIIz3T8DqTTQ==", + "dev": true, + "optional": true + }, + "@swc/core-android-arm64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.189.tgz", + "integrity": "sha512-smsb+YkDw2OKwg66Z63E/G4NlFApDbsiOPmZXFZbtZbNBD9v+wnk6WVA//XR1bdUI9VbzNKlMPKJxQTE685QDw==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-arm64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.189.tgz", + "integrity": "sha512-OGjZRkTulKirJMLYbW9etb59lA9ueDXVwYRVD9SrNh8tRMTf0Nq+SUT/C3LVhBBGC4KSdWOzBAYbDTTdsnY++Q==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.189.tgz", + "integrity": "sha512-BEcxnBHx51514Voe2dn/y1y5H9VNyw7Zpp9+mPekZqx5o/onPD5wZ1ZfAsPrA4UlvM3v16u6ITE/cLawJ/GdAQ==", + "dev": true, + "optional": true + }, + "@swc/core-freebsd-x64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.189.tgz", + "integrity": "sha512-B6g2NWeh2iw6WPOaM19Uj3VE4se6alT265kWibLUshjcofRfnYT1lNhhkrF1D0EVnpC8I96I/xXNQo4Am9z4zQ==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.189.tgz", + "integrity": "sha512-6WhPG9pyN5AahQTVQk8MoN1I9Z/Ytfqizuie1wV7mW8FMNmMkiJvBekKtE6ftxu80Hqa34r86WfEwmJKm5MqYw==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.189.tgz", + "integrity": "sha512-frJTGnxsDe7h2d7BtnBivOdOJTtabkQIIZmHd4JHqafcoekI6teyveIax17axLyimvWl278yTo3hf7ePagD/eg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.189.tgz", + "integrity": "sha512-27K38LoZpME5lojDJIUNo7zdTDwAKLm0BMQ7HXWcYOyiDAekhSidI+SrBWxCfLzfuClhFu6/VE3E7j32VFJsiA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.189.tgz", + "integrity": "sha512-Ha5oJKOyQm9w7+e+WdRm4ypijzEmglWZGtgBR6vV6ViqqHcTBAU4nG87ex7y7AS9p+Cbc6EOSR9X1qIB8KxtbA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.189.tgz", + "integrity": "sha512-/p5yXa9HEzpVEuE4ivkW1IvwyYu6fT+L2OvVEs5oXIba80F0Wjy7InWqaa83gwrdMH+bXV6loG8LzZUZu/lpjA==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.189.tgz", + "integrity": "sha512-o/1ueM6/sifNjYnO6NMEXB895spVfJs5oQIPxQG9vJ/4zWLw8YmAx+u1xJY+XGyK6gnroHt7yPiS87qWdbeF6w==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.189.tgz", + "integrity": "sha512-YDwRkzykaf+dw5Z7u189cC/Tttkn2NVV84hrGL3LbVuh7wT5PaDhZs4Yz4unZQSlPV12olmZWgNr/i27h5wlpg==", + "dev": true, + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.189.tgz", + "integrity": "sha512-Nge8Z/ZkAp5p5No50yBDpBG7+ZYaVWGSuwtPj6OJe7orzvDCEm9GgcVE6J9GEjbclSWlCH8B8lUe17GaKRZHbg==", + "dev": true, + "optional": true + }, + "@tootallnate/once": { + "version": "1.1.2", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.2", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/component-emitter": { + "version": "1.2.11", + "dev": true + }, + "@types/connect": { + "version": "3.4.35", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/cookie": { + "version": "0.4.1", + "dev": true + }, + "@types/cors": { + "version": "2.8.12", + "dev": true + }, + "@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "dev": true + }, + "@types/express": { + "version": "4.17.13", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/http-proxy": { + "version": "1.17.8", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "3.6.11", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.10", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/json-schema": { + "version": "7.0.9", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/mime": { + "version": "1.3.2", + "dev": true + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "@types/node": { + "version": "12.11.7", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "dev": true + }, + "@types/prettier": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", + "integrity": "sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "dev": true + }, + "@types/retry": { + "version": "0.12.1", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.19", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.2.2", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", + "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.17.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "dev": true + }, + "abab": { + "version": "2.0.5", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "adm-zip": { + "version": "0.4.16", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.0", + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.9.0", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "angularx-qrcode": { + "version": "13.0.3", + "requires": { + "@cordobo/qrcode": "1.5.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "ansi-colors": { + "version": "4.1.1", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1" + }, + "ansi-styles": { + "version": "3.2.1", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "apexcharts": { + "version": "3.33.1", + "requires": { + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, + "aproba": { + "version": "2.0.0", + "dev": true + }, + "are-we-there-yet": { + "version": "3.0.0", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "arg": { + "version": "4.1.3", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "dev": true + } + } + }, + "array-flatten": { + "version": "2.1.2", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "dev": true + }, + "asn1": { + "version": "0.2.6", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "2.6.3", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "asynckit": { + "version": "0.4.0", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "dev": true + }, + "autoprefixer": { + "version": "10.4.2", + "dev": true, + "requires": { + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001297", + "fraction.js": "^4.1.2", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "dev": true + }, + "babel-loader": { + "version": "8.2.3", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "dev": true + }, + "batch": { + "version": "0.6.1", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "dev": true + }, + "bl": { + "version": "4.1.0", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "blocking-proxy": { + "version": "1.0.1", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "body-parser": { + "version": "1.19.1", + "dev": true, + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.19.1", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "browserstack": { + "version": "1.6.1", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + }, + "dependencies": { + "agent-base": { + "version": "4.3.0", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "debug": { + "version": "3.2.7", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + } + } + }, + "buffer": { + "version": "5.7.1", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "dev": true + }, + "bytes": { + "version": "3.1.1", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.2", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001312", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "dev": true + }, + "circular-dependency-plugin": { + "version": "5.2.2", + "dev": true, + "requires": {} + }, + "clean-stack": { + "version": "2.2.0", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "dev": true + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "cli-width": { + "version": "3.0.0", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "requires": { + "is-regexp": "^2.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "dev": true + }, + "colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "dev": true + }, + "colors": { + "version": "1.4.0", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "dev": true + }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "dev": true + }, + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "dev": true + }, + "connect": { + "version": "3.7.0", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "dev": true + }, + "copy-anything": { + "version": "2.0.6", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "copy-webpack-plugin": { + "version": "10.2.1", + "dev": true, + "requires": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "array-union": { + "version": "3.0.1", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globby": { + "version": "12.2.0", + "dev": true, + "requires": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "schema-utils": { + "version": "4.0.0", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "core-js": { + "version": "3.20.3", + "dev": true + }, + "core-js-compat": { + "version": "3.21.0", + "dev": true, + "requires": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "dev": true + } + } + }, + "core-js-pure": { + "version": "3.22.6", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.6.tgz", + "integrity": "sha512-u5yG2VL6NKXz9BZHr9RAm6eWD1DTNjG7jJnJgLGR+Im0whdPcPXqwqxd+dcUrZvpvPan5KMgn/3pI+Q/aGqPOA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "dev": true + }, + "cors": { + "version": "2.8.5", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "7.0.1", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "critters": { + "version": "0.0.16", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "css": { + "version": "3.0.0", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "dev": true + } + } + }, + "css-blank-pseudo": { + "version": "3.0.3", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-functions-list": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", + "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", + "dev": true + }, + "css-has-pseudo": { + "version": "3.0.4", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.5.1", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "dev": true, + "requires": {} + }, + "css-select": { + "version": "4.2.1", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "5.1.0", + "dev": true + }, + "cssdb": { + "version": "5.1.0", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "dev": true + }, + "custom-event": { + "version": "1.0.1", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "4.0.3", + "dev": true + }, + "debug": { + "version": "4.3.3", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decode-uri-component": { + "version": "0.2.0", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-gateway": { + "version": "6.0.3", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "2.2.2", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "dev": true + }, + "depd": { + "version": "1.1.2", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "dev": true + }, + "detect-it": { + "version": "4.0.1" + }, + "detect-node": { + "version": "2.1.0", + "dev": true + }, + "detect-passive-events": { + "version": "2.0.3", + "requires": { + "detect-it": "^4.0.1" + } + }, + "di": { + "version": "0.0.1", + "dev": true + }, + "diff": { + "version": "4.0.2", + "dev": true + }, + "dijkstrajs": { + "version": "1.0.2" + }, + "dir-glob": { + "version": "3.0.1", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "dns-equal": { + "version": "1.0.0", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serialize": { + "version": "2.2.1", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "1.3.2", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "dev": true + }, + "domhandler": { + "version": "4.3.0", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "dev": true + }, + "ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.4.68", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0" + }, + "emojis-list": { + "version": "3.0.0", + "dev": true + }, + "encode-utf8": { + "version": "1.0.3" + }, + "encodeurl": { + "version": "1.0.2", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "6.1.2", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "ws": "~8.2.3" + } + }, + "engine.io-parser": { + "version": "5.0.3", + "dev": true, + "requires": { + "@socket.io/base64-arraybuffer": "~1.0.2" + } + }, + "enhanced-resolve": { + "version": "5.9.0", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "ent": { + "version": "2.2.0", + "dev": true + }, + "entities": { + "version": "2.2.0", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "dev": true + }, + "err-code": { + "version": "2.0.3", + "dev": true + }, + "errno": { + "version": "0.1.8", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "dev": true + }, + "es6-promise": { + "version": "4.2.8", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "esbuild": { + "version": "0.14.14", + "dev": true, + "optional": true, + "requires": { + "esbuild-android-arm64": "0.14.14", + "esbuild-darwin-64": "0.14.14", + "esbuild-darwin-arm64": "0.14.14", + "esbuild-freebsd-64": "0.14.14", + "esbuild-freebsd-arm64": "0.14.14", + "esbuild-linux-32": "0.14.14", + "esbuild-linux-64": "0.14.14", + "esbuild-linux-arm": "0.14.14", + "esbuild-linux-arm64": "0.14.14", + "esbuild-linux-mips64le": "0.14.14", + "esbuild-linux-ppc64le": "0.14.14", + "esbuild-linux-s390x": "0.14.14", + "esbuild-netbsd-64": "0.14.14", + "esbuild-openbsd-64": "0.14.14", + "esbuild-sunos-64": "0.14.14", + "esbuild-windows-32": "0.14.14", + "esbuild-windows-64": "0.14.14", + "esbuild-windows-arm64": "0.14.14" + } + }, + "esbuild-darwin-64": { + "version": "0.14.14", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.14.14", + "dev": true + }, + "escalade": { + "version": "3.1.1" + }, + "escape-html": { + "version": "1.0.3", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + }, + "eslint": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", + "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "dev": true, + "requires": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "dev": true + }, + "etag": { + "version": "1.8.1", + "dev": true + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "dev": true + }, + "events": { + "version": "3.3.0", + "dev": true + }, + "execa": { + "version": "5.1.1", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "requires": { + "clone-regexp": "^2.1.0" + } + }, + "exit": { + "version": "0.1.2", + "dev": true + }, + "express": { + "version": "4.17.2", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "dev": true + }, + "cookie": { + "version": "0.4.1", + "dev": true + }, + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extsprintf": { + "version": "1.3.0", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figures": { + "version": "3.2.0", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "fill-range": { + "version": "7.0.1", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "dev": true + }, + "follow-redirects": { + "version": "1.14.8", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "dev": true + }, + "fraction.js": { + "version": "4.1.3", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gauge": { + "version": "4.0.0", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5" + }, + "get-intrinsic": { + "version": "1.1.1", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "dev": true + }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.2.0", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + } + } + }, + "globals": { + "version": "11.12.0", + "dev": true + }, + "globby": { + "version": "5.0.0", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, + "graceful-fs": { + "version": "4.2.9", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "dev": true + } + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-unicode": { + "version": "2.0.1", + "dev": true + }, + "hdr-histogram-js": { + "version": "2.0.3", + "dev": true, + "requires": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "hpack.js": { + "version": "2.1.6", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-entities": { + "version": "2.3.2", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "dev": true + }, + "html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "dev": true + }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "peer": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + }, + "dependencies": { + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "peer": true + } + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.5", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.3", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "http-signature": { + "version": "1.2.0", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "dev": true, + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "dev": true + }, + "ignore-walk": { + "version": "4.0.1", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "dev": true + }, + "immutable": { + "version": "4.0.0", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "dev": true + } + } + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "dev": true + }, + "ini": { + "version": "2.0.0", + "dev": true + }, + "inquirer": { + "version": "8.2.0", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "rxjs": { + "version": "7.5.4", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "2.3.1", + "dev": true + } + } + }, + "ip": { + "version": "1.1.5", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "dev": true + }, + "is-arguments": { + "version": "1.1.1", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.8.1", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0" + }, + "is-glob": { + "version": "4.0.3", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "3.0.0", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "dev": true + }, + "is-what": { + "version": "3.14.1", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.8", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.1.0", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.1.4", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jasmine": { + "version": "2.8.0", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "dependencies": { + "jasmine-core": { + "version": "2.8.0", + "dev": true + } + } + }, + "jasmine-core": { + "version": "3.8.0", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "5.0.2", + "dev": true, + "requires": { + "colors": "1.4.0" + } + }, + "jasminewd2": { + "version": "2.2.0", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true + }, + "json-schema": { + "version": "0.4.0", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "dev": true + }, + "json5": { + "version": "2.2.0", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonc-parser": { + "version": "3.0.0", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "dev": true + }, + "jsprim": { + "version": "1.4.2", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.7.1", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "karma": { + "version": "6.3.16", + "dev": true, + "requires": { + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "colors": "1.4.0", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.2.0", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "3.1.0", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + } + }, + "karma-jasmine": { + "version": "4.0.1", + "dev": true, + "requires": { + "jasmine-core": "^3.6.0" + } + }, + "karma-jasmine-html-reporter": { + "version": "1.5.4", + "dev": true, + "requires": {} + }, + "karma-source-map-support": { + "version": "1.4.0", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "kind-of": { + "version": "6.0.3", + "dev": true + }, + "klona": { + "version": "2.0.5", + "dev": true + }, + "known-css-properties": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", + "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "dev": true + }, + "less": { + "version": "4.1.2", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mime": { + "version": "1.6.0", + "dev": true, + "optional": true + }, + "pify": { + "version": "4.0.1", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "dev": true, + "optional": true + }, + "tslib": { + "version": "2.3.1", + "dev": true + } + } + }, + "less-loader": { + "version": "10.2.0", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "license-webpack-plugin": { + "version": "4.0.1", + "dev": true, + "requires": { + "webpack-sources": "^3.0.0" + } + }, + "lie": { + "version": "3.3.0", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "dev": true + }, + "lint-staged": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz", + "integrity": "sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true + } + } + }, + "listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + } + } + }, + "loader-runner": { + "version": "4.2.0", + "dev": true + }, + "loader-utils": { + "version": "3.2.0", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "log4js": { + "version": "6.4.1", + "dev": true, + "requires": { + "date-format": "^4.0.3", + "debug": "^4.3.3", + "flatted": "^3.2.4", + "rfdc": "^1.3.0", + "streamroller": "^3.0.2" + } + }, + "loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true + }, + "loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "lru-cache": { + "version": "6.0.0", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "magic-string": { + "version": "0.25.7", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.1.0", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "dev": true + }, + "make-fetch-happen": { + "version": "9.1.0", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "dev": true + }, + "memfs": { + "version": "3.4.1", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "dev": true + }, + "methods": { + "version": "1.1.2", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "2.6.0", + "dev": true + }, + "mime-db": { + "version": "1.51.0", + "dev": true + }, + "mime-types": { + "version": "2.1.34", + "dev": true, + "requires": { + "mime-db": "1.51.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.5.3", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + } + } + }, + "minipass": { + "version": "3.1.6", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "needle": { + "version": "2.9.1", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "negotiator": { + "version": "0.6.3", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "dev": true + }, + "ng-apexcharts": { + "version": "1.7.0", + "requires": { + "tslib": "^2.0.0" + } + }, + "ngx-color-picker": { + "version": "12.0.0", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1" + } + } + }, + "nice-napi": { + "version": "1.0.2", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node-addon-api": { + "version": "3.2.1", + "dev": true + }, + "node-forge": { + "version": "1.3.0", + "dev": true + }, + "node-gyp": { + "version": "8.4.1", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-gyp-build": { + "version": "4.3.0", + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "dev": true + }, + "nopt": { + "version": "5.0.0", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "dev": true + }, + "npm-bundled": { + "version": "1.1.2", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true + }, + "npm-package-arg": { + "version": "8.1.5", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "3.0.0", + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-registry-fetch": { + "version": "12.0.2", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "dependencies": { + "@tootallnate/once": { + "version": "2.0.0", + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "lru-cache": { + "version": "7.3.1", + "dev": true + }, + "make-fetch-happen": { + "version": "10.0.2", + "dev": true, + "requires": { + "agentkeepalive": "^4.2.0", + "cacache": "^15.3.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.3.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.4.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.1.1", + "ssri": "^8.0.1" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "6.0.1", + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "nx": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/nx/-/nx-13.1.3.tgz", + "integrity": "sha512-clM0NQhQKYkqcNz2E3uYRMLwhp2L/9dBhJhQi9XBX4IAyA2gWAomhRIlLm5Xxg3g4h1xwSpP3eJ5t89VikY8Pw==", + "dev": true, + "requires": { + "@nrwl/cli": "*" + } + }, + "oauth-sign": { + "version": "0.9.0", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "dev": true + }, + "object-inspect": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", + "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "dev": true + }, + "once": { + "version": "1.4.0", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "5.4.1", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.1", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "dev": true + } + } + }, + "p-try": { + "version": "2.2.0", + "dev": true + }, + "pacote": { + "version": "12.0.3", + "dev": true, + "requires": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "dev": true + }, + "parse5": { + "version": "5.1.1", + "optional": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "dev": true + } + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "dev": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "dev": true + } + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "dev": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "dev": true + } + } + }, + "parseurl": { + "version": "1.3.3", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "dev": true + }, + "pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "piscina": { + "version": "3.2.0", + "dev": true, + "requires": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0", + "nice-napi": "^1.0.2" + } + }, + "pkg-dir": { + "version": "4.2.0", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "pngjs": { + "version": "5.0.0" + }, + "portfinder": { + "version": "1.0.28", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "postcss": { + "version": "8.4.5", + "dev": true, + "requires": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.0", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.2" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.2", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.3", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.0.2", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.0", + "dev": true, + "requires": {} + }, + "postcss-custom-properties": { + "version": "12.1.4", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.0", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.4", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-double-position-gradients": { + "version": "3.0.5", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.5", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-focus-visible": { + "version": "6.0.4", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "dev": true, + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.3", + "dev": true, + "requires": {} + }, + "postcss-html": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.4.1.tgz", + "integrity": "sha512-OKihuWxPuBQrQeLNsavP7ytJ9IYNj/ViAXB2v7Qjh56LnfESKrkahKA9si4VfPN8xtz6oqUE6KdL0bTPrHJr6g==", + "dev": true, + "peer": true, + "requires": { + "htmlparser2": "^7.1.2", + "postcss": "^8.4.0", + "postcss-safe-parser": "^6.0.0" + } + }, + "postcss-image-set-function": { + "version": "4.0.6", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "14.0.2", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "dev": true, + "requires": {} + }, + "postcss-lab-function": { + "version": "4.1.0", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-loader": { + "version": "6.2.1", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + } + }, + "postcss-logical": { + "version": "5.0.4", + "dev": true, + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "dev": true, + "requires": {} + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nesting": { + "version": "10.1.2", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.8" + } + }, + "postcss-overflow-shorthand": { + "version": "3.0.3", + "dev": true, + "requires": {} + }, + "postcss-page-break": { + "version": "3.0.4", + "dev": true, + "requires": {} + }, + "postcss-place": { + "version": "7.0.4", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.2.3", + "dev": true, + "requires": { + "autoprefixer": "^10.4.2", + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001299", + "css-blank-pseudo": "^3.0.2", + "css-has-pseudo": "^3.0.3", + "css-prefers-color-scheme": "^6.0.2", + "cssdb": "^5.0.0", + "postcss-attribute-case-insensitive": "^5.0.0", + "postcss-color-functional-notation": "^4.2.1", + "postcss-color-hex-alpha": "^8.0.2", + "postcss-color-rebeccapurple": "^7.0.2", + "postcss-custom-media": "^8.0.0", + "postcss-custom-properties": "^12.1.2", + "postcss-custom-selectors": "^6.0.0", + "postcss-dir-pseudo-class": "^6.0.3", + "postcss-double-position-gradients": "^3.0.4", + "postcss-env-function": "^4.0.4", + "postcss-focus-visible": "^6.0.3", + "postcss-focus-within": "^5.0.3", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.2", + "postcss-image-set-function": "^4.0.4", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.0.3", + "postcss-logical": "^5.0.3", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.2", + "postcss-overflow-shorthand": "^3.0.2", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.3", + "postcss-pseudo-class-any-link": "^7.0.2", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^5.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.1", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "dev": true, + "requires": {} + }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "requires": {} + }, + "postcss-scss": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", + "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "5.0.0", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true + }, + "prettier-eslint": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-14.1.0.tgz", + "integrity": "sha512-K0TRVaAUXtI5xz1ZaVZfvGMmunDNyIGXFkE845hVl6FzSxzRN9E03YmK3IiapcRFv3w4PyAL25LIPsy2sRz2tw==", + "dev": true, + "requires": { + "@types/eslint": "^8.4.2", + "@types/prettier": "^2.6.0", + "@typescript-eslint/parser": "^5.10.0", + "common-tags": "^1.4.0", + "dlv": "^1.1.0", + "eslint": "^8.7.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^2.5.1", + "pretty-format": "^23.0.1", + "require-relative": "^0.8.7", + "typescript": "^4.5.4", + "vue-eslint-parser": "^8.0.1" + } + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-bytes": { + "version": "5.6.0", + "dev": true + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "protractor": { + "version": "7.0.0", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.1.7", + "yargs": "^15.3.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "6.0.0", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "y18n": { + "version": "4.0.3", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "proxy-addr": { + "version": "2.0.7", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "dev": true + } + } + }, + "prr": { + "version": "1.0.1", + "dev": true, + "optional": true + }, + "psl": { + "version": "1.8.0", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "dev": true + }, + "q": { + "version": "1.4.1", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "dev": true + }, + "qs": { + "version": "6.9.6", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "dev": true + }, + "raw-body": { + "version": "2.4.2", + "dev": true, + "requires": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-cache": { + "version": "1.0.0", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.1", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.0.1", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.6.0", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "dev": true + } + } + }, + "request": { + "version": "2.88.2", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.3", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1" + }, + "require-from-string": { + "version": "2.0.2", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "dev": true + }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "dev": true + }, + "resolve": { + "version": "1.22.0", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "dev": true + }, + "resolve-url-loader": { + "version": "5.0.0", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "dev": true + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.12.0", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "6.5.5", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "rxjs-for-await": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", + "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", + "dev": true, + "requires": {} + }, + "safe-buffer": { + "version": "5.1.2", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "dev": true + }, + "sass": { + "version": "1.49.0", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "12.4.0", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "saucelabs": { + "version": "1.5.0", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + }, + "dependencies": { + "agent-base": { + "version": "4.3.0", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "debug": { + "version": "3.2.7", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + } + } + }, + "sax": { + "version": "1.2.4", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "dev": true + } + } + }, + "select-hose": { + "version": "2.0.0", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "tmp": { + "version": "0.0.30", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "2.0.0", + "dev": true, + "requires": { + "node-forge": "^1.2.0" + } + }, + "semver": { + "version": "7.3.5", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.17.2", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "dev": true + }, + "ms": { + "version": "2.1.3", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "dev": true + }, + "ms": { + "version": "2.0.0", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "dev": true + }, + "slash": { + "version": "4.0.0", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + } + } + }, + "smart-buffer": { + "version": "4.2.0", + "dev": true + }, + "socket.io": { + "version": "4.4.1", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" + } + }, + "socket.io-adapter": { + "version": "2.3.3", + "dev": true + }, + "socket.io-parser": { + "version": "4.0.4", + "dev": true, + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + } + }, + "sockjs": { + "version": "0.3.24", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "socks": { + "version": "2.6.2", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "6.1.1", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + } + }, + "source-map": { + "version": "0.7.3", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "dev": true + }, + "source-map-loader": { + "version": "3.0.1", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-resolve": { + "version": "0.6.0", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "source-map-support": { + "version": "0.4.18", + "dev": true, + "requires": { + "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "dev": true + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true + }, + "sshpk": { + "version": "1.17.0", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "statuses": { + "version": "1.5.0", + "dev": true + }, + "streamroller": { + "version": "3.0.2", + "dev": true, + "requires": { + "date-format": "^4.0.3", + "debug": "^4.1.1", + "fs-extra": "^10.0.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "dev": true + } + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, + "stylelint": { + "version": "14.8.3", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.3.tgz", + "integrity": "sha512-aLpskXwSgFEBYbFRKA/BfuyYMGuXNtn2t5GqoffNPSezvw97x/vVNWcZNF0+cwt+LBjfvyq9/MRE3OjInGRgNA==", + "dev": true, + "requires": { + "balanced-match": "^2.0.0", + "colord": "^2.9.2", + "cosmiconfig": "^7.0.1", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", + "execall": "^2.0.0", + "fast-glob": "^3.2.11", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.0", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.25.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.13", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", + "svg-tags": "^1.0.0", + "table": "^6.8.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.1" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "stylelint-config-html": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", + "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", + "dev": true, + "requires": {} + }, + "stylelint-config-prettier": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", + "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", + "dev": true, + "requires": {} + }, + "stylelint-config-prettier-scss": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier-scss/-/stylelint-config-prettier-scss-0.0.1.tgz", + "integrity": "sha512-lBAYG9xYOh2LeWEPC/64xeUxwOTnQ8nDyBijQoWoJb10/bMGrUwnokpt8jegGck2Vbtxh6XGwH63z5qBcVHreQ==", + "dev": true, + "requires": { + "stylelint-config-prettier": ">=9.0.3" + } + }, + "stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "requires": {} + }, + "stylelint-config-recommended-scss": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", + "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", + "dev": true, + "requires": { + "postcss-scss": "^4.0.2", + "stylelint-config-recommended": "^6.0.0", + "stylelint-scss": "^4.0.0" + }, + "dependencies": { + "stylelint-config-recommended": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", + "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "dev": true, + "requires": {} + } + } + }, + "stylelint-config-scss": { + "version": "1.0.0-security", + "resolved": "https://registry.npmjs.org/stylelint-config-scss/-/stylelint-config-scss-1.0.0-security.tgz", + "integrity": "sha512-8Pgul2mNpzTeK2KCuyV5RcDC1BgzWzU7dCLVJWuxpkKgmxrMqCvjqgyosaKbAVZy2AiaMU0zfHBt7prg7/NaxA==", + "dev": true + }, + "stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^7.0.0" + } + }, + "stylelint-config-standard-scss": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz", + "integrity": "sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==", + "dev": true, + "requires": { + "stylelint-config-recommended-scss": "^5.0.2", + "stylelint-config-standard": "^24.0.0" + }, + "dependencies": { + "stylelint-config-recommended": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", + "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "dev": true, + "requires": {} + }, + "stylelint-config-standard": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", + "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^6.0.0" + } + } + } + }, + "stylelint-scss": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", + "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", + "dev": true, + "requires": { + "lodash": "^4.17.21", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0" + } + }, + "stylus": { + "version": "0.56.0", + "dev": true, + "requires": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + } + }, + "stylus-loader": { + "version": "6.2.0", + "dev": true, + "requires": { + "fast-glob": "^3.2.7", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svg.draggable.js": { + "version": "2.2.2", + "requires": { + "svg.js": "^2.0.1" + } + }, + "svg.easing.js": { + "version": "2.0.0", + "requires": { + "svg.js": ">=2.3.x" + } + }, + "svg.filter.js": { + "version": "2.0.2", + "requires": { + "svg.js": "^2.2.5" + } + }, + "svg.js": { + "version": "2.7.1" + }, + "svg.pathmorphing.js": { + "version": "0.1.3", + "requires": { + "svg.js": "^2.4.0" + } + }, + "svg.resize.js": { + "version": "1.4.3", + "requires": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "dependencies": { + "svg.select.js": { + "version": "2.1.2", + "requires": { + "svg.js": "^2.2.5" + } + } + } + }, + "svg.select.js": { + "version": "3.0.1", + "requires": { + "svg.js": "^2.6.5" + } + }, + "symbol-observable": { + "version": "4.0.0", + "dev": true + }, + "table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "tapable": { + "version": "2.2.1", + "dev": true + }, + "tar": { + "version": "6.1.11", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "dev": true + } + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "terser": { + "version": "5.10.0", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.21", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "dev": true + } + } + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.1", + "dev": true, + "requires": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "dev": true + }, + "through": { + "version": "2.3.8", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tree-kill": { + "version": "1.2.2", + "dev": true + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "ts-node": { + "version": "8.3.0", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "tslib": { + "version": "2.0.3" + }, + "tunnel-agent": { + "version": "0.6.0", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.21.3", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-assert": { + "version": "1.0.8", + "dev": true + }, + "typescript": { + "version": "4.5.5", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.31", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "2.0.0", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "dev": true + }, + "verror": { + "version": "1.10.0", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "void-elements": { + "version": "2.0.1", + "dev": true + }, + "vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "requires": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "watchpack": { + "version": "2.3.1", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webdriver-js-extender": { + "version": "2.1.0", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webdriver-manager": { + "version": "12.1.8", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "ini": { + "version": "1.3.8", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "dev": true + } + } + }, + "webpack": { + "version": "5.67.0", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.0", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.7.3", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "dev": true + }, + "del": { + "version": "6.0.0", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, + "globby": { + "version": "11.1.0", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "is-path-cwd": { + "version": "2.2.0", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "slash": { + "version": "3.0.0", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "dev": true + }, + "webpack-subresource-integrity": { + "version": "5.1.0", + "dev": true, + "requires": { + "typed-assert": "^1.0.8" + } + }, + "websocket-driver": { + "version": "0.7.4", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "dev": true + }, + "which": { + "version": "1.3.1", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "dev": true + }, + "wide-align": { + "version": "1.1.5", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.0", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4" + } + } + }, + "wrappy": { + "version": "1.0.2", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.2.3", + "dev": true, + "requires": {} + }, + "xml2js": { + "version": "0.4.23", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "dev": true + }, + "y18n": { + "version": "5.0.8" + }, + "yallist": { + "version": "4.0.0", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + }, + "yn": { + "version": "3.1.1", + "dev": true + }, + "zone.js": { + "version": "0.11.4", + "requires": { + "tslib": "^2.0.0" + } + } + } +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f0dd62211..281f0ce34 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -15,33 +15,34 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.0.0": - version: 2.1.1 - resolution: "@ampproject/remapping@npm:2.1.1" +"@ampproject/remapping@npm:^2.1.0": + version: 2.2.0 + resolution: "@ampproject/remapping@npm:2.2.0" dependencies: - "@jridgewell/trace-mapping": ^0.3.0 - checksum: cc5cf29833e2b8bdf420821a6e027a35cf6a48605df64ae5095b55cb722581b236554fc8f920138e6da3f7a99ec99273b07ebe2e2bb98b6a4a8d8e33648cac77 + "@jridgewell/gen-mapping": ^0.1.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: d74d170d06468913921d72430259424b7e4c826b5a7d39ff839a29d547efb97dc577caa8ba3fb5cf023624e9af9d09651afc3d4112a45e2050328abc9b3a2292 languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1302.3": - version: 0.1302.3 - resolution: "@angular-devkit/architect@npm:0.1302.3" +"@angular-devkit/architect@npm:0.1302.6": + version: 0.1302.6 + resolution: "@angular-devkit/architect@npm:0.1302.6" dependencies: - "@angular-devkit/core": 13.2.3 + "@angular-devkit/core": 13.2.6 rxjs: 6.6.7 - checksum: c9b85a71b5765bd319ff474825a18a510bfb0aeb8f632cdab258dc8fbba9a79b9db5c1802312f9b446614190c283d6527474dd30ca83405ce429ed0c559a847e + checksum: d6f1b706bc56bfc8c1ab4c25bff52714728895ac58ea52c6ad7d0ffa73e55326982f7f024ab2bb14cb7ba5a330b05e797df004f4404995a22c26163d106e9a42 languageName: node linkType: hard "@angular-devkit/build-angular@npm:13.2.x": - version: 13.2.3 - resolution: "@angular-devkit/build-angular@npm:13.2.3" + version: 13.2.6 + resolution: "@angular-devkit/build-angular@npm:13.2.6" dependencies: "@ampproject/remapping": 1.1.1 - "@angular-devkit/architect": 0.1302.3 - "@angular-devkit/build-webpack": 0.1302.3 - "@angular-devkit/core": 13.2.3 + "@angular-devkit/architect": 0.1302.6 + "@angular-devkit/build-webpack": 0.1302.6 + "@angular-devkit/core": 13.2.6 "@babel/core": 7.16.12 "@babel/generator": 7.16.8 "@babel/helper-annotate-as-pure": 7.16.7 @@ -52,7 +53,7 @@ __metadata: "@babel/runtime": 7.16.7 "@babel/template": 7.16.7 "@discoveryjs/json-ext": 0.5.6 - "@ngtools/webpack": 13.2.3 + "@ngtools/webpack": 13.2.6 ansi-colors: 4.1.1 babel-loader: 8.2.3 babel-plugin-istanbul: 6.1.1 @@ -63,8 +64,8 @@ __metadata: core-js: 3.20.3 critters: 0.0.16 css-loader: 6.5.1 - esbuild: 0.14.14 - esbuild-wasm: 0.14.14 + esbuild: 0.14.22 + esbuild-wasm: 0.14.22 glob: 7.2.0 https-proxy-agent: 5.0.0 inquirer: 8.2.0 @@ -72,7 +73,7 @@ __metadata: karma-source-map-support: 1.4.0 less: 4.1.2 less-loader: 10.2.0 - license-webpack-plugin: 4.0.1 + license-webpack-plugin: 4.0.2 loader-utils: 3.2.0 mini-css-extract-plugin: 2.5.3 minimatch: 3.0.4 @@ -94,7 +95,7 @@ __metadata: source-map-support: 0.5.21 stylus: 0.56.0 stylus-loader: 6.2.0 - terser: 5.10.0 + terser: 5.11.0 text-table: 0.2.0 tree-kill: 1.2.2 tslib: 2.3.1 @@ -128,26 +129,26 @@ __metadata: optional: true tailwindcss: optional: true - checksum: 4138cfbfa5e68984073294a69e6fb6b58df9712c42deba6aae75767e072df2a4e4e165b0d1590d2e72cdeea4e0eb77e87db336a8a579d6a403a0bd3ebdbb7729 + checksum: f5b041a64661bcb845d9f91d19397fa9f14760c4904266974c6d4a7c9db3360ae7f76e2dd1332a85e68fc3d1d0051964a40fc2fdfbfef5ff912a29b67ffca978 languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1302.3": - version: 0.1302.3 - resolution: "@angular-devkit/build-webpack@npm:0.1302.3" +"@angular-devkit/build-webpack@npm:0.1302.6": + version: 0.1302.6 + resolution: "@angular-devkit/build-webpack@npm:0.1302.6" dependencies: - "@angular-devkit/architect": 0.1302.3 + "@angular-devkit/architect": 0.1302.6 rxjs: 6.6.7 peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^4.0.0 - checksum: 827f35b1186093efda0be38e7b5b44634f59ba9203a6ba21dd9701af0cc487f55cf2bafc0a6729ac1534226cd9e9c97d9f35297517ac73304f67f19f9e74d6e6 + checksum: 9c8f7737211eeb5240a2abd0caf6a61a19065829d2b75f47faa9dd2ed48c117ad0572641701cddabc264201ec3c4c2037c139a4efb8514c991704f4ac030e0df languageName: node linkType: hard -"@angular-devkit/core@npm:13.2.3": - version: 13.2.3 - resolution: "@angular-devkit/core@npm:13.2.3" +"@angular-devkit/core@npm:13.2.6": + version: 13.2.6 + resolution: "@angular-devkit/core@npm:13.2.6" dependencies: ajv: 8.9.0 ajv-formats: 2.1.1 @@ -160,20 +161,20 @@ __metadata: peerDependenciesMeta: chokidar: optional: true - checksum: 523671d26806046aa2e81aac45dbd2a18afdd4a8803ad9bdf5883e818357dcbd31174a1439fb1c7dc1b1dbe5ce885b385cb03dae6e7034c87b3d4f58b25a5c88 + checksum: ec8589dbd7b2b1c444229be9915c6a1f5ddb3ee26dcc43612f930125d93c7b2be1aabba09e0ba46008883908cd908c7883e67d534313d979cb3ad86b30e06019 languageName: node linkType: hard -"@angular-devkit/schematics@npm:13.2.3": - version: 13.2.3 - resolution: "@angular-devkit/schematics@npm:13.2.3" +"@angular-devkit/schematics@npm:13.2.6": + version: 13.2.6 + resolution: "@angular-devkit/schematics@npm:13.2.6" dependencies: - "@angular-devkit/core": 13.2.3 + "@angular-devkit/core": 13.2.6 jsonc-parser: 3.0.0 magic-string: 0.25.7 ora: 5.4.1 rxjs: 6.6.7 - checksum: 98748b5a3456798b7e32362b8b67ece9c00757d154a2d95275a18989713bd66174bff158bd3db8b3b50f054c9f2d33e418e79067992bd3a6e1205c5bb9bac0f2 + checksum: 0aeade80952a08ddf461f6e3b8fb3643089e5691c5aecb3b330610a197cdb3b0b2f2032a131128d9355146ad6a46a10af2e986dd5eac7d93a7e89374e772a6da languageName: node linkType: hard @@ -281,19 +282,19 @@ __metadata: linkType: hard "@angular/animations@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/animations@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/animations@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.2 - checksum: 8b976929ca0db1ccb7a81e1769506edb7ceab16406bfdd936c30da51513b34234ecda8ad3d5a3d57b6e5cd39b4b3369990ce21cbbbe19d3f810eac7072e3e5f4 + "@angular/core": 13.2.7 + checksum: e444c814cdbfd2c1360e9f7f01d853baeccf17551eba178fd30b5c29b8e95386fe7d315ccc951b8f0c5d2cfa04091f5ecffecde71f6a2a3137c711218d4cae58 languageName: node linkType: hard "@angular/cdk@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/cdk@npm:13.2.2" + version: 13.2.6 + resolution: "@angular/cdk@npm:13.2.6" dependencies: parse5: ^5.0.0 tslib: ^2.3.0 @@ -304,18 +305,18 @@ __metadata: dependenciesMeta: parse5: optional: true - checksum: 940b3e9d43bc425cdf0b8211ea00601e0a5ca98fe3bb8dca6981ec0738690c56dddf34c79fb71890d0064643cccd1e18956506aefabf24c9729aa7a624741619 + checksum: 8cce0c7568bf7d83ec1b8f6be171f061531c9c0c681b94967f6345f33e7febf58b71266d1414ef71e16808229de03c3fa835cb032e043a494c398fffc62f7d24 languageName: node linkType: hard "@angular/cli@npm:13.2.x": - version: 13.2.3 - resolution: "@angular/cli@npm:13.2.3" + version: 13.2.6 + resolution: "@angular/cli@npm:13.2.6" dependencies: - "@angular-devkit/architect": 0.1302.3 - "@angular-devkit/core": 13.2.3 - "@angular-devkit/schematics": 13.2.3 - "@schematics/angular": 13.2.3 + "@angular-devkit/architect": 0.1302.6 + "@angular-devkit/core": 13.2.6 + "@angular-devkit/schematics": 13.2.6 + "@schematics/angular": 13.2.6 "@yarnpkg/lockfile": 1.1.0 ansi-colors: 4.1.1 debug: 4.3.3 @@ -333,65 +334,65 @@ __metadata: uuid: 8.3.2 bin: ng: bin/ng.js - checksum: b979a704ac248f6ee11d2b2bd57a86504be440afa8f07747b908080e32848d22aa220d3ca2580c50e4ec62e613d61a332ae5ee8fc15f4d3acea34fefa2585936 + checksum: 66765d8ead86045949dc3b8fe45577e9ae705354975da4e6bfdce51654102f313dde667f67ae2106cd79e810bec44192e4cca2f75a19522fac3561314a10d84c languageName: node linkType: hard "@angular/common@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/common@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/common@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.2 + "@angular/core": 13.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: c7a6eadf9b31c6bcffc5072f8c2159c1624fc87a2056f092d7c2bc2ab8b925c8ee9a099262a1dae7bead451a29ad5cea5ed42688a2c9f6c0b85c1302ed5dafd7 + checksum: dbf2c324b3e3a549312d5d196da02cb07854fc75c7db5afeeb5efd7d432dff0d9539b097a1f150a2e8e8291cc85f0405a3e5cf7ec7a888a86f6f66a2213e269e languageName: node linkType: hard "@angular/compiler-cli@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/compiler-cli@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/compiler-cli@npm:13.2.7" dependencies: - "@babel/core": ^7.8.6 + "@babel/core": ^7.17.2 chokidar: ^3.0.0 convert-source-map: ^1.5.1 dependency-graph: ^0.11.0 - magic-string: ^0.25.0 + magic-string: ^0.26.0 reflect-metadata: ^0.1.2 semver: ^7.0.0 sourcemap-codec: ^1.4.8 tslib: ^2.3.0 yargs: ^17.2.1 peerDependencies: - "@angular/compiler": 13.2.2 + "@angular/compiler": 13.2.7 typescript: ">=4.4.2 <4.6" bin: ng-xi18n: bundles/src/bin/ng_xi18n.js ngc: bundles/src/bin/ngc.js ngcc: bundles/ngcc/main-ngcc.js - checksum: ade19505c9ca98a1c68f266532eed47798ba979612bbfca2be3d16a23a6b6af4aeeaad6fe000bfcdb8fdaf7cf48de3192ebf39dc38707a2ce0112d7d8c6b4918 + checksum: 9c7f207285174bd2e5bbb52363eadbaf143bc1582dca44fa941fc8c29cefd580173a8aa9444f152dc9b96bd8a267415890cd4509a08b786211a14a3f85cf1254 languageName: node linkType: hard "@angular/compiler@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/compiler@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/compiler@npm:13.2.7" dependencies: tslib: ^2.3.0 - checksum: 338a0da25cbbf47bf09ed617fd948fa3565c39b971f92718ce119c14f287ee1191de898e0c9adeee774754c203f130e1ba87132353a5084b3ae69012a326e988 + checksum: 6d63ede056a8625d237e9895c2c532009b90b4a3b0ed232a84cea5a6005021c4cd91746fe38398af72c8b6794107386bba56e1b1c65163635bde5f325fb67674 languageName: node linkType: hard "@angular/core@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/core@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/core@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.11.4 - checksum: 801240fe14c4dbf4d1c5fb80418ecf067b8cf2741b5a3fb2a29c4e11aa2494c853a593a28f35c270fbc88979095bfbf0c985983e0cfec7ba1d5b52957d2538d5 + checksum: 7601052ae8c2206a6c26a1e1f92077cc2cf2c84aeed2562b68b3f45066adac1fb581622b175475e4979ca5aee5ce3bc9c9d5cb95325ca61b6546c5366d10adcc languageName: node linkType: hard @@ -411,84 +412,84 @@ __metadata: linkType: hard "@angular/forms@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/forms@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/forms@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.2 - "@angular/core": 13.2.2 - "@angular/platform-browser": 13.2.2 + "@angular/common": 13.2.7 + "@angular/core": 13.2.7 + "@angular/platform-browser": 13.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: bba5e568453db12a0857d0be1d7840e0f3d6755816dafc4320318ec38663e1b7e261291ef56fd8a08f5e595164fca6890d72d589e5064bd308b09a3a3a103896 + checksum: bea0ae8de61695b341846982821a7463ee9cded9f8afb1e3fd1c344935898682ce73743adfdadc4d2be075cb77fab2af2b1fe4ab4c5ec3cc8bd371c71c99876b languageName: node linkType: hard "@angular/language-service@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/language-service@npm:13.2.2" - checksum: 2eca2dd49dae04c57637e1f9f5f5da528d98823f31a899e8466974bae5e1b4ad6ea9b397094cda26667605f934948baf4dffed180f746f3d4ad3be7d430ebd6a + version: 13.2.7 + resolution: "@angular/language-service@npm:13.2.7" + checksum: 2054f85287c822143ba891659148dfa5934b8abe2186d5b8a7544d6e890e7cec7d8afaaeddd76c004cc4e4b45414b24d4902ef176aa1dfca9b28c0598246968f languageName: node linkType: hard "@angular/material@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/material@npm:13.2.2" + version: 13.2.6 + resolution: "@angular/material@npm:13.2.6" dependencies: tslib: ^2.3.0 peerDependencies: "@angular/animations": ^13.0.0 || ^14.0.0-0 - "@angular/cdk": 13.2.2 + "@angular/cdk": 13.2.6 "@angular/common": ^13.0.0 || ^14.0.0-0 "@angular/core": ^13.0.0 || ^14.0.0-0 "@angular/forms": ^13.0.0 || ^14.0.0-0 "@angular/platform-browser": ^13.0.0 || ^14.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - checksum: 0a6ab51fb588d2911f770b2abfdb7e1d55c28441b83e5650c29b0056855e553dcc364fd40a398fb06ab28f922cfdbc6257ad8822a3ec70f5a252c1e91a4d9355 + checksum: 0287d8b966271cf1cd4587859ec59124a2019c542bffc561251238221e4a31b9fb2dfe57c1e47263b8c195aa66175ae0f8bc4373c66db11299793ff1aee406a7 languageName: node linkType: hard "@angular/platform-browser-dynamic@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/platform-browser-dynamic@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/platform-browser-dynamic@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.2 - "@angular/compiler": 13.2.2 - "@angular/core": 13.2.2 - "@angular/platform-browser": 13.2.2 - checksum: 6dbf65da1460d855b623a4d1c10468241c0f0f5efcd3aab4f3b150d262c8f58e6a96f8e48732aacd9d8a930512c751af7d693c71d8c01a6cd8d2a21361540be7 + "@angular/common": 13.2.7 + "@angular/compiler": 13.2.7 + "@angular/core": 13.2.7 + "@angular/platform-browser": 13.2.7 + checksum: f8423844f0c63d618b8861e2911163c266ce972fb9ebcc8e0bb49d8a176e9abedb5019a9b300100d7d7f054784c343410dd80ac7f1e62946910e5484513cec7c languageName: node linkType: hard "@angular/platform-browser@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/platform-browser@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/platform-browser@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/animations": 13.2.2 - "@angular/common": 13.2.2 - "@angular/core": 13.2.2 + "@angular/animations": 13.2.7 + "@angular/common": 13.2.7 + "@angular/core": 13.2.7 peerDependenciesMeta: "@angular/animations": optional: true - checksum: 22472d8dba4151654d413b9d3e65d56ab15867007e6b838261ca46e48ac0794d770fea57149ccdbf8d973d787afcead0a4dd73121c0fd36a377ca38fa7e305ad + checksum: 92c9020c6cd0b3e61e251fff6d86d8173e8af26c95b11f82548a81afc5b305507bd366b2a107d7596477b9d82a1b39397bdd20cf39f5779902ccbbd1dba6a816 languageName: node linkType: hard "@angular/router@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/router@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/router@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.2 - "@angular/core": 13.2.2 - "@angular/platform-browser": 13.2.2 + "@angular/common": 13.2.7 + "@angular/core": 13.2.7 + "@angular/platform-browser": 13.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: de28b31d2552b0551b3bc70f1e5a9e46effe7a421ece8c72a3708d0d0d53394feee4ca362f842d19fc909c08cfeeae3e8e36028e3b9c05274c7ffaf0480578f5 + checksum: ac3403e65a6eb420b040179e0ccb927744b654b0c335f58b7b0e144f53b0af5d5273284fdc143265c17a872680b94be304b61d031910f19375bafadd8b1389af languageName: node linkType: hard @@ -508,10 +509,10 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.16.4, @babel/compat-data@npm:^7.16.8": - version: 7.17.0 - resolution: "@babel/compat-data@npm:7.17.0" - checksum: fe5afaf529d107a223cd5937dace248464b6df1e9f4ea4031a5723e9571b46a4db1c4ff226bac6351148b1bc02ba1b39cb142662cd235aa99c1dda77882f8c9d +"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.16.8, @babel/compat-data@npm:^7.17.10": + version: 7.17.10 + resolution: "@babel/compat-data@npm:7.17.10" + checksum: e85051087cd4690de5061909a2dd2d7f8b6434a3c2e30be6c119758db2027ae1845bcd75a81127423dd568b706ac6994a1a3d7d701069a23bf5cfe900728290b languageName: node linkType: hard @@ -538,30 +539,30 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.12.3, @babel/core@npm:^7.8.6": - version: 7.17.2 - resolution: "@babel/core@npm:7.17.2" +"@babel/core@npm:^7.12.3, @babel/core@npm:^7.17.2": + version: 7.18.0 + resolution: "@babel/core@npm:7.18.0" dependencies: - "@ampproject/remapping": ^2.0.0 + "@ampproject/remapping": ^2.1.0 "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.17.0 - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helpers": ^7.17.2 - "@babel/parser": ^7.17.0 + "@babel/generator": ^7.18.0 + "@babel/helper-compilation-targets": ^7.17.10 + "@babel/helper-module-transforms": ^7.18.0 + "@babel/helpers": ^7.18.0 + "@babel/parser": ^7.18.0 "@babel/template": ^7.16.7 - "@babel/traverse": ^7.17.0 - "@babel/types": ^7.17.0 + "@babel/traverse": ^7.18.0 + "@babel/types": ^7.18.0 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.1.2 + json5: ^2.2.1 semver: ^6.3.0 - checksum: 68ab3459f41b41feb5cb263937f15e418e1c46998d482d1b6dfe34f78064765466cfd5b10205c22fb16b69dbd1d46e7a3c26c067884ca4eb514b3dac1e09a57f + checksum: 350b7724a48c80b76f8af11e3cac1ad8ec9021325389f5ae20c713b10d4359c5e60aa7e71a309a3e1893826c46e72eef5df4978eb63eaabc403e8cc4ce5e94fc languageName: node linkType: hard -"@babel/generator@npm:7.16.8, @babel/generator@npm:^7.16.8": +"@babel/generator@npm:7.16.8": version: 7.16.8 resolution: "@babel/generator@npm:7.16.8" dependencies: @@ -572,14 +573,14 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.17.0": - version: 7.17.0 - resolution: "@babel/generator@npm:7.17.0" +"@babel/generator@npm:^7.16.8, @babel/generator@npm:^7.18.0": + version: 7.18.0 + resolution: "@babel/generator@npm:7.18.0" dependencies: - "@babel/types": ^7.17.0 + "@babel/types": ^7.18.0 + "@jridgewell/gen-mapping": ^0.3.0 jsesc: ^2.5.1 - source-map: ^0.5.0 - checksum: 2987dbebb484727a227f1ce3db90810320986cfb3ffd23e6d1d87f75bbd8e7871b5bc44252822d4d5f048a2d872a5702b2a9bf7bab7e07f087d7f306f0ea6c0a + checksum: 0854b21d94f99e3ac68249a9bbaa0c3a914a600c69c12fffa4a01377d89282174a67e619654e401be4c791414a1d5e825671f089f1c2407694a494dcfab8b06c languageName: node linkType: hard @@ -602,46 +603,46 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-compilation-targets@npm:7.16.7" +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.7, @babel/helper-compilation-targets@npm:^7.17.10": + version: 7.17.10 + resolution: "@babel/helper-compilation-targets@npm:7.17.10" dependencies: - "@babel/compat-data": ^7.16.4 + "@babel/compat-data": ^7.17.10 "@babel/helper-validator-option": ^7.16.7 - browserslist: ^4.17.5 + browserslist: ^4.20.2 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 7238aaee78c011a42fb5ca92e5eff098752f7b314c2111d7bb9cdd58792fcab1b9c819b59f6a0851dc210dc09dc06b30d130a23982753e70eb3111bc65204842 + checksum: 5f547c7ebd372e90fa72c2aaea867e7193166e9f469dec5acde4f0e18a78b80bdca8e02a0f641f3e998be984fb5b802c729a9034faaee8b1a9ef6670cb76f120 languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.16.10, @babel/helper-create-class-features-plugin@npm:^7.16.7": - version: 7.17.1 - resolution: "@babel/helper-create-class-features-plugin@npm:7.17.1" +"@babel/helper-create-class-features-plugin@npm:^7.17.12, @babel/helper-create-class-features-plugin@npm:^7.18.0": + version: 7.18.0 + resolution: "@babel/helper-create-class-features-plugin@npm:7.18.0" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-member-expression-to-functions": ^7.16.7 + "@babel/helper-function-name": ^7.17.9 + "@babel/helper-member-expression-to-functions": ^7.17.7 "@babel/helper-optimise-call-expression": ^7.16.7 "@babel/helper-replace-supers": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0 - checksum: fb791071dcaa664640d7f1d041772c6b57a8a456720bf7cb21aa055845fad98c644cc7707f03aa94abe8720d19a7c69fd5984fe02fe57b7e99a69f77aa501fc8 + checksum: 9a6ef175350f1cf87abe7a738e8c9b603da7fcdb153c74e49af509183f8705278020baddb62a12c7f9ca059487fef97d75a4adea6a1446598ad9901d010e4296 languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.16.7": - version: 7.17.0 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.0" +"@babel/helper-create-regexp-features-plugin@npm:^7.16.7, @babel/helper-create-regexp-features-plugin@npm:^7.17.12": + version: 7.17.12 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.12" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 regexpu-core: ^5.0.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: eb66d9241544c705e9ce96d2d122b595ef52d926e6e031653e09af8a01050bd9d7e7fee168bf33a863342774d7d6a8cc7e8e9e5a45b955e9c01121c7a2d51708 + checksum: fe49d26b0f6c58d4c1748a4d0e98b343882b428e6db43c4ba5e0aa7ff2296b3a557f0a88de9f000599bb95640a6c47c0b0c9a952b58c11f61aabb06bcc304329 languageName: node linkType: hard @@ -681,23 +682,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-function-name@npm:7.16.7" +"@babel/helper-function-name@npm:^7.16.7, @babel/helper-function-name@npm:^7.17.9": + version: 7.17.9 + resolution: "@babel/helper-function-name@npm:7.17.9" dependencies: - "@babel/helper-get-function-arity": ^7.16.7 "@babel/template": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: fc77cbe7b10cfa2a262d7a37dca575c037f20419dfe0c5d9317f589599ca24beb5f5c1057748011159149eaec47fe32338c6c6412376fcded68200df470161e1 - languageName: node - linkType: hard - -"@babel/helper-get-function-arity@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-get-function-arity@npm:7.16.7" - dependencies: - "@babel/types": ^7.16.7 - checksum: 25d969fb207ff2ad5f57a90d118f6c42d56a0171022e200aaa919ba7dc95ae7f92ec71cdea6c63ef3629a0dc962ab4c78e09ca2b437185ab44539193f796e0c3 + "@babel/types": ^7.17.0 + checksum: a59b2e5af56d8f43b9b0019939a43774754beb7cb01a211809ca8031c71890999d07739e955343135ec566c4d8ff725435f1f60fb0af3bb546837c1f9f84f496 languageName: node linkType: hard @@ -710,12 +701,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-member-expression-to-functions@npm:7.16.7" +"@babel/helper-member-expression-to-functions@npm:^7.16.7, @babel/helper-member-expression-to-functions@npm:^7.17.7": + version: 7.17.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.17.7" dependencies: - "@babel/types": ^7.16.7 - checksum: e275378022278a7e7974a3f65566690f1804ac88c5f4e848725cf936f61cd1e2557e88cfb6cb4fea92ae5a95ad89d78dbccc9a53715d4363f84c9fd109272c18 + "@babel/types": ^7.17.0 + checksum: 70f361bab627396c714c3938e94a569cb0da522179328477cdbc4318e4003c2666387ad4931d6bd5de103338c667c9e4bbe3e917fc8c527b3f3eb6175b888b7d languageName: node linkType: hard @@ -728,19 +719,19 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-module-transforms@npm:7.16.7" +"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.0": + version: 7.18.0 + resolution: "@babel/helper-module-transforms@npm:7.18.0" dependencies: "@babel/helper-environment-visitor": ^7.16.7 "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-simple-access": ^7.16.7 + "@babel/helper-simple-access": ^7.17.7 "@babel/helper-split-export-declaration": ^7.16.7 "@babel/helper-validator-identifier": ^7.16.7 "@babel/template": ^7.16.7 - "@babel/traverse": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: 6e930ce776c979f299cdbeaf80187f4ab086d75287b96ecc1c6896d392fcb561065f0d6219fc06fa79b4ceb4bbdc1a9847da8099aba9b077d0a9e583500fb673 + "@babel/traverse": ^7.18.0 + "@babel/types": ^7.18.0 + checksum: 824c3967c08d75bb36adc18c31dcafebcd495b75b723e2e17c6185e88daf5c6db62a6a75d9f791b5f38618a349e7cb32503e715a1b9a4e8bad4d0f43e3e6b523 languageName: node linkType: hard @@ -753,10 +744,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.16.7 - resolution: "@babel/helper-plugin-utils@npm:7.16.7" - checksum: d08dd86554a186c2538547cd537552e4029f704994a9201d41d82015c10ed7f58f9036e8d1527c3760f042409163269d308b0b3706589039c5f1884619c6d4ce +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.17.12, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.17.12 + resolution: "@babel/helper-plugin-utils@npm:7.17.12" + checksum: 4813cf0ddb0f143de032cb88d4207024a2334951db330f8216d6fa253ea320c02c9b2667429ef1a34b5e95d4cfbd085f6cb72d418999751c31d0baf2422cc61d languageName: node linkType: hard @@ -784,12 +775,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-simple-access@npm:7.16.7" +"@babel/helper-simple-access@npm:^7.17.7": + version: 7.17.7 + resolution: "@babel/helper-simple-access@npm:7.17.7" dependencies: - "@babel/types": ^7.16.7 - checksum: 8d22c46c5ec2ead0686c4d5a3d1d12b5190c59be676bfe0d9d89df62b437b51d1a3df2ccfb8a77dded2e585176ebf12986accb6d45a18cff229eef3b10344f4b + "@babel/types": ^7.17.0 + checksum: 58a9bfd054720024f6ff47fbb113c96061dc2bd31a5e5285756bd3c2e83918c6926900e00150d0fb175d899494fe7d69bf2a8b278c32ef6f6bea8d032e6a3831 languageName: node linkType: hard @@ -837,62 +828,62 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.17.2": - version: 7.17.2 - resolution: "@babel/helpers@npm:7.17.2" +"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.18.0": + version: 7.18.0 + resolution: "@babel/helpers@npm:7.18.0" dependencies: "@babel/template": ^7.16.7 - "@babel/traverse": ^7.17.0 - "@babel/types": ^7.17.0 - checksum: 5fa06bbf59636314fb4098bb2e70cf488e0fb6989553438abab90356357b79976102ac129fb16fc8186893c79e0809de1d90e3304426d6fcdb1750da2b6dff9d + "@babel/traverse": ^7.18.0 + "@babel/types": ^7.18.0 + checksum: 3f41631c0797b052cc22337ee56290700fe7db7bc06b847fcdf2c0043cddc35861855a1acc4c948397838675d2dc694f4fb1b102d1c7eb484ea01e9029916b55 languageName: node linkType: hard "@babel/highlight@npm:^7.16.7": - version: 7.16.10 - resolution: "@babel/highlight@npm:7.16.10" + version: 7.17.12 + resolution: "@babel/highlight@npm:7.17.12" dependencies: "@babel/helper-validator-identifier": ^7.16.7 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 1f1bdd752a90844f4efc22166a46303fb651ba0fd75a06daba3ebae2575ab3edc1da9827c279872a3aaf305f50a18473c5fa1966752726a2b253065fd4c0745e + checksum: 841a11aa353113bcce662b47085085a379251bf8b09054e37e1e082da1bf0d59355a556192a6b5e9ee98e8ee6f1f2831ac42510633c5e7043e3744dda2d6b9d6 languageName: node linkType: hard -"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.0": - version: 7.17.0 - resolution: "@babel/parser@npm:7.17.0" +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.18.0": + version: 7.18.0 + resolution: "@babel/parser@npm:7.18.0" bin: parser: ./bin/babel-parser.js - checksum: d0ac5ffba0b234dde516f867edf5da5d92d6f841592b370ae3244cd7c8f27a7f5e3e3d4e90ca9c15ea58bc46823f1643f3f75b6eb9a9f676ae16e8b2365e922a + checksum: 253b5828bf4a0b443301baedc5993d6f7f35aa0d81cf8f2f2f53940904b7067eab7bd2380aee4b3be1d8efd5ae1008eb0fad19bde28f5fbc213c0fdf9a414466 languageName: node linkType: hard "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0 - checksum: bbb0f82a4cf297bdbb9110eea570addd4b883fd1b61535558d849822b087aa340fe4e9c31f8a39b087595c8310b58d0f5548d6be0b72c410abefb23a5734b7bc + checksum: 6ef739b3a2b0ac0b22b60ff472c118163ceb8d414dd08c8186cc563fddc2be62ad4d8681e02074a1c7f0056a72e7146493a85d12ded02e50904b0009ed85d8bf languageName: node linkType: hard "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 - "@babel/plugin-proposal-optional-chaining": ^7.16.7 + "@babel/plugin-proposal-optional-chaining": ^7.17.12 peerDependencies: "@babel/core": ^7.13.0 - checksum: 81b372651a7d886a06596b02df7fb65ea90265a8bd60c9f0d5c1777590a598e6cccbdc3239033ee0719abf904813e69577eeb0ed5960b40e07978df023b17a6a + checksum: 68520a8f26e56bc8d90c22133537a9819e82598e3c82007f30bdaf8898b0e12a7bfa0cd3044aca35a7f362fd6bc04e4cd8052a571fc2eb40ad8f1cf24e0fc45f languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:7.16.8, @babel/plugin-proposal-async-generator-functions@npm:^7.16.8": +"@babel/plugin-proposal-async-generator-functions@npm:7.16.8": version: 7.16.8 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.8" dependencies: @@ -905,28 +896,41 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-async-generator-functions@npm:^7.16.8": + version: 7.17.12 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.17.12" + dependencies: + "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-remap-async-to-generator": ^7.16.8 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 16a3c7f68a27031b4973b7c64ca009873c91b91afd7b3a4694ec7f1c6d8e91a6ee142eafd950113810fae122faa1031de71140333b2b1bd03d5367b1a05b1d91 + languageName: node + linkType: hard + "@babel/plugin-proposal-class-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-class-properties@npm:7.17.12" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.17.12 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3977e841e17b45b47be749b9a5b67b9e8b25ff0840f9fdad3f00cbcb35db4f5ff15f074939fe19b01207a29688c432cc2c682351959350834d62920b7881f803 + checksum: 884df6a4617a18cdc2a630096b2a10954bcc94757c893bb01abd6702fdc73343ca5c611f4884c4634e0608f5e86c3093ea6b973ce00bf21b248ba54de92c837d languageName: node linkType: hard "@babel/plugin-proposal-class-static-block@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.18.0" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.18.0 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-class-static-block": ^7.14.5 peerDependencies: "@babel/core": ^7.12.0 - checksum: 3b95b5137e089f0be17de667299ea2e28867b6310ab94219a5a89ac7675824e69f316d31930586142b9f432122ef3b98eb05fffdffae01b5587019ce9aab4ef3 + checksum: 70fd622fd7c62cca2aa99c70532766340a5c30105e35cb3f1187b450580d43adc78b3fcb1142ed339bcfccf84be95ea03407adf467331b318ce6874432736c89 languageName: node linkType: hard @@ -943,50 +947,50 @@ __metadata: linkType: hard "@babel/plugin-proposal-export-namespace-from@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5016079a5305c1c130fea587b42cdce501574739cfefa5b63469dbc1f32d436df0ff42fabf04089fe8b6a00f4ea7563869e944744b457e186c677995983cb166 + checksum: 41c9cd4c0a5629b65725d2554867c15b199f534cea5538bd1ae379c0d13e7206d8590e23b23cb05a8b243e33e6eb88c1de3fd03a55cdbc6d4cf8634a6bebe43d languageName: node linkType: hard "@babel/plugin-proposal-json-strings@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-json-strings@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-json-strings@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-json-strings": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ea6487918f8d88322ac2a4e5273be6163b0d84a34330c31cee346e23525299de3b4f753bc987951300a79f55b8f4b1971b24d04c0cdfcb7ceb4d636975c215e8 + checksum: 8ed4ee3fbc28e44fac17c48bd95b5b8c3ffc852053a9fffd36ab498ec0b0ba069b8b2f5658edc18332748948433b9d3e1e376f564a1d65cb54592ba9943be09b languageName: node linkType: hard "@babel/plugin-proposal-logical-assignment-operators@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c4cf18e10f900d40eaa471c4adce4805e67bd845f997a4b9d5653eced4e653187b9950843b2bf7eab6c0c3e753aba222b1d38888e3e14e013f87295c5b014f19 + checksum: 0d48451836219b7beeca4be22a8aeb4a177a4944be4727afb94a4a11f201dde8b0b186dd2ad65b537d61e9af3fa1afda734f7096bec8602debd76d07aa342e21 languageName: node linkType: hard "@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bfafc2701697b5c763dbbb65dd97b56979bfb0922e35be27733699a837aeff22316313ddfdd0fb45129efa3f86617219b77110d05338bc4dca4385d8ce83dd19 + checksum: 7881d8005d0d4e17a94f3bfbfa4a0d8af016d2f62ed90912fabb8c5f8f0cc0a15fd412f09c230984c40b5c893086987d403c73198ef388ffcb3726ff72efc009 languageName: node linkType: hard @@ -1003,17 +1007,17 @@ __metadata: linkType: hard "@babel/plugin-proposal-object-rest-spread@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.18.0" dependencies: - "@babel/compat-data": ^7.16.4 - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/compat-data": ^7.17.10 + "@babel/helper-compilation-targets": ^7.17.10 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-transform-parameters": ^7.16.7 + "@babel/plugin-transform-parameters": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2d3740e4df6d3f51d57862100c45c000104571aa98b7f798fdfc05ae0c12b9e7cc9b55f4a28612d626e29f3369a1481a0ee8a0241b23508b9d3da00c55f99d41 + checksum: 2b49bcf9a6b11fd8b6a1d4962a64f3c846a63f8340eca9824c907f75bfcff7422ca35b135607fc3ef2d4e7e77ce6b6d955b772dc3c1c39f7ed24a0d8a560ec78 languageName: node linkType: hard @@ -1029,54 +1033,54 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-optional-chaining@npm:7.16.7" +"@babel/plugin-proposal-optional-chaining@npm:^7.16.7, @babel/plugin-proposal-optional-chaining@npm:^7.17.12": + version: 7.17.12 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e4a6c1ac7e6817b92a673ea52ab0b7dc1fb39d29fb0820cd414e10ae2cd132bd186b4238dcca881a29fc38fe9d38ed24fc111ba22ca20086481682d343f4f130 + checksum: a27b220573441a0ad3eecf8ddcb249556a64de45add236791d76cfa164a8fd34181857528fa7d21d03d6b004e7c043bd929cce068e611ee1ac72aaf4d397aa12 languageName: node linkType: hard "@babel/plugin-proposal-private-methods@npm:^7.16.11": - version: 7.16.11 - resolution: "@babel/plugin-proposal-private-methods@npm:7.16.11" + version: 7.17.12 + resolution: "@babel/plugin-proposal-private-methods@npm:7.17.12" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.10 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.17.12 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b333e5aa91c265bb394a57b5f4ae1a34fc8ee73a8d75506b12df258d8b5342107cbd9261f95e606bd3264a5b023db77f1f95be30c2e526683916c57f793f7943 + checksum: a1e5bd6a0a541af55d133d7bcf51ff8eb4ac7417a30f518c2f38107d7d033a3d5b7128ea5b3a910b458d7ceb296179b6ff9d972be60d1c686113d25fede8bed3 languageName: node linkType: hard "@babel/plugin-proposal-private-property-in-object@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.17.12" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.17.12 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 666d668f51d8c01aaf0dd87b27a83fc0392884d2c8e9d8e17b3b7011c0d348865dee94b44dc2d7070726e58e3b579728dc2588aaa8140d563f7390743ee90f0a + checksum: 056cb77994b2ee367301cdf8c5b7ed71faf26d60859bbba1368b342977481b0884712a1b97fbd9b091750162923d0265bf901119d46002775aa66e4a9f30f411 languageName: node linkType: hard "@babel/plugin-proposal-unicode-property-regex@npm:^7.16.7, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": - version: 7.16.7 - resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.17.12" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.17.12 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b8a33713d456183f0b7d011011e7bd932c08cc06216399a7b2015ab39284b511993dc10a89bbb15d1d728e6a2ef42ca08c3202619aa148cbd48052422ea3995 + checksum: 0e4194510415ed11849f1617fcb32d996df746ba93cd05ebbabecb63cfc02c0e97b585c97da3dcf68acdd3c8b71cfae964abe5d5baba6bd3977a475d9225ad9e languageName: node linkType: hard @@ -1235,17 +1239,17 @@ __metadata: linkType: hard "@babel/plugin-transform-arrow-functions@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2a6aa982c6fc80f4de7ccd973507ce5464fab129987cb6661136a7b9b6a020c2b329b912cbc46a68d39b5a18451ba833dcc8d1ca8d615597fec98624ac2add54 + checksum: 48f99e74f523641696d5d9fb3f5f02497eca2e97bc0e9b8230a47f388e37dc5fd84b8b29e9f5a0c82d63403f7ba5f085a28e26939678f6e917d5c01afd884b50 languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.16.8, @babel/plugin-transform-async-to-generator@npm:^7.16.8": +"@babel/plugin-transform-async-to-generator@npm:7.16.8": version: 7.16.8 resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.8" dependencies: @@ -1258,6 +1262,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.16.8": + version: 7.17.12 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.17.12" + dependencies: + "@babel/helper-module-imports": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-remap-async-to-generator": ^7.16.8 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 052dd56eb3b10bc31f5aaced0f75fc7307713f74049ccfb91cd087bebfc890a6d462b59445c5299faaca9030814172cac290c941c76b731a38dcb267377c9187 + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.16.7": version: 7.16.7 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.16.7" @@ -1270,53 +1287,53 @@ __metadata: linkType: hard "@babel/plugin-transform-block-scoping@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-block-scoping@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-block-scoping@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f93b5441af573fc274655f1707aeb4f67a43e926b58f56d89cc35a27877ae0bf198648603cbc19f442579489138f93c3838905895f109aa356996dbc3ed97a68 + checksum: ea3d4d88e38367d62a1029d204c5cc0ac410b00779179c8507448001c64784bf8e34c6fa57f23d8b95a835541a2fc67d1076650b1efc99c78f699de354472e49 languageName: node linkType: hard "@babel/plugin-transform-classes@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-classes@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-classes@npm:7.17.12" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 + "@babel/helper-function-name": ^7.17.9 "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/helper-replace-supers": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 791526a1bf3c4659b94d619536e3181d3ad54887d50539066628c6e695789a3bb264dc1fbc8540169d62a222f623df54defb490c1811ae63bad1e3557d6b3bb0 + checksum: 0127b1cc432373965edf28cbfd9e85df5bc77e974ceb80ba32691e050e8fb6792f207d1941529c81d1b9e7a6e82da26ecc445f6f547f0ad5076cd2b27adc18ac languageName: node linkType: hard "@babel/plugin-transform-computed-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-computed-properties@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-computed-properties@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 28b17f7cfe643f45920b76dc040cab40d4e54eccf5074fba2658c484feacda9b4885b3854ffaf26292189783fdecc97211519c61831b6708fcbf739cfbcbf31c + checksum: 5d05418617e0967bec4818556b7febb6f8c40813e32035f0bd6b7dbd7b9d63e9ab7c7c8fd7bd05bab2a599dad58e7b69957d9559b41079d112c219bbc3649aa1 languageName: node linkType: hard "@babel/plugin-transform-destructuring@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-destructuring@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-transform-destructuring@npm:7.18.0" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d1c2e15e7be2a7c57ac8ec4df06fbb706c7ecc872ab7bc2193606e6d6a01929b6d5a1bb41540e41180e42a5ce0e70dce22e7896cb6578dd581d554f77780971b + checksum: d85d60737c3b05c4db71bc94270e952122d360bd6ebf91b5f98cf16fb8564558b615d115354fe0ef41e2aae9c4540e6e16144284d881ecaef687693736cd2a79 languageName: node linkType: hard @@ -1333,13 +1350,13 @@ __metadata: linkType: hard "@babel/plugin-transform-duplicate-keys@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b96f6e9f7b33a91ad0eb6b793e4da58b7a0108b58269109f391d57078d26e043b3872c95429b491894ae6400e72e44d9b744c9b112b8433c99e6969b767e30ed + checksum: fb6ad550538830b0dc5b1b547734359f2d782209570e9d61fe9b84a6929af570fcc38ab579a67ee7cd6a832147db91a527f4cceb1248974f006fe815980816bb languageName: node linkType: hard @@ -1356,13 +1373,13 @@ __metadata: linkType: hard "@babel/plugin-transform-for-of@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-for-of@npm:7.16.7" + version: 7.18.1 + resolution: "@babel/plugin-transform-for-of@npm:7.18.1" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 35c9264ee4bef814818123d70afe8b2f0a85753a0a9dc7b73f93a71cadc5d7de852f1a3e300a7c69a491705805704611de1e2ccceb5686f7828d6bca2e5a7306 + checksum: cdc6e1f1170218cc6ac5b26b4b8f011ec5c36666101e00e0061aaa5772969b093bad5b2af8ce908c184126d5bb0c26b89dd4debb96b2375aba2e20e427a623a8 languageName: node linkType: hard @@ -1380,13 +1397,13 @@ __metadata: linkType: hard "@babel/plugin-transform-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-literals@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-literals@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a9565d999fc7a72a391ef843cf66028c38ca858537c7014d9ea8ea587a59e5f952d9754bdcca6ca0446e84653e297d417d4faedccb9e4221af1aa30f25d918e0 + checksum: 09280fc1ed23b81deafd4fcd7a35d6c0944668de2317f14c1b8b78c5c201f71a063bb8d174d2fc97d86df480ff23104c8919d3aacf19f33c2b5ada584203bf1c languageName: node linkType: hard @@ -1402,78 +1419,79 @@ __metadata: linkType: hard "@babel/plugin-transform-modules-amd@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-modules-amd@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-transform-modules-amd@npm:7.18.0" dependencies: - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-module-transforms": ^7.18.0 + "@babel/helper-plugin-utils": ^7.17.12 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9ac251ee96183b10cf9b4ec8f9e8d52e14ec186a56103f6c07d0c69e99faa60391f6bac67da733412975e487bd36adb403e2fc99bae6b785bf1413e9d928bc71 + checksum: bed3ff5cd81f236981360fc4a6fd2262685c1202772c657ce3ab95b7930437f8fa22361021b481c977b6f47988dfcc07c7782a1c91b90d3a5552c91401f4631a languageName: node linkType: hard "@babel/plugin-transform-modules-commonjs@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.16.8" + version: 7.18.0 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.18.0" dependencies: - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-simple-access": ^7.16.7 + "@babel/helper-module-transforms": ^7.18.0 + "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-simple-access": ^7.17.7 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c0ac00f5457e12cac7825b14725b6fc787bef78945181469ff79f07ef0fd7df021cb00fe1d3a9f35fc9bc92ae59e6e3fc9075a70b627dfe10e00d0907892aace + checksum: debb8c952b689def0d3f02d2944b8d031650adcad042277f91c4d137d96c4de1796576d2791fc55217c19004947a37f031c9870d830861075d33d279fe02dda8 languageName: node linkType: hard "@babel/plugin-transform-modules-systemjs@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.18.0" dependencies: "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-module-transforms": ^7.18.0 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/helper-validator-identifier": ^7.16.7 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2e50ae45a725eeafac5a9d30e07a5e17ab8dcf62c3528cf4efe444fc6f12cd3c4e42e911a9aa37abab169687a98b29a4418eeafcf2031f9917162ac36105cb1b + checksum: 80fccfc546aab76238d3f4aeb454f61ed885670578f1ab6dc063bba5b5d4cbdf821439ac6ca8bc24449eed752359600b47be717196103d2eabba06de1bf3f732 languageName: node linkType: hard "@babel/plugin-transform-modules-umd@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-modules-umd@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-transform-modules-umd@npm:7.18.0" dependencies: - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-module-transforms": ^7.18.0 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d1433f8b0e0b3c9f892aa530f08fe3ba653a5e51fe1ed6034ac7d45d4d6f22c3ba99186b72e41ad9ce5d8dcf964104c3da2419f15fcdcf5ba05c5fda3ea2cefc + checksum: 4081a79cfd4c6fda785c2137f9f2721e35c06a9d2f23c304172838d12e9317a24d3cb5b652a9db61e58319b370c57b1b44991429efe709679f98e114d98597fb languageName: node linkType: hard "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.16.8" + version: 7.17.12 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.17.12" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.17.12 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0 - checksum: 73e149f5ff690f5b8e3764a881e8e5240f12f394256e7d5217705d0cbeae074c3faff394783190fe1a41f9fc5a53b960b6021158b7e5174391b5fc38f4ba047a + checksum: cff9d91d0abd87871da6574583e79093ed75d5faecea45b6a13350ba243b1a595d349a6e7d906f5dfdf6c69c643cba9df662c3d01eaa187c5b1a01cb5838e848 languageName: node linkType: hard "@babel/plugin-transform-new-target@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-new-target@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-new-target@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7410c3e68abc835f87a98d40269e65fb1a05c131decbb6721a80ed49a01bd0c53abb6b8f7f52d5055815509022790e1accca32e975c02f2231ac3cf13d8af768 + checksum: bec26350fa49c9a9431d23b4ff234f8eb60554b8cdffca432a94038406aae5701014f343568c0e0cc8afae6f95d492f6bae0d0e2c101c1a484fb20eec75b2c07 languageName: node linkType: hard @@ -1489,14 +1507,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-parameters@npm:7.16.7" +"@babel/plugin-transform-parameters@npm:^7.16.7, @babel/plugin-transform-parameters@npm:^7.17.12": + version: 7.17.12 + resolution: "@babel/plugin-transform-parameters@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4d6904376db82d0b35f0a6cce08f630daf8608d94e903d6c7aff5bd742b251651bd1f88cdf9f16cad98aba5fc7c61da8635199364865fad6367d5ae37cf56cc1 + checksum: d9ed5ec61dc460835bade8fa710b42ec9f207bd448ead7e8abd46b87db0afedbb3f51284700fd2a6892fdf6544ec9b949c505c6542c5ba0a41ca4e8749af00f0 languageName: node linkType: hard @@ -1512,24 +1530,25 @@ __metadata: linkType: hard "@babel/plugin-transform-regenerator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-regenerator@npm:7.16.7" + version: 7.18.0 + resolution: "@babel/plugin-transform-regenerator@npm:7.18.0" dependencies: - regenerator-transform: ^0.14.2 + "@babel/helper-plugin-utils": ^7.17.12 + regenerator-transform: ^0.15.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 12b1f9a4f324027af69f49522fbe7feea2ac53285ca5c7e27a70de09f56c74938bfda8b09ac06e57fa1207e441f00efb7adbc462afc9be5e8abd0c2a07715e01 + checksum: ebacf2bbe9e2fb6f2bd7996e19b41bfc9848628950ae06a1a832802a0b8e32a32003c6b89318da6ca521f79045c91324dcb4c97247ed56f86fa58d7401a7316f languageName: node linkType: hard "@babel/plugin-transform-reserved-words@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-reserved-words@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-reserved-words@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 00218a646e99a97c1f10b77c41c178ca1b91d0e6cf18dd4ca3c59b8a5ad721db04ef508f49be4cd0dcca7742490dbb145307b706a2dbea1917d5e5f7ba2f31b7 + checksum: d8a617cb79ca5852ac2736a9f81c15a3b0760919720c3b9069a864e2288006ebcaab557dbb36a3eba936defd6699f82e3bf894915925aa9185f5d9bcbf3b29fd languageName: node linkType: hard @@ -1561,14 +1580,14 @@ __metadata: linkType: hard "@babel/plugin-transform-spread@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-spread@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-spread@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6e961af1a70586bb72dd85e8296cee857c5dadd73225fccd0fe261c0d98652a82d69c65f3e9dc31ce019a12e9677262678479b96bd2d9140ddf6514618362828 + checksum: 3a95e4f163d598c0efc9d983e5ce3e8716998dd2af62af8102b11cb8d6383c71b74c7106adbce73cda6e48d3d3e927627847d36d76c2eb688cd0e2e07f67fb51 languageName: node linkType: hard @@ -1584,24 +1603,24 @@ __metadata: linkType: hard "@babel/plugin-transform-template-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-template-literals@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-template-literals@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b55a519dd8b957247ebad3cab21918af5adca4f6e6c87819501cfe3d4d4bccda25bc296c7dfc8a30909b4ad905902aeb9d55ad955cb9f5cbc74b42dab32baa18 + checksum: fec220cea6e7bcd7720c65245e628cdf8e8276379e8ee041e49217b5ebb426911cb738d5b66afa5b1c7d17fc8dbe76d8041dbbce442925d83f08fb510f90507e languageName: node linkType: hard "@babel/plugin-transform-typeof-symbol@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.16.7" + version: 7.17.12 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.17.12" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.17.12 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 739a8c439dacbd9af62cfbfa0a7cbc3f220849e5fc774e5ef708a09186689a724c41a1d11323e7d36588d24f5481c8b702c86ff7be8da2e2fed69bed0175f625 + checksum: e30bd03c8abc1b095f8b2a10289df6850e3bc3cd0aea1cbc29050aa3b421cbb77d0428b0cd012333632a7a930dc8301cd888e762b2dd601e7dc5dac50f4140c9 languageName: node linkType: hard @@ -1728,16 +1747,16 @@ __metadata: linkType: hard "@babel/runtime-corejs3@npm:^7.10.2": - version: 7.17.9 - resolution: "@babel/runtime-corejs3@npm:7.17.9" + version: 7.18.0 + resolution: "@babel/runtime-corejs3@npm:7.18.0" dependencies: core-js-pure: ^3.20.2 regenerator-runtime: ^0.13.4 - checksum: c0893eb1ba4fd8a5a0e43d0fd5c3ad61c020dc5953bb74a76e9e10a0adfde7a5d8fd7e78d59b08dce3a0774948c6c40c81df0fdd0a1130c414fd3535fae365cb + checksum: 9890cd59456847d5c9da7a5a118ab7ae76d0eba7ea7fae62e802fa807f87ea7e960acc9be7af3282cbd9ca7e8930a7b566e348d83af3d21e78667cfabbaa595f languageName: node linkType: hard -"@babel/runtime@npm:7.16.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:7.16.7": version: 7.16.7 resolution: "@babel/runtime@npm:7.16.7" dependencies: @@ -1746,6 +1765,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": + version: 7.18.0 + resolution: "@babel/runtime@npm:7.18.0" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: 9d0caa5fe690623fb6c5df6fb3b3581d227b55ef9f7c35eba0da83d10aa756669a81fe521ac4dbc007e5790716bac40ebe71ff098e2d1a9599dd696a282a3e95 + languageName: node + linkType: hard + "@babel/template@npm:7.16.7, @babel/template@npm:^7.16.7": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" @@ -1757,31 +1785,38 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.17.0": - version: 7.17.0 - resolution: "@babel/traverse@npm:7.17.0" +"@babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.18.0": + version: 7.18.0 + resolution: "@babel/traverse@npm:7.18.0" dependencies: "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.17.0 + "@babel/generator": ^7.18.0 "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 + "@babel/helper-function-name": ^7.17.9 "@babel/helper-hoist-variables": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.17.0 - "@babel/types": ^7.17.0 + "@babel/parser": ^7.18.0 + "@babel/types": ^7.18.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: 9b7de053d8a29453fd7b9614a028d8dc811817f02948eaee02093274b67927a1cfb0513b521bc4a9328c9b6e5b021fd343b358c3526bbb6ee61ec078d4110c0c + checksum: b80b49ba5cead42c4b09bdfbe926d94179f884d35319a0a3ab5a798c85f16102a7342799fac928b3041337ea2c3f5194f17c4a08f611a474de6eea719b640dd4 languageName: node linkType: hard -"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.17.0 - resolution: "@babel/types@npm:7.17.0" +"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.18.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.18.0 + resolution: "@babel/types@npm:7.18.0" dependencies: "@babel/helper-validator-identifier": ^7.16.7 to-fast-properties: ^2.0.0 - checksum: 12e5a287986fe557188e87b2c5202223f1dc83d9239a196ab936fdb9f8c1eb0be717ff19f934b5fad4e29a75586d5798f74bed209bccea1c20376b9952056f0e + checksum: 151485f94c929171fd6539430c0ae519e8bb67fbc0d856b285328f5e6ecbaf4237b52d7a581b413f5e7b6268d31a4db6ca9bc01372b284b2966aa473fc902f27 + languageName: node + linkType: hard + +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 languageName: node linkType: hard @@ -1800,13 +1835,23 @@ __metadata: linkType: hard "@csstools/postcss-progressive-custom-properties@npm:^1.1.0": - version: 1.1.0 - resolution: "@csstools/postcss-progressive-custom-properties@npm:1.1.0" + version: 1.3.0 + resolution: "@csstools/postcss-progressive-custom-properties@npm:1.3.0" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.3 - checksum: d8bb27f1fd83d3b5f3740cd9c85063c63178c765acf4af59cef62f5af90c46f49e5048a37e274f614cfd6b37c4d653a43b67ffb391fde306829997936bc76a24 + checksum: e281845fde5b8a80d06ec20147bd74e96a9351bebbec5e5c3a6fb37ea30a597ff84172601786a8a270662f58f708b4a3bf8d822d6318023def9773d2f6589962 + languageName: node + linkType: hard + +"@csstools/selector-specificity@npm:1.0.0": + version: 1.0.0 + resolution: "@csstools/selector-specificity@npm:1.0.0" + peerDependencies: + postcss: ^8.3 + postcss-selector-parser: ^6.0.10 + checksum: cfdabe0df7d9d5e1e0b23c68d4f39afd33520d3196a4c9b9dc52942d14f32b594bd227a91cc7e933b3191306b447ca231b00b5f3ad3c2676958f3b364725a0a0 languageName: node linkType: hard @@ -1817,48 +1862,24 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.2.1": - version: 1.2.1 - resolution: "@eslint/eslintrc@npm:1.2.1" - dependencies: - ajv: ^6.12.4 - debug: ^4.3.2 - espree: ^9.3.1 - globals: ^13.9.0 - ignore: ^5.2.0 - import-fresh: ^3.2.1 - js-yaml: ^4.1.0 - minimatch: ^3.0.4 - strip-json-comments: ^3.1.1 - checksum: 1f797b9f94d71b965992cf6c44e3bcb574643014fd1e3d4862d25056bd5568f59c488461a7e9a1c1758ca7f0def5d3cb69c3d8b38581bcf4a53af74371243797 - languageName: node - linkType: hard - -"@eslint/eslintrc@npm:^1.2.2": - version: 1.2.2 - resolution: "@eslint/eslintrc@npm:1.2.2" +"@eslint/eslintrc@npm:^1.3.0": + version: 1.3.0 + resolution: "@eslint/eslintrc@npm:1.3.0" dependencies: ajv: ^6.12.4 debug: ^4.3.2 - espree: ^9.3.1 - globals: ^13.9.0 + espree: ^9.3.2 + globals: ^13.15.0 ignore: ^5.2.0 import-fresh: ^3.2.1 js-yaml: ^4.1.0 - minimatch: ^3.0.4 + minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: d891036bbffb0efec1462aa4a603ed6e349d546b1632dde7d474ddd15c2a8b6895671b25293f1d3ba10ff629c24a3649ad049373fe695a0e44b612537088563c - languageName: node - linkType: hard - -"@gar/promisify@npm:^1.0.1": - version: 1.1.2 - resolution: "@gar/promisify@npm:1.1.2" - checksum: d05081e0887a49c178b75ee3067bd6ee086f73c154d121b854fb2e044e8a89cb1cbb6de3a0dd93a519b80f0531fda68b099dd7256205f7fbb3490324342f2217 + checksum: a1e734ad31a8b5328dce9f479f185fd4fc83dd7f06c538e1fa457fd8226b89602a55cc6458cd52b29573b01cdfaf42331be8cfc1fec732570086b591f4ed6515 languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": +"@gar/promisify@npm:^1.0.1, @gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 @@ -1903,38 +1924,66 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.1.0": + version: 0.1.1 + resolution: "@jridgewell/gen-mapping@npm:0.1.1" + dependencies: + "@jridgewell/set-array": ^1.0.0 + "@jridgewell/sourcemap-codec": ^1.4.10 + checksum: 3bcc21fe786de6ffbf35c399a174faab05eb23ce6a03e8769569de28abbf4facc2db36a9ddb0150545ae23a8d35a7cf7237b2aa9e9356a7c626fb4698287d5cc + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.0": + version: 0.3.1 + resolution: "@jridgewell/gen-mapping@npm:0.3.1" + dependencies: + "@jridgewell/set-array": ^1.0.0 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: e9e7bb3335dea9e60872089761d4e8e089597360cdb1af90370e9d53b7d67232c1e0a3ab65fbfef4fc785745193fbc56bff9f3a6cab6c6ce3f15e12b4191f86b + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.5 - resolution: "@jridgewell/resolve-uri@npm:3.0.5" - checksum: 1ee652b693da7979ac4007926cc3f0a32b657ffeb913e111f44e5b67153d94a2f28a1d560101cc0cf8087625468293a69a00f634a2914e1a6d0817ba2039a913 + version: 3.0.7 + resolution: "@jridgewell/resolve-uri@npm:3.0.7" + checksum: 94f454f4cef8f0acaad85745fd3ca6cd0d62ef731cf9f952ecb89b8b2ce5e20998cd52be31311cedc5fa5b28b1708a15f3ad9df0fe1447ee4f42959b036c4b5b + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.0.0": + version: 1.1.1 + resolution: "@jridgewell/set-array@npm:1.1.1" + checksum: cc5d91e0381c347e3edee4ca90b3c292df9e6e55f29acbe0dd97de8651b4730e9ab761406fd572effa79972a0edc55647b627f8c72315e276d959508853d9bf2 languageName: node linkType: hard "@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.11 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.11" - checksum: 3b2afaf8400fb07a36db60e901fcce6a746cdec587310ee9035939d89878e57b2dec8173b0b8f63176f647efa352294049a53c49739098eb907ff81fec2547c8 + version: 1.4.13 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.13" + checksum: f14449096f60a5f921262322fef65ce0bbbfb778080b3b20212080bcefdeba621c43a58c27065bd536ecb4cc767b18eb9c45f15b6b98a4970139572b60603a1c languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.0": - version: 0.3.4 - resolution: "@jridgewell/trace-mapping@npm:0.3.4" +"@jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.13 + resolution: "@jridgewell/trace-mapping@npm:0.3.13" dependencies: "@jridgewell/resolve-uri": ^3.0.3 "@jridgewell/sourcemap-codec": ^1.4.10 - checksum: ab8bce84bbbc8c34f3ba8325ed926f8f2d3098983c10442a80c55764c4eb6e47d5b92d8ff20a0dd868c3e76a3535651fd8a0138182c290dbfc8396195685c37b + checksum: e38254e830472248ca10a6ed1ae75af5e8514f0680245a5e7b53bc3c030fd8691d4d3115d80595b45d3badead68269769ed47ecbbdd67db1343a11f05700e75a languageName: node linkType: hard -"@ngtools/webpack@npm:13.2.3": - version: 13.2.3 - resolution: "@ngtools/webpack@npm:13.2.3" +"@ngtools/webpack@npm:13.2.6": + version: 13.2.6 + resolution: "@ngtools/webpack@npm:13.2.6" peerDependencies: "@angular/compiler-cli": ^13.0.0 typescript: ">=4.4.3 <4.6" webpack: ^5.30.0 - checksum: 1d37be74c4f79b0f021f768e62bd670e7e4e800af835f51700cb00fc6753edefc0f20befe1760bb0c9c54f04203f90bdb2984a6bdc5d4a6c554f502a1c00b4d4 + checksum: 9dfc5d89393bb90b56fd78fb08da536a88ab23f43d772493cf78c8b9aad94f4b859cfd770aa520c3776a261444a999550467803bc074b69b66a7c3c7dc4170d6 languageName: node linkType: hard @@ -2061,12 +2110,12 @@ __metadata: languageName: node linkType: hard -"@nrwl/cli@npm:*, @nrwl/cli@npm:13.10.1": - version: 13.10.1 - resolution: "@nrwl/cli@npm:13.10.1" +"@nrwl/cli@npm:*, @nrwl/cli@npm:14.1.7": + version: 14.1.7 + resolution: "@nrwl/cli@npm:14.1.7" dependencies: - nx: 13.10.1 - checksum: 132962f23d549de4e0293fa3fb60d7fcdab4ea37aba94acc35049ca14f18ce4156100a3eb3c601f3c9c11815df1e1e09fc9af0ab6f2e6112224676831476dc2f + nx: 14.1.7 + checksum: 37a0a8535a282dd012ed7847249dd08bf45674bab0b560dfd02540076d4ba327c4c49fdd5561516d023eacc0237a3a93dd147f09d13494bd81e6e11975e64460 languageName: node linkType: hard @@ -2105,14 +2154,14 @@ __metadata: languageName: node linkType: hard -"@nrwl/tao@npm:13.10.1": - version: 13.10.1 - resolution: "@nrwl/tao@npm:13.10.1" +"@nrwl/tao@npm:14.1.7": + version: 14.1.7 + resolution: "@nrwl/tao@npm:14.1.7" dependencies: - nx: 13.10.1 + nx: 14.1.7 bin: tao: index.js - checksum: cd6fbef7ad36e0f04f8c00959170d12386a733e6c4adabddc448311db48162ab71807615d9f69ed5cb1cb3e2a629418f224168d035a2a6b4d2b39328760ba878 + checksum: ac5c58666975c34fb1c3be98936516b9dc030e51308fa0f980a600119a1faf0c77f1299124e4d9b5e1004b3ba2b45d5c331662bc3189419b90c62e85676f9dfa languageName: node linkType: hard @@ -2127,165 +2176,159 @@ __metadata: languageName: node linkType: hard -"@schematics/angular@npm:13.2.3": - version: 13.2.3 - resolution: "@schematics/angular@npm:13.2.3" +"@schematics/angular@npm:13.2.6": + version: 13.2.6 + resolution: "@schematics/angular@npm:13.2.6" dependencies: - "@angular-devkit/core": 13.2.3 - "@angular-devkit/schematics": 13.2.3 + "@angular-devkit/core": 13.2.6 + "@angular-devkit/schematics": 13.2.6 jsonc-parser: 3.0.0 - checksum: eab01246d34fa7a9af89017a95a921a3ec2c38d23e5e0efa3d33475030d91ff076fc1bfb68e282e116360e4c57080538facca9a596b00fb9c66cea6ab41ca26e - languageName: node - linkType: hard - -"@socket.io/base64-arraybuffer@npm:~1.0.2": - version: 1.0.2 - resolution: "@socket.io/base64-arraybuffer@npm:1.0.2" - checksum: fa3e58c7581643d0557969cd3bece20e198596df77968ff29ede6be329d488e65104bef900e68a67f39d8855abfa59baa2b08d96fb856504bd01cbdd8f52249c + checksum: f4f74d1d3ceee65374f49419ef5240a8aa8fb8df2eadf89f2f6af3bfc38b7d2b688bedb65e69200cfafceffbce790dfbb0855f1e0d408b330284c6e88da5dbc7 languageName: node linkType: hard -"@swc-node/core@npm:^1.8.2": - version: 1.8.2 - resolution: "@swc-node/core@npm:1.8.2" +"@swc-node/core@npm:^1.9.0": + version: 1.9.0 + resolution: "@swc-node/core@npm:1.9.0" dependencies: - "@swc/core": ^1.2.119 - checksum: e137b1751983a27ce120286960ce6d87071e2605a8467a9f6b8875b305e0328fa67b53582dfab52b5d6be8d6e86091777244a1aaa579de6d6c84e31b1f66d223 + "@swc/core": ^1.2.172 + checksum: 6fb86ce83456443ecd1f5be423fcaa72a144dbc925b4db5a2213179be93c5bfb4d7d1572c05cd9052a2f06612285e1562ccb1f9f77a8c5829dfb46c0e3fa84a1 languageName: node linkType: hard "@swc-node/register@npm:^1.4.2": - version: 1.4.2 - resolution: "@swc-node/register@npm:1.4.2" + version: 1.5.1 + resolution: "@swc-node/register@npm:1.5.1" dependencies: - "@swc-node/core": ^1.8.2 - "@swc-node/sourcemap-support": ^0.1.11 - chalk: 4 - debug: ^4.3.3 - pirates: ^4.0.4 - tslib: ^2.3.1 - typescript: ^4.5.3 - checksum: cb4a62f07ade0f207c42ef282243502ec24d0f8e4593973394eac94a364ac53b8c1fb3652afcd14191237ae0843eeb0fb6574ce1fc0696b017e271f06a14e248 + "@swc-node/core": ^1.9.0 + "@swc-node/sourcemap-support": ^0.2.0 + colorette: ^2.0.16 + debug: ^4.3.4 + pirates: ^4.0.5 + tslib: ^2.4.0 + peerDependencies: + typescript: ">= 4.3" + checksum: d4e4442e850bfd2416fcdf9b4a22afdcacde5bbb7c0ff21f5a3336ea3211c046805d7fadf8fbec1ab9f65b64eea07ef91cdc5d91792ba9c9045098bf8ba4c096 languageName: node linkType: hard -"@swc-node/sourcemap-support@npm:^0.1.11": - version: 0.1.11 - resolution: "@swc-node/sourcemap-support@npm:0.1.11" +"@swc-node/sourcemap-support@npm:^0.2.0": + version: 0.2.0 + resolution: "@swc-node/sourcemap-support@npm:0.2.0" dependencies: source-map-support: ^0.5.21 - checksum: 1dd5007d3516c9d034757d5012a03fc05cbe63935a5432c2e414611e0f8006a591d7505fb4243842da3c88efb6dbbc419e9297f526210365bd819938baa6956d + checksum: b202ada28394f1b4127a98f3ad3cde9e46f0a3b1e9e80580c2ac7fd52d246b3bfe78dc8fc3e1ceb96bf4fc572687cc5d1cfc67ceb44aefb0fceaad40684d27c9 languageName: node linkType: hard -"@swc/core-android-arm-eabi@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-android-arm-eabi@npm:1.2.165" +"@swc/core-android-arm-eabi@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-android-arm-eabi@npm:1.2.189" conditions: os=android & cpu=arm languageName: node linkType: hard -"@swc/core-android-arm64@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-android-arm64@npm:1.2.165" +"@swc/core-android-arm64@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-android-arm64@npm:1.2.189" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-darwin-arm64@npm:1.2.165" +"@swc/core-darwin-arm64@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-darwin-arm64@npm:1.2.189" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-darwin-x64@npm:1.2.165" +"@swc/core-darwin-x64@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-darwin-x64@npm:1.2.189" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-freebsd-x64@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-freebsd-x64@npm:1.2.165" +"@swc/core-freebsd-x64@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-freebsd-x64@npm:1.2.189" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.2.165" +"@swc/core-linux-arm-gnueabihf@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.2.189" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-linux-arm64-gnu@npm:1.2.165" +"@swc/core-linux-arm64-gnu@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-linux-arm64-gnu@npm:1.2.189" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-linux-arm64-musl@npm:1.2.165" +"@swc/core-linux-arm64-musl@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-linux-arm64-musl@npm:1.2.189" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-linux-x64-gnu@npm:1.2.165" +"@swc/core-linux-x64-gnu@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-linux-x64-gnu@npm:1.2.189" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-linux-x64-musl@npm:1.2.165" +"@swc/core-linux-x64-musl@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-linux-x64-musl@npm:1.2.189" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-win32-arm64-msvc@npm:1.2.165" +"@swc/core-win32-arm64-msvc@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-win32-arm64-msvc@npm:1.2.189" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-win32-ia32-msvc@npm:1.2.165" +"@swc/core-win32-ia32-msvc@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-win32-ia32-msvc@npm:1.2.189" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.2.165": - version: 1.2.165 - resolution: "@swc/core-win32-x64-msvc@npm:1.2.165" +"@swc/core-win32-x64-msvc@npm:1.2.189": + version: 1.2.189 + resolution: "@swc/core-win32-x64-msvc@npm:1.2.189" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:^1.2.119, @swc/core@npm:^1.2.152": - version: 1.2.165 - resolution: "@swc/core@npm:1.2.165" - dependencies: - "@swc/core-android-arm-eabi": 1.2.165 - "@swc/core-android-arm64": 1.2.165 - "@swc/core-darwin-arm64": 1.2.165 - "@swc/core-darwin-x64": 1.2.165 - "@swc/core-freebsd-x64": 1.2.165 - "@swc/core-linux-arm-gnueabihf": 1.2.165 - "@swc/core-linux-arm64-gnu": 1.2.165 - "@swc/core-linux-arm64-musl": 1.2.165 - "@swc/core-linux-x64-gnu": 1.2.165 - "@swc/core-linux-x64-musl": 1.2.165 - "@swc/core-win32-arm64-msvc": 1.2.165 - "@swc/core-win32-ia32-msvc": 1.2.165 - "@swc/core-win32-x64-msvc": 1.2.165 +"@swc/core@npm:^1.2.172, @swc/core@npm:^1.2.173": + version: 1.2.189 + resolution: "@swc/core@npm:1.2.189" + dependencies: + "@swc/core-android-arm-eabi": 1.2.189 + "@swc/core-android-arm64": 1.2.189 + "@swc/core-darwin-arm64": 1.2.189 + "@swc/core-darwin-x64": 1.2.189 + "@swc/core-freebsd-x64": 1.2.189 + "@swc/core-linux-arm-gnueabihf": 1.2.189 + "@swc/core-linux-arm64-gnu": 1.2.189 + "@swc/core-linux-arm64-musl": 1.2.189 + "@swc/core-linux-x64-gnu": 1.2.189 + "@swc/core-linux-x64-musl": 1.2.189 + "@swc/core-win32-arm64-msvc": 1.2.189 + "@swc/core-win32-ia32-msvc": 1.2.189 + "@swc/core-win32-x64-msvc": 1.2.189 dependenciesMeta: "@swc/core-android-arm-eabi": optional: true @@ -2315,7 +2358,7 @@ __metadata: optional: true bin: swcx: run_swcx.js - checksum: 366138976873b86ad376b619300f71ad4d1d3cf4c61185ddd0696e4170e09d3be66124a51e7402718d904622ad22f3a96dd5c710ce749be3154356f5ac2fded2 + checksum: 606013d0cf9e49a9c0498c77d070464394e2430b82728b5157b210300bc2d0dc84a071b98554292c8694a0d4e295c4248aebfe25ddd022764a75b9030e9ddf90 languageName: node linkType: hard @@ -2402,17 +2445,24 @@ __metadata: languageName: node linkType: hard -"@types/eslint@npm:*": - version: 8.4.1 - resolution: "@types/eslint@npm:8.4.1" +"@types/eslint@npm:*, @types/eslint@npm:^8.4.2": + version: 8.4.2 + resolution: "@types/eslint@npm:8.4.2" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: b5790997ee9d3820d16350192d41849b0e2448c9e93650acac672ddf502e35c0a5a25547172a9eec840a96687cd94ba1cee672cbd86640f8f4ff1b65960d2ab9 + checksum: e81268cdeb8d64d84af649344df88f064ece0f05db62072657c976b6162ffe16f94b6480a5367d627c629272c2d86d0ee8c24f7095e98f8e743b16f98500673b + languageName: node + linkType: hard + +"@types/estree@npm:*": + version: 0.0.51 + resolution: "@types/estree@npm:0.0.51" + checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^0.0.50": +"@types/estree@npm:^0.0.50": version: 0.0.50 resolution: "@types/estree@npm:0.0.50" checksum: 9a2b6a4a8c117f34d08fbda5e8f69b1dfb109f7d149b60b00fd7a9fb6ac545c078bc590aa4ec2f0a256d680cf72c88b3b28b60c326ee38a7bc8ee1ee95624922 @@ -2443,15 +2493,22 @@ __metadata: linkType: hard "@types/http-proxy@npm:^1.17.8": - version: 1.17.8 - resolution: "@types/http-proxy@npm:1.17.8" + version: 1.17.9 + resolution: "@types/http-proxy@npm:1.17.9" dependencies: "@types/node": "*" - checksum: 3b3d683498267096c8aca03652702243b1e087bc20e77a9abe74fdbee1c89c8283ee41c47d245cda2f422483b01980d70a1030b92a8ff24b280e0aa868241a8b + checksum: 7a6746d00729b2a9fe9f9dd3453430b099931df879ec8f7a7b5f07b1795f6d99b0512640c45a67390b1e4bacb9401e36824952aeeaf089feba8627a063cf8e00 languageName: node linkType: hard -"@types/jasmine@npm:*, @types/jasmine@npm:3.6.x": +"@types/jasmine@npm:*": + version: 4.0.3 + resolution: "@types/jasmine@npm:4.0.3" + checksum: 9d2af9ddb5750667bc9a3b439542fc9457c6eac2ab2c74801f598b03b9bc5cee1338fa8e75e6804dff1856e64539e8b0e3962ed8664a702f362d3db62eb9cd3a + languageName: node + linkType: hard + +"@types/jasmine@npm:3.6.x": version: 3.6.11 resolution: "@types/jasmine@npm:3.6.11" checksum: ccb4b749dc43b9ccb4365f36b14bdba8aac5ad7fdd00cc693695064acfbddb6b32fd2d59accd7e70b8f3a1eba69b49e8afa263e96f2b29aed565d0b911efe6c4 @@ -2468,9 +2525,9 @@ __metadata: linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": - version: 7.0.9 - resolution: "@types/json-schema@npm:7.0.9" - checksum: 259d0e25f11a21ba5c708f7ea47196bd396e379fddb79c76f9f4f62c945879dc21657904914313ec2754e443c5018ea8372362f323f30e0792897fdb2098a705 + version: 7.0.11 + resolution: "@types/json-schema@npm:7.0.11" + checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d languageName: node linkType: hard @@ -2495,7 +2552,14 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:12.11.x, @types/node@npm:>=10.0.0": +"@types/node@npm:*, @types/node@npm:>=10.0.0": + version: 17.0.35 + resolution: "@types/node@npm:17.0.35" + checksum: 7a24946ae7fd20267ed92466384f594e448bfb151081158d565cc635d406ecb29ea8fb85fcd2a1f71efccf26fb5bd3c6f509bde56077eb8b832b847a6664bc62 + languageName: node + linkType: hard + +"@types/node@npm:12.11.x": version: 12.11.7 resolution: "@types/node@npm:12.11.7" checksum: 7e60bb298fe80f85e3c4b26592d74983452f2eedc7a4bd65157c2059160685ff8ef26f670768ca4d20e41cd7195be2892bea9b483215faf8da1a737fd180e635 @@ -2516,6 +2580,13 @@ __metadata: languageName: node linkType: hard +"@types/prettier@npm:^2.6.0": + version: 2.6.1 + resolution: "@types/prettier@npm:2.6.1" + checksum: b25ec46d18129fa40c1a1f42feb7406e8f19901ba5261ba3c71600ad14996ae07b4f4b727a9b83da673948011e59d870fc519166f05b5d49e9ad39db1aea8c93 + languageName: node + linkType: hard + "@types/q@npm:^0.0.32": version: 0.0.32 resolution: "@types/q@npm:0.0.32" @@ -2537,17 +2608,17 @@ __metadata: languageName: node linkType: hard -"@types/retry@npm:^0.12.0": - version: 0.12.1 - resolution: "@types/retry@npm:0.12.1" - checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c +"@types/retry@npm:0.12.0": + version: 0.12.0 + resolution: "@types/retry@npm:0.12.0" + checksum: 61a072c7639f6e8126588bf1eb1ce8835f2cb9c2aba795c4491cf6310e013267b0c8488039857c261c387e9728c1b43205099223f160bb6a76b4374f741b5603 languageName: node linkType: hard "@types/selenium-webdriver@npm:^3.0.0": - version: 3.0.19 - resolution: "@types/selenium-webdriver@npm:3.0.19" - checksum: 81d5a4da6a73ad1bc53ae36de0a981919f21e9863777fb16198fd2c963040476d782fc0f41263f902b60ae07d49adacad87b86c0eff67a10b2e047b829462498 + version: 3.0.20 + resolution: "@types/selenium-webdriver@npm:3.0.20" + checksum: 6f96abc30b0dc00b833f3dd69dde561984f4d5aa5ad8b234e222d301e74be898b5acc01d4810bb012743ce4b1ae902605365900b3f7ad04c4f522d6e663c9fff languageName: node linkType: hard @@ -2580,11 +2651,11 @@ __metadata: linkType: hard "@types/ws@npm:^8.2.2": - version: 8.2.2 - resolution: "@types/ws@npm:8.2.2" + version: 8.5.3 + resolution: "@types/ws@npm:8.5.3" dependencies: "@types/node": "*" - checksum: 308957864b9a5a0378ac82f1b084fa31b1bbe85106fb0d84ed2b392e4829404f21ab6ab2c1eb782d556e59cd33d57c75ad2d0cedc4b9b9d0ca3b2311bc915578 + checksum: 0ce46f850d41383fcdc2149bcacc86d7232fa7a233f903d2246dff86e31701a02f8566f40af5f8b56d1834779255c04ec6ec78660fe0f9b2a69cf3d71937e4ae languageName: node linkType: hard @@ -2622,7 +2693,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.17.0": +"@typescript-eslint/parser@npm:5.17.0, @typescript-eslint/parser@npm:^5.10.0": version: 5.17.0 resolution: "@typescript-eslint/parser@npm:5.17.0" dependencies: @@ -2639,23 +2710,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.10.0": - version: 5.21.0 - resolution: "@typescript-eslint/parser@npm:5.21.0" - dependencies: - "@typescript-eslint/scope-manager": 5.21.0 - "@typescript-eslint/types": 5.21.0 - "@typescript-eslint/typescript-estree": 5.21.0 - debug: ^4.3.2 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: c0a4f03dccfba699c95788bef35312ec2ab7fa0dd7164916bce7762293b00f12f454d44dea2f1553d516d87a5fcc262ea3c5b7efa958cbfda7e4b9b73d67b54f - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/scope-manager@npm:5.17.0" @@ -2666,16 +2720,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.21.0": - version: 5.21.0 - resolution: "@typescript-eslint/scope-manager@npm:5.21.0" - dependencies: - "@typescript-eslint/types": 5.21.0 - "@typescript-eslint/visitor-keys": 5.21.0 - checksum: 2bcb5947d7882f08fb8f40eea154c15152957105a3dc80bf8481212a66d35a8d2239437e095a9a7526c6c0043e9bd6bececf4f87d40da85abb2d2b69f774d805 - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/type-utils@npm:5.17.0" @@ -2699,13 +2743,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.21.0": - version: 5.21.0 - resolution: "@typescript-eslint/types@npm:5.21.0" - checksum: 1581bf79f8c9236844ca8891e26c84503b654359fbfee80d76f9f57fb90c24210687cd30f61592e7d44cacf5417c83aaa5ae8559a4a8b6ce6b6c4a163b8b86c2 - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/typescript-estree@npm:5.17.0" @@ -2724,24 +2761,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.21.0": - version: 5.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.21.0" - dependencies: - "@typescript-eslint/types": 5.21.0 - "@typescript-eslint/visitor-keys": 5.21.0 - debug: ^4.3.2 - globby: ^11.0.4 - is-glob: ^4.0.3 - semver: ^7.3.5 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 4f78d61be2f35775d0f2d7fc4e3bb0bfc6b84e608e96a297c948f84a7254c1b9f0062f61a1dce67a8d4eb67476a9b4a9ebd8b6412e97db76f675c03363a5a0ad - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/utils@npm:5.17.0" @@ -2768,16 +2787,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.21.0": - version: 5.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.21.0" - dependencies: - "@typescript-eslint/types": 5.21.0 - eslint-visitor-keys: ^3.0.0 - checksum: 328b18faa61872160f3e5faacb5b68022bdabd04b5414f115133245a4a1ecfb5762c67fd645ab3253005480bd25a38598f57fdc2ff2b01d830ac68b37d3d06a5 - languageName: node - linkType: hard - "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -2951,9 +2960,9 @@ __metadata: linkType: hard "abab@npm:^2.0.5": - version: 2.0.5 - resolution: "abab@npm:2.0.5" - checksum: 0ec951b46d5418c2c2f923021ec193eaebdb4e802ffd5506286781b454be722a13a8430f98085cd3e204918401d9130ec6cc8f5ae19be315b3a0e857d83196e1 + version: 2.0.6 + resolution: "abab@npm:2.0.6" + checksum: 6ffc1af4ff315066c62600123990d87551ceb0aafa01e6539da77b0f5987ac7019466780bf480f1787576d4385e3690c81ccc37cfda12819bf510b8ab47e5a3e languageName: node linkType: hard @@ -2964,7 +2973,7 @@ __metadata: languageName: node linkType: hard -"accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.7": +"accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" dependencies: @@ -2983,7 +2992,7 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.3.1": +"acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" peerDependencies: @@ -2992,12 +3001,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0": - version: 8.7.0 - resolution: "acorn@npm:8.7.0" +"acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.1": + version: 8.7.1 + resolution: "acorn@npm:8.7.1" bin: acorn: bin/acorn - checksum: e0f79409d68923fbf1aa6d4166f3eedc47955320d25c89a20cc822e6ba7c48c5963d5bc657bc242d68f7a4ac9faf96eef033e8f73656da6c640d4219935fdfd0 + checksum: aca0aabf98826717920ac2583fdcad0a6fbe4e583fdb6e843af2594e907455aeafe30b1e14f1757cd83ce1776773cf8296ffc3a4acf13f0bd3dfebcf1db6ae80 languageName: node linkType: hard @@ -3036,18 +3045,7 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.0": - version: 4.2.0 - resolution: "agentkeepalive@npm:4.2.0" - dependencies: - debug: ^4.1.0 - depd: ^1.1.2 - humanize-ms: ^1.2.1 - checksum: 89806f83ceebbcaabf6bd581a8dce4870910fd2a11f66df8f505b4cd4ce4ca5ab9e6eec8d11ce8531a6b60f6748b75b0775e0e2fa33871503ef00d535418a19a - languageName: node - linkType: hard - -"agentkeepalive@npm:^4.2.1": +"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": version: 4.2.1 resolution: "agentkeepalive@npm:4.2.1" dependencies: @@ -3102,7 +3100,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.9.0, ajv@npm:^8.0.0, ajv@npm:^8.8.0": +"ajv@npm:8.9.0": version: 8.9.0 resolution: "ajv@npm:8.9.0" dependencies: @@ -3126,7 +3124,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.1": +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": version: 8.11.0 resolution: "ajv@npm:8.11.0" dependencies: @@ -3139,24 +3137,31 @@ __metadata: linkType: hard "angularx-qrcode@npm:13.0.x": - version: 13.0.3 - resolution: "angularx-qrcode@npm:13.0.3" + version: 13.0.15 + resolution: "angularx-qrcode@npm:13.0.15" dependencies: "@cordobo/qrcode": 1.5.0 tslib: ^2.3.0 peerDependencies: "@angular/core": ^13.0.0 - checksum: e36ae1afc87a2ce3c5d25845f3c940956dfca8a4d0e53e33f5d085046e96bd5390288183e04aae9968597177d45b7138fe1a409c8be8b0a9743aea9543c7b173 + checksum: 53cdb02a041d9fb489f83c798b0be119a0331bd67d295bda112622a8eb435ba9fc151544f1c0e524659c15a5223bc76ca75f41b3fd2cfd9ea12de2c39f27f744 languageName: node linkType: hard -"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": +"ansi-colors@npm:4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 languageName: node linkType: hard +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e + languageName: node + linkType: hard + "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -3246,8 +3251,8 @@ __metadata: linkType: hard "apexcharts@npm:3.33.x": - version: 3.33.1 - resolution: "apexcharts@npm:3.33.1" + version: 3.33.2 + resolution: "apexcharts@npm:3.33.2" dependencies: svg.draggable.js: ^2.2.2 svg.easing.js: ^2.0.0 @@ -3255,7 +3260,7 @@ __metadata: svg.pathmorphing.js: ^0.1.3 svg.resize.js: ^1.4.3 svg.select.js: ^3.0.1 - checksum: 15f3fe73cb249ce33a8588333c282873046b114430f0a0e9350dd0cdbbc0cd6ad61142e7ce311d064b3385f26c2f3f996c7da333b4f55a2bf495c6081c6e2409 + checksum: 094cae1017e84d8519f4d800c020e10957601ce7edc421f2e666717e73c10016ee69ad8407363e648265fd2bf9f0fd4d509408001ffd82d8d6531072144c2d03 languageName: node linkType: hard @@ -3383,19 +3388,19 @@ __metadata: languageName: node linkType: hard -"async@npm:0.9.x": - version: 0.9.2 - resolution: "async@npm:0.9.2" - checksum: 87dbf129292b8a6c32a4e07f43f462498162aa86f404a7e11f978dbfdf75cfb163c26833684bb07b9d436083cd604cbbf730a57bfcbe436c6ae1ed266cdc56bb - languageName: node - linkType: hard - "async@npm:^2.6.2": - version: 2.6.3 - resolution: "async@npm:2.6.3" + version: 2.6.4 + resolution: "async@npm:2.6.4" dependencies: lodash: ^4.17.14 - checksum: 5e5561ff8fca807e88738533d620488ac03a5c43fce6c937451f7e35f943d33ad06c24af3f681a48cca3d2b0002b3118faff0a128dc89438a9bf0226f712c499 + checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 + languageName: node + linkType: hard + +"async@npm:^3.2.3": + version: 3.2.3 + resolution: "async@npm:3.2.3" + checksum: c4bee57ab2249af3dc83ca3ef9acfa8e822c0d5e5aa41bae3eaf7f673648343cd64ecd7d26091ffd357f3f044428b17b5f00098494b6cf8b6b3e9681f0636ca1 languageName: node linkType: hard @@ -3423,12 +3428,12 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.2": - version: 10.4.2 - resolution: "autoprefixer@npm:10.4.2" + version: 10.4.7 + resolution: "autoprefixer@npm:10.4.7" dependencies: - browserslist: ^4.19.1 - caniuse-lite: ^1.0.30001297 - fraction.js: ^4.1.2 + browserslist: ^4.20.3 + caniuse-lite: ^1.0.30001335 + fraction.js: ^4.2.0 normalize-range: ^0.1.2 picocolors: ^1.0.0 postcss-value-parser: ^4.2.0 @@ -3436,7 +3441,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: dbd13e641eaa7d7e3121769c22cc439222f1a9d0371a583d12300849de7287ece1e793767ff9902842dbfd56c4b7c19ed9fe1947c9f343ba2f4f3519dbddfdef + checksum: 0e55d0d19806c672ec0c79cc23c27cf77e90edf2600670735266ba33ec5294458f404baaa2f7cd4cfe359cf7a97b3c86f01886bdbdc129a4f2f76ca5977a91af languageName: node linkType: hard @@ -3614,21 +3619,23 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.19.1, body-parser@npm:^1.19.0": - version: 1.19.1 - resolution: "body-parser@npm:1.19.1" +"body-parser@npm:1.20.0, body-parser@npm:^1.19.0": + version: 1.20.0 + resolution: "body-parser@npm:1.20.0" dependencies: - bytes: 3.1.1 + bytes: 3.1.2 content-type: ~1.0.4 debug: 2.6.9 - depd: ~1.1.2 - http-errors: 1.8.1 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 iconv-lite: 0.4.24 - on-finished: ~2.3.0 - qs: 6.9.6 - raw-body: 2.4.2 + on-finished: 2.4.1 + qs: 6.10.3 + raw-body: 2.5.1 type-is: ~1.6.18 - checksum: 9197a300a6580b8723c7b6b1e22cebd5ba47cd4a6fd45c153350efcde79293869ddee8d17d95fb52724812d649d89d62775faab072608d3243a0cbb00582234e + unpipe: 1.0.0 + checksum: 12fffdeac82fe20dddcab7074215d5156e7d02a69ae90cbe9fee1ca3efa2f28ef52097cbea76685ee0a1509c71d85abd0056a08e612c09077cad6277a644cf88 languageName: node linkType: hard @@ -3663,7 +3670,16 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.1, braces@npm:^3.0.2, braces@npm:~3.0.2": +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: ^1.0.0 + checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + +"braces@npm:^3.0.2, braces@npm:~3.0.2": version: 3.0.2 resolution: "braces@npm:3.0.2" dependencies: @@ -3672,18 +3688,18 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.17.5, browserslist@npm:^4.19.1, browserslist@npm:^4.9.1": - version: 4.19.1 - resolution: "browserslist@npm:4.19.1" +"browserslist@npm:^4.14.5, browserslist@npm:^4.19.1, browserslist@npm:^4.20.2, browserslist@npm:^4.20.3, browserslist@npm:^4.9.1": + version: 4.20.3 + resolution: "browserslist@npm:4.20.3" dependencies: - caniuse-lite: ^1.0.30001286 - electron-to-chromium: ^1.4.17 + caniuse-lite: ^1.0.30001332 + electron-to-chromium: ^1.4.118 escalade: ^3.1.1 - node-releases: ^2.0.1 + node-releases: ^2.0.3 picocolors: ^1.0.0 bin: browserslist: cli.js - checksum: c0777fd483691638fd6801e16c9d809e1d65f6d2b06db2e806654be51045cbab1452a89841a2c5caea2cbe19d621b4f1d391cffbb24512aa33280039ab345875 + checksum: 1e4b719ac2ca0fe235218a606e8b8ef16b8809e0973b924158c39fbc435a0b0fe43437ea52dd6ef5ad2efcb83fcb07431244e472270177814217f7c563651f7d languageName: node linkType: hard @@ -3734,14 +3750,14 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.1": - version: 3.1.1 - resolution: "bytes@npm:3.1.1" - checksum: 949ab99a385d6acf4d2c69f1afc618615dc905936e0b0b9aa94a9e94d722baaba44d6a0851536585a0892ae4d462b5a270ccb1b04c774640742cbde5538ca328 +"bytes@npm:3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: e4bcd3948d289c5127591fbedf10c0b639ccbf00243504e4e127374a15c3bc8eed0d28d4aaab08ff6f1cf2abc0cce6ba3085ed32f4f90e82a5683ce0014e1b6e languageName: node linkType: hard -"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0, cacache@npm:^15.3.0": +"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0": version: 15.3.0 resolution: "cacache@npm:15.3.0" dependencies: @@ -3767,15 +3783,15 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^16.0.2": - version: 16.0.4 - resolution: "cacache@npm:16.0.4" +"cacache@npm:^16.1.0": + version: 16.1.0 + resolution: "cacache@npm:16.1.0" dependencies: "@npmcli/fs": ^2.1.0 "@npmcli/move-file": ^2.0.0 chownr: ^2.0.0 fs-minipass: ^2.1.0 - glob: ^7.2.0 + glob: ^8.0.1 infer-owner: ^1.0.4 lru-cache: ^7.7.1 minipass: ^3.1.6 @@ -3789,7 +3805,7 @@ __metadata: ssri: ^9.0.0 tar: ^6.1.11 unique-filename: ^1.1.1 - checksum: f5ddd45e5b1ff5001f9d1fcbc95f1dc210e6b04fbaf92782dd16a514e9a8082efba6eac43dac3d881e2ab5829f5ad857d7deda58cbef235e93d075e8f378214a + checksum: ddfcf92f079f24ccecef4e2ca1e4428443787b61429b921803b020fd0f33d9ac829ac47837b74b40868d8ae4f1b2ed82e164cdaa5508fbd790eee005a9d88469 languageName: node linkType: hard @@ -3828,10 +3844,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001286, caniuse-lite@npm:^1.0.30001297, caniuse-lite@npm:^1.0.30001299": - version: 1.0.30001312 - resolution: "caniuse-lite@npm:1.0.30001312" - checksum: 753fb9ea1e02e999430b323a71b5acab5120f3b5fc0161b01669f54a3ef5c5296240b6ae9b79b12a3742e3aed216aa9ee3d5398a23c16d08625ccd376b79545d +"caniuse-lite@npm:^1.0.30001299, caniuse-lite@npm:^1.0.30001332, caniuse-lite@npm:^1.0.30001335": + version: 1.0.30001342 + resolution: "caniuse-lite@npm:1.0.30001342" + checksum: 9ad47aec82e85017c59aaa0acee8027d910a715c7481cf66c9b4e296f3d10cc5d96df86d3c3033326b0110f5792b3f117a1dc935e3299abf2139fa345bb326f1 languageName: node linkType: hard @@ -3842,16 +3858,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:4, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc - languageName: node - linkType: hard - "chalk@npm:4.1.0": version: 4.1.0 resolution: "chalk@npm:4.1.0" @@ -3886,6 +3892,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard + "chardet@npm:^0.7.0": version: 0.7.0 resolution: "chardet@npm:0.7.0" @@ -4066,7 +4082,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.2": +"color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -4190,7 +4206,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": +"console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed @@ -4229,10 +4245,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.4.1": - version: 0.4.1 - resolution: "cookie@npm:0.4.1" - checksum: bd7c47f5d94ab70ccdfe8210cde7d725880d2fcda06d8e375afbdd82de0c8d3b73541996e9ce57d35f67f672c4ee6d60208adec06b3c5fc94cebb85196084cf8 +"cookie@npm:0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 languageName: node linkType: hard @@ -4269,19 +4285,19 @@ __metadata: linkType: hard "core-js-compat@npm:^3.20.2, core-js-compat@npm:^3.21.0": - version: 3.21.0 - resolution: "core-js-compat@npm:3.21.0" + version: 3.22.6 + resolution: "core-js-compat@npm:3.22.6" dependencies: - browserslist: ^4.19.1 + browserslist: ^4.20.3 semver: 7.0.0 - checksum: 7914d2f8a2f7c1b400e1c04c7560f4c96028bf23cec3cea6063ba594e38023cccbd38ad88af41c5d6b65450e97a989eb37598f609e3f7fbc6ebc1856d4195cbb + checksum: 6b83b87abeb04c08b54bdc6a6756ad6d1503aeadebc598a162bfe1044a31183864bb3016f16c839d40235545c009dc8aea6421ada0d2e18b5fdf93c6059eb380 languageName: node linkType: hard "core-js-pure@npm:^3.20.2": - version: 3.21.1 - resolution: "core-js-pure@npm:3.21.1" - checksum: 00a5dff599b7fb0b30746a638b9d0edbdc0df24ed1580ca56be595fbe3c78c375d37fc4e1bff23627109229702c9ee8ea2587a66b8280eb33b85160aa4e401e9 + version: 3.22.6 + resolution: "core-js-pure@npm:3.22.6" + checksum: 90737229b00fb26b0896bf5d22351702e595db7c60258f76d58cbd6a0dcef37a45017c97dc91632d168e2242e6dfff3f442ebcea1090e6154462fac5f119bc2d languageName: node linkType: hard @@ -4292,13 +4308,20 @@ __metadata: languageName: node linkType: hard -"core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": +"core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab languageName: node linkType: hard +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 + languageName: node + linkType: hard + "cors@npm:~2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" @@ -4410,22 +4433,22 @@ __metadata: linkType: hard "css-select@npm:^4.2.0": - version: 4.2.1 - resolution: "css-select@npm:4.2.1" + version: 4.3.0 + resolution: "css-select@npm:4.3.0" dependencies: boolbase: ^1.0.0 - css-what: ^5.1.0 - domhandler: ^4.3.0 + css-what: ^6.0.1 + domhandler: ^4.3.1 domutils: ^2.8.0 nth-check: ^2.0.1 - checksum: 6617193ec7c332217204c4ea371d332c6845603fda415e36032e7e9e18206d7c368a14e3c57532082314d2689955b01122aa1097c1c52b6c1cab7ad90970d3c6 + checksum: d6202736839194dd7f910320032e7cfc40372f025e4bf21ca5bf6eb0a33264f322f50ba9c0adc35dadd342d3d6fae5ca244779a4873afbfa76561e343f2058e0 languageName: node linkType: hard -"css-what@npm:^5.1.0": - version: 5.1.0 - resolution: "css-what@npm:5.1.0" - checksum: 0b75d1bac95c885c168573c85744a6c6843d8c33345f54f717218b37ea6296b0e99bb12105930ea170fd4a921990392a7c790c16c585c1d8960c49e2b7ec39f7 +"css-what@npm:^6.0.1": + version: 6.1.0 + resolution: "css-what@npm:6.1.0" + checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe languageName: node linkType: hard @@ -4472,10 +4495,10 @@ __metadata: languageName: node linkType: hard -"date-format@npm:^4.0.3": - version: 4.0.3 - resolution: "date-format@npm:4.0.3" - checksum: 8ae4d9de3532010169a89bc7b079342051ba3ec88552636aa677bfb53e8eb15113af8394679aea7d41367dc8bb6e9865da17f21ac2802202180b09d6e3f2339e +"date-format@npm:^4.0.10": + version: 4.0.10 + resolution: "date-format@npm:4.0.10" + checksum: 677c8e022a5efcead2c869d18b3b1a3b61df6c2c464545ec91e68ea890088dae7704e5822009af420ea8bb26325ffce01be599cc9f087879ea4aefc70a964c7b languageName: node linkType: hard @@ -4488,7 +4511,19 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.3, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + +"debug@npm:4.3.3": version: 4.3.3 resolution: "debug@npm:4.3.3" dependencies: @@ -4509,18 +4544,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - "decamelize-keys@npm:^1.1.0": version: 1.1.0 resolution: "decamelize-keys@npm:1.1.0" @@ -4592,11 +4615,12 @@ __metadata: linkType: hard "define-properties@npm:^1.1.3": - version: 1.1.3 - resolution: "define-properties@npm:1.1.3" + version: 1.1.4 + resolution: "define-properties@npm:1.1.4" dependencies: - object-keys: ^1.0.12 - checksum: da80dba55d0cd76a5a7ab71ef6ea0ebcb7b941f803793e4e0257b384cb772038faa0c31659d244e82c4342edef841c1a1212580006a05a5068ee48223d787317 + has-property-descriptors: ^1.0.0 + object-keys: ^1.1.1 + checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b languageName: node linkType: hard @@ -4616,8 +4640,8 @@ __metadata: linkType: hard "del@npm:^6.0.0": - version: 6.0.0 - resolution: "del@npm:6.0.0" + version: 6.1.0 + resolution: "del@npm:6.1.0" dependencies: globby: ^11.0.1 graceful-fs: ^4.2.4 @@ -4627,7 +4651,7 @@ __metadata: p-map: ^4.0.0 rimraf: ^3.0.2 slash: ^3.0.0 - checksum: 5742891627e91aaf62385714025233f4664da28bc55b6ab825649dcdea4691fed3cf329a2b1913fd2d2612e693e99e08a03c84cac7f36ef54bacac9390520192 + checksum: 150ecf2617e0468df931df60d309dc46c7256e53baa82f6b368a2b6d71be062781602fa9336ebd31d6a2df8e19d8f07c29c5f7512385db6b95d00187ec5ee9e1 languageName: node linkType: hard @@ -4645,6 +4669,13 @@ __metadata: languageName: node linkType: hard +"depd@npm:2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a + languageName: node + linkType: hard + "depd@npm:^1.1.2, depd@npm:~1.1.2": version: 1.1.2 resolution: "depd@npm:1.1.2" @@ -4659,10 +4690,10 @@ __metadata: languageName: node linkType: hard -"destroy@npm:~1.0.4": - version: 1.0.4 - resolution: "destroy@npm:1.0.4" - checksum: da9ab4961dc61677c709da0c25ef01733042614453924d65636a7db37308fef8a24cd1e07172e61173d471ca175371295fbc984b0af5b2b4ff47cd57bd784c03 +"destroy@npm:1.2.0": + version: 1.2.0 + resolution: "destroy@npm:1.2.0" + checksum: 0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38 languageName: node linkType: hard @@ -4774,29 +4805,29 @@ __metadata: linkType: hard "dom-serializer@npm:^1.0.1": - version: 1.3.2 - resolution: "dom-serializer@npm:1.3.2" + version: 1.4.1 + resolution: "dom-serializer@npm:1.4.1" dependencies: domelementtype: ^2.0.1 domhandler: ^4.2.0 entities: ^2.0.0 - checksum: bff48714944d67b160db71ba244fb0f3fe72e77ef2ec8414e2eeb56f2d926e404a13456b8b83a5392e217ba47dec2ec0c368801b31481813e94d185276c3e964 + checksum: fbb0b01f87a8a2d18e6e5a388ad0f7ec4a5c05c06d219377da1abc7bb0f674d804f4a8a94e3f71ff15f6cb7dcfc75704a54b261db672b9b3ab03da6b758b0b22 languageName: node linkType: hard "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": - version: 2.2.0 - resolution: "domelementtype@npm:2.2.0" - checksum: 24cb386198640cd58aa36f8c987f2ea61859929106d06ffcc8f547e70cb2ed82a6dc56dcb8252b21fba1f1ea07df6e4356d60bfe57f77114ca1aed6828362629 + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 languageName: node linkType: hard -"domhandler@npm:^4.2.0, domhandler@npm:^4.3.0": - version: 4.3.0 - resolution: "domhandler@npm:4.3.0" +"domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": + version: 4.3.1 + resolution: "domhandler@npm:4.3.1" dependencies: domelementtype: ^2.2.0 - checksum: d2a2dbf40dd99abf936b65ad83c6b530afdb3605a87cad37a11b5d9220e68423ebef1b86c89e0f6d93ffaf315cc327cf1a988652e7a9a95cce539e3984f4c64d + checksum: 4c665ceed016e1911bf7d1dadc09dc888090b64dee7851cccd2fcf5442747ec39c647bb1cb8c8919f8bbdd0f0c625a6bafeeed4b2d656bbecdbae893f43ffaaa languageName: node linkType: hard @@ -4907,20 +4938,20 @@ __metadata: linkType: hard "ejs@npm:^3.1.5": - version: 3.1.6 - resolution: "ejs@npm:3.1.6" + version: 3.1.8 + resolution: "ejs@npm:3.1.8" dependencies: - jake: ^10.6.1 + jake: ^10.8.5 bin: - ejs: ./bin/cli.js - checksum: 81a9cdea0b4ded3b5a4b212b7c17e20bb07468f08394e2d519708d367957a70aef3d282a6d5d38bf6ad313ba25802b9193d4227f29b084d2ee0f28d115141d48 + ejs: bin/cli.js + checksum: 1d40d198ad52e315ccf37e577bdec06e24eefdc4e3c27aafa47751a03a0c7f0ec4310254c9277a5f14763c3cd4bbacce27497332b2d87c74232b9b1defef8efc languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.17": - version: 1.4.68 - resolution: "electron-to-chromium@npm:1.4.68" - checksum: d7654d07ab7c504a0683cf29715db227bdbd3e397ad3a41bad3d1e35e9f837447be2bc5dea54b3350d51be5c9c7b79756dcbe44903fbee5949d67d783e788acb +"electron-to-chromium@npm:^1.4.118": + version: 1.4.137 + resolution: "electron-to-chromium@npm:1.4.137" + checksum: 639d7b94906efafcf363519c3698eecc44be46755a6a5cdc9088954329978866cc93fbd57e08b97290599b68d5226243d21de9fa50be416b8a5d3fa8fd42c3a0 languageName: node linkType: hard @@ -4977,18 +5008,16 @@ __metadata: languageName: node linkType: hard -"engine.io-parser@npm:~5.0.0": - version: 5.0.3 - resolution: "engine.io-parser@npm:5.0.3" - dependencies: - "@socket.io/base64-arraybuffer": ~1.0.2 - checksum: 88d664420a441dd02db17d110f7bbbd9efe971747918150bf666b82ee138df596a2f5038f461c8a01864c83af67cb202548364e4174543f8c0bf5f4776ca6e0d +"engine.io-parser@npm:~5.0.3": + version: 5.0.4 + resolution: "engine.io-parser@npm:5.0.4" + checksum: d4ad0cef6ff63c350e35696da9bb3dbd180f67b56e93e90375010cc40393e6c0639b780d5680807e1d93a7e2e3d7b4a1c3b27cf75db28eb8cbf605bc1497da03 languageName: node linkType: hard -"engine.io@npm:~6.1.0": - version: 6.1.2 - resolution: "engine.io@npm:6.1.2" +"engine.io@npm:~6.2.0": + version: 6.2.0 + resolution: "engine.io@npm:6.2.0" dependencies: "@types/cookie": ^0.4.1 "@types/cors": ^2.8.12 @@ -4998,19 +5027,19 @@ __metadata: cookie: ~0.4.1 cors: ~2.8.5 debug: ~4.3.1 - engine.io-parser: ~5.0.0 + engine.io-parser: ~5.0.3 ws: ~8.2.3 - checksum: bd98d6ce2b1e868e8ff0f65d7667a885b90bce62065d851ea0394a00c86686925be824ab91237151222d6a5acfd5610634f36966fa9b4c502e7cf362fbdf974a + checksum: cc485c5ba2e0c4f6ca02dcafd192b22f9dad89d01dc815005298780d3fb910db4cebab4696e8615290c473c2eeb259e8bee2a1fb7ab594d9c80f9f3485771911 languageName: node linkType: hard "enhanced-resolve@npm:^5.8.3": - version: 5.9.0 - resolution: "enhanced-resolve@npm:5.9.0" + version: 5.9.3 + resolution: "enhanced-resolve@npm:5.9.3" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 06435f52670229eb7fd8d92ea2d988a275f9a1b8c08b9023ba45767c6ed844bc87aa9c9c7e6d044eef99ad73fc3b066b78101163696e5c53121909e5bf8efe8b + checksum: 64c2dbbdd608d1a4df93b6e60786c603a1faf3b2e66dfd051d62cf4cfaeeb5e800166183685587208d62e9f7afff3f78f3d5978e32cd80125ba0c83b59a79d78 languageName: node linkType: hard @@ -5094,163 +5123,171 @@ __metadata: languageName: node linkType: hard -"esbuild-android-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-android-arm64@npm:0.14.14" +"esbuild-android-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-android-arm64@npm:0.14.22" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-darwin-64@npm:0.14.14" +"esbuild-darwin-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-darwin-64@npm:0.14.22" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-darwin-arm64@npm:0.14.14" +"esbuild-darwin-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-darwin-arm64@npm:0.14.22" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-freebsd-64@npm:0.14.14" +"esbuild-freebsd-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-freebsd-64@npm:0.14.22" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-freebsd-arm64@npm:0.14.14" +"esbuild-freebsd-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-freebsd-arm64@npm:0.14.22" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-32@npm:0.14.14" +"esbuild-linux-32@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-32@npm:0.14.22" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-64@npm:0.14.14" +"esbuild-linux-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-64@npm:0.14.22" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-arm64@npm:0.14.14" +"esbuild-linux-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-arm64@npm:0.14.22" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-arm@npm:0.14.14" +"esbuild-linux-arm@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-arm@npm:0.14.22" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-mips64le@npm:0.14.14" +"esbuild-linux-mips64le@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-mips64le@npm:0.14.22" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-ppc64le@npm:0.14.14" +"esbuild-linux-ppc64le@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-ppc64le@npm:0.14.22" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-s390x@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-s390x@npm:0.14.14" +"esbuild-linux-riscv64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-riscv64@npm:0.14.22" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"esbuild-linux-s390x@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-s390x@npm:0.14.22" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-netbsd-64@npm:0.14.14" +"esbuild-netbsd-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-netbsd-64@npm:0.14.22" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-openbsd-64@npm:0.14.14" +"esbuild-openbsd-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-openbsd-64@npm:0.14.22" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-sunos-64@npm:0.14.14" +"esbuild-sunos-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-sunos-64@npm:0.14.22" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-wasm@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-wasm@npm:0.14.14" +"esbuild-wasm@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-wasm@npm:0.14.22" bin: esbuild: bin/esbuild - checksum: 047703c94952561da3e8ab58dbadf3581e0a71dfbd866eef6c2f89b58315aab90b7d0087b1b99882c5698a18fe1fa0103cdc5ab292751a039fc0cee14f0432b1 + checksum: 56a75d428e086440126132bb465f64ad33e40fd9ecf91dbdd4fe5b1e16ca65b30d8cec8f90eb538c99ad0c5f57dcb15df809cbe626ed6ea5106d723de985a53b languageName: node linkType: hard -"esbuild-windows-32@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-windows-32@npm:0.14.14" +"esbuild-windows-32@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-windows-32@npm:0.14.22" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-windows-64@npm:0.14.14" +"esbuild-windows-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-windows-64@npm:0.14.22" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-windows-arm64@npm:0.14.14" +"esbuild-windows-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-windows-arm64@npm:0.14.22" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"esbuild@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild@npm:0.14.14" - dependencies: - esbuild-android-arm64: 0.14.14 - esbuild-darwin-64: 0.14.14 - esbuild-darwin-arm64: 0.14.14 - esbuild-freebsd-64: 0.14.14 - esbuild-freebsd-arm64: 0.14.14 - esbuild-linux-32: 0.14.14 - esbuild-linux-64: 0.14.14 - esbuild-linux-arm: 0.14.14 - esbuild-linux-arm64: 0.14.14 - esbuild-linux-mips64le: 0.14.14 - esbuild-linux-ppc64le: 0.14.14 - esbuild-linux-s390x: 0.14.14 - esbuild-netbsd-64: 0.14.14 - esbuild-openbsd-64: 0.14.14 - esbuild-sunos-64: 0.14.14 - esbuild-windows-32: 0.14.14 - esbuild-windows-64: 0.14.14 - esbuild-windows-arm64: 0.14.14 +"esbuild@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild@npm:0.14.22" + dependencies: + esbuild-android-arm64: 0.14.22 + esbuild-darwin-64: 0.14.22 + esbuild-darwin-arm64: 0.14.22 + esbuild-freebsd-64: 0.14.22 + esbuild-freebsd-arm64: 0.14.22 + esbuild-linux-32: 0.14.22 + esbuild-linux-64: 0.14.22 + esbuild-linux-arm: 0.14.22 + esbuild-linux-arm64: 0.14.22 + esbuild-linux-mips64le: 0.14.22 + esbuild-linux-ppc64le: 0.14.22 + esbuild-linux-riscv64: 0.14.22 + esbuild-linux-s390x: 0.14.22 + esbuild-netbsd-64: 0.14.22 + esbuild-openbsd-64: 0.14.22 + esbuild-sunos-64: 0.14.22 + esbuild-windows-32: 0.14.22 + esbuild-windows-64: 0.14.22 + esbuild-windows-arm64: 0.14.22 dependenciesMeta: esbuild-android-arm64: optional: true @@ -5274,6 +5311,8 @@ __metadata: optional: true esbuild-linux-ppc64le: optional: true + esbuild-linux-riscv64: + optional: true esbuild-linux-s390x: optional: true esbuild-netbsd-64: @@ -5290,7 +5329,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: af09b271777ee6e63900cd7fbbe6a8845992454f97febba514d29cc91c2b23c385d9450badf6a538f7c78714421c21121ec9859c086b78aebf748a52d9fa456a + checksum: 8b99a61203b289ff0ad2be1dfc78420d3f838384ddef6a302203e5a9d8e4913f2c152d58dd775be9ab15ce6c77ea9588934c0af27b25806be48d56472e661676 languageName: node linkType: hard @@ -5393,56 +5432,11 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.12.0": - version: 8.13.0 - resolution: "eslint@npm:8.13.0" - dependencies: - "@eslint/eslintrc": ^1.2.1 - "@humanwhocodes/config-array": ^0.9.2 - ajv: ^6.10.0 - chalk: ^4.0.0 - cross-spawn: ^7.0.2 - debug: ^4.3.2 - doctrine: ^3.0.0 - escape-string-regexp: ^4.0.0 - eslint-scope: ^7.1.1 - eslint-utils: ^3.0.0 - eslint-visitor-keys: ^3.3.0 - espree: ^9.3.1 - esquery: ^1.4.0 - esutils: ^2.0.2 - fast-deep-equal: ^3.1.3 - file-entry-cache: ^6.0.1 - functional-red-black-tree: ^1.0.1 - glob-parent: ^6.0.1 - globals: ^13.6.0 - ignore: ^5.2.0 - import-fresh: ^3.0.0 - imurmurhash: ^0.1.4 - is-glob: ^4.0.0 - js-yaml: ^4.1.0 - json-stable-stringify-without-jsonify: ^1.0.1 - levn: ^0.4.1 - lodash.merge: ^4.6.2 - minimatch: ^3.0.4 - natural-compare: ^1.4.0 - optionator: ^0.9.1 - regexpp: ^3.2.0 - strip-ansi: ^6.0.1 - strip-json-comments: ^3.1.0 - text-table: ^0.2.0 - v8-compile-cache: ^2.0.3 - bin: - eslint: bin/eslint.js - checksum: bc7d2fc9a046d42cb2c8987573ecaf534c1fae14747c4695e32388e179c5f21b83eefdb3e4f35924bf76152e226d7cc9dfb21f12348baf12c803109005b17439 - languageName: node - linkType: hard - -"eslint@npm:^8.7.0": - version: 8.14.0 - resolution: "eslint@npm:8.14.0" +"eslint@npm:^8.12.0, eslint@npm:^8.7.0": + version: 8.16.0 + resolution: "eslint@npm:8.16.0" dependencies: - "@eslint/eslintrc": ^1.2.2 + "@eslint/eslintrc": ^1.3.0 "@humanwhocodes/config-array": ^0.9.2 ajv: ^6.10.0 chalk: ^4.0.0 @@ -5453,14 +5447,14 @@ __metadata: eslint-scope: ^7.1.1 eslint-utils: ^3.0.0 eslint-visitor-keys: ^3.3.0 - espree: ^9.3.1 + espree: ^9.3.2 esquery: ^1.4.0 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 functional-red-black-tree: ^1.0.1 glob-parent: ^6.0.1 - globals: ^13.6.0 + globals: ^13.15.0 ignore: ^5.2.0 import-fresh: ^3.0.0 imurmurhash: ^0.1.4 @@ -5469,7 +5463,7 @@ __metadata: json-stable-stringify-without-jsonify: ^1.0.1 levn: ^0.4.1 lodash.merge: ^4.6.2 - minimatch: ^3.0.4 + minimatch: ^3.1.2 natural-compare: ^1.4.0 optionator: ^0.9.1 regexpp: ^3.2.0 @@ -5479,18 +5473,18 @@ __metadata: v8-compile-cache: ^2.0.3 bin: eslint: bin/eslint.js - checksum: 87d2e3e5eb93216d4ab36006e7b8c0bfad02f40b0a0f193f1d42754512cd3a9d8244152f1c69df5db2e135b3c4f1c10d0ed2f0881fe8a8c01af55465968174c1 + checksum: 654a0200b49dc07280673fee13cdfb04326466790e031dfa9660b69fba3b1cf766a51504328f9de56bd18e6b5eb7578985cf29dc7f016c5ec851220ff9db95eb languageName: node linkType: hard -"espree@npm:^9.0.0, espree@npm:^9.3.1": - version: 9.3.1 - resolution: "espree@npm:9.3.1" +"espree@npm:^9.0.0, espree@npm:^9.3.2": + version: 9.3.2 + resolution: "espree@npm:9.3.2" dependencies: - acorn: ^8.7.0 - acorn-jsx: ^5.3.1 + acorn: ^8.7.1 + acorn-jsx: ^5.3.2 eslint-visitor-keys: ^3.3.0 - checksum: d7161db30b65427e0799383699ac4c441533a38faee005153694b68b933ba7a24666680edfc490fa77e3a84a22dbd955768034a6f811af5049774eead83063a5 + checksum: 9a790d6779847051e87f70d720a0f6981899a722419e80c92ab6dee01e1ab83b8ce52d11b4dc96c2c490182efb5a4c138b8b0d569205bfe1cd4629e658e58c30 languageName: node linkType: hard @@ -5605,40 +5599,41 @@ __metadata: linkType: hard "express@npm:^4.17.1": - version: 4.17.2 - resolution: "express@npm:4.17.2" + version: 4.18.1 + resolution: "express@npm:4.18.1" dependencies: - accepts: ~1.3.7 + accepts: ~1.3.8 array-flatten: 1.1.1 - body-parser: 1.19.1 + body-parser: 1.20.0 content-disposition: 0.5.4 content-type: ~1.0.4 - cookie: 0.4.1 + cookie: 0.5.0 cookie-signature: 1.0.6 debug: 2.6.9 - depd: ~1.1.2 + depd: 2.0.0 encodeurl: ~1.0.2 escape-html: ~1.0.3 etag: ~1.8.1 - finalhandler: ~1.1.2 + finalhandler: 1.2.0 fresh: 0.5.2 + http-errors: 2.0.0 merge-descriptors: 1.0.1 methods: ~1.1.2 - on-finished: ~2.3.0 + on-finished: 2.4.1 parseurl: ~1.3.3 path-to-regexp: 0.1.7 proxy-addr: ~2.0.7 - qs: 6.9.6 + qs: 6.10.3 range-parser: ~1.2.1 safe-buffer: 5.2.1 - send: 0.17.2 - serve-static: 1.14.2 + send: 0.18.0 + serve-static: 1.15.0 setprototypeof: 1.2.0 - statuses: ~1.5.0 + statuses: 2.0.1 type-is: ~1.6.18 utils-merge: 1.0.1 vary: ~1.1.2 - checksum: 1535d56d20e65a1a39b5f056c025dd635290a744478ac69cc47633aeb4b2ce51458f8eb4080cfb7ba47c853ba5cfd794d404cff822a25127f1556b726ec3914a + checksum: c3d44c92e48226ef32ec978becfedb0ecf0ca21316bfd33674b3c5d20459840584f2325726a4f17f33d9c99f769636f728982d1c5433a5b6fe6eb95b8cf0c854 languageName: node linkType: hard @@ -5660,13 +5655,20 @@ __metadata: languageName: node linkType: hard -"extsprintf@npm:1.3.0, extsprintf@npm:^1.2.0": +"extsprintf@npm:1.3.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 languageName: node linkType: hard +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -5765,11 +5767,11 @@ __metadata: linkType: hard "filelist@npm:^1.0.1": - version: 1.0.2 - resolution: "filelist@npm:1.0.2" + version: 1.0.4 + resolution: "filelist@npm:1.0.4" dependencies: - minimatch: ^3.0.4 - checksum: 4d6953cb6f76c5345a52fc50222949e244946f485462ab6bae977176fff64fe5200cc1f44db175c27fc887f91cead401504c22eefcdcc064012ee44759947561 + minimatch: ^5.0.1 + checksum: a303573b0821e17f2d5e9783688ab6fbfce5d52aaac842790ae85e704a6f5e4e3538660a63183d6453834dedf1e0f19a9dadcebfa3e926c72397694ea11f5160 languageName: node linkType: hard @@ -5782,7 +5784,7 @@ __metadata: languageName: node linkType: hard -"finalhandler@npm:1.1.2, finalhandler@npm:~1.1.2": +"finalhandler@npm:1.1.2": version: 1.1.2 resolution: "finalhandler@npm:1.1.2" dependencies: @@ -5797,6 +5799,21 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:1.2.0": + version: 1.2.0 + resolution: "finalhandler@npm:1.2.0" + dependencies: + debug: 2.6.9 + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + on-finished: 2.4.1 + parseurl: ~1.3.3 + statuses: 2.0.1 + unpipe: ~1.0.0 + checksum: 92effbfd32e22a7dff2994acedbd9bcc3aa646a3e919ea6a53238090e87097f8ef07cced90aa2cc421abdf993aefbdd5b00104d55c7c5479a8d00ed105b45716 + languageName: node + linkType: hard + "find-cache-dir@npm:^3.3.1": version: 3.3.2 resolution: "find-cache-dir@npm:3.3.2" @@ -5837,7 +5854,7 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.1.0, flatted@npm:^3.2.4": +"flatted@npm:^3.1.0, flatted@npm:^3.2.5": version: 3.2.5 resolution: "flatted@npm:3.2.5" checksum: 3c436e9695ccca29620b4be5671dd72e5dd0a7500e0856611b7ca9bd8169f177f408c3b9abfa78dfe1493ee2d873e2c119080a8a9bee4e1a186a9e60ca6c89f1 @@ -5845,12 +5862,12 @@ __metadata: linkType: hard "follow-redirects@npm:^1.0.0": - version: 1.14.8 - resolution: "follow-redirects@npm:1.14.8" + version: 1.15.0 + resolution: "follow-redirects@npm:1.15.0" peerDependenciesMeta: debug: optional: true - checksum: 40c67899c2e3149a27e8b6498a338ff27f39fe138fde8d7f0756cb44b073ba0bfec3d52af28f20c5bdd67263d564d0d8d7b5efefd431de95c18c42f7b4aef457 + checksum: eaec81c3e0ae57aae2422e38ad3539d0e7279b3a63f9681eeea319bb683dea67502c4e097136b8ce9721542b4e236e092b6b49e34e326cdd7733c274f0a3f378 languageName: node linkType: hard @@ -5879,10 +5896,10 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.1.2": - version: 4.1.3 - resolution: "fraction.js@npm:4.1.3" - checksum: d00065afce4814998b6e42fd439bbed17edbd9616b134927dbd75ebe1b94d6eb0820c0ce0e2cf8f26100e552cb72aff83f4816ef90cb1b329b6d12a531a26aaa +"fraction.js@npm:^4.2.0": + version: 4.2.0 + resolution: "fraction.js@npm:4.2.0" + checksum: 8c76a6e21dedea87109d6171a0ac77afa14205794a565d71cb10d2925f629a3922da61bf45ea52dbc30bce4d8636dc0a27213a88cbd600eab047d82f9a3a94c5 languageName: node linkType: hard @@ -5900,14 +5917,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^10.0.0": - version: 10.0.0 - resolution: "fs-extra@npm:10.0.0" +"fs-extra@npm:^10.1.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: 5285a3d8f34b917cf2b66af8c231a40c1623626e9d701a20051d3337be16c6d7cac94441c8b3732d47a92a2a027886ca93c69b6a4ae6aee3c89650d2a8880c0a + checksum: dc94ab37096f813cc3ca12f0f1b5ad6744dfed9ed21e953d72530d103cea193c2f81584a39e9dee1bea36de5ee66805678c0dddc048e8af1427ac19c00fffc50 languageName: node linkType: hard @@ -5979,20 +5996,26 @@ __metadata: languageName: node linkType: hard -"gauge@npm:^4.0.0": - version: 4.0.0 - resolution: "gauge@npm:4.0.0" +"functions-have-names@npm:^1.2.2": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 + languageName: node + linkType: hard + +"gauge@npm:^4.0.3": + version: 4.0.4 + resolution: "gauge@npm:4.0.4" dependencies: - ansi-regex: ^5.0.1 aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.2 - console-control-strings: ^1.0.0 + color-support: ^1.1.3 + console-control-strings: ^1.1.0 has-unicode: ^2.0.1 - signal-exit: ^3.0.0 + signal-exit: ^3.0.7 string-width: ^4.2.3 strip-ansi: ^6.0.1 - wide-align: ^1.1.2 - checksum: 637b34c84f518defa89319dbef68211a24e9302182ad2a619e3be1be5b7dcf2a962c8359e889294af667440f4722e7e6e61671859e00bd8ec280a136ded89b25 + wide-align: ^1.1.5 + checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d languageName: node linkType: hard @@ -6010,7 +6033,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2": +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1": version: 1.1.1 resolution: "get-intrinsic@npm:1.1.1" dependencies: @@ -6090,17 +6113,44 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": - version: 7.2.0 - resolution: "glob@npm:7.2.0" +"glob@npm:7.2.0": + version: 7.2.0 + resolution: "glob@npm:7.2.0" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.4 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 + languageName: node + linkType: hard + +"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + +"glob@npm:^8.0.1": + version: 8.0.3 + resolution: "glob@npm:8.0.3" dependencies: fs.realpath: ^1.0.0 inflight: ^1.0.4 inherits: 2 - minimatch: ^3.0.4 + minimatch: ^5.0.1 once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 + checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 languageName: node linkType: hard @@ -6131,12 +6181,12 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.6.0, globals@npm:^13.9.0": - version: 13.13.0 - resolution: "globals@npm:13.13.0" +"globals@npm:^13.15.0": + version: 13.15.0 + resolution: "globals@npm:13.15.0" dependencies: type-fest: ^0.20.2 - checksum: c55ea8fd3afecb72567bac41605577e19e68476993dfb0ca4c49b86075af5f0ae3f0f5502525f69010f7c5ea5db6a1c540a80a4f80ebdfb2f686d87b0f05d7e9 + checksum: 383ade0873b2ab29ce6d143466c203ed960491575bc97406395e5c8434026fb02472ab2dfff5bc16689b8460269b18fda1047975295cd0183904385c51258bae languageName: node linkType: hard @@ -6190,9 +6240,9 @@ __metadata: linkType: hard "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": - version: 4.2.9 - resolution: "graceful-fs@npm:4.2.9" - checksum: 68ea4e07ff2c041ada184f9278b830375f8e0b75154e3f080af6b70f66172fabb4108d19b3863a96b53fc068a310b9b6493d86d1291acc5f3861eb4b79d26ad6 + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da languageName: node linkType: hard @@ -6250,10 +6300,19 @@ __metadata: languageName: node linkType: hard +"has-property-descriptors@npm:^1.0.0": + version: 1.0.0 + resolution: "has-property-descriptors@npm:1.0.0" + dependencies: + get-intrinsic: ^1.1.1 + checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + languageName: node + linkType: hard + "has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2": - version: 1.0.2 - resolution: "has-symbols@npm:1.0.2" - checksum: 2309c426071731be792b5be43b3da6fb4ed7cbe8a9a6bcfca1862587709f01b33d575ce8f5c264c1eaad09fca2f9a8208c0a2be156232629daa2dd0c0740976b + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 languageName: node linkType: hard @@ -6329,9 +6388,9 @@ __metadata: linkType: hard "html-entities@npm:^2.3.2": - version: 2.3.2 - resolution: "html-entities@npm:2.3.2" - checksum: 522d8d202df301ff51b517a379e642023ed5c81ea9fb5674ffad88cff386165733d00b6089d5c2fcc644e44777d6072017b6216d8fa40f271d3610420d00a886 + version: 2.3.3 + resolution: "html-entities@npm:2.3.3" + checksum: 92521501da8aa5f66fee27f0f022d6e9ceae62667dae93aa6a2f636afa71ad530b7fb24a18d4d6c124c9885970cac5f8a52dbf1731741161002816ae43f98196 languageName: node linkType: hard @@ -6363,16 +6422,16 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:1.8.1": - version: 1.8.1 - resolution: "http-errors@npm:1.8.1" +"http-errors@npm:2.0.0": + version: 2.0.0 + resolution: "http-errors@npm:2.0.0" dependencies: - depd: ~1.1.2 + depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 - statuses: ">= 1.5.0 < 2" + statuses: 2.0.1 toidentifier: 1.0.1 - checksum: d3c7e7e776fd51c0a812baff570bdf06fe49a5dc448b700ab6171b1250e4cf7db8b8f4c0b133e4bfe2451022a5790c1ca6c2cae4094dedd6ac8304a1267f91d2 + checksum: 9b0a3782665c52ce9dc658a0d1560bcb0214ba5699e4ea15aefb2a496e2ca83db03ebc42e1cce4ac1f413e4e0d2d736a3fd755772c556a9a06853ba2a0b7d920 languageName: node linkType: hard @@ -6389,9 +6448,9 @@ __metadata: linkType: hard "http-parser-js@npm:>=0.5.1": - version: 0.5.5 - resolution: "http-parser-js@npm:0.5.5" - checksum: 85e67f12d99d67565be6c82dd86d4cf71939825fdf9826e10047b2443460bfef13235859ca67c0235d54e553db242204ec813febc86f11f83ed8ebd3cd475b65 + version: 0.5.6 + resolution: "http-parser-js@npm:0.5.6" + checksum: 8a92f6782542211c77936104ea1eca3c86a95420eb286b100f6421630f29d8f94fd4cc7a245df8e078791d86cd9a237091094440ffb0cd1b44a3f85bfbf539fa languageName: node linkType: hard @@ -6418,8 +6477,8 @@ __metadata: linkType: hard "http-proxy-middleware@npm:^2.0.0": - version: 2.0.3 - resolution: "http-proxy-middleware@npm:2.0.3" + version: 2.0.6 + resolution: "http-proxy-middleware@npm:2.0.6" dependencies: "@types/http-proxy": ^1.17.8 http-proxy: ^1.18.1 @@ -6431,7 +6490,7 @@ __metadata: peerDependenciesMeta: "@types/express": optional: true - checksum: f121d5515e3c4613b6e9299f03ab70021bf2b5794a4052bcd5218cc754a19b3375e4d3744f3e70c682d9a1a5e125c8769a211d883cd0a24455ef18a5a310abf5 + checksum: 2ee85bc878afa6cbf34491e972ece0f5be0a3e5c98a60850cf40d2a9a5356e1fc57aab6cff33c1fc37691b0121c3a42602d2b1956c52577e87a5b77b62ae1c3a languageName: node linkType: hard @@ -6457,7 +6516,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.0, https-proxy-agent@npm:^5.0.0": +"https-proxy-agent@npm:5.0.0": version: 5.0.0 resolution: "https-proxy-agent@npm:5.0.0" dependencies: @@ -6477,6 +6536,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: 6 + debug: 4 + checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -6674,9 +6743,9 @@ __metadata: linkType: hard "ip@npm:^1.1.0, ip@npm:^1.1.5": - version: 1.1.5 - resolution: "ip@npm:1.1.5" - checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a + version: 1.1.8 + resolution: "ip@npm:1.1.8" + checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb languageName: node linkType: hard @@ -6720,7 +6789,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.5.0": +"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1": version: 2.9.0 resolution: "is-core-module@npm:2.9.0" dependencies: @@ -6729,15 +6798,6 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.8.1": - version: 2.8.1 - resolution: "is-core-module@npm:2.8.1" - dependencies: - has: ^1.0.3 - checksum: 418b7bc10768a73c41c7ef497e293719604007f88934a6ffc5f7c78702791b8528102fb4c9e56d006d69361549b3d9519440214a74aefc7e0b79e5e4411d377f - languageName: node - linkType: hard - "is-date-object@npm:^1.0.1": version: 1.0.5 resolution: "is-date-object@npm:1.0.5" @@ -6938,9 +6998,9 @@ __metadata: linkType: hard "isbinaryfile@npm:^4.0.8": - version: 4.0.8 - resolution: "isbinaryfile@npm:4.0.8" - checksum: 606e3bb648d1a0dee23459d1d937bb2560e66a5281ec7c9ff50e585402d73321ac268d0f34cb7393125b3ebc4c7962d39e50a01cdb8904b52fce08b7ccd2bf9f + version: 4.0.10 + resolution: "isbinaryfile@npm:4.0.10" + checksum: a6b28db7e23ac7a77d3707567cac81356ea18bd602a4f21f424f862a31d0e7ab4f250759c98a559ece35ffe4d99f0d339f1ab884ffa9795172f632ab8f88e686 languageName: node linkType: hard @@ -6980,15 +7040,15 @@ __metadata: linkType: hard "istanbul-lib-instrument@npm:^5.0.4": - version: 5.1.0 - resolution: "istanbul-lib-instrument@npm:5.1.0" + version: 5.2.0 + resolution: "istanbul-lib-instrument@npm:5.2.0" dependencies: "@babel/core": ^7.12.3 "@babel/parser": ^7.14.7 "@istanbuljs/schema": ^0.1.2 istanbul-lib-coverage: ^3.2.0 semver: ^6.3.0 - checksum: 8b82e733c69fe9f94d2e21f3e5760c9bedb110329aa75df4bd40df95f1cac3bf38767e43f35b125cc547ceca7376b72ce7d95cc5238b7e9088345c7b589233d3 + checksum: 7c242ed782b6bf7b655656576afae8b6bd23dcc020e5fdc1472cca3dfb6ddb196a478385206d0df5219b9babf46ac4f21fea5d8ea9a431848b6cca6007012353 languageName: node linkType: hard @@ -7026,27 +7086,34 @@ __metadata: languageName: node linkType: hard -"jake@npm:^10.6.1": - version: 10.8.4 - resolution: "jake@npm:10.8.4" +"jake@npm:^10.8.5": + version: 10.8.5 + resolution: "jake@npm:10.8.5" dependencies: - async: 0.9.x + async: ^3.2.3 chalk: ^4.0.2 filelist: ^1.0.1 minimatch: ^3.0.4 bin: jake: ./bin/cli.js - checksum: ab411cfb852d04089b2d7e25cec232421a9d3656f58784954a93cdbef6ef190e445da9c5a1a24ce018ef7ed41a08191828ea367bd9d1b0bac31465b40feeaad6 + checksum: 56c913ecf5a8d74325d0af9bc17a233bad50977438d44864d925bb6c45c946e0fee8c4c1f5fe2225471ef40df5222e943047982717ebff0d624770564d3c46ba languageName: node linkType: hard -"jasmine-core@npm:3.8.x, jasmine-core@npm:^3.6.0": +"jasmine-core@npm:3.8.x": version: 3.8.0 resolution: "jasmine-core@npm:3.8.0" checksum: 6490373bd96d4d2a3c74214d307f8c37394370b1e892b9f203d4c5eda56941d53f84aabee0e65538b6921a190710618f1e73af44bd1d93e6c6e6c3f5c4bbfab6 languageName: node linkType: hard +"jasmine-core@npm:^3.6.0": + version: 3.99.1 + resolution: "jasmine-core@npm:3.99.1" + checksum: 4e4a89739d99e471b86c7ccc4c5c244a77cc6d1e17b2b0d87d81266b8415697354d8873f7e764790a10661744f73a753a6e9bcd9b3e48c66a0c9b8a092b071b7 + languageName: node + linkType: hard + "jasmine-core@npm:~2.8.0": version: 2.8.0 resolution: "jasmine-core@npm:2.8.0" @@ -7209,14 +7276,12 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.1.2": - version: 2.2.0 - resolution: "json5@npm:2.2.0" - dependencies: - minimist: ^1.2.5 +"json5@npm:^2.1.2, json5@npm:^2.2.1": + version: 2.2.1 + resolution: "json5@npm:2.2.1" bin: json5: lib/cli.js - checksum: e88fc5274bb58fc99547baa777886b069d2dd96d9cfc4490b305fd16d711dabd5979e35a4f90873cefbeb552e216b041a304fe56702bedba76e19bc7845f208d + checksum: 74b8a23b102a6f2bf2d224797ae553a75488b5adbaee9c9b6e5ab8b510a2fc6e38f876d4c77dea672d4014a44b2399e15f2051ac2b37b87f74c0c7602003543b languageName: node linkType: hard @@ -7260,23 +7325,23 @@ __metadata: linkType: hard "jszip@npm:^3.1.3": - version: 3.7.1 - resolution: "jszip@npm:3.7.1" + version: 3.10.0 + resolution: "jszip@npm:3.10.0" dependencies: lie: ~3.3.0 pako: ~1.0.2 readable-stream: ~2.3.6 - set-immediate-shim: ~1.0.1 - checksum: 67d737a82b294cc102e7451e32d5acbbab29860399be460cae598084327e6f2ea0c9bca2d3dad701da6a75ddf77f34c6a1dd7db0c3d5c0fec5998b7e56d6d59d + setimmediate: ^1.0.5 + checksum: 80cc8e0e466467e9e21447f604f9262509b29a9c65170a3fee415ac0a403beb370840973cdc17f75d2b92ab3e60685f94d267706510d46bed2dd14858a38e459 languageName: node linkType: hard "karma-chrome-launcher@npm:3.1.x": - version: 3.1.0 - resolution: "karma-chrome-launcher@npm:3.1.0" + version: 3.1.1 + resolution: "karma-chrome-launcher@npm:3.1.1" dependencies: which: ^1.2.1 - checksum: 63431ddec9aa40e2a0439d9e2bcfa58a6822efd08e2666bdbc3f55dfbe8fcc0b401035b71b1f6f21340339dc56c172edaed8e8c0ddc6949873318ad1666b2dd9 + checksum: 8442219105e1f11a9284fd47f2e21e34720f7e725f25ea08f7525a7ec2088e2c1b65e2def4d7780139d296afc5c30bf4e1d4a839a097eb814031c2f6b379b39f languageName: node linkType: hard @@ -7305,13 +7370,13 @@ __metadata: linkType: hard "karma-jasmine@npm:4.0.x": - version: 4.0.1 - resolution: "karma-jasmine@npm:4.0.1" + version: 4.0.2 + resolution: "karma-jasmine@npm:4.0.2" dependencies: jasmine-core: ^3.6.0 peerDependencies: karma: "*" - checksum: 28337c82941ed6c68e0c47ef432c2c91511214e84a336e78d9068daebd61a3c1cee6209207ddc5fe3ad78124597f58054b93aa0f973ff6dcc8a8fcd1951c9851 + checksum: bf884704af1fd19816d9f4e96b25e286ff1a57adcabe1f15e3d2b3e9c1da873c1c843b9eab4274c27e63a99f1c3dea864f1f5eca1a10dc065e6e9d5796c207b4 languageName: node linkType: hard @@ -7325,13 +7390,13 @@ __metadata: linkType: hard "karma@npm:6.3.x": - version: 6.3.16 - resolution: "karma@npm:6.3.16" + version: 6.3.20 + resolution: "karma@npm:6.3.20" dependencies: + "@colors/colors": 1.5.0 body-parser: ^1.19.0 braces: ^3.0.2 chokidar: ^3.5.1 - colors: 1.4.0 connect: ^3.7.0 di: ^0.0.1 dom-serialize: ^2.2.1 @@ -7347,14 +7412,14 @@ __metadata: qjobs: ^1.2.0 range-parser: ^1.2.1 rimraf: ^3.0.2 - socket.io: ^4.2.0 + socket.io: ^4.4.1 source-map: ^0.6.1 tmp: ^0.2.1 ua-parser-js: ^0.7.30 yargs: ^16.1.1 bin: karma: bin/karma - checksum: eb1703d4907ac31a47019e2b6b5f69e1ecd7870dabee1ed8f284d9730f665e02ae9ef1a75733b5d4b6a27fe68069536d0845b9e41747c43507128b3ac645c87f + checksum: 7060afc367c49b2ce2e145f6555c428eadfd72bd68a5d4ae392ec51c8d1c0484706fdd52cfd06d69a811c25517fd5129689a69173be850814dc68c3b7eafa6c8 languageName: node linkType: hard @@ -7436,9 +7501,9 @@ __metadata: languageName: node linkType: hard -"license-webpack-plugin@npm:4.0.1": - version: 4.0.1 - resolution: "license-webpack-plugin@npm:4.0.1" +"license-webpack-plugin@npm:4.0.2": + version: 4.0.2 + resolution: "license-webpack-plugin@npm:4.0.2" dependencies: webpack-sources: ^3.0.0 peerDependenciesMeta: @@ -7446,7 +7511,7 @@ __metadata: optional: true webpack-sources: optional: true - checksum: ed3f8183bcb6cc6ec8aeeb17823d11a99d872609eedcf704ff4bb1a212df8d653c83eb42b7da04e0e25bb4924d784b6b603c5ba3a816dd17d5419b2273f70e5c + checksum: e88ebdb9c8bdfc0926dd7211d7fe2ee8697a44bb00a96bb5e6ca844b6acb7d24dd54eb17ec485e2e0140c3cc86709d1c2bd46e091ab52af076e1e421054c8322 languageName: node linkType: hard @@ -7474,8 +7539,8 @@ __metadata: linkType: hard "lint-staged@npm:^12.3.7": - version: 12.3.7 - resolution: "lint-staged@npm:12.3.7" + version: 12.4.1 + resolution: "lint-staged@npm:12.4.1" dependencies: cli-truncate: ^3.1.0 colorette: ^2.0.16 @@ -7493,7 +7558,7 @@ __metadata: yaml: ^1.10.2 bin: lint-staged: bin/lint-staged.js - checksum: b595097556880eb2ab7ce2cf105047f6f22525ccfd608fbdc63ab243259aa36a26949576fadff673748fb885e7cb37ad3f4471d964ad54c1205d57a58999ba24 + checksum: b57183b537064cda6caef6679918bf271903145f7c28d09567e918b8b13094048b579f8df808ea590dbd7ea2ec332327c5e372cf3d77e85b7b0254f6541ce4c3 languageName: node linkType: hard @@ -7519,9 +7584,9 @@ __metadata: linkType: hard "loader-runner@npm:^4.2.0": - version: 4.2.0 - resolution: "loader-runner@npm:4.2.0" - checksum: e61aea8b6904b8af53d9de6f0484da86c462c0001f4511bedc837cec63deb9475cea813db62f702cd7930420ccb0e75c78112270ca5c8b61b374294f53c0cb3a + version: 4.3.0 + resolution: "loader-runner@npm:4.3.0" + checksum: a90e00dee9a16be118ea43fec3192d0b491fe03a32ed48a4132eb61d498f5536a03a1315531c19d284392a8726a4ecad71d82044c28d7f22ef62e029bf761569 languageName: node linkType: hard @@ -7577,6 +7642,13 @@ __metadata: languageName: node linkType: hard +"lodash.sortby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.sortby@npm:4.7.0" + checksum: db170c9396d29d11fe9a9f25668c4993e0c1331bcb941ddbd48fb76f492e732add7f2a47cfdf8e9d740fa59ac41bbfaf931d268bc72aab3ab49e9f89354d718c + languageName: node + linkType: hard + "lodash.truncate@npm:^4.4.2": version: 4.4.2 resolution: "lodash.truncate@npm:4.4.2" @@ -7614,15 +7686,15 @@ __metadata: linkType: hard "log4js@npm:^6.4.1": - version: 6.4.1 - resolution: "log4js@npm:6.4.1" + version: 6.5.1 + resolution: "log4js@npm:6.5.1" dependencies: - date-format: ^4.0.3 - debug: ^4.3.3 - flatted: ^3.2.4 + date-format: ^4.0.10 + debug: ^4.3.4 + flatted: ^3.2.5 rfdc: ^1.3.0 - streamroller: ^3.0.2 - checksum: 0614949662314573ec7dcd841769a4d23d8cb8268685458a40fcd94f2ae6ec628234cfb9a6bc17821fb6ea6ce3765e779b4966ba1cf918f393dc37155a3615cb + streamroller: ^3.1.1 + checksum: 53e16766caa2c8235b054420799d053e2397fc816e99c1d93119ee07ab7d06239ee319cc9f1627c6119dd30994e7c4941f71e03e9fab8996206303bde7867220 languageName: node linkType: hard @@ -7652,21 +7724,14 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.3.1": - version: 7.3.1 - resolution: "lru-cache@npm:7.3.1" - checksum: 34bb50c015ffc29fd83545e912f28cea6e03fbf41c497fa220c4f131b990f9ddf95babac98745b416cbc6c0d835254d61668d09b8a4ecb476934546afc9e51bd - languageName: node - linkType: hard - "lru-cache@npm:^7.7.1": - version: 7.8.1 - resolution: "lru-cache@npm:7.8.1" - checksum: 31ea67388c9774300331d70f4affd5a433869bcf0fae5405f967d19d7b447930b713b0566a2e95362c9082034a8b496f3671ccf8f0c061d8e8048412663f9432 + version: 7.10.1 + resolution: "lru-cache@npm:7.10.1" + checksum: e8b190d71ed0fcd7b29c71a3e9b01f851c92d1ef8865ff06b5581ca991db1e5e006920ed4da8b56da1910664ed51abfd76c46fb55e82ac252ff6c970ff910d72 languageName: node linkType: hard -"magic-string@npm:0.25.7, magic-string@npm:^0.25.0": +"magic-string@npm:0.25.7": version: 0.25.7 resolution: "magic-string@npm:0.25.7" dependencies: @@ -7675,6 +7740,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.26.0": + version: 0.26.2 + resolution: "magic-string@npm:0.26.2" + dependencies: + sourcemap-codec: ^1.4.8 + checksum: b4db4e2b370ac8d9ffc6443a2b591b75364bf1fc9121b5a4068d5b89804abff6709d1fa4a0e0c2d54f2e61e0e44db83efdfe219a5ab0ba6d25ee1f2b51fbed55 + languageName: node + linkType: hard + "make-dir@npm:^2.1.0": version: 2.1.0 resolution: "make-dir@npm:2.1.0" @@ -7701,36 +7775,12 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.1": - version: 10.0.2 - resolution: "make-fetch-happen@npm:10.0.2" - dependencies: - agentkeepalive: ^4.2.0 - cacache: ^15.3.0 - http-cache-semantics: ^4.1.0 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 - is-lambda: ^1.0.1 - lru-cache: ^7.3.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^1.4.1 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - negotiator: ^0.6.3 - promise-retry: ^2.0.1 - socks-proxy-agent: ^6.1.1 - ssri: ^8.0.1 - checksum: 5a620f84ab9e28673f70b0640b15133cb329ca4dbeec17e24b5029f07b134c52d96ebac150cab99979c48c5e6e99056978ed0321e4ce1e22a4d4d6fc7148c2fd - languageName: node - linkType: hard - -"make-fetch-happen@npm:^10.0.3": - version: 10.1.2 - resolution: "make-fetch-happen@npm:10.1.2" +"make-fetch-happen@npm:^10.0.1, make-fetch-happen@npm:^10.0.3": + version: 10.1.5 + resolution: "make-fetch-happen@npm:10.1.5" dependencies: agentkeepalive: ^4.2.1 - cacache: ^16.0.2 + cacache: ^16.1.0 http-cache-semantics: ^4.1.0 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.0 @@ -7745,7 +7795,7 @@ __metadata: promise-retry: ^2.0.1 socks-proxy-agent: ^6.1.1 ssri: ^9.0.0 - checksum: 42825d119a7e4f5b1a8e7048a86d328cd36bb1ff875d155ce7079d9a0afdd310c198fb310096af358cfa9ecdf643cecf960380686792457dccb36e17efe89eb0 + checksum: b0b42a1ccdcbc3180749727a52cf6887d9df6218d8ca35101bb9f7ab35729dd166d99203b70149a19a818d1ba72de40b982002ddb0b308c548457f5725d6e7f6 languageName: node linkType: hard @@ -7801,12 +7851,12 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.2.2": - version: 3.4.1 - resolution: "memfs@npm:3.4.1" +"memfs@npm:^3.2.2, memfs@npm:^3.4.3": + version: 3.4.3 + resolution: "memfs@npm:3.4.3" dependencies: fs-monkey: 1.0.3 - checksum: 6d2f49d447d1be24ff9c747618933784eeb059189bc6a0d77b7a51c7daf06e2d3a74674a2e2ff1520e2c312bf91e719ed37144cf05087379b3ba0aef0b6aa062 + checksum: c947ef46e2036524ba120cb42fa502fd75dae8d49d0c53e818d3d3780b9a3a47845705cd1cf51eec04c70f1db590ca7b6c7f78dd5a65883bb253fcedf86f412c languageName: node linkType: hard @@ -7858,17 +7908,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": - version: 4.0.4 - resolution: "micromatch@npm:4.0.4" - dependencies: - braces: ^3.0.1 - picomatch: ^2.2.3 - checksum: ef3d1c88e79e0a68b0e94a03137676f3324ac18a908c245a9e5936f838079fcc108ac7170a5fadc265a9c2596963462e402841406bda1a4bb7b68805601d631c - languageName: node - linkType: hard - -"micromatch@npm:^4.0.5": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": version: 4.0.5 resolution: "micromatch@npm:4.0.5" dependencies: @@ -7878,19 +7918,19 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.51.0, mime-db@npm:>= 1.43.0 < 2": - version: 1.51.0 - resolution: "mime-db@npm:1.51.0" - checksum: 613b1ac9d6e725cc24444600b124a7f1ce6c60b1baa654f39a3e260d0995a6dffc5693190217e271af7e2a5612dae19f2a73f3e316707d797a7391165f7ef423 +"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f languageName: node linkType: hard "mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": - version: 2.1.34 - resolution: "mime-types@npm:2.1.34" + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" dependencies: - mime-db: 1.51.0 - checksum: 67013de9e9d6799bde6d669d18785b7e18bcd212e710d3e04a4727f92f67a8ad4e74aee24be28b685adb794944814bde649119b58ee3282ffdbee58f9278d9f3 + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 languageName: node linkType: hard @@ -7953,12 +7993,21 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4": - version: 3.1.1 - resolution: "minimatch@npm:3.1.1" +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" dependencies: brace-expansion: ^1.1.7 - checksum: e9e3772e4ea06ea3a888d39bc7690d3c812ee7e5a70c2d2f568ccadac0249a027f865589d19ad03ed937e6ca3b4ad35f85411db9670f7877d8fc2ed452f1cd37 + checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a + languageName: node + linkType: hard + +"minimatch@npm:^5.0.1": + version: 5.1.0 + resolution: "minimatch@npm:5.1.0" + dependencies: + brace-expansion: ^2.0.1 + checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90 languageName: node linkType: hard @@ -7973,7 +8022,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": +"minimist@npm:^1.2.0, minimist@npm:^1.2.6": version: 1.2.6 resolution: "minimist@npm:1.2.6" checksum: d15428cd1e11eb14e1233bcfb88ae07ed7a147de251441d61158619dfb32c4d7e9061d09cab4825fdee18ecd6fce323228c8c47b5ba7cd20af378ca4048fb3fb @@ -8076,13 +8125,13 @@ __metadata: linkType: hard "mkdirp@npm:^0.5.5": - version: 0.5.5 - resolution: "mkdirp@npm:0.5.5" + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" dependencies: - minimist: ^1.2.5 + minimist: ^1.2.6 bin: mkdirp: bin/cmd.js - checksum: 3bce20ea525f9477befe458ab85284b0b66c8dc3812f94155af07c827175948cdd8114852ac6c6d82009b13c1048c37f6d98743eb019651ee25c39acc8aabe7d + checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 languageName: node linkType: hard @@ -8102,14 +8151,14 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.2, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f languageName: node linkType: hard -"ms@npm:2.1.3": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -8142,16 +8191,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.1.30": - version: 3.2.0 - resolution: "nanoid@npm:3.2.0" - bin: - nanoid: bin/nanoid.cjs - checksum: 3d1d5a69fea84e538057cf64106e713931c4ef32af344068ecff153ff91252f39b0f2b472e09b0dfff43ac3cf520c92938d90e6455121fe93976e23660f4fccc - languageName: node - linkType: hard - -"nanoid@npm:^3.3.3": +"nanoid@npm:^3.1.30, nanoid@npm:^3.3.4": version: 3.3.4 resolution: "nanoid@npm:3.3.4" bin: @@ -8195,8 +8235,8 @@ __metadata: linkType: hard "ng-apexcharts@npm:1.7.x": - version: 1.7.0 - resolution: "ng-apexcharts@npm:1.7.0" + version: 1.7.1 + resolution: "ng-apexcharts@npm:1.7.1" dependencies: tslib: ^2.0.0 peerDependencies: @@ -8204,20 +8244,20 @@ __metadata: "@angular/core": ">=13.0.0" apexcharts: ^3.31.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: a615eea8a3c6f6fc145f6a16a77732207bf37a2387bcb9148a2da29ef58fbca10dedb3efb5f4ddb86bbe72850385f5ecd330dcca32ff5e0c9ffbf9ae862db5f9 + checksum: 27b1de2ffde1e52e957c407accad6e03e5cb9ce03b40f627ac2bbe09c17143f3588f7c12f4dd0cd4c4ad033f97440d39fadca00aaf2aca3dae90d4cc4a8a3a88 languageName: node linkType: hard "ngx-color-picker@npm:12.0.x": - version: 12.0.0 - resolution: "ngx-color-picker@npm:12.0.0" + version: 12.0.1 + resolution: "ngx-color-picker@npm:12.0.1" dependencies: tslib: ^2.3.0 peerDependencies: "@angular/common": ">=9.0.0" "@angular/core": ">=9.0.0" "@angular/forms": ">=9.0.0" - checksum: 6e8f9676ddfc390e4a4d0e7751e97cebd4b16b6cb4086254e9f5f5823ada12a5ee101a2a868f726a1b1dc412fa47aee885173461228d2a9904ed36041df38772 + checksum: f0c2361ec92c4cd76ddf5561e9beeb1b5da56d4aafe929189c648bda21166c0cddd6d3e44278fd28eb76e00e24a9254a73de6b9ffff9d3b9197c052ea373577e languageName: node linkType: hard @@ -8241,21 +8281,21 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^1.2.0": - version: 1.3.0 - resolution: "node-forge@npm:1.3.0" - checksum: 3d8124168dd82006fafbb079f40a529afa0de5bf4d77e6a5a471877e9d39bece31fdc8339e8aee30d5480dc79ffcd1059cfcb21983d350dd3f2a9f226db6ca85 +"node-forge@npm:^1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 languageName: node linkType: hard "node-gyp-build@npm:^4.2.2, node-gyp-build@npm:^4.3.0": - version: 4.3.0 - resolution: "node-gyp-build@npm:4.3.0" + version: 4.4.0 + resolution: "node-gyp-build@npm:4.4.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 1ecab16d9f275174d516e223f60f65ebe07540347d5c04a6a7d6921060b7f2e3af4f19463d9d1dcedc452e275c2ae71354a99405e55ebd5b655bb2f38025c728 + checksum: 972a059f960253d254e0b23ce10f54c8982236fc0edcab85166d0b7f87443b2ce98391c877cfb2f6eeafcf03c538c5f4dd3e0bfff03828eb48634f58f4c64343 languageName: node linkType: hard @@ -8299,10 +8339,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.1": - version: 2.0.2 - resolution: "node-releases@npm:2.0.2" - checksum: da858bf86b4d512842379749f5a5e4196ddab05ba18ffcf29f05bf460beceaca927f070f4430bb5046efec18941ddbc85e4c5fdbb83afc28a38dd6069a2f255e +"node-releases@npm:^2.0.3": + version: 2.0.4 + resolution: "node-releases@npm:2.0.4" + checksum: b32d6c2032c7b169ae3938b416fc50f123f5bd577d54a79b2ae201febf27b22846b01c803dd35ac8689afe840f8ba4e5f7154723db629b80f359836b6707b92f languageName: node linkType: hard @@ -8355,13 +8395,6 @@ __metadata: languageName: node linkType: hard -"normalize-selector@npm:^0.2.0": - version: 0.2.0 - resolution: "normalize-selector@npm:0.2.0" - checksum: 6cc88334df26cf1f809692892f4069e1112958574403d0a6753fe5b1e41707170e242e1602e21fa62ea92618827882c4d18a773bc99075a77553bd527eec9930 - languageName: node - linkType: hard - "npm-bundled@npm:^1.1.1": version: 1.1.2 resolution: "npm-bundled@npm:1.1.2" @@ -8448,23 +8481,23 @@ __metadata: linkType: hard "npmlog@npm:^6.0.0": - version: 6.0.1 - resolution: "npmlog@npm:6.0.1" + version: 6.0.2 + resolution: "npmlog@npm:6.0.2" dependencies: are-we-there-yet: ^3.0.0 console-control-strings: ^1.1.0 - gauge: ^4.0.0 + gauge: ^4.0.3 set-blocking: ^2.0.0 - checksum: f1a4078a73ebc89896a832bbf869f491c32ecb12e0434b9a7499878ce8f29f22e72befe3c53cd8cdc9dbf4b4057297e783ab0b6746a8b067734de6205af4d538 + checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a languageName: node linkType: hard "nth-check@npm:^2.0.1": - version: 2.0.1 - resolution: "nth-check@npm:2.0.1" + version: 2.1.0 + resolution: "nth-check@npm:2.1.0" dependencies: boolbase: ^1.0.0 - checksum: 5386d035c48438ff304fe687704d93886397349d1bed136de97aeae464caba10e8ffac55a04b215b86b3bc8897f33e0a5aa1045a9d8b2f251ae61b2a3ad3e450 + checksum: c555b2f7ccd2525aa5eb372cefd490b8a0d1310d751762593489de0e1338b1ce9a27b2c2f22094c0d0da9f9df725307f72df74bf0c22ea76392afb9f5e1bfc03 languageName: node linkType: hard @@ -8479,25 +8512,26 @@ __metadata: languageName: node linkType: hard -"nx@npm:13.10.1": - version: 13.10.1 - resolution: "nx@npm:13.10.1" +"nx@npm:14.1.7": + version: 14.1.7 + resolution: "nx@npm:14.1.7" dependencies: - "@nrwl/cli": 13.10.1 - "@nrwl/tao": 13.10.1 + "@nrwl/cli": 14.1.7 + "@nrwl/tao": 14.1.7 "@parcel/watcher": 2.0.4 "@swc-node/register": ^1.4.2 - "@swc/core": ^1.2.152 + "@swc/core": ^1.2.173 chalk: 4.1.0 chokidar: ^3.5.1 cli-cursor: 3.1.0 cli-spinners: 2.6.1 + cliui: ^7.0.2 dotenv: ~10.0.0 enquirer: ~2.3.6 fast-glob: 3.2.7 figures: 3.2.0 flat: ^5.0.2 - fs-extra: ^9.1.0 + fs-extra: ^10.1.0 glob: 7.1.4 ignore: ^5.0.4 jsonc-parser: 3.0.0 @@ -8507,6 +8541,7 @@ __metadata: rxjs: ^6.5.4 rxjs-for-await: 0.0.2 semver: 7.3.4 + string-width: ^4.2.3 tar-stream: ~2.2.0 tmp: ~0.2.1 tsconfig-paths: ^3.9.0 @@ -8516,7 +8551,7 @@ __metadata: yargs-parser: 21.0.1 bin: nx: bin/nx.js - checksum: 9528eda785369f7d77398df01f8708983df7dc16d5068be78edf9faf0a35eabd45f7f61b5b8017e8c008b0de20ed9d88446f9b57b8ad03c5aee828419e7aff39 + checksum: b6b4047092c4ed4c0a563180674c6274953ca6613d90d0a481fa44fc2cdaff1980d549f53d9b137488f05953c118d05768ef4756504b1c7182e225c72f00fc50 languageName: node linkType: hard @@ -8534,10 +8569,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0": - version: 1.12.0 - resolution: "object-inspect@npm:1.12.0" - checksum: 2b36d4001a9c921c6b342e2965734519c9c58c355822243c3207fbf0aac271f8d44d30d2d570d450b2cc6f0f00b72bcdba515c37827d2560e5f22b1899a31cf4 +"object-inspect@npm:^1.12.0, object-inspect@npm:^1.9.0": + version: 1.12.1 + resolution: "object-inspect@npm:1.12.1" + checksum: 5c7c3b641417606db7f545760cfdbc686870c4ac03c86d05f3e1194b19de39b48030f2145ef813e6e8228268d48408eceb9bdcfeb0a502d8d9e5a057982c31a0 languageName: node linkType: hard @@ -8551,7 +8586,7 @@ __metadata: languageName: node linkType: hard -"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": +"object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a @@ -8577,6 +8612,15 @@ __metadata: languageName: node linkType: hard +"on-finished@npm:2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: 1.1.1 + checksum: d20929a25e7f0bb62f937a425b5edeb4e4cde0540d77ba146ec9357f00b0d497cdb3b9b05b9c8e46222407d1548d08166bff69cc56dfa55ba0e4469228920ff0 + languageName: node + linkType: hard + "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -8688,12 +8732,12 @@ __metadata: linkType: hard "p-retry@npm:^4.5.0": - version: 4.6.1 - resolution: "p-retry@npm:4.6.1" + version: 4.6.2 + resolution: "p-retry@npm:4.6.2" dependencies: - "@types/retry": ^0.12.0 + "@types/retry": 0.12.0 retry: ^0.13.1 - checksum: e6d540413bb3d0b96e0db44f74a7af1dce41f5005e6e84d617960110b148348c86a3987be07797749e3ddd55817dd3a8ffd6eae3428758bc2994d987e48c3a70 + checksum: 45c270bfddaffb4a895cea16cb760dcc72bdecb6cb45fef1971fa6ea2e91ddeafddefe01e444ac73e33b1b3d5d29fb0dd18a7effb294262437221ddc03ce0f2e languageName: node linkType: hard @@ -8880,7 +8924,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 050c865ce81119c4822c45d3c84f1ced46f93a0126febae20737bd05ca20589c564d6e9226977df859ed5e03dc73f02584a2b0faad36e896936238238b0446cf @@ -8926,7 +8970,7 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.4": +"pirates@npm:^4.0.5": version: 4.0.5 resolution: "pirates@npm:4.0.5" checksum: c9994e61b85260bec6c4fc0307016340d9b0c4f4b6550a957afaaff0c9b1ad58fbbea5cfcf083860a25cb27a375442e2b0edf52e2e1e40e69934e08dcc52d227 @@ -8987,13 +9031,13 @@ __metadata: linkType: hard "postcss-color-functional-notation@npm:^4.2.1": - version: 4.2.2 - resolution: "postcss-color-functional-notation@npm:4.2.2" + version: 4.2.3 + resolution: "postcss-color-functional-notation@npm:4.2.3" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 77cc5d5526c3228737f2642472546498f0d963b8617c7cae453423331ecb868712ed1557007eab0cd5ff183d60bba24fa2e4bc83e550ddd45f1399e354704b81 + checksum: 1be72dd64b99a33dd8827aec0373067568721cc06d1e059d72d9680280d06546fe67bc30ed508c89c7878a9bf8ac455ec8f12af9335dcfee45a4be872476abf1 languageName: node linkType: hard @@ -9029,13 +9073,13 @@ __metadata: linkType: hard "postcss-custom-properties@npm:^12.1.2": - version: 12.1.4 - resolution: "postcss-custom-properties@npm:12.1.4" + version: 12.1.7 + resolution: "postcss-custom-properties@npm:12.1.7" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: c5ef5c5ff126a65adcae9f1842b6c82df8adc337481497a405de5dfb2121c799d381e75aed3dbfe855f4258e9d90927e77801012166fb667ea5c8f7d79562ada + checksum: 98c313c2318679b727080297a12fb6674e5ea5a3343f693167e985793afd9c7d71ce25a17139864ccfe76d32d7474bb89a2ad02830c8e40fa57ccb0a699b528d languageName: node linkType: hard @@ -9062,24 +9106,25 @@ __metadata: linkType: hard "postcss-double-position-gradients@npm:^3.0.4": - version: 3.0.5 - resolution: "postcss-double-position-gradients@npm:3.0.5" + version: 3.1.1 + resolution: "postcss-double-position-gradients@npm:3.1.1" dependencies: + "@csstools/postcss-progressive-custom-properties": ^1.1.0 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 43c676a085ba908e7ceabffb180e367a949d3a566d31271ca2194fb398260393de4e1aaa95733184d5c337a97ec47cd8c42e42573ed5d60736533f44a1bb5ebd + checksum: c59131b2d03022fbb69336766786e8cc33f6e78c8040e17d2ba499fce789c675c5dcdc4fd3abe1e76e0ecd3dc910ad8c56d49a307c0115047d21a59544afc527 languageName: node linkType: hard "postcss-env-function@npm:^4.0.4": - version: 4.0.5 - resolution: "postcss-env-function@npm:4.0.5" + version: 4.0.6 + resolution: "postcss-env-function@npm:4.0.6" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 0dca3569f136e8f6d11a98d61494054f8ca84de4a679ad80909891d60929f725a188c5e9dc53a3b1c419b00db240b3b64c43bf7bafac081fe718e10460fbb5be + checksum: 645b2363cfa21be9dcce7fe4a0f172f0af70c00d6a4c1eb3d7ff7e9cfe26d569e291ec2533114d77b12d610023cd168a92d62c38f2fc969fa333b5ae2bff5ffe languageName: node linkType: hard @@ -9157,14 +9202,14 @@ __metadata: linkType: hard "postcss-lab-function@npm:^4.0.3": - version: 4.1.0 - resolution: "postcss-lab-function@npm:4.1.0" + version: 4.2.0 + resolution: "postcss-lab-function@npm:4.2.0" dependencies: "@csstools/postcss-progressive-custom-properties": ^1.1.0 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: cf48efa4e23a94a2c7c639b929e57b3ef1ccbb21f636d64ae571a1a72bb0538878c8239c95371e9372448e06890bc2010303e75cc8a715bd2323cc189895872e + checksum: 89ca828b8ed16feb201be7b050254560786c76392ce0a4262732438521ce13d083d1e542addf9d14da75249c58802d721df3152316bb591c9f627c7038166c2a languageName: node linkType: hard @@ -9252,13 +9297,14 @@ __metadata: linkType: hard "postcss-nesting@npm:^10.1.2": - version: 10.1.2 - resolution: "postcss-nesting@npm:10.1.2" + version: 10.1.7 + resolution: "postcss-nesting@npm:10.1.7" dependencies: - postcss-selector-parser: ^6.0.8 + "@csstools/selector-specificity": 1.0.0 + postcss-selector-parser: ^6.0.10 peerDependencies: - postcss: ^8.3 - checksum: 571385eb40ba2874d9e87ed4f8e6e1743c2626e8e626d2d8dc857eeaff7299cc378aa5a662aea93de807955bd32282cf4eaa0434bdb4bd0fdaa2b74703ba98f7 + postcss: ^8.4 + checksum: 610bf1f32ea235ea825b943dbe50cf0fcf5077a63272bee45e66761ffe4f665cf460f3b7ba171fb758a5082aa2893bc7c834d5cb9e6eb6759bb2c65f0141daa1 languageName: node linkType: hard @@ -9335,13 +9381,13 @@ __metadata: linkType: hard "postcss-pseudo-class-any-link@npm:^7.0.2": - version: 7.1.1 - resolution: "postcss-pseudo-class-any-link@npm:7.1.1" + version: 7.1.4 + resolution: "postcss-pseudo-class-any-link@npm:7.1.4" dependencies: - postcss-selector-parser: ^6.0.9 + postcss-selector-parser: ^6.0.10 peerDependencies: postcss: ^8.4 - checksum: d177b7ad6025c1b0dd348b0efa49892d670bc5f4e742f53084625a3110595f45b30ae8fb60d19a09f57112e441d1e1e31421a0fb212e2fde5f8375dc07644efb + checksum: 86e10d56daf57837ccde1d6275425522cafecbae96bd955ef200befc7fd143eedc3b00907e53e8f5207c6290b277600a8ff3f72ec037113867d6031c5d31e7f2 languageName: node linkType: hard @@ -9390,7 +9436,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.6": +"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6, postcss-selector-parser@npm:^6.0.9": version: 6.0.10 resolution: "postcss-selector-parser@npm:6.0.10" dependencies: @@ -9400,16 +9446,6 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.8, postcss-selector-parser@npm:^6.0.9": - version: 6.0.9 - resolution: "postcss-selector-parser@npm:6.0.9" - dependencies: - cssesc: ^3.0.0 - util-deprecate: ^1.0.2 - checksum: f8161ab4d3e5c76b8467189c6d164ba0f6b6e74677435f29e34caa1df01e052b582b4ae4f7468b2243c4befdd8bdcdb7685542d1b2fca8deae21b3e849c78802 - languageName: node - linkType: hard - "postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -9417,7 +9453,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.5, postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7": +"postcss@npm:8.4.5": version: 8.4.5 resolution: "postcss@npm:8.4.5" dependencies: @@ -9428,14 +9464,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.13": - version: 8.4.13 - resolution: "postcss@npm:8.4.13" +"postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7, postcss@npm:^8.4.13": + version: 8.4.14 + resolution: "postcss@npm:8.4.14" dependencies: - nanoid: ^3.3.3 + nanoid: ^3.3.4 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 514fb3552805a5d039a2d6b4df3e73f657001716ca93c0d57e6067b0473abdea70276d80afc96005c9aaff82ed5d98062bd97724d3f47ca400fba0b5e9e436ed + checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 languageName: node linkType: hard @@ -9447,9 +9483,11 @@ __metadata: linkType: hard "prettier-eslint@npm:^14.0.1": - version: 14.0.1 - resolution: "prettier-eslint@npm:14.0.1" + version: 14.1.0 + resolution: "prettier-eslint@npm:14.1.0" dependencies: + "@types/eslint": ^8.4.2 + "@types/prettier": ^2.6.0 "@typescript-eslint/parser": ^5.10.0 common-tags: ^1.4.0 dlv: ^1.1.0 @@ -9462,7 +9500,7 @@ __metadata: require-relative: ^0.8.7 typescript: ^4.5.4 vue-eslint-parser: ^8.0.1 - checksum: 41537cc86ade12a0f09aee7a709aaf21bba2e26f45a97c5a1b056ce6a8e3ac631af81d554563182e3d5b3753592e05a398cb81efd292f2a5766afb2e65996ada + checksum: b286c81590f12ce17b9fd3dbfa53f15ae81036b865504d4342111e2649ddfb80ed05cc17a2a2110d375b2a64d900d9d2ac707a5ece76658a9cdcea083080ca8b languageName: node linkType: hard @@ -9582,13 +9620,20 @@ __metadata: languageName: node linkType: hard -"q@npm:1.4.1, q@npm:^1.4.1": +"q@npm:1.4.1": version: 1.4.1 resolution: "q@npm:1.4.1" checksum: 22c8e1f24f416d0977e6da63f24712189c5dd789489999fc040467480e4e0ef4bd0e3126cce1b8ef72c709bbe1fcce10eba0f4991a03fc64ecb5a17e05ed8d35 languageName: node linkType: hard +"q@npm:^1.4.1": + version: 1.5.1 + resolution: "q@npm:1.5.1" + checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12 + languageName: node + linkType: hard + "qjobs@npm:^1.2.0": version: 1.2.0 resolution: "qjobs@npm:1.2.0" @@ -9596,10 +9641,12 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.9.6": - version: 6.9.6 - resolution: "qs@npm:6.9.6" - checksum: cb6df402bb8a3dbefa4bd46eba0dfca427079baca923e6b8d28a03e6bfb16a5c1dcdb96e69388f9c5813ac8ff17bb8bbca22f2ecd31fe1e344a55cb531b5fabf +"qs@npm:6.10.3": + version: 6.10.3 + resolution: "qs@npm:6.10.3" + dependencies: + side-channel: ^1.0.4 + checksum: 0fac5e6c7191d0295a96d0e83c851aeb015df7e990e4d3b093897d3ac6c94e555dbd0a599739c84d7fa46d7fee282d94ba76943983935cf33bba6769539b8019 languageName: node linkType: hard @@ -9640,15 +9687,15 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.4.2": - version: 2.4.2 - resolution: "raw-body@npm:2.4.2" +"raw-body@npm:2.5.1": + version: 2.5.1 + resolution: "raw-body@npm:2.5.1" dependencies: - bytes: 3.1.1 - http-errors: 1.8.1 + bytes: 3.1.2 + http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - checksum: c6f8d6a75c65c0a047f888cb29efc97f60fb36e950ba2cb31fefce694f98186e844a03367920faa7dc5bffaf33df08aee0b9dd935280e366439fa6492a5b163e + checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e languageName: node linkType: hard @@ -9769,12 +9816,12 @@ __metadata: languageName: node linkType: hard -"regenerator-transform@npm:^0.14.2": - version: 0.14.5 - resolution: "regenerator-transform@npm:0.14.5" +"regenerator-transform@npm:^0.15.0": + version: 0.15.0 + resolution: "regenerator-transform@npm:0.15.0" dependencies: "@babel/runtime": ^7.8.4 - checksum: a467a3b652b4ec26ff964e9c5f1817523a73fc44cb928b8d21ff11aebeac5d10a84d297fe02cea9f282bcec81a0b0d562237da69ef0f40a0160b30a4fa98bc94 + checksum: 86e54849ab1167618d28bb56d214c52a983daf29b0d115c976d79840511420049b6b42c9ebdf187defa8e7129bdd74b6dd266420d0d3868c9fa7f793b5d15d49 languageName: node linkType: hard @@ -9786,12 +9833,13 @@ __metadata: linkType: hard "regexp.prototype.flags@npm:^1.2.0": - version: 1.4.1 - resolution: "regexp.prototype.flags@npm:1.4.1" + version: 1.4.3 + resolution: "regexp.prototype.flags@npm:1.4.3" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.3 - checksum: 77944a3ea5ae84f391fa80bff9babfedc47eadc9dc38e282b5fd746368fb787deec89c68ce3114195bf6b5782b160280a278b62d41ccc6e125afab1a7f816de8 + functions-have-names: ^1.2.2 + checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 languageName: node linkType: hard @@ -10035,7 +10083,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.5.x, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": +"rxjs@npm:6.5.x": version: 6.5.5 resolution: "rxjs@npm:6.5.5" dependencies: @@ -10044,7 +10092,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.6.7": +"rxjs@npm:6.6.7, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -10053,16 +10101,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.2.0": - version: 7.5.4 - resolution: "rxjs@npm:7.5.4" - dependencies: - tslib: ^2.1.0 - checksum: 6f55f835f2543bc8214900f9e28b6320e6adc95875011fbca63e80a66eb18c9ff7cfdccb23b2180cbb6412762b98ed158c89fd51cb020799d127c66ea38c3c0e - languageName: node - linkType: hard - -"rxjs@npm:^7.5.5": +"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": version: 7.5.5 resolution: "rxjs@npm:7.5.5" dependencies: @@ -10071,14 +10110,14 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.1.2, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": +"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -10197,11 +10236,11 @@ __metadata: linkType: hard "selfsigned@npm:^2.0.0": - version: 2.0.0 - resolution: "selfsigned@npm:2.0.0" + version: 2.0.1 + resolution: "selfsigned@npm:2.0.1" dependencies: - node-forge: ^1.2.0 - checksum: 43fca39a5aded2a8e97c1756af74c049a9dde12d47d302820f7d507d25c2ad7da4b04bc439a36620d63b4c0149bcf34ae7a729f978bf3b1bf48859c36ae34cee + node-forge: ^1 + checksum: 864e65c2f31ca877bce3ccdaa3bdef5e1e992b63b2a03641e00c24cd305bf2acce093431d1fed2e5ae9f526558db4be5e90baa2b3474c0428fcf7e25cc86ac93 languageName: node linkType: hard @@ -10234,7 +10273,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.3.5, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": +"semver@npm:7.3.5": version: 7.3.5 resolution: "semver@npm:7.3.5" dependencies: @@ -10254,24 +10293,35 @@ __metadata: languageName: node linkType: hard -"send@npm:0.17.2": - version: 0.17.2 - resolution: "send@npm:0.17.2" +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": + version: 7.3.7 + resolution: "semver@npm:7.3.7" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + languageName: node + linkType: hard + +"send@npm:0.18.0": + version: 0.18.0 + resolution: "send@npm:0.18.0" dependencies: debug: 2.6.9 - depd: ~1.1.2 - destroy: ~1.0.4 + depd: 2.0.0 + destroy: 1.2.0 encodeurl: ~1.0.2 escape-html: ~1.0.3 etag: ~1.8.1 fresh: 0.5.2 - http-errors: 1.8.1 + http-errors: 2.0.0 mime: 1.6.0 ms: 2.1.3 - on-finished: ~2.3.0 + on-finished: 2.4.1 range-parser: ~1.2.1 - statuses: ~1.5.0 - checksum: c28f36deb4ccba9b8d6e6a1e472b8e7c40a1f51575bdf8f67303568cc9e71131faa3adc36fdb72611616ccad1584358bbe4c3ebf419e663ecc5de868ad3d3f03 + statuses: 2.0.1 + checksum: 74fc07ebb58566b87b078ec63e5a3e41ecd987e4272ba67b7467e86c6ad51bc6b0b0154133b6d8b08a2ddda360464f71382f7ef864700f34844a76c8027817a8 languageName: node linkType: hard @@ -10299,15 +10349,15 @@ __metadata: languageName: node linkType: hard -"serve-static@npm:1.14.2": - version: 1.14.2 - resolution: "serve-static@npm:1.14.2" +"serve-static@npm:1.15.0": + version: 1.15.0 + resolution: "serve-static@npm:1.15.0" dependencies: encodeurl: ~1.0.2 escape-html: ~1.0.3 parseurl: ~1.3.3 - send: 0.17.2 - checksum: d97f3183b1dfcd8ce9c0e37e18e87fd31147ed6c8ee0b2c3a089d795e44ee851ca5061db01574f806d54f4e4b70bc694d9ca64578653514e04a28cbc97a1de05 + send: 0.18.0 + checksum: af57fc13be40d90a12562e98c0b7855cf6e8bd4c107fe9a45c212bf023058d54a1871b1c89511c3958f70626fff47faeb795f5d83f8cf88514dbaeb2b724464d languageName: node linkType: hard @@ -10318,10 +10368,10 @@ __metadata: languageName: node linkType: hard -"set-immediate-shim@npm:~1.0.1": - version: 1.0.1 - resolution: "set-immediate-shim@npm:1.0.1" - checksum: 5085c84039d1e5eee73d2bf48ce765fcec76159021d0cc7b40e23bcdf62cb6d450ffb781e3c62c1118425242c48eae96df712cba0a20a437e86b0d4a15d51a11 +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd languageName: node linkType: hard @@ -10364,7 +10414,18 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"side-channel@npm:^1.0.4": + version: 1.0.4 + resolution: "side-channel@npm:1.0.4" + dependencies: + call-bind: ^1.0.0 + get-intrinsic: ^1.0.2 + object-inspect: ^1.9.0 + checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -10424,10 +10485,10 @@ __metadata: languageName: node linkType: hard -"socket.io-adapter@npm:~2.3.3": - version: 2.3.3 - resolution: "socket.io-adapter@npm:2.3.3" - checksum: 73890e0a33e48a9e4be83e5fa2b8ea9728d2a35ae2fed373cad4d6744c6512c0e1c735e7820df9821e58c4738dc355bdaec5aae30bc56f4d6a41d999596d0c82 +"socket.io-adapter@npm:~2.4.0": + version: 2.4.0 + resolution: "socket.io-adapter@npm:2.4.0" + checksum: a84639946dce13547b95f6e09fe167cdcd5d80941afc2e46790cc23384e0fd3c901e690ecc9bdd600939ce6292261ee15094a0b486f797ed621cfc8783d87a0c languageName: node linkType: hard @@ -10442,17 +10503,17 @@ __metadata: languageName: node linkType: hard -"socket.io@npm:^4.2.0": - version: 4.4.1 - resolution: "socket.io@npm:4.4.1" +"socket.io@npm:^4.4.1": + version: 4.5.1 + resolution: "socket.io@npm:4.5.1" dependencies: accepts: ~1.3.4 base64id: ~2.0.0 debug: ~4.3.2 - engine.io: ~6.1.0 - socket.io-adapter: ~2.3.3 + engine.io: ~6.2.0 + socket.io-adapter: ~2.4.0 socket.io-parser: ~4.0.4 - checksum: a559ae52359f1ca3ce5a347368cf985c72259e1ab1bf2bf769ca0add5db34e2a86f4e183a58f37f32676ec482c71fedb7b08d873dc31cf581f5ba0797a8382fe + checksum: 86afd6dcce0c96de85b20a0e37fa4a21e2e96bd6e36d2518acfad37597bcb5208feafbbac20cd34ee4b9356d40418a43938bcf4a206ba693ba3c771ffcef724f languageName: node linkType: hard @@ -10468,17 +10529,17 @@ __metadata: linkType: hard "socks-proxy-agent@npm:^6.0.0, socks-proxy-agent@npm:^6.1.1": - version: 6.1.1 - resolution: "socks-proxy-agent@npm:6.1.1" + version: 6.2.0 + resolution: "socks-proxy-agent@npm:6.2.0" dependencies: agent-base: ^6.0.2 - debug: ^4.3.1 - socks: ^2.6.1 - checksum: 9a8a4f791bba0060315cf7291ca6f9db37d6fc280fd0860d73d8887d3efe4c22e823aa25a8d5375f6079279f8dc91b50c075345179bf832bfe3c7c26d3582e3c + debug: ^4.3.3 + socks: ^2.6.2 + checksum: 6723fd64fb50334e2b340fd0a80fd8488ffc5bc43d85b7cf1d25612044f814dd7d6ea417fd47602159941236f7f4bd15669fa5d7e1f852598a31288e1a43967b languageName: node linkType: hard -"socks@npm:^2.6.1": +"socks@npm:^2.6.2": version: 2.6.2 resolution: "socks@npm:2.6.2" dependencies: @@ -10558,6 +10619,15 @@ __metadata: languageName: node linkType: hard +"source-map@npm:~0.8.0-beta.0": + version: 0.8.0-beta.0 + resolution: "source-map@npm:0.8.0-beta.0" + dependencies: + whatwg-url: ^7.0.0 + checksum: e94169be6461ab0ac0913313ad1719a14c60d402bd22b0ad96f4a6cffd79130d91ab5df0a5336a326b04d2df131c1409f563c9dc0d21a6ca6239a44b6c8dbd92 + languageName: node + linkType: hard + "sourcemap-codec@npm:1.4.8, sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" @@ -10673,29 +10743,36 @@ __metadata: linkType: hard "ssri@npm:^9.0.0": - version: 9.0.0 - resolution: "ssri@npm:9.0.0" + version: 9.0.1 + resolution: "ssri@npm:9.0.1" dependencies: minipass: ^3.1.1 - checksum: bf33174232d07cc64e77ab1c51b55d28352273380c503d35642a19627e88a2c5f160039bb0a28608a353485075dda084dbf0390c7070f9f284559eb71d01b84b + checksum: fb58f5e46b6923ae67b87ad5ef1c5ab6d427a17db0bead84570c2df3cd50b4ceb880ebdba2d60726588272890bae842a744e1ecce5bd2a2a582fccd5068309eb + languageName: node + linkType: hard + +"statuses@npm:2.0.1": + version: 2.0.1 + resolution: "statuses@npm:2.0.1" + checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb languageName: node linkType: hard -"statuses@npm:>= 1.4.0 < 2, statuses@npm:>= 1.5.0 < 2, statuses@npm:~1.5.0": +"statuses@npm:>= 1.4.0 < 2, statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c languageName: node linkType: hard -"streamroller@npm:^3.0.2": - version: 3.0.2 - resolution: "streamroller@npm:3.0.2" +"streamroller@npm:^3.1.1": + version: 3.1.1 + resolution: "streamroller@npm:3.1.1" dependencies: - date-format: ^4.0.3 - debug: ^4.1.1 - fs-extra: ^10.0.0 - checksum: 1f323824f0e81cc085c24f33addfd8ef00d0c15aafee520a8cf207ca6e2dc674fd852528c7b4450cc87f4335d1269ed18b3f0188853d45d7f0912c9a205d1fc1 + date-format: ^4.0.10 + debug: ^4.3.4 + fs-extra: ^10.1.0 + checksum: 8a30f0940ffcd9eca3f0a06a938adb3a7ba08fc1d1e694570ce3a922054735badf27de4539fb5b26c90f44368ba044a00203a7540b6955a481c8de83e9c00bb4 languageName: node linkType: hard @@ -10934,8 +11011,8 @@ __metadata: linkType: hard "stylelint@npm:^14.8.2": - version: 14.8.2 - resolution: "stylelint@npm:14.8.2" + version: 14.8.3 + resolution: "stylelint@npm:14.8.3" dependencies: balanced-match: ^2.0.0 colord: ^2.9.2 @@ -10960,7 +11037,6 @@ __metadata: meow: ^9.0.0 micromatch: ^4.0.5 normalize-path: ^3.0.0 - normalize-selector: ^0.2.0 picocolors: ^1.0.0 postcss: ^8.4.13 postcss-media-query-parser: ^0.2.3 @@ -10980,7 +11056,7 @@ __metadata: write-file-atomic: ^4.0.1 bin: stylelint: bin/stylelint.js - checksum: befe14048b9b442f58a5233a0d1ad1d0ec28ec95bbe940f9394fc43cc90b19546fe06a762790a1dc8066177d33eb57a3a2ca482f848e46f880afadeb46bd536c + checksum: b9446b298ddfea7d2c9ad03dd50204b7213b08e2e8eba96b8cf94bd4dddaa6c8fa4b1ecb6b421e35db65c51aed751820e80e4a2b3abeca7103da086d7fd89140 languageName: node linkType: hard @@ -11226,21 +11302,31 @@ __metadata: languageName: node linkType: hard -"terser@npm:5.10.0, terser@npm:^5.7.2": - version: 5.10.0 - resolution: "terser@npm:5.10.0" +"terser@npm:5.11.0": + version: 5.11.0 + resolution: "terser@npm:5.11.0" dependencies: + acorn: ^8.5.0 commander: ^2.20.0 source-map: ~0.7.2 source-map-support: ~0.5.20 - peerDependencies: + bin: + terser: bin/terser + checksum: cc72b7a0e87421b5a6ef3f8a3c86ef251f6e7f8d6327b83c63045b8991a041cc4a42ea64e07701128e1786489902c8c44b5904056b0f12ceedb52924d493db04 + languageName: node + linkType: hard + +"terser@npm:^5.7.2": + version: 5.13.1 + resolution: "terser@npm:5.13.1" + dependencies: acorn: ^8.5.0 - peerDependenciesMeta: - acorn: - optional: true + commander: ^2.20.0 + source-map: ~0.8.0-beta.0 + source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: 1080faeb6d5cd155bb39d9cc41d20a590eafc9869560d5285f255f6858604dcd135311e344188a106f87fedb12d096ad3799cfc2e65acd470b85d468b1c7bd4c + checksum: 0b1f5043cf5c3973005fe2ae4ff3be82511c336a6430599dacd4e2acf77c974d4474b0f1eec4823977c1f33823147e736ff712ca8e098bee3db25946480fa29d languageName: node linkType: hard @@ -11336,6 +11422,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: ^2.1.0 + checksum: 96d4ed46bc161db75dbf9247a236ea0bfcaf5758baae6749e92afab0bc5a09cb59af21788ede7e55080f2bf02dce3e4a8f2a484cc45164e29f4b5e68f7cbcc1a + languageName: node + linkType: hard + "tree-kill@npm:1.2.2": version: 1.2.2 resolution: "tree-kill@npm:1.2.2" @@ -11381,14 +11476,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.0.x, tslib@npm:^2.0.0": +"tslib@npm:2.0.x": version: 2.0.3 resolution: "tslib@npm:2.0.3" checksum: 00fcdd1f9995c9f8eb6a4a1ad03f55bc95946321b7f55434182dddac259d4e095fedf78a84f73b6e32dd3f881d9281f09cb583123d3159ed4bdac9ad7393ef8b languageName: node linkType: hard -"tslib@npm:2.3.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1": +"tslib@npm:2.3.1": version: 2.3.1 resolution: "tslib@npm:2.3.1" checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 @@ -11402,6 +11497,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -11484,13 +11586,13 @@ __metadata: linkType: hard "typed-assert@npm:^1.0.8": - version: 1.0.8 - resolution: "typed-assert@npm:1.0.8" - checksum: bed460f76da5b142da561b75a10164c3a226ac99353fa503ad1874aa375b51823088c72406148c8cbfb277ca2a416fbbd250689be84b2734944be101e79f4117 + version: 1.0.9 + resolution: "typed-assert@npm:1.0.9" + checksum: 79351bd3ea184a552bf55a77bd3012f128741c841ed718d054c5abbbc8925362aa033ae2cdcc79e1f445a15112447c8a95a08ddf7ff8aeb04f805e92187f77c1 languageName: node linkType: hard -"typescript@npm:4.5.x, typescript@npm:^4.5.3": +"typescript@npm:4.5.x": version: 4.5.5 resolution: "typescript@npm:4.5.5" bin: @@ -11501,16 +11603,16 @@ __metadata: linkType: hard "typescript@npm:^4.5.4": - version: 4.6.3 - resolution: "typescript@npm:4.6.3" + version: 4.6.4 + resolution: "typescript@npm:4.6.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 255bb26c8cb846ca689dd1c3a56587af4f69055907aa2c154796ea28ee0dea871535b1c78f85a6212c77f2657843a269c3a742d09d81495b97b914bf7920415b + checksum: e7bfcc39cd4571a63a54e5ea21f16b8445268b9900bf55aee0e02ad981be576acc140eba24f1af5e3c1457767c96cea6d12861768fb386cf3ffb34013718631a languageName: node linkType: hard -"typescript@patch:typescript@4.5.x#~builtin, typescript@patch:typescript@^4.5.3#~builtin": +"typescript@patch:typescript@4.5.x#~builtin": version: 4.5.5 resolution: "typescript@patch:typescript@npm%3A4.5.5#~builtin::version=4.5.5&hash=bda367" bin: @@ -11521,12 +11623,12 @@ __metadata: linkType: hard "typescript@patch:typescript@^4.5.4#~builtin": - version: 4.6.3 - resolution: "typescript@patch:typescript@npm%3A4.6.3#~builtin::version=4.6.3&hash=bda367" + version: 4.6.4 + resolution: "typescript@patch:typescript@npm%3A4.6.4#~builtin::version=4.6.4&hash=bda367" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 6bf45caf847062420592e711bc9c28bf5f9a9a7fa8245343b81493e4ededae33f1774009d1234d911422d1646a2c839f44e1a23ecb111b40a60ac2ea4c1482a8 + checksum: 1cb434fbc637d347be90e3a0c6cd05e33c38f941713c8786d3031faf1842c2c148ba91d2fac01e7276b0ae3249b8633f1660e32686cc7a8c6a8fd5361dc52c66 languageName: node linkType: hard @@ -11768,7 +11870,14 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:5.3.0, webpack-dev-middleware@npm:^5.3.0": +"webidl-conversions@npm:^4.0.2": + version: 4.0.2 + resolution: "webidl-conversions@npm:4.0.2" + checksum: c93d8dfe908a0140a4ae9c0ebc87a33805b416a33ee638a605b551523eec94a9632165e54632f6d57a39c5f948c4bab10e0e066525e9a4b87a79f0d04fbca374 + languageName: node + linkType: hard + +"webpack-dev-middleware@npm:5.3.0": version: 5.3.0 resolution: "webpack-dev-middleware@npm:5.3.0" dependencies: @@ -11783,6 +11892,21 @@ __metadata: languageName: node linkType: hard +"webpack-dev-middleware@npm:^5.3.0": + version: 5.3.3 + resolution: "webpack-dev-middleware@npm:5.3.3" + dependencies: + colorette: ^2.0.10 + memfs: ^3.4.3 + mime-types: ^2.1.31 + range-parser: ^1.2.1 + schema-utils: ^4.0.0 + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + checksum: dd332cc6da61222c43d25e5a2155e23147b777ff32fdf1f1a0a8777020c072fbcef7756360ce2a13939c3f534c06b4992a4d659318c4a7fe2c0530b52a8a6621 + languageName: node + linkType: hard + "webpack-dev-server@npm:4.7.3": version: 4.7.3 resolution: "webpack-dev-server@npm:4.7.3" @@ -11914,6 +12038,17 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^7.0.0": + version: 7.1.0 + resolution: "whatwg-url@npm:7.1.0" + dependencies: + lodash.sortby: ^4.7.0 + tr46: ^1.0.1 + webidl-conversions: ^4.0.2 + checksum: fecb07c87290b47d2ec2fb6d6ca26daad3c9e211e0e531dd7566e7ff95b5b3525a57d4f32640ad4adf057717e0c215731db842ad761e61d947e81010e05cf5fd + languageName: node + linkType: hard + "which-module@npm:^2.0.0": version: 2.0.0 resolution: "which-module@npm:2.0.0" @@ -11943,7 +12078,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.2": +"wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: @@ -12005,7 +12140,22 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.1.0, ws@npm:~8.2.3": +"ws@npm:^8.1.0": + version: 8.6.0 + resolution: "ws@npm:8.6.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: e2fca82059f1e087d0c78e2f37135e1b8332bc804fce46f83c2db1cb8571685abf9d2c99b964bab3752536ad90b99b46fb8d1428899aed3e560684ab4641bffd + languageName: node + linkType: hard + +"ws@npm:~8.2.3": version: 8.2.3 resolution: "ws@npm:8.2.3" peerDependencies: @@ -12131,8 +12281,8 @@ __metadata: linkType: hard "yargs@npm:^17.2.1, yargs@npm:^17.3.1, yargs@npm:^17.4.0": - version: 17.4.1 - resolution: "yargs@npm:17.4.1" + version: 17.5.1 + resolution: "yargs@npm:17.5.1" dependencies: cliui: ^7.0.2 escalade: ^3.1.1 @@ -12141,7 +12291,7 @@ __metadata: string-width: ^4.2.3 y18n: ^5.0.5 yargs-parser: ^21.0.0 - checksum: e9012322870d7e4e912a6ae1f63b203e365f911c0cf158be92c36edefddfb3bd38ce17eb9ef0d18858a4777f047c50589ea22dacb44bd949169ba37dc6d34bee + checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde languageName: node linkType: hard @@ -12153,10 +12303,10 @@ __metadata: linkType: hard "zone.js@npm:~0.11.4": - version: 0.11.4 - resolution: "zone.js@npm:0.11.4" + version: 0.11.5 + resolution: "zone.js@npm:0.11.5" dependencies: - tslib: ^2.0.0 - checksum: 20c3e39898019de4747a434a29ed528e5d730a674570c3e72775a57f9d57dba812e70d67c3932ff54e95db9b778f06a9b18119c5184dfd9552d3622544a6729f + tslib: ^2.3.0 + checksum: 7dba3af83cc68e881e8a5cc549e4a7bab5f32c5289838f14eb251871816210a39ed8bcd89030593b56d469b4aa8271d644e71ad408fcadd4ccd61b8c7203f2ef languageName: node linkType: hard From 5af640c481b76887b1c2e46af297b98c29ebd978 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 25 Oct 2022 20:59:41 +0200 Subject: [PATCH 008/498] Fixed two minor documentation issues, leading to openapi generator issues --- .../kotlin/dev/dres/api/rest/handler/CollectionHandler.kt | 1 + .../dres/api/rest/handler/CompetitionRunClientInfoHandler.kt | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt index abaeb3cca..309b69969 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt @@ -131,6 +131,7 @@ class DeleteCollectionHandler(collections: DAO, items: DAO Date: Wed, 26 Oct 2022 05:35:34 +0200 Subject: [PATCH 009/498] Converted User model and UserManager --- backend/src/main/kotlin/dev/dres/DRES.kt | 23 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 2 +- .../dev/dres/api/rest/handler/LoginHandler.kt | 13 +- .../dev/dres/api/rest/handler/UserHandler.kt | 2 +- .../kotlin/dev/dres/data/model/admin/Role.kt | 29 +- .../kotlin/dev/dres/data/model/admin/User.kt | 66 ++-- .../model/basics/media/MediaItemSegment.kt | 1 - .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 288 +++++++++--------- .../utilities/extensions/StringExtensions.kt | 47 ++- 9 files changed, 234 insertions(+), 237 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index d97c0395c..4588d7d9e 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -3,8 +3,11 @@ package dev.dres import dev.dres.api.cli.Cli import dev.dres.api.cli.OpenApiCommand import dev.dres.api.rest.RestApi -import dev.dres.data.dbo.DataAccessLayer import dev.dres.data.model.Config +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.basics.media.MediaItemSegment +import dev.dres.data.model.basics.media.MediaType import dev.dres.mgmt.admin.UserManager import dev.dres.run.RunExecutor import dev.dres.run.audit.AuditLogger @@ -13,8 +16,11 @@ import dev.dres.run.eventstream.handlers.ResultLogStatisticsHandler import dev.dres.run.eventstream.handlers.SubmissionStatisticsHandler import dev.dres.run.eventstream.handlers.TeamCombinationScoreHandler import dev.dres.utilities.FFmpegUtil +import kotlinx.dnq.XdModel +import kotlinx.dnq.store.container.StaticStoreContainer +import kotlinx.dnq.util.initMetaData import java.io.File -import java.nio.file.Paths + object DRES { @@ -38,11 +44,18 @@ object DRES { println("Found FFmpeg at ${FFmpegUtil.ffmpegBin}") println("Initializing...") - /* Initialize data access layer. */ - val dataAccessLayer = DataAccessLayer(Paths.get(config.dataPath)) + /* Initialize Xodus based data store. */ + XdModel.registerNodes( + MediaType, + MediaCollection, + MediaItem, + MediaItemSegment + ) + val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") + initMetaData(XdModel.hierarchy, store) /* Initialize user manager. */ - UserManager.init(dataAccessLayer.users) + UserManager.init(store) /* Initialize run executor. */ RunExecutor.init(dataAccessLayer.runs) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 493011a4a..cff7a5a36 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -31,7 +31,7 @@ object RestApi { private val logMarker = MarkerFactory.getMarker("REST") private val logger = LoggerFactory.getLogger(this.javaClass) - fun init(config: Config, dataAccessLayer: DataAccessLayer) { + fun init(config: Config, store: Entity) { val runExecutor = RunExecutor diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt index ded69b604..af56b2b5d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt @@ -3,9 +3,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.dbo.DAO -import dev.dres.data.model.admin.PlainPassword -import dev.dres.data.model.admin.UserName +import dev.dres.data.model.admin.Password import dev.dres.mgmt.admin.UserManager import dev.dres.mgmt.admin.UserManager.getMatchingUser import dev.dres.run.audit.AuditLogEntry @@ -41,11 +39,10 @@ class LoginHandler(private val audit: DAO) : RestHandler, PostRes throw ErrorStatusException(400, "Invalid parameters. This is a programmers error.", ctx) } - val username = UserName(loginRequest.username) - val password = PlainPassword(loginRequest.password) - - val user = getMatchingUser(username, password) - ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) + /* Validate login request. */ + val username = loginRequest.username + val password = Password.Plain(loginRequest.password) + val user = getMatchingUser(username, password) ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) AccessManager.setUserForSession(ctx.sessionId(), user) AuditLogger.login(loginRequest.username, ctx.sessionId(), LogEventSource.REST) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt index 6b436f32a..0e2b5af69 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt @@ -19,7 +19,7 @@ import io.javalin.openapi.* data class SessionId(val sessionId: String) -data class UserRequest(val username: String, val password: String?, val role: Role?) +data class UserRequest(val username: String, val password: String?, val role: RestApiRole?) data class UserDetails(val id: String, val username: String, val role: Role, val sessionId: String? = null) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 3eef71b5c..1c63c1096 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -1,14 +1,29 @@ package dev.dres.data.model.admin import dev.dres.api.rest.RestApiRole +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp /** - * Roles currently supported by DRES. + * The [Role]s currently supported by DRES. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 */ -enum class Role { - ADMIN, JUDGE, VIEWER, PARTICIPANT; +class Role(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val VIEWER by enumField { description = "VIEWER" } + val PARTICIPANT by enumField { description = "PARTICIPANT" } + val JUDGE by enumField { description = "JDUGE" } + val ADMIN by enumField { description = "ADMIN" } - companion object { + /** + * Generates and returns the [Role] that corresponds to the given [RestApiRole]. + * + * @param role [RestApiRole] + */ fun fromRestRole(role: RestApiRole): Role = when(role) { RestApiRole.ANYONE, RestApiRole.VIEWER -> VIEWER @@ -17,4 +32,8 @@ enum class Role { RestApiRole.ADMIN -> ADMIN } } -} \ No newline at end of file + + /** Name / description of the [Role]. */ + var description by xdRequiredStringProp(unique = true) + private set +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index d4f53a004..f1c1a73b5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -1,55 +1,37 @@ package dev.dres.data.model.admin -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonProperty import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID -import org.mindrot.jbcrypt.BCrypt +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.simple.length +import kotlinx.dnq.xdLink1 +import kotlinx.dnq.xdRequiredStringProp typealias UserId = UID -data class User constructor( - override var id: UserId = UID.EMPTY, - val username: UserName, - val password: HashedPassword, - val role: Role) : PersistentEntity { - override fun toString(): String = "User(id=$id, username=${username.name}, role=$role)" -} +/** + * A [User] in the DRES user management model. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class User(entity: Entity): PersistentEntity(entity) { + companion object : XdNaturalEntityType() -data class UserName @JsonCreator constructor(@JsonProperty("name") val name: String){ - val length = name.length -} + /** The name held by this [User]. Must be unique!*/ + var username by xdRequiredStringProp(unique = true, trimmed = false) { length(4, 16, "Username must consist of between 4 and 16 characters")} -sealed class Password(private val pass: String) + /** The password held by this [User]. */ + var password by xdRequiredStringProp(unique = false, trimmed = false) -class PlainPassword(internal val pass: String) : Password(pass){ + /** The [Role] of this [User]. */ + var role by xdLink1(Role) - fun hash(): HashedPassword { - return HashedPassword(BCrypt.hashpw(pass, BCrypt.gensalt())) - } + /** + * The [Password.Hashed] held by this [User]. + */ + fun hashedPassword() = Password.Hashed(this.password) - val length = pass.length - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as PlainPassword - - if (pass != other.pass) return false - - return true - } - - override fun hashCode(): Int { - return pass.hashCode() - } - - -} - -class HashedPassword @JsonCreator constructor (@JsonProperty("hash") val hash: String) : Password(hash) { - fun check(plain: PlainPassword): Boolean { - return BCrypt.checkpw(plain.pass, this.hash) - } + override fun toString(): String = "User(id=$id, username=${username}, role=$role)" } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt index 048706c27..9ffbb9619 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.basics.media import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.UID import dev.dres.data.model.basics.time.TemporalPoint import dev.dres.data.model.basics.time.TemporalRange import jetbrains.exodus.entitystore.Entity diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 2e8d1698b..a74c1b12a 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -1,201 +1,193 @@ package dev.dres.mgmt.admin -import dev.dres.api.rest.handler.UserHandler import dev.dres.api.rest.handler.UserRequest -import dev.dres.data.dbo.DAO import dev.dres.data.model.UID import dev.dres.data.model.admin.* -import dev.dres.utilities.extensions.toPlainPassword -import dev.dres.utilities.extensions.toUsername +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* /** - * User management of DRES. - * Single access to DAO for users. Requires initialisation ONCE + * User management class of DRES. Requires one-time initialisation * * @author Loris Sauter - * @version 1.0 + * @version 2.0.0 */ object UserManager { - const val MIN_LENGTH_USERNAME = 4 const val MIN_LENGTH_PASSWORD = 6 - private lateinit var users: DAO + /** The [TransientEntityStore] instance used by this [UserManager]. */ + private lateinit var store: TransientEntityStore - - fun init(users: DAO) { - this.users = users - } - - fun create(username: UserName, password: HashedPassword, role: Role): Boolean { - validateInitalised() - validateUsernameLengthOrEscalate(username) - validateUsernameUniqueOrEscalate(username) - val newUser = User(username = username, password = password, role = role) - return create(newUser) - } - - fun create(username: UserName, password: PlainPassword, role: Role): Boolean { - validateInitalised() - validateUsernameLengthOrEscalate(username) - validateUsernameUniqueOrEscalate(username) - validatePasswordLengthOrEscalate(password) - val newUser = User(username = username, password = password.hash(), role = role) - return create(newUser) - } - - private fun validateUsernameLengthOrEscalate(username: UserName){ - if(username.length < MIN_LENGTH_USERNAME){ - throw RuntimeException("Username is less than $MIN_LENGTH_USERNAME characters") - } + fun init(store: TransientEntityStore) { + this.store = store } - private fun validatePasswordLengthOrEscalate(password: PlainPassword){ - if(password.length < MIN_LENGTH_PASSWORD){ - throw RuntimeException("Password is less than $MIN_LENGTH_PASSWORD characters") - } - } - - private fun validateUsernameUniqueOrEscalate(username: UserName){ - if(username in users.map ( User::username )){ - throw RuntimeException("Username is already taken: $username") - } - } - - private fun create(user:User): Boolean{ - for (existingUser in this.users) { - if (existingUser in users) { - return false + /** + * Creates a [User] with the given [username], [password] and [role]. + * + * @param username The name of the [User]. Must be unique. + * @param password The [Password.Hashed] of the user. + * @param role The [Role] of the new user. + */ + fun create(username: String, password: Password.Hashed, role: Role): Boolean { + check(::store.isInitialized) { "PUserManager requires an initialized store which is unavailable. This is a programmer's error!"} + try { + this.store.transactional { + User.new { + this.username = username + this.password = password.password + this.role = role + } } + } catch (e: Throwable) { + return false } - users.append(user) return true } - fun update(id: UID?, username: UserName?, password: HashedPassword?, role: Role?): Boolean { - validateInitalised() - val updateId = id(id, username) - if (updateId != null) { - val currentUser = users[updateId] - if (currentUser != null) { - val updatedUser = currentUser.copy(id = currentUser.id, username = username ?: currentUser.username, password = password ?: currentUser.password, role = role ?: currentUser.role) - users.update(updatedUser) - return true - } - } - return false - } - - fun update(id: UID?, username: UserName?, password: PlainPassword?, role: Role?): Boolean { - validateInitalised() - val updateId = id(id, username) - if (updateId != null) { - val currentUser = users[updateId] - if (currentUser != null) { - val updatedUser = currentUser.copy(id = currentUser.id, username = username ?: currentUser.username, password = password?.hash() - ?: currentUser.password, role = role ?: currentUser.role) - users.update(updatedUser) - return true - } - } - return false + /** + * Creates a [User] with the given [username], [password] and [role]. + * + * @param username The name of the [User]. Must be unique. + * @param password The [Password.Plain] of the user. + * @param role The [Role] of the new user. + */ + fun create(username: String, password: Password.Plain, role: Role): Boolean { + check(::store.isInitialized) { "UserManager requires an initialized store which is unavailable. This is a programmer's error!"} + return create(username, password.hash(), role) } - fun delete(id: UID?, username: UserName?): Boolean { - validateInitalised() - val delId = id(id, username) - if (delId != null && exists(delId)) { - this.users.delete(delId) - return true + /** + * Updates a [User] with the given [UID], [username], [password] and [role]. + * + * @param id The [UID] of the user to update. + * @param username The name of the [User]. Must be unique. + * @param password The [Password.Hashed] of the user. + * @param role The [Role] of the new user. + */ + fun update(id: UID?, username: String?, password: Password.Hashed?, role: Role?): Boolean = this.store.transactional { + val user = if (id != null) { + User.query(User::id eq id.string).firstOrNull() + } else if (username != null) { + User.query(User::username eq username).firstOrNull() } else { - return false + null } + if (user == null) return@transactional false + if (username != null) user.username = username + if (password != null) user.password = password.password + if (role != null) user.role = role + true } - fun delete(id:UID?):Boolean{ - return delete(id=id,username = null) - } - - fun list(): List { - validateInitalised() - return users.toList() - } - - fun exists(id: UID?, username: UserName?): Boolean { - validateInitalised() - val searchId = id(id, username) - return this.users.exists(searchId ?: UID.EMPTY) - } - - fun exists(id: UID?): Boolean { - return exists(id = id, username = null) - } + /** + * Updates a [User] with the given [UID], [username], [password] and [role]. + * + * @param id The [UID] of the user to update. + * @param username The name of the [User]. Must be unique. + * @param password The [Password.Plain] of the user. + * @param role The [Role] of the new user. + */ + fun update(id: UID?, username: String?, password: Password.Plain?, role: Role?): Boolean + = update(id, username, password?.hash(), role) - fun exists(username: UserName?): Boolean { - return exists(id = null, username = username) - } + /** + * Updates a [User] for the given [id] based o the [request]. + * + * @param id The [UID] of the user to update. + * @param request The [UserRequest] detailing the update + * @return True on success, false otherwise. + */ + fun update(id: UID?, request: UserRequest): Boolean + = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.let { Role.fromRestRole(it) }) - fun get(id: UID?, username: UserName?): User? { - validateInitalised() - val _id = id(id, username) - return if (exists(id = _id)) { - users[_id!!] // !! is safe here, because theres a nulll check in exists + /** + * Deletes the [User] for the given [UID]. + * + * @param username The name of the [User] to delete. + * @return True on success, false otherwise. + */ + fun delete(username: String): Boolean = this.store.transactional { + val user = User.query(User::username eq username).firstOrNull() + if (user != null) { + user.delete() + true } else { - null + false } } - fun get(id: UID?): User? { - return get(id = id, username = null) - } - - fun get(username: UserName?): User? { - return get(id = null, username = username) - } - /** - * Returns the id - * if the id is not null - * if the username is not null, the corresponding id - * null if failed + * Deletes the [User] for the given [UID]. + * + * @param id The [UID] of the [User] to delete. + * @return True on success, false otherwise. */ - fun id(id: UID?, username: UserName?): UID? { - return when { - id != null -> id - username != null -> users.find { it.username == username }?.id - else -> null + fun delete(id: UID):Boolean = this.store.transactional { + val user = User.query(User::id eq id.string).firstOrNull() + if (user != null) { + user.delete() + true + } else { + false } } /** - * Either returns a user for this username/password tuple or null + * Lists all [User] objects in DRES. + * + * @return List of all [User]s. */ - fun getMatchingUser(username: UserName, password: PlainPassword) : User? { - val user = users.find { it.username == username } ?: return null - return if (user.password.check(password)) user else null + fun list(): List = this.store.transactional(readonly = true) { + User.all().toList() } - private fun validateInitalised() { - if (isInit().not()) { - throw RuntimeException("The UserManager was not initialised with a DAO") - } + /** + * Checks for the existence of the [User] with the given [UID]. + * + * @param id [UID] to check. + * @return True if [User] exists, false otherwise. + */ + fun exists(id: UID): Boolean = this.store.transactional(readonly = true) { + User.query(User::id eq id.string).isNotEmpty } - private fun isInit(): Boolean { - return ::users.isInitialized + /** + * Checks for the existence of the [User] with the given [UID]. + * + * @param username User name to check. + * @return True if [User] exists, false otherwise. + */ + fun exists(username: String): Boolean = this.store.transactional(readonly = true) { + User.query(User::username eq username).isNotEmpty } - fun create(toCreate: UserRequest): Boolean { - return create(UserName(toCreate.username), if(toCreate.password != null){PlainPassword(toCreate.password)}else{ - PlainPassword("") - }, toCreate.role!!) + /** + * Returns the [User] for the given [UID] or null if [User] doesn't exist. + * + * @param id The [UID] of the [User] to fetch. + * @return [User] or null + */ + fun get(id: UID): User? = this.store.transactional(readonly = true) { + User.query(User::id eq id.string).firstOrNull() } - fun updateEntirely(id:UID?, user: UserRequest): Boolean { - return update(id=id, username = user.username.toUsername(), password = user.password.toPlainPassword(), role = user.role) + /** + * Returns the [User] for the given [username] or null if [User] doesn't exist. + * + * @param username The name of the [User] to fetch. + * @return [User] or null + */ + fun get(username: String): User? = this.store.transactional(readonly = true) { + User.query(User::username eq username).firstOrNull() } - fun update(id:UID?, user:UserRequest):Boolean{ - return update(id=id, username = user.username.toUsername(), password = user.password?.toPlainPassword(), role=user.role) + /** + * Either returns a user for this username/password tuple or null + */ + fun getMatchingUser(username: String, password: Password.Plain) : User? { + val user = get(username) + return if (user?.hashedPassword()?.check(password) == true) user else null } } diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt index c4122af6e..4af43aa10 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt @@ -2,38 +2,33 @@ package dev.dres.utilities.extensions import dev.dres.api.rest.handler.SessionId import dev.dres.data.model.UID -import dev.dres.data.model.admin.PlainPassword -import dev.dres.data.model.admin.UserName -import dev.dres.mgmt.admin.UserManager import java.util.* import java.util.regex.Matcher -fun String?.toUsername(): UserName? { - if (this == null || this.isEmpty()) { - return null - } - if (this.length < UserManager.MIN_LENGTH_USERNAME) { - throw IndexOutOfBoundsException("Username too short. Must be at least ${UserManager.MIN_LENGTH_USERNAME}") - } - return UserName(this) -} - -fun String?.toPlainPassword(): PlainPassword? { - if (this == null || this.isEmpty()) { - return null - } - if (this.length < UserManager.MIN_LENGTH_PASSWORD) { - throw IndexOutOfBoundsException("Password too short. Must be at least ${UserManager.MIN_LENGTH_PASSWORD}") - } - return PlainPassword(this) -} - -fun String.toSessionId(): SessionId { - return SessionId(this) -} +/** + * Converts a [String] to a [SessionId]. + * + * @return [SessionId] + */ +fun String.toSessionId(): SessionId = SessionId(this) +/** + * Converts a [String] to a [UID]. + * + * @return [UID] + */ fun String.UID(): UID = UID(UUID.fromString(this)) +/** + * Converts a [String] to a [UID]. + * + * @return [UID] + */ fun String.cleanPathString() = this.trim().replaceFirst("^~", Matcher.quoteReplacement(System.getProperty("user.home"))) +/** + * Converts a [String] to a [UID]. + * + * @return [UID] + */ fun String.toPathParamKey() = "{$this}" \ No newline at end of file From 405610560a5f9c1d0a2857bd1b6595730f542a7e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Oct 2022 23:16:34 +0200 Subject: [PATCH 010/498] Added allowCredentials header and switched from anyHost to reflect client host to enable dev mode --- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 778324dea..5c365b2e3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -186,7 +186,8 @@ object RestApi { javalin = Javalin.create { it.plugins.enableCors { cors -> cors.add { corsPluginConfig -> - corsPluginConfig.anyHost() + corsPluginConfig.reflectClientOrigin = true // anyHost() has similar implications and might be used in production? I'm not sure how to cope with production and dev here simultaneously + corsPluginConfig.allowCredentials = true } } From d0ef4d8e89783585d61fc47c17fd8425c4caea61 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 27 Oct 2022 09:30:48 +0200 Subject: [PATCH 011/498] Added missing files. --- .../dev/dres/data/model/PersistentEntity.kt | 13 ++++++++++ .../dev/dres/data/model/admin/Password.kt | 26 +++++++++++++++++++ .../basics/media/MediaItemSegmentList.kt | 15 +++++++++++ .../dres/data/model/basics/media/MediaType.kt | 22 ++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt new file mode 100644 index 000000000..33bcd30bd --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt @@ -0,0 +1,13 @@ +package dev.dres.data.model + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEntity +import kotlinx.dnq.xdRequiredStringProp + +/** + * + */ +abstract class PersistentEntity(entity: Entity): XdEntity(entity) { + /** */ + var id: String by xdRequiredStringProp(unique = true, trimmed = false) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt new file mode 100644 index 000000000..82f63eb39 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt @@ -0,0 +1,26 @@ +package dev.dres.data.model.admin + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import dev.dres.mgmt.admin.UserManager +import org.mindrot.jbcrypt.BCrypt + +/** + * + * @author Ralph Gasser + * @version 1.0.0 + */ +sealed class Password(val password: String) { + class Plain(password: String): Password(password) { + init { + require (password.length < UserManager.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${UserManager.MIN_LENGTH_PASSWORD}" } + } + fun hash(): Hashed = Hashed(BCrypt.hashpw(this.password, BCrypt.gensalt())) + val length: Int + get() = this.password.length + } + + class Hashed @JsonCreator constructor (@JsonProperty("hash") hash: String) : Password(hash) { + fun check(plain: Plain): Boolean = BCrypt.checkpw(plain.password, this.password) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt new file mode 100644 index 000000000..f5b618939 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt @@ -0,0 +1,15 @@ +package dev.dres.data.model.basics.media + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.UID + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +data class MediaItemSegmentList(override var id: UID, val mediaItemId: UID, val segments: MutableList) : PersistentEntity { + init { + require(segments.all { it.mediaItemId == mediaItemId } ){"All segments need to belong to the same media item"} + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt new file mode 100644 index 000000000..5bd6c2016 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt @@ -0,0 +1,22 @@ +package dev.dres.data.model.basics.media + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * A persistable media type enumeration such as a video or an image + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class MediaType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val IMAGE by enumField { description = "IMAGE" } + val VIDEO by enumField { description = "VIDEO" } + } + + var description by xdRequiredStringProp(unique = true) + private set +} From b03a54c4508b5fd9c2bae66e3ecafaeef05d1721 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 28 Oct 2022 05:47:48 +0200 Subject: [PATCH 012/498] Continued migration to Xodus storge; adjusted first batch of commands (User, MediaCollection, AuditLogEntry) and started migration of CompetitionDescription --- backend/src/main/kotlin/dev/dres/DRES.kt | 31 +- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 34 +- .../dev/dres/api/cli/CompetitionRunCommand.kt | 6 +- .../kotlin/dev/dres/api/cli/UserCommand.kt | 144 +++-- .../kotlin/dev/dres/api/rest/AccessManager.kt | 129 ++-- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 128 ++-- .../rest/handler/AbstractPreviewHandler.kt | 244 -------- .../dres/api/rest/handler/AuditLogHandler.kt | 174 ------ .../api/rest/handler/CollectionHandler.kt | 551 ------------------ .../api/rest/handler/CompetitionHandler.kt | 6 +- .../handler/CompetitionRunAdminHandler.kt | 69 +-- .../CompetitionRunClientInfoHandler.kt | 10 +- .../api/rest/handler/CompetitionRunHandler.kt | 10 +- .../handler/CompetitionRunScoreHandler.kt | 10 +- .../dres/api/rest/handler/DownloadHandler.kt | 4 +- .../dres/api/rest/handler/GetMediaHandler.kt | 74 --- .../dres/api/rest/handler/JudgementHandler.kt | 18 +- .../dev/dres/api/rest/handler/LogHandler.kt | 10 +- .../dres/api/rest/handler/LogoutHandler.kt | 38 -- .../dev/dres/api/rest/handler/RestHandler.kt | 1 - .../dres/api/rest/handler/StatusHandler.kt | 96 --- .../dev/dres/api/rest/handler/UserHandler.kt | 281 --------- .../handler/audit/AbstractAuditLogHandler.kt | 21 + .../handler/audit/GetAuditLogInfoHandler.kt | 30 + .../handler/audit/ListAuditLogsHandler.kt | 60 ++ .../audit/ListAuditLogsInRangeHandler.kt | 50 ++ .../collection/AbstractCollectionHandler.kt | 33 ++ .../collection/AddCollectionHandler.kt | 60 ++ .../handler/collection/AddMediaItemHandler.kt | 72 +++ .../collection/DeleteCollectionHandler.kt | 40 ++ .../collection/DeleteMediaItemHandler.kt | 51 ++ .../collection/ListCollectionHandler.kt | 39 ++ .../collection/ListExternalItemHandler.kt | 59 ++ .../collection/ListMediaItemHandler.kt | 49 ++ .../collection/RandomMediaItemHandler.kt | 51 ++ .../ResolveMediaItemListByNameHandler.kt | 62 ++ .../collection/ShowCollectionHandler.kt | 42 ++ .../collection/ShowMediaItemHandler.kt | 50 ++ .../collection/UpdateCollectionHandler.kt | 58 ++ .../collection/UpdateMediaItemHandler.kt | 61 ++ .../handler/preview/AbstractPreviewHandler.kt | 128 ++++ .../rest/handler/preview/GetMediaHandler.kt | 71 +++ .../handler/preview/MediaPreviewHandler.kt | 53 ++ .../preview/SubmissionPreviewHandler.kt | 82 +++ .../BatchSubmissionHandler.kt | 6 +- .../{ => submission}/SubmissionHandler.kt | 176 +++--- .../rest/handler/system/CurrentTimeHandler.kt | 30 + .../api/rest/handler/system/InfoHandler.kt | 58 ++ .../rest/handler/{ => system}/LoginHandler.kt | 21 +- .../api/rest/handler/system/LogoutHandler.kt | 44 ++ .../rest/handler/users/AbstractUserHandler.kt | 35 ++ .../rest/handler/users/CreateUsersHandler.kt | 62 ++ .../rest/handler/users/DeleteUsersHandler.kt | 45 ++ .../handler/users/ListActiveUsersHandler.kt | 51 ++ .../rest/handler/users/ListUsersHandler.kt | 36 ++ .../users/ShowCurrentSessionHandler.kt | 38 ++ .../handler/users/ShowCurrentUserHandler.kt | 36 ++ .../rest/handler/users/UpdateUsersHandler.kt | 65 +++ .../rest/handler/users/UserDetailsHandler.kt | 41 ++ .../api/rest/types/WebSocketConnection.kt | 2 +- .../dres/api/rest/types/audit/AuditLogInfo.kt | 4 +- .../types/collection/RestMediaCollection.kt | 20 +- .../rest/types/collection/RestMediaItem.kt | 38 +- .../types/competition/RestDetailedTeam.kt | 2 +- .../api/rest/types/competition/RestTeam.kt | 2 +- .../rest/types/competition/RestTeamGroup.kt | 2 +- .../dev/dres/api/rest/types/run/TeamInfo.kt | 2 +- .../dres/api/rest/types/system/CurrentTime.kt | 9 + .../dres/api/rest/types/system/DresInfo.kt | 23 + .../dev/dres/api/rest/types/users/ApiRole.kt | 11 + .../dres/api/rest/types/users/UserDetails.kt | 19 + .../dres/api/rest/types/users/UserRequest.kt | 11 + .../kotlin/dev/dres/data/model/admin/Role.kt | 38 +- .../kotlin/dev/dres/data/model/admin/User.kt | 28 +- .../dres/data/model/audit/AuditLogEntry.kt | 48 ++ .../dres/data/model/audit/AuditLogSource.kt | 24 + .../dev/dres/data/model/audit/AuditLogType.kt | 33 ++ .../model/basics/media/MediaCollection.kt | 13 +- .../dres/data/model/basics/media/MediaItem.kt | 4 +- .../model/basics/media/MediaItemSegment.kt | 7 +- .../dres/data/model/basics/media/MediaType.kt | 14 + .../competition/CompetitionDescription.kt | 44 +- .../dev/dres/data/model/competition/Ids.kt | 7 +- .../data/model/competition/TaskDescription.kt | 30 +- .../dres/data/model/competition/TaskGroup.kt | 24 +- .../dev/dres/data/model/competition/Team.kt | 25 - .../dres/data/model/competition/TeamGroup.kt | 67 --- .../competition/options/ConfiguredOption.kt | 2 +- .../dres/data/model/competition/team/Team.kt | 57 ++ .../model/competition/team/TeamAggregator.kt | 25 + .../competition/team/TeamAggregatorImpl.kt | 56 ++ .../data/model/competition/team/TeamGroup.kt | 39 ++ .../run/InteractiveAsynchronousCompetition.kt | 2 +- .../run/InteractiveSynchronousCompetition.kt | 2 +- .../dres/data/model/run/RunActionContext.kt | 4 +- .../dev/dres/data/model/run/interfaces/Ids.kt | 1 - .../data/serializers/TeamGroupSerializer.kt | 3 +- .../dres/data/serializers/TeamSerializer.kt | 2 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 90 ++- .../run/InteractiveAsynchronousRunManager.kt | 3 +- .../run/InteractiveSynchronousRunManager.kt | 6 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 19 +- .../dev/dres/run/audit/AuditLogEntry.kt | 85 --- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 314 ++++++++-- .../scoreboard/MaxNormalizingScoreBoard.kt | 2 +- .../dres/run/score/scoreboard/Scoreboard.kt | 2 +- .../judged/BasicJudgementValidator.kt | 2 +- .../utilities/extensions/ContextExtensions.kt | 10 + .../utilities/extensions/StringExtensions.kt | 7 - 109 files changed, 3060 insertions(+), 2258 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/handler/{ => submission}/BatchSubmissionHandler.kt (95%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{ => submission}/SubmissionHandler.kt (84%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/handler/{ => system}/LoginHandler.kt (76%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/system/CurrentTime.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/system/DresInfo.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/TeamGroup.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index a82aeb816..18cf2be56 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -4,6 +4,9 @@ import dev.dres.api.cli.Cli import dev.dres.api.cli.OpenApiCommand import dev.dres.api.rest.RestApi import dev.dres.data.model.Config +import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.AuditLogType import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.basics.media.MediaItemSegment @@ -48,6 +51,9 @@ object DRES { /* Initialize Xodus based data store. */ XdModel.registerNodes( + AuditLogSource, + AuditLogType, + AuditLogEntry, MediaType, MediaCollection, MediaItem, @@ -56,30 +62,31 @@ object DRES { val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") initMetaData(XdModel.hierarchy, store) - /* Initialize user manager. */ + /* Initialize UserManager. */ UserManager.init(store) - /* Initialize run executor. */ - RunExecutor.init(dataAccessLayer.runs) + /* Initialize RunExecutor. */ + RunExecutor.init(store) - /* Initialize audit logger */ - AuditLogger.init(dataAccessLayer.audit) + /* Initialize AuditLogger */ + AuditLogger.init(store) - /* Initialize Event Stream Processor */ - EventStreamProcessor.register(SubmissionStatisticsHandler(), ResultLogStatisticsHandler(dataAccessLayer.mediaSegmentItemIdIndex), TeamCombinationScoreHandler()) + /* Initialize EventStreamProcessor */ + EventStreamProcessor.register(SubmissionStatisticsHandler(), ResultLogStatisticsHandler(store), TeamCombinationScoreHandler()) EventStreamProcessor.init() /* Initialize Rest API. */ - RestApi.init(config, dataAccessLayer) + RestApi.init(config, store) - println("done") + println("Initialization complete!") - if(args.isNotEmpty() && args.first() == "openapi"){ + if (args.isNotEmpty() && args.first() == "openapi") { OpenApiCommand().parse(args) - }else{ - Cli.loop(dataAccessLayer, config) //blocks until quit command is given + } else { + Cli.loop(store, config) //blocks until quit command is given } + /* Stop. */ RestApi.stop() RunExecutor.stop() EventStreamProcessor.stop() diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 3b8aec4c4..7ebd8be3f 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -7,8 +7,8 @@ import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.output.CliktHelpFormatter import com.github.ajalt.clikt.output.HelpFormatter -import dev.dres.data.dbo.DataAccessLayer import dev.dres.data.model.Config +import jetbrains.exodus.database.TransientEntityStore import org.jline.builtins.Completers import org.jline.builtins.Completers.TreeCompleter.node import org.jline.reader.* @@ -20,7 +20,12 @@ import java.util.* import java.util.regex.Pattern import kotlin.system.exitProcess - +/** + * This is a singleton instance of the [Cli]. + * + * @version 1.0.0 + * @author Loris Sauter + */ object Cli { private const val PROMPT = "DRES> " @@ -28,21 +33,18 @@ object Cli { private lateinit var clikt:CliktCommand /** - * blocking call + * Blocking call that executes the CLI subsystem. + * + * @param store The [TransientEntityStore] instance used to access persistent data. + * @param config The [Config] instance with which DRES was started. */ - fun loop(dataAccessLayer: DataAccessLayer, config: Config) { + fun loop(store: TransientEntityStore, config: Config) { clikt = DRESBaseCommand().subcommands( - CompetitionCommand(dataAccessLayer.competitions, dataAccessLayer.collections, config), + CompetitionCommand(store, config), UserCommand(), - MediaCollectionCommand( - dataAccessLayer.collections, - dataAccessLayer.mediaItems, - dataAccessLayer.mediaItemPathIndex, - dataAccessLayer.mediaItemCollectionIndex, - dataAccessLayer.mediaSegments - ), - CompetitionRunCommand(dataAccessLayer.runs), + MediaCollectionCommand(store), + CompetitionRunCommand(store), OpenApiCommand(), ExecutionCommand() ) @@ -88,7 +90,7 @@ object Cli { while (true) { try { val line = lineReader.readLine(PROMPT).trim() - if (line.toLowerCase() == "exit" || line.toLowerCase() == "quit") { + if (line.toLowerCase() == "exit" || line.lowercase() == "quit") { break } if (line.toLowerCase() == "help") { @@ -105,9 +107,9 @@ object Cli { when (e) { is com.github.ajalt.clikt.core.NoSuchSubcommand -> println("command not found") is com.github.ajalt.clikt.core.PrintHelpMessage -> println(e.command.getFormattedHelp()) - is com.github.ajalt.clikt.core.UsageError -> println("invalid command") -// is com.github.ajalt.clikt.core.MissingParameter -> println(e.localizedMessage) + is com.github.ajalt.clikt.core.MissingParameter -> println(e.localizedMessage) is com.github.ajalt.clikt.core.NoSuchOption -> println(e.localizedMessage) + is com.github.ajalt.clikt.core.UsageError -> println("invalid command") else -> e.printStackTrace() } } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index 7db9bdfd7..a7b7737da 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -22,13 +22,17 @@ import dev.dres.data.model.submissions.aspects.TextAspect import dev.dres.run.* import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.toDateString +import jetbrains.exodus.database.TransientEntityStore import java.io.FileOutputStream import java.io.OutputStream import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption -class CompetitionRunCommand(internal val runs: DAO) : NoOpCliktCommand(name = "run") { +/** + * + */ +class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpCliktCommand(name = "run") { init { subcommands( diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 7e35a149d..ef959a2e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -5,19 +5,14 @@ import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.parameters.options.* -import com.github.ajalt.clikt.parameters.types.enum -import com.github.ajalt.clikt.parameters.types.long import com.jakewharton.picnic.table -import dev.dres.data.model.UID -import dev.dres.data.model.admin.PlainPassword +import dev.dres.data.model.admin.Password import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User -import dev.dres.data.model.admin.UserName -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_PASSWORD +import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_USERNAME +import dev.dres.data.model.admin.UserId import dev.dres.mgmt.admin.UserManager -import dev.dres.mgmt.admin.UserManager.MIN_LENGTH_PASSWORD -import dev.dres.mgmt.admin.UserManager.MIN_LENGTH_USERNAME -import dev.dres.utilities.extensions.UID import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOption @@ -26,40 +21,41 @@ import java.nio.file.StandardOpenOption * A collection of [CliktCommand]s for user management * * @author Ralph Gasser - * @version 1.1 + * @version 2.0.0 */ -class UserCommand : NoOpCliktCommand(name = "user") { - +sealed class UserCommand : NoOpCliktCommand(name = "user") { init { - this.subcommands(CreateUserCommand(), UpdateUserCommand(), DeleteUserCommand(), ListUsers(), ListRoles(), ExportUserCommand(), ImportUserCommand()) + this.subcommands(Create(), Update(), Delete(), List(), Roles(), Export(), Import()) } override fun aliases(): Map> { return mapOf( - "ls" to listOf("list"), - "remove" to listOf("delete"), - "rm" to listOf("delete"), - "drop" to listOf("delete"), - "add" to listOf("create") + "ls" to listOf("list"), + "remove" to listOf("delete"), + "rm" to listOf("delete"), + "drop" to listOf("delete"), + "add" to listOf("create") ) } /** * [CliktCommand] to create a new [User]. */ - inner class CreateUserCommand : CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { - private val username: UserName by option("-u", "--username", help = "Username of at least $MIN_LENGTH_USERNAME characters length") - .convert { UserName(it) } + inner class Create : CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { + /** The name of the newly created user. */ + private val username: String by option("-u", "--username", help = "Username of at least $MIN_LENGTH_USERNAME characters length. Must be unique!") .required() - .validate { require(it.name.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } + .validate { require(it.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } - private val password: PlainPassword by option("-p", "--password", help = "Password of at least $MIN_LENGTH_PASSWORD characters length") - .convert { PlainPassword(it) } + /** The [Password.Plain] of the newly created user. */ + private val password: Password.Plain by option("-p", "--password", help = "Password of at least $MIN_LENGTH_PASSWORD characters length.") + .convert { Password.Plain(it) } .required() - .validate { require(it.pass.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } + .validate { require(it.password.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } - private val role: Role by option("-r", "--role", help = "Role of the new User").enum().required() + /** The desired [Role] of the newly created user. */ + private val role: Role by option("-r", "--role", help = "Role of the new user.").convert { Role.parse(it) }.required() override fun run() { val successful = UserManager.create(username = this.username, password = this.password, role = role) @@ -76,77 +72,67 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to update an existing [User]. */ - inner class UpdateUserCommand : CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { - private val id: UID? by option("-i", "--id").convert { it.UID() } - private val username: UserName? by option("-u", "--username", help = "Username of the user to be updated") - .convert { UserName(it) } - .validate { require(it.name.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } + inner class Update : CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { + private val id: UserId? by option("-i", "--id") + + /** The new username. */ + private val username: String? by option("-u", "--username", help = "Username of the user to be updated") + .validate { require(it.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } - private val password: PlainPassword? by option("-p", "--password", help = "New Password of at least $MIN_LENGTH_PASSWORD characters length") - .convert { PlainPassword(it) } - .validate { require(it.pass.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } + /** The new [Password.Plain] of the updated user. Left unchanged if null! */ + private val password: Password.Plain? by option("-p", "--password", help = "New Password of at least $MIN_LENGTH_PASSWORD characters length") + .convert { Password.Plain(it) } + .validate { require(it.password.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } - private val role: Role? by option("-r", "--role", help = "New user Role").enum() + /** The new [Role] of the updated user. Left unchanged if null! */ + private val role: Role? by option("-r", "--role", help = "New user Role").convert { Role.parse(it) } override fun run() { - if (UserManager.id(id = this.id, username = this.username) == null) { + if (this.id == null && this.username == null) { println("You must specify a valid username or user ID in order to update a user!") return } - if (UserManager.exists(id = this.id, username = this.username)) { - val userId = UserManager.id(id = id, username = username)!! - val currentUser = UserManager.get(id = userId) - val success = UserManager.update(id = id, username = username, password = password, role = role) - if (success) { - val updatedUser = UserManager.get(id = id, username = username) - println("User ${userId.string} updated successfully (old: $currentUser, new: $updatedUser)!") - return - } + if (UserManager.update(id = this.id, username = this.username, password = this.password, role = this.role)) { + println("User updated successfully!") + return + } else { + println("User updated failed. It probably doesn't exist!") } - - println("User[id=${id?.string ?: "N/A"},username=${username - ?: "N/A"}] could not be updated because it doesn't exist!") } } /** * [CliktCommand] to delete a [User]. */ - inner class DeleteUserCommand : CliktCommand(name = "delete", help = "Deletes an existing user.", printHelpOnEmptyArgs = true) { - private val id: UID? by option("-i", "--id", help = "ID of the user to be deleted.").convert { it.UID() } - private val username: UserName? by option("-u", "--username", help = "Username of the user to be deleted.") - .convert { UserName(it) } - .validate { require(it.name.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } + inner class Delete : CliktCommand(name = "delete", help = "Deletes an existing user.", printHelpOnEmptyArgs = true) { + private val id: UserId? by option("-i", "--id", help = "ID of the user to be deleted.") + private val username: String? by option("-u", "--username", help = "Username of the user to be deleted.") + .validate { require(it.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } override fun run() { - val delId = UserManager.id(id = id, username = username) - if (delId == null) { + if (this.id == null && this.username == null) { println("You must specify a valid username or user ID in order to delete a user!") } - if (UserManager.exists(id = delId)) { - val user = UserManager.get(delId)!! // !! okay, exists checks - val success = UserManager.delete(id = id, username = username) - if (success) { - println("User $user deleted successfully!") - return - } + val success = UserManager.delete(id = this.id, username = this.username) + if (success) { + println("User deleted successfully!") + return + } else { + println("User could not be deleted because it doesn't exist!") } - println("User with ID ${delId?.string ?: "N/A"} could not be deleted because it doesn't exist!") } } /** * [CliktCommand] to export a [User]. */ - inner class ExportUserCommand : CliktCommand(name = "export", help = "Exports one or multiple user(s) as JSON.", printHelpOnEmptyArgs = true) { - private val id: UID? by option("-i", "--id", help = "ID of the user to be exported.").convert { it.UID() } - private val username: UserName? by option("-u", "--username", help = "Username of the user to be exported.") - .convert { UserName(it) } - .validate { require(it.name.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } + inner class Export : CliktCommand(name = "export", help = "Exports one or multiple user(s) as JSON.", printHelpOnEmptyArgs = true) { + private val id: UserId? by option("-i", "--id", help = "ID of the user to be exported.") + private val username: String? by option("-u", "--username", help = "Username of the user to be exported.") + .validate { require(it.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } private val path: String by option("-o", "--output").required() override fun run() { - val id = UserManager.id(id = id, username = username) - if (id == null) { + if (this.id == null && this.username == null) { val users = UserManager.list() val path = Paths.get(this.path) val mapper = ObjectMapper() @@ -156,16 +142,16 @@ class UserCommand : NoOpCliktCommand(name = "user") { println("Successfully wrote ${users.size} users to $path.") return } else { - val user = UserManager.get(id) + val user = UserManager.get(id = this.id, username = this.username) if (user != null) { val path = Paths.get(this.path) val mapper = ObjectMapper() Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { mapper.writeValue(it, user) } - println("Successfully wrote user ${user.id.string} to $path.") + println("Successfully wrote user ${user.id} to $path.") } else { - println("User with ID ${id.string} does not exist.") + println("User with ID ${id} does not exist.") } } } @@ -174,7 +160,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * Imports a specific competition from JSON. */ - inner class ImportUserCommand : CliktCommand(name = "import", help = "Imports a user description from JSON. Either a single user or an array of users", printHelpOnEmptyArgs = true) { + inner class Import: CliktCommand(name = "import", help = "Imports a user description from JSON. Either a single user or an array of users", printHelpOnEmptyArgs = true) { private val new: Boolean by option("-n", "--new", help = "Flag indicating whether users should be created anew.").flag("-u", "--update", default = true) @@ -196,9 +182,9 @@ class UserCommand : NoOpCliktCommand(name = "user") { import.forEach { if (new) { - UserManager.create(it.username, it.password, it.role) + UserManager.create(it.username, it.hashedPassword(), it.role) } else { - UserManager.update(it.id, it.username, it.password, it.role) + UserManager.update(it.id, it.username, it.hashedPassword(), it.role) } } println("done") @@ -209,7 +195,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to list all [User]s. */ - inner class ListUsers : CliktCommand(name = "list", help = "Lists all Users") { + inner class List: CliktCommand(name = "list", help = "Lists all Users") { val plain by option("-p", "--plain", help = "Plain print: No fancy table. Might be easier if the output should be processed").flag(default = false) override fun run() { val users = UserManager.list() @@ -231,7 +217,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { } body { users.forEach { - row(it.id.string, it.username.name, it.role) + row(it.id, it.username, it.role) } } } @@ -243,7 +229,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to list all [Role]s. */ - inner class ListRoles : CliktCommand(name = "roles", help = "Lists all Roles") { + inner class Roles : CliktCommand(name = "roles", help = "Lists all Roles") { override fun run() { println("Available roles: ${Role.values().joinToString(", ")}") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 13f1f4fa6..e2fc1dfda 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -1,77 +1,126 @@ package dev.dres.api.rest -import dev.dres.data.model.UID +import dev.dres.api.rest.handler.users.SessionId +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.UserId import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionId import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.http.Handler +import java.util.* import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.collections.HashMap +import kotlin.concurrent.read +import kotlin.concurrent.write + +/** + * + */ +object AccessManager { + /** An internal [ConcurrentHashMap] that maps [SessionId]s to [ApiRole]s. */ + private val sessionRoleMap = HashMap>() -object AccessManager { + /** An internal [ConcurrentHashMap] that maps [SessionId]s to [UserId]s. */ + private val sessionUserMap = HashMap() + + /** Map keeping track of all [RunManager]s a specific user is eligible for. */ + private val usersToRunMap = HashMap>() + + /** A [Set] of all [SessionId]s. */ + val currentSessions: Set + get() = this.locks.read { Collections.unmodifiableSet(this.sessionUserMap.keys) } - fun manage(handler: Handler, ctx: Context, permittedRoles: Set) { + /** A [ReentrantReadWriteLock] that mediates access to maps. */ + private val locks = ReentrantReadWriteLock() + + /** + * Helper function that manages access for requests through the Javalin RESTful API. + * + * @param handler + * @param ctx + * @param permittedRoles + */ + fun manage(handler: Handler, ctx: Context, permittedRoles: Set) = this.locks.read { when { permittedRoles.isEmpty() -> handler.handle(ctx) //fallback in case no roles are set, none are required - permittedRoles.contains(RestApiRole.ANYONE) -> handler.handle(ctx) + permittedRoles.contains(ApiRole.ANYONE) -> handler.handle(ctx) rolesOfSession(ctx.sessionId()).any { it in permittedRoles } -> handler.handle(ctx) else -> ctx.status(401) } } - private val sessionRoleMap = ConcurrentHashMap>() - private val sessionUserMap = ConcurrentHashMap() - - /** Map keeping track of all [RunManager]s a specific user is eligible for. */ - private val usersToRunMap = ConcurrentHashMap>() - - val currentSessions: Set - get() = sessionUserMap.keys - - fun setUserForSession(sessionId: String, user: User){ - - if (!sessionRoleMap.containsKey(sessionId)){ - sessionRoleMap[sessionId] = mutableSetOf() + /** + * Registers a [User] for a given [SessionId]. Usually happens upon login. + * + * @param sessionId The [SessionId] to register the [User] for. + * @param user The [User] to register. + */ + fun registerUserForSession(sessionId: String, user: User) = this.locks.write { + if (!this.sessionRoleMap.containsKey(sessionId)){ + this.sessionRoleMap[sessionId] = mutableSetOf() } - sessionRoleMap[sessionId]!!.addAll( - when(user.role) { - dev.dres.data.model.admin.Role.ADMIN -> arrayOf(RestApiRole.VIEWER, RestApiRole.PARTICIPANT, RestApiRole.JUDGE, RestApiRole.ADMIN) - dev.dres.data.model.admin.Role.JUDGE -> arrayOf(RestApiRole.VIEWER, RestApiRole.JUDGE) - dev.dres.data.model.admin.Role.PARTICIPANT -> arrayOf(RestApiRole.VIEWER, RestApiRole.PARTICIPANT) - dev.dres.data.model.admin.Role.VIEWER -> arrayOf(RestApiRole.VIEWER) - } + this.sessionUserMap[sessionId] = user.id + this.sessionRoleMap[sessionId]!!.addAll( + when(user.role) { + Role.ADMIN -> arrayOf(ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.JUDGE, ApiRole.ADMIN) + Role.JUDGE -> arrayOf(ApiRole.VIEWER, ApiRole.JUDGE) + Role.PARTICIPANT -> arrayOf(ApiRole.VIEWER, ApiRole.PARTICIPANT) + Role.VIEWER -> arrayOf(ApiRole.VIEWER) + else -> throw IllegalStateException("Role held by user is unknown!") + } ) + } - sessionUserMap[sessionId] = user.id - + /** + * Deregisters a [User] for a given [SessionId]. Usually happens upon logout. + * + * @param sessionId The [SessionId] to register the [User] for. + */ + fun deregisterUserSession(sessionId: String) = this.locks.write { + this.sessionRoleMap.remove(sessionId) + this.sessionUserMap.remove(sessionId) } - fun clearUserSession(sessionId: String){ - sessionRoleMap.remove(sessionId) - sessionUserMap.remove(sessionId) + /** + * Queries and returns the [UserId] for the given [SessionId]. + * + * @param sessionId The [SessionId] to query. + * @return [UserId] or null if no [User] is logged in. + */ + fun userIdForSession(sessionId: String): UserId? = this.locks.read { + this.sessionUserMap[sessionId] } - fun rolesOfSession(sessionId: String): Set = sessionRoleMap[sessionId] ?: emptySet() + /** + * Queries and returns the [ApiRole]s for the given [SessionId]. + * + * @param sessionId The [SessionId] to query. + * @return Set of [ApiRole] or empty set if no user is logged in. + */ + fun rolesOfSession(sessionId: String): Set = this.locks.read { + this.sessionRoleMap[sessionId] ?: emptySet() + } - fun getUserIdForSession(sessionId: String): UID? = sessionUserMap[sessionId] /** * Registers a [RunManager] for quick lookup of user ID to eligible [RunManager]. * * @param runManager The [RunManager] to register. */ - fun registerRunManager(runManager: RunManager) { + fun registerRunManager(runManager: RunManager) = this.locks.write { runManager.description.teams.flatMap { t -> t.users }.forEach { if (this.usersToRunMap.containsKey(it)) { this.usersToRunMap[it]?.add(runManager) } else { - this.usersToRunMap.put(it, mutableSetOf(runManager)) + this.usersToRunMap[it] = mutableSetOf(runManager) } } - } /** @@ -79,9 +128,9 @@ object AccessManager { * * @param runManager The [RunManager] to deregister. */ - fun deregisterRunManager(runManager: RunManager) { + fun deregisterRunManager(runManager: RunManager) = this.locks.write { /* Remove the RunManager. */ - val idsToDrop = mutableSetOf() + val idsToDrop = mutableSetOf() for ((k,v) in this.usersToRunMap) { if (v.contains(runManager)) { v.remove(runManager) @@ -98,11 +147,9 @@ object AccessManager { /** * Returns all registered [RunManager]s for the given [userId]. * - * @param userId The ID of the [User] to return [RunManager]s for. + * @param userId The [UserId] of the [User] to return [RunManager]s for. */ - fun getRunManagerForUser(userId: UID): Set { + fun getRunManagerForUser(userId: UserId): Set = this.locks.read { return this.usersToRunMap[userId] ?: emptySet() } -} - -enum class RestApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN } \ No newline at end of file +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 3ba1e8784..5ed99edea 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,8 +1,20 @@ package dev.dres.api.rest +import GetAuditLogInfoHandler import dev.dres.api.rest.handler.* +import dev.dres.api.rest.handler.audit.ListAuditLogsHandler +import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler +import dev.dres.api.rest.handler.collection.* +import dev.dres.api.rest.handler.preview.GetMediaHandler +import dev.dres.api.rest.handler.preview.MediaPreviewHandler +import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler +import dev.dres.api.rest.handler.submission.SubmissionHandler +import dev.dres.api.rest.handler.system.CurrentTimeHandler +import dev.dres.api.rest.handler.system.InfoHandler +import dev.dres.api.rest.handler.system.LoginHandler +import dev.dres.api.rest.handler.system.LogoutHandler +import dev.dres.api.rest.handler.users.* import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.dbo.DataAccessLayer import dev.dres.data.model.Config import dev.dres.run.RunExecutor import dev.dres.utilities.NamedThreadFactory @@ -12,8 +24,7 @@ import io.javalin.community.ssl.SSLPlugin import io.javalin.http.staticfiles.Location import io.javalin.openapi.plugin.OpenApiConfiguration import io.javalin.openapi.plugin.OpenApiPlugin -import io.javalin.openapi.plugin.swagger.SwaggerConfiguration -import io.javalin.openapi.plugin.swagger.SwaggerPlugin +import jetbrains.exodus.database.TransientEntityStore import org.eclipse.jetty.http.HttpCookie import org.eclipse.jetty.server.* import org.eclipse.jetty.server.session.DefaultSessionCache @@ -31,7 +42,7 @@ object RestApi { private val logMarker = MarkerFactory.getMarker("REST") private val logger = LoggerFactory.getLogger(this.javaClass) - fun init(config: Config, store: Entity) { + fun init(config: Config, store: TransientEntityStore) { val runExecutor = RunExecutor @@ -47,72 +58,51 @@ object RestApi { val apiRestHandlers = listOf( // User - LoginHandler(dataAccessLayer.audit), - LogoutHandler(dataAccessLayer.audit), + LoginHandler(), + LogoutHandler(), ListUsersHandler(), - CurrentUsersHandler(), - DeleteUsersHandler(), + ListActiveUsersHandler(), + ShowCurrentUserHandler(), + ShowCurrentSessionHandler(), CreateUsersHandler(), + DeleteUsersHandler(), UpdateUsersHandler(), - CurrentUsersSessionIdHandler(), - UserDetailsHandler(), // Must be AFTER CurrentUserHandler - ActiveSessionsHandler(dataAccessLayer.users), + UserDetailsHandler(), // Media - MediaPreviewHandler(dataAccessLayer.collections, dataAccessLayer.mediaItemCollectionNameIndex, config), - SubmissionPreviewHandler(dataAccessLayer.collections, dataAccessLayer.mediaItemCollectionNameIndex, config), - GetMediaHandler(dataAccessLayer.mediaItemCollectionUidIndex, dataAccessLayer.collectionUidIndex), + MediaPreviewHandler(store, config), + SubmissionPreviewHandler(store, config), + GetMediaHandler(store), // Collection - ListCollectionHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - ShowCollectionHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - AddCollectionHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - UpdateCollectionHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - DeleteCollectionHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - AddMediaItemHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - UpdateMediaItemHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - DeleteMediaItemHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - GetMediaItemHandler(dataAccessLayer.collections, dataAccessLayer.mediaItems), - RandomMediaItemHandler( - dataAccessLayer.collections, - dataAccessLayer.mediaItems, - dataAccessLayer.mediaItemCollectionUidIndex - ), // Must be before ListMediaItem - ResolveMediaItemListByNameHandler( - dataAccessLayer.collections, - dataAccessLayer.mediaItems, - dataAccessLayer.mediaItemCollectionNameIndex - ), // Must be before ListMediaItem - ListMediaItemHandler( - dataAccessLayer.collections, - dataAccessLayer.mediaItems, - dataAccessLayer.mediaItemCollectionNameIndex - ), + ListCollectionHandler(store), + ShowCollectionHandler(store), + AddCollectionHandler(store), + UpdateCollectionHandler(store), + DeleteCollectionHandler(store), + AddMediaItemHandler(store), + UpdateMediaItemHandler(store), + DeleteMediaItemHandler(store), + ShowCollectionHandler(store), + RandomMediaItemHandler(store), // Must be before ListMediaItem + ResolveMediaItemListByNameHandler(store), // Must be before ListMediaItem + ListMediaItemHandler(store), ListExternalItemHandler(config), // Competition - ListCompetitionHandler(dataAccessLayer.competitions), - CreateCompetitionHandler(dataAccessLayer.competitions), - UpdateCompetitionHandler(dataAccessLayer.competitions, config, dataAccessLayer.mediaItems), - GetCompetitionHandler(dataAccessLayer.competitions), - DeleteCompetitionHandler(dataAccessLayer.competitions), - ListTeamHandler(dataAccessLayer.competitions), - ListDetailedTeamHandler(dataAccessLayer.competitions), - ListTaskHandler(dataAccessLayer.competitions), + ListCompetitionHandler(store), + CreateCompetitionHandler(store), + UpdateCompetitionHandler(store, config), + GetCompetitionHandler(store), + DeleteCompetitionHandler(store), + ListTeamHandler(store), + ListDetailedTeamHandler(store), + ListTaskHandler(store), GetTeamLogoHandler(config), // Submission - SubmissionHandler( - dataAccessLayer.collections, - dataAccessLayer.mediaItemCollectionNameIndex, - dataAccessLayer.mediaSegmentItemIdIndex, - config - ), - JsonBatchSubmissionHandler( - dataAccessLayer.collections, - dataAccessLayer.mediaItemCollectionNameIndex, - dataAccessLayer.mediaSegmentItemIdIndex - ), + SubmissionHandler(store, config), + JsonBatchSubmissionHandler(store), // Log QueryLogHandler(), @@ -124,7 +114,7 @@ object RestApi { GetCompetitionRunInfoHandler(), GetCompetitionRunStateHandler(), CurrentTaskHintHandler(config), - CurrentTaskTargetHandler(config, dataAccessLayer.collections), + CurrentTaskTargetHandler(config, store), CurrentTaskInfoHandler(), SubmissionInfoHandler(), RecentSubmissionInfoHandler(), @@ -139,7 +129,7 @@ object RestApi { TeamGroupScoreHandler(), // Competition run admin - CreateCompetitionRunAdminHandler(dataAccessLayer.competitions, dataAccessLayer.collections, config), + CreateCompetitionRunAdminHandler(store, config), StartCompetitionRunAdminHandler(), NextTaskCompetitionRunAdminHandler(), PreviousTaskCompetitionRunAdminHandler(), @@ -157,30 +147,29 @@ object RestApi { UpdateRunPropertiesAdminHandler(), // Judgement - NextOpenJudgementHandler(dataAccessLayer.collections), - NextOpenVoteJudgementHandler(dataAccessLayer.collections), + NextOpenJudgementHandler(store), + NextOpenVoteJudgementHandler(store), PostJudgementHandler(), JudgementStatusHandler(), JudgementVoteHandler(), // Audit Log - GetAuditLogInfoHandler(dataAccessLayer.auditTimes), - ListAuditLogsInRangeHandler(dataAccessLayer.auditTimes, dataAccessLayer.audit), - ListAuditLogsHandler(dataAccessLayer.auditTimes, dataAccessLayer.audit), + GetAuditLogInfoHandler(store), + ListAuditLogsInRangeHandler(store), + ListAuditLogsHandler(store), // Status CurrentTimeHandler(), InfoHandler(), - AdminInfoHandler(), //API Client ListCompetitionRunClientInfoHandler(), CompetitionRunClientCurrentTaskInfoHandler(), // Downloads - DownloadHandler.CompetitionRun(dataAccessLayer.runs), - DownloadHandler.CompetitionRunScoreHandler(dataAccessLayer.runs), - DownloadHandler.CompetitionDesc(dataAccessLayer.competitions) + DownloadHandler.CompetitionRun(store), + DownloadHandler.CompetitionRunScoreHandler(store), + DownloadHandler.CompetitionDesc(store) ) javalin = Javalin.create { @@ -243,11 +232,10 @@ object RestApi { path(apiGroup.key) { apiGroup.value.forEach { handler -> path(handler.route) { - val permittedRoles = if (handler is AccessManagedRestHandler) { handler.permittedRoles.toTypedArray() } else { - arrayOf(RestApiRole.ANYONE) + arrayOf(ApiRole.ANYONE) } if (handler is GetRestHandler<*>) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt deleted file mode 100644 index 10b53e8cc..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/AbstractPreviewHandler.kt +++ /dev/null @@ -1,244 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.RestApi -import dev.dres.api.rest.RestApiRole -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.dbo.DAO -import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect -import dev.dres.run.InteractiveRunManager -import dev.dres.run.RunExecutor -import dev.dres.utilities.FFmpegUtil -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.errorResponse -import dev.dres.utilities.extensions.sendFile -import dev.dres.utilities.extensions.streamFile -import io.javalin.http.Context -import io.javalin.openapi.* -import java.io.File -import java.io.FileNotFoundException -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException - -abstract class AbstractPreviewHandler( - private val collections: DAO, - private val itemIndex: DaoIndexer>, - config: Config -) : GetRestHandler, AccessManagedRestHandler { - override val apiVersion = "v1" - override val permittedRoles = setOf(RestApiRole.VIEWER) - private val cacheLocation = Paths.get(config.cachePath + "/previews") - -// private val waitingMap = ConcurrentHashMap() -// private val timeOut = 10_000 - - protected fun handlePreviewRequest(collectionId: UID, itemName: String, time: Long?, ctx: Context) { - - val item = itemIndex[collectionId to itemName].firstOrNull() - if (item == null) { - ctx.errorResponse(404, "Media item $itemName (collection = $collectionId) not found!") - return - } - - handlePreviewRequest(item, time, ctx) - - } - - private val missingImage = this.javaClass.getResourceAsStream("/img/missing.png")!!.readAllBytes() - private val waitingImage = this.javaClass.getResourceAsStream("/img/loading.png")!!.readAllBytes() - - protected fun handlePreviewRequest(item: MediaItem, time: Long?, ctx: Context) { - - val collection = this.collections[item.collection] - ?: throw ErrorStatusException(404, "Collection ${item.collection} does not exist.", ctx) - - val basePath = File(collection.path) - - - if (item is MediaItem.ImageItem) { - //TODO scale down image if too large - ctx.header("Cache-Control", "max-age=31622400") - ctx.streamFile(File(basePath, item.location)) - return - } else if (item is MediaItem.VideoItem) { - - /* Prepare cache directory for item. */ - val cacheDir = cacheLocation.resolve("${item.collection}/${item.name}") - if (!Files.exists(cacheDir)) { - Files.createDirectories(cacheDir) - } - - /* check timestamp. */ - if (time == null) { - throw ErrorStatusException(400, "Timestamp unspecified or invalid.", ctx) - } - - - val imgPath = cacheDir.resolve("${time}.jpg") - - if (Files.exists(imgPath)) { //if file is available, send contents immediately - ctx.header("Cache-Control", "max-age=31622400") - ctx.sendFile(imgPath.toFile()) - } else { //if not, wait for it if necessary - - val future = FFmpegUtil.executeFFmpegAsync(Path.of(collection.path, item.location), time, imgPath) - - val waitTime = if (RestApi.readyThreadCount > 500) { - 3L - } else { - 1L - } - - try { - val path = future.get(waitTime, TimeUnit.SECONDS) ?: throw FileNotFoundException() - ctx.sendFile(path.toFile()) - } catch (e: TimeoutException) { - ctx.status(408) - ctx.header("Cache-Control", "max-age=30") - ctx.contentType("image/png") - ctx.result(waitingImage) - } catch (t: Throwable) { - ctx.status(429) - ctx.header("Cache-Control", "max-age=600") - ctx.contentType("image/png") - ctx.result(missingImage) - } - } - } - } - - init { - Files.createDirectories(this.cacheLocation) - } - -} - -class MediaPreviewHandler( - collections: DAO, - itemIndex: DaoIndexer>, - config: Config -) : AbstractPreviewHandler(collections, itemIndex, config) { - - @OpenApi( - summary = "Returns a preview image from a collection item", - path = "/api/v1/preview/item/{collection}/{item}/{time}", - pathParams = [ - OpenApiParam("collectionId", String::class, "Unique ID of the collection."), - OpenApiParam("item", String::class, "Name of the MediaItem"), - OpenApiParam("time", Long::class, "Time into the video in milliseconds (for videos only).") - ], - tags = ["Media"], - responses = [OpenApiResponse( - "200", - [OpenApiContent(type = "image/png")] - ), OpenApiResponse("401"), OpenApiResponse("400")], - ignore = true, - methods = [HttpMethod.GET] - ) - override fun get(ctx: Context) { - - try { - val params = ctx.pathParamMap() - - val collectionId = params["collection"]?.UID() - ?: throw ErrorStatusException(400, "Collection ID not specified or invalid.", ctx) - val itemName = params["item"] ?: throw ErrorStatusException(400, "Item name not specified.", ctx) - val time = params["time"]?.toLongOrNull() - - handlePreviewRequest(collectionId, itemName, time, ctx) - } catch (e: ErrorStatusException) { - ctx.errorResponse(e) - } - - } - - - override val route: String = "preview/item/{collection}/{item}/{time}" - - //not used - override fun doGet(ctx: Context): Any = "" - -} - - -class SubmissionPreviewHandler( - collections: DAO, - itemIndex: DaoIndexer>, - config: Config -) : AbstractPreviewHandler(collections, itemIndex, config) { - - @OpenApi( - summary = "Returns a preview image for a submission", - path = "/api/v1/preview/submission/{runId}/{submissionId}", - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("submissionId", String::class, "Subission ID") - ], - tags = ["Media"], - responses = [OpenApiResponse( - "200", - [OpenApiContent(type = "image/png")] - ), OpenApiResponse("401"), OpenApiResponse("400")], - ignore = true, - methods = [HttpMethod.GET] - ) - override fun get(ctx: Context) { - - try { - val params = ctx.pathParamMap() - - val runId = params["runId"]?.UID() - ?: throw ErrorStatusException(404, "Parameter 'runId' is invalid", ctx) - - val submissionId = params["submissionId"]?.UID() - ?: throw ErrorStatusException(404, "Parameter 'submissionId' is missing", ctx) - - val run = RunExecutor.managerForId(runId) - ?: throw ErrorStatusException(404, "Competition Run $runId not found", ctx) - - if (run !is InteractiveRunManager) { - throw ErrorStatusException(404, "Competition Run $runId is not interactive", ctx) - } - - val submission = run.allSubmissions.find { it.uid == submissionId } - ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) - - when (submission) { - is ItemAspect -> { - handlePreviewRequest( - submission.item, - if (submission is TemporalSubmissionAspect) submission.start else null, ctx - ) - } - is TextAspect -> { - ctx.header("Cache-Control", "max-age=31622400") - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) - } - else -> { - ctx.header("Cache-Control", "max-age=31622400") - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) - } - } - } catch (e: ErrorStatusException) { - ctx.errorResponse(e) - } - - } - - override val route: String = "preview/submission/{runId}/{submissionId}" - - //not used - override fun doGet(ctx: Context): Any = "" - -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt deleted file mode 100644 index 09a4e068a..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/AuditLogHandler.kt +++ /dev/null @@ -1,174 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.RestApiRole -import dev.dres.api.rest.types.audit.AuditLogInfo -import dev.dres.api.rest.types.audit.RestAuditLogEntry -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.dbo.DAO -import dev.dres.data.dbo.NumericDaoIndexer -import dev.dres.run.audit.AuditLogEntry -import dev.dres.utilities.extensions.toPathParamKey -import io.javalin.http.Context -import io.javalin.openapi.* -import io.javalin.security.RouteRole - -abstract class AuditLogHandler(val auditTimes: NumericDaoIndexer) : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(RestApiRole.ADMIN) - override val apiVersion = "v1" - -} - -class GetAuditLogInfoHandler(auditTimes: NumericDaoIndexer) : AuditLogHandler(auditTimes), GetRestHandler { - - override val route = "audit/info" - - @OpenApi( - summary = "Gives information about the audit log. Namely size and latest timestamp of known audit logs", - path = "/api/v1/audit/info", - tags = ["Audit"], - responses = [ - OpenApiResponse("200", [OpenApiContent(AuditLogInfo::class)], description = "The audit log info"), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): AuditLogInfo { - return AuditLogInfo(size = auditTimes.index.size, latest = auditTimes.index.keys.maxOrNull() ?: 0L) - } -} - - -class ListAuditLogsInRangeHandler(auditTimes: NumericDaoIndexer, val audit: DAO): AuditLogHandler(auditTimes), GetRestHandler>{ - - override val route = "audit/log/list/since/{since}/{upto}" - - @OpenApi( - summary = "Lists all audit logs matching the query", - path = "/api/v1/audit/log/list/since/{since}/{upto}", - pathParams = [ - OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include"), - OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.") - ], - tags = ["Audit"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): Array { - var settings = 0 - val since = ctx.pathParam("{since}").let { - if(it.isNotBlank()){ - try{ - return@let it.toLong() - }catch(e: NumberFormatException){ - settings = 1 - return@let 0L - } - }else{ - settings = 1 - return@let 0L - } - } - val upto = ctx.pathParam("{upto}").let{ - if(it.isNotBlank()){ - try{ - return@let it.toLong() - }catch(e: NumberFormatException){ - settings += 2 - return@let Long.MAX_VALUE - } - }else{ - settings += 2 - return@let Long.MAX_VALUE - } - } - return when(settings){ - 0 -> auditTimes.inRange(since, upto) - 1 -> auditTimes.atMost(upto) - 2 -> auditTimes.atLeast(since) - else -> audit - }.map { RestAuditLogEntry.convert(it) }.toTypedArray() - - } - -} - -class ListAuditLogsHandler(auditTimes: NumericDaoIndexer, val audit: DAO) : AuditLogHandler(auditTimes), GetRestHandler> { - - override val route = "audit/log/list/limit/${LIMIT_PARAM.toPathParamKey()}/${PAGE_INDEX_PARAM.toPathParamKey()}" - - companion object { - const val LIMIT_PARAM = "limit" - const val PAGE_INDEX_PARAM = "page" - const val DEFAULT_LIMIT = 500 - - const val TYPE_PARAM = "type" // Coupled to 1047, not yet ready - const val RELATION_PARAM = "when" - const val BEFORE_PARAM_KEY = "before" - const val UPTO_PARAM_KEY = "upto" - /* - // See https://github.com/tipsy/javalin/issues/1047 - we currently are behind - enum class TemporalRelation{ - BEFORE, - UPTO - }*/ - } - - @OpenApi( - summary = "Lists all audit logs matching the query.", - path = "/api/v1/audit/log/list/limit/{limit}/{page}", - pathParams = [ - OpenApiParam(LIMIT_PARAM, Int::class, "The maximum number of results. Default: 500"), - OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.") - ], - tags = ["Audit"], - responses = [ -// OpenApiResponse("200", [OpenApiContent(AuditLogPage::class)], description = "The audit logs"), - OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): Array { - val limit = getLimitFromParams(ctx) - val index = getIndexFromParams(ctx) - return auditTimes.index.toSortedMap { o1, o2 -> o2.compareTo(o1) }.values.flatten() - .drop(index * limit) - .take(limit) - .map { RestAuditLogEntry.convert(this.audit[it]!!) }.toTypedArray() - } - - private fun getLimitFromParams(ctx:Context): Int { - return ctx.pathParam(LIMIT_PARAM).let outer@ { l -> - try { - return@outer l.toInt().let inner@ { i -> - return@inner if (i <= 0) { - DEFAULT_LIMIT - } else { - i - } - } - } catch (e: NumberFormatException) { - return@outer DEFAULT_LIMIT - } - } - } - - private fun getIndexFromParams(ctx:Context):Int{ - return ctx.pathParam(PAGE_INDEX_PARAM).let outer@ {i -> - try{ - return@outer i.toInt().let inner@ { - return@inner if(it <= 0){ - 0 - }else{ - it - } - } - } catch(e: NumberFormatException){ - return@outer 0 - } - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt deleted file mode 100644 index 309b69969..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CollectionHandler.kt +++ /dev/null @@ -1,551 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.RestApiRole -import dev.dres.api.rest.types.collection.RestFullMediaCollection -import dev.dres.api.rest.types.collection.RestMediaCollection -import dev.dres.api.rest.types.collection.RestMediaItem -import dev.dres.api.rest.types.collection.RestMediaItemType -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.dbo.DAO -import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.cleanPathString -import io.javalin.http.BadRequestResponse -import io.javalin.http.Context -import io.javalin.openapi.* -import io.javalin.security.RouteRole -import java.nio.file.FileVisitOption -import java.nio.file.Files -import java.nio.file.Paths -import kotlin.random.Random -import kotlin.streams.toList - -abstract class CollectionHandler( - protected val collections: DAO, - protected val items: DAO -) : RestHandler, AccessManagedRestHandler { - - override val permittedRoles: Set = setOf(RestApiRole.ADMIN) - override val apiVersion = "v1" - - private fun collectionId(ctx: Context): UID = - ctx.pathParamMap().getOrElse("collectionId") { - throw ErrorStatusException(404, "Parameter 'collectionId' is missing!'", ctx) - }.UID() - - private fun collectionById(id: UID, ctx: Context): MediaCollection = - collections[id] ?: throw ErrorStatusException( - 404, - "Collection with ID $id not found.'", - ctx - ) - - protected fun collectionFromContext(ctx: Context): MediaCollection = - collectionById(collectionId(ctx), ctx) - -} - -class ListCollectionHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), GetRestHandler> { - - @OpenApi( - summary = "Lists all available media collections with basic information about their content.", - path = "/api/v1/collection/list", - tags = ["Collection"], - methods = [HttpMethod.GET], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context) = - this.collections.map { RestMediaCollection.fromMediaCollection(it) } - - override val route: String = "collection/list" -} - -class AddCollectionHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), PostRestHandler { - - @OpenApi( - summary = "Adds a new media collection", - path = "/api/v1/collection", - tags = ["Collection"], - methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - - val restCollection = try { - ctx.bodyAsClass(RestMediaCollection::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - if (restCollection.basePath == null) { - throw ErrorStatusException( - 400, - "Invalid parameters, collection base path not set.", - ctx - ) - } - - if (collections.find { it.name == restCollection.name } != null) { - throw ErrorStatusException( - 400, - "Invalid parameters, collection with name ${restCollection.name} already exists.", - ctx - ) - } - - val collection = MediaCollection( - UID.EMPTY, - restCollection.name, - restCollection.description, - restCollection.basePath.cleanPathString() - ) - collections.append(collection) - - return SuccessStatus("Collection added") - - } - - override val route: String = "collection" - -} - -class DeleteCollectionHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), DeleteRestHandler { - - @OpenApi( - summary = "Deletes a media collection", - path = "/api/v1/collection/{collectionId}", - pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], - tags = ["Collection"], - methods = [HttpMethod.DELETE], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doDelete(ctx: Context): SuccessStatus { - val collection = collectionFromContext(ctx) - - collections.delete(collection) - - return SuccessStatus("Collection ${collection.id.string} deleted") - } - - override val route: String = "collection/{collectionId}" - -} - -class UpdateCollectionHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), PatchRestHandler { - - @OpenApi( - summary = "Updates a media collection", - path = "/api/v1/collection", - tags = ["Collection"], - methods = [HttpMethod.PATCH], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPatch(ctx: Context): SuccessStatus { - - val restCollection = try { - ctx.bodyAsClass(RestMediaCollection::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val collection = collections[restCollection.id.UID()] - ?: throw ErrorStatusException( - 400, - "Invalid parameters, collection with ID ${restCollection.id} does not exist.", - ctx - ) - - val updatedCollection = MediaCollection( - collection.id, - restCollection.name.trim(), - restCollection.description ?: collection.description, - restCollection.basePath?.cleanPathString() ?: collection.basePath - ) - collections.update(updatedCollection) - - return SuccessStatus("Collection updated") - - } - - override val route: String = "collection" - -} - -class ShowCollectionHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), GetRestHandler { - - @OpenApi( - summary = "Shows the content of the specified media collection.", - path = "/api/v1/collection/{collectionId}", - pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(RestFullMediaCollection::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): RestFullMediaCollection { - val collection = collectionFromContext(ctx) //also checks if collection exists - val items = - items.filter { it.collection == collection.id }.map { RestMediaItem.fromMediaItem(it) } - return RestFullMediaCollection(RestMediaCollection.fromMediaCollection(collection), items) - } - - override val route: String = "collection/{collectionId}" -} - -class AddMediaItemHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), PostRestHandler { - - @OpenApi( - summary = "Adds a Media Item to the specified Media Collection.", - path = "/api/v1/mediaItem", - methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaItem::class)]), - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - - val mediaItem = try { - ctx.bodyAsClass(RestMediaItem::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val collectionId = mediaItem.collectionId.UID() - val existing = - items.find { it.collection == collectionId && it.name.trim() == mediaItem.name.trim() } - if (existing != null) { - throw ErrorStatusException( - 400, - "item with name '${mediaItem.name}' already exists in collection: $existing", - ctx - ) - } - - if (mediaItem.type == RestMediaItemType.VIDEO) { - if (mediaItem.durationMs == null) { - throw ErrorStatusException(400, "Duration needs to be set for a video item", ctx) - } - if (mediaItem.fps == null) { - throw ErrorStatusException(400, "Frame rate needs to be set for a video item", ctx) - } - } - - items.append(mediaItem.toMediaItem()) - return SuccessStatus("Media Item added") - - } - - override val route: String = "mediaItem" -} - -class UpdateMediaItemHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), PatchRestHandler { - - @OpenApi( - summary = "Updates a Media Item to the specified Media Collection.", - path = "/api/v1/mediaItem", - methods = [HttpMethod.PATCH], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaItem::class)]), - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPatch(ctx: Context): SuccessStatus { - - val mediaItem = try { - ctx.bodyAsClass(RestMediaItem::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - items[mediaItem.id.UID()] ?: throw ErrorStatusException( - 400, - "item with name '${mediaItem.name}' does not exists", - ctx - ) - - - if (mediaItem.type == RestMediaItemType.VIDEO) { - if (mediaItem.durationMs == null) { - throw ErrorStatusException(400, "Duration needs to be set for a video item", ctx) - } - if (mediaItem.fps == null) { - throw ErrorStatusException(400, "Frame rate needs to be set for a video item", ctx) - } - } - - items.update(mediaItem.toMediaItem()) - return SuccessStatus("Media Item updated") - - } - - override val route: String = "mediaItem" -} - -class GetMediaItemHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), GetRestHandler { - - @OpenApi( - summary = "Selects and returns a specific media item.", - path = "/api/v1/mediaItem/{mediaId}", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("mediaId", String::class, "Media item ID") - ], - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(RestMediaItem::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): RestMediaItem { - val mediaId = ctx.pathParamMap().getOrElse("mediaId") { - throw ErrorStatusException(404, "Parameter 'mediaId' is missing!'", ctx) - }.UID() - val item = this.items[mediaId] ?: throw ErrorStatusException( - 404, - "Media item with ID $mediaId not found.'", - ctx - ) - - return RestMediaItem.fromMediaItem(item) - } - - override val route: String = "mediaItem/{mediaId}" -} - -class DeleteMediaItemHandler(collections: DAO, items: DAO) : - CollectionHandler(collections, items), DeleteRestHandler { - - @OpenApi( - summary = "Selects and returns a specific media item.", - path = "/api/v1/mediaItem/{mediaId}", - methods = [HttpMethod.DELETE], - pathParams = [ - OpenApiParam("mediaId", String::class, "Media item ID") - ], - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doDelete(ctx: Context): SuccessStatus { - val mediaId = ctx.pathParamMap().getOrElse("mediaId") { - throw ErrorStatusException(404, "Parameter 'mediaId' is missing!'", ctx) - }.UID() - val item = this.items[mediaId] ?: throw ErrorStatusException( - 404, - "Media item with ID $mediaId not found.'", - ctx - ) - - this.items.delete(item) - - return SuccessStatus("Item deleted") - } - - override val route: String = "mediaItem/{mediaId}" -} - -class ResolveMediaItemListByNameHandler( - collections: DAO, - items: DAO, - private val mediaItemCollectionNameIndex: DaoIndexer> -) : CollectionHandler(collections, items), PostRestHandler> { - override val route = "collection/{collectionId}/resolve" - - @OpenApi( - summary = "Resolves a list of media item names to media items", - path = "/api/v1/collection/{collectionId}/resolve", - methods = [HttpMethod.POST], - pathParams = [ - OpenApiParam("collectionId", String::class, "Collection ID") - ], - requestBody = OpenApiRequestBody([OpenApiContent(Array::class)]), - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): List { - val queriedNames = try { - ctx.bodyAsClass(Array::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException( - 400, - "Invalid parameters: Expected Array - This is a programmers error!", - ctx - ) - } - val collection = collectionFromContext(ctx) - val names = mediaItemCollectionNameIndex.keys().filter { it.first == collection.id } - return if (!queriedNames.isEmpty()) { - names.filter { queriedNames.contains(it.second) } - .map { RestMediaItem.fromMediaItem(mediaItemCollectionNameIndex[it].first()) } - } else { - emptyList() - } - } - -} - -class ListMediaItemHandler( - collections: DAO, - items: DAO, - private val mediaItemCollectionNameIndex: DaoIndexer> -) : CollectionHandler(collections, items), GetRestHandler> { - @OpenApi( - summary = "Lists media items from a given media collection whose name start with the given string.", - path = "/api/v1/collection/{collectionId}/{startsWith}", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("collectionId", String::class, "Collection ID"), - OpenApiParam("startsWith", String::class, "Name starts with", required = false) - ], - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): List { - val collection = collectionFromContext(ctx) - val startsWith = ctx.pathParamMap()["startsWith"] - val names = mediaItemCollectionNameIndex.keys().filter { it.first == collection.id } - - return if (!startsWith.isNullOrBlank()) { - names.filter { - it.second.startsWith(startsWith) - }.take(50).map { RestMediaItem.fromMediaItem(mediaItemCollectionNameIndex[it].first()) } - } else { - names.take(50) - .map { RestMediaItem.fromMediaItem(mediaItemCollectionNameIndex[it].first()) } - } - } - - override val route: String = "collection/{collectionId}/{startsWith}" -} - -class RandomMediaItemHandler( - collections: DAO, - items: DAO, - private val mediaItemCollectionUidIndex: DaoIndexer> -) : CollectionHandler(collections, items), GetRestHandler { - - private val rand = - Random(System.currentTimeMillis()) // TODO Decide upon seed -- time based or fixed? - - @OpenApi( - summary = "Selects and returns a random media item from a given media collection.", - path = "/api/v1/collection/{collectionId}/random", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("collectionId", String::class, "Collection ID") - ], - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(RestMediaItem::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): RestMediaItem { - val collection = collectionFromContext(ctx) - val ids = mediaItemCollectionUidIndex.keys().filter { it.first == collection.id } - val itemId = ids[rand.nextInt(ids.size)].second - return RestMediaItem.fromMediaItem(items[itemId]!!) - } - - override val route: String = "collection/{collectionId}/random" -} - -/** - * Lists and returns the media items in the external media item directory. - */ -class ListExternalItemHandler(config: Config) : GetRestHandler> { - - override val apiVersion = "v1" - - /** Path to the directory that contains the external items. */ - val path = Paths.get(config.externalPath) - - init { - /* Check if directory exists and create it, if it doesn't. */ - if (!Files.exists(this.path)) { - Files.createDirectories(this.path) - } - } - - @OpenApi( - summary = "Lists items from the external media collection whose name start with the given string.", - path = "/api/v1/external/", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("startsWith", String::class, "Name starts with.", required = false) - ], - tags = ["Collection"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): Array { - val startsWith = ctx.pathParamMap()["startsWith"] ?: "" - val list = Files.walk(this.path, 1, FileVisitOption.FOLLOW_LINKS).filter { - Files.isRegularFile(it) && it.fileName.toString().startsWith(startsWith) - }.limit(50).map { it.toString() }.toList() - return list.toTypedArray() - } - - override val route: String = "external/" -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt index 16ad10b06..e4679cfdb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.handler -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.competition.RestCompetitionDescription import dev.dres.api.rest.types.competition.RestDetailedTeam import dev.dres.api.rest.types.competition.RestTaskDescription @@ -13,7 +13,7 @@ import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.errorResponse import io.javalin.http.BadRequestResponse @@ -25,7 +25,7 @@ import java.nio.file.Files abstract class CompetitionHandler(protected val competitions: DAO) : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(RestApiRole.ADMIN) + override val permittedRoles: Set = setOf(ApiRole.ADMIN) override val apiVersion = "v1" private fun competitionId(ctx: Context): UID = diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index b23fb9419..4b9e8ecb9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -1,16 +1,16 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.collection.RestMediaItem import dev.dres.api.rest.types.competition.CompetitionStartMessage import dev.dres.api.rest.types.run.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.UID +import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.run.InteractiveSynchronousCompetition @@ -23,7 +23,6 @@ import dev.dres.data.model.submissions.aspects.TextAspect import dev.dres.mgmt.admin.UserManager import dev.dres.run.* import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId @@ -38,8 +37,8 @@ import java.io.File abstract class AbstractCompetitionRunAdminRestHandler( override val permittedRoles: Set = setOf( - RestApiRole.ADMIN, - RestApiRole.PARTICIPANT + ApiRole.ADMIN, + ApiRole.PARTICIPANT ) ) : AccessManagedRestHandler { @@ -78,7 +77,7 @@ abstract class AbstractCompetitionRunAdminRestHandler( return } - if (!AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN)) { + if (!AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { throw ErrorStatusException(403, "Access Denied.", ctx); } @@ -92,7 +91,7 @@ class CreateCompetitionRunAdminHandler( private val competitions: DAO, private val collections: DAO, config: Config -) : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), +) : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { private val cacheLocation = File(config.cachePath + "/tasks") @@ -197,7 +196,7 @@ class CreateCompetitionRunAdminHandler( /** * REST handler to start a [InteractiveSynchronousCompetition]. */ -class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), +class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/start" @@ -222,7 +221,7 @@ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(s try { run.start(rac) - AuditLogger.competitionStart(run.id, run.description, LogEventSource.REST, ctx.sessionId()) + AuditLogger.competitionStart(run.id, run.description, AuditLogSource.REST, ctx.sessionId()) return SuccessStatus("Run $runId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -264,7 +263,7 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle val rac = runActionContext(ctx, run) if (run is InteractiveAsynchronousRunManager - && !AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN) + && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) && run.currentTask(rac)?.status != TaskRunStatus.ENDED) { throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) } @@ -300,7 +299,7 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle /** * REST handler to move to the next task in a [InteractiveSynchronousCompetition]. */ -class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), +class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/task/switch/{idx}" @@ -359,7 +358,7 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand * REST handler to move to the previous task in a [InteractiveSynchronousCompetition]. */ class PreviousTaskCompetitionRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), + AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/task/previous" @@ -435,13 +434,7 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl val rac = runActionContext(ctx, run) try { run.startTask(rac) - AuditLogger.taskStart( - run.id, - run.currentTask(rac)!!.uid, - run.currentTaskDescription(rac), - LogEventSource.REST, - ctx.sessionId() - ) + AuditLogger.taskStart(run.id, run.currentTask(rac)!!.uid, run.currentTaskDescription(rac), AuditLogSource.REST, ctx.sessionId()) return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -458,7 +451,7 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl /** * REST handler to abort the current task in a [InteractiveSynchronousCompetition]. */ -class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), +class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/task/abort" @@ -481,7 +474,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl try { val task = run.currentTaskDescription(rac) run.abortTask(rac) - AuditLogger.taskEnd(run.id, task.id, task, LogEventSource.REST, ctx.sessionId()) + AuditLogger.taskEnd(run.id, task.id, AuditLogSource.REST, ctx.sessionId()) return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully aborted.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -499,7 +492,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl * REST handler to terminate a [InteractiveSynchronousCompetition]. */ class TerminateCompetitionRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), + AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/terminate" @@ -521,7 +514,7 @@ class TerminateCompetitionRunAdminHandler : val rac = runActionContext(ctx, run) try { run.end(rac) - AuditLogger.competitionEnd(run.id, LogEventSource.REST, ctx.sessionId()) + AuditLogger.competitionEnd(run.id, AuditLogSource.REST, ctx.sessionId()) return SuccessStatus("Run $runId was successfully terminated.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -539,7 +532,7 @@ class TerminateCompetitionRunAdminHandler : * REST handler to adjust a [InteractiveSynchronousCompetition.Task]'s duration. */ class AdjustDurationRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), + AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/adjust/{duration}" @@ -570,9 +563,9 @@ class AdjustDurationRunAdminHandler : run.adjustDuration(rac, duration) AuditLogger.taskModified( run.id, - run.currentTaskDescription(rac).name, + run.currentTaskDescription(rac).id, "Task duration adjusted by ${duration}s.", - LogEventSource.REST, + AuditLogSource.REST, ctx.sessionId() ) return SuccessStatus("Duration for run $runId was successfully adjusted.") @@ -598,7 +591,7 @@ class AdjustDurationRunAdminHandler : * */ class ListPastTasksPerTaskRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), + AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), GetRestHandler> { override val route: String = "run/admin/{runId}/task/past/list" @@ -641,7 +634,7 @@ class ListPastTasksPerTaskRunAdminHandler : * */ class ListSubmissionsPerTaskRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), + AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), GetRestHandler> { override val route: String = "run/admin/{runId}/submission/list/{taskId}" @@ -683,7 +676,7 @@ class ListSubmissionsPerTaskRunAdminHandler : teamId = sub.teamId.string, teamName = teams[sub.teamId]?.name, memberId = sub.memberId.string, - memberName = UserManager.get(sub.memberId)?.username?.name, + memberName = UserManager.get(sub.memberId)?.username, status = sub.status, timestamp = sub.timestamp, item = if (sub is ItemAspect) RestMediaItem.fromMediaItem(sub.item) else null, @@ -701,7 +694,7 @@ class ListSubmissionsPerTaskRunAdminHandler : * */ class OverwriteSubmissionStatusRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), + AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PatchRestHandler { override val route: String = "run/admin/{runId}/submission/override" @@ -751,13 +744,7 @@ class OverwriteSubmissionStatusRunAdminHandler : } if (run.updateSubmission(rac, submissionId, status)) { val submission = run.allSubmissions.single { it.uid == submissionId } - AuditLogger.overrideSubmission( - runId, - submissionId, - submission.status, - LogEventSource.REST, - ctx.sessionId() - ) + AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionId()) return SubmissionInfo(submission) } else { throw ErrorStatusException( @@ -772,7 +759,7 @@ class OverwriteSubmissionStatusRunAdminHandler : /** * REST handler to list all viewers for a [InteractiveSynchronousCompetition]. */ -class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), +class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), GetRestHandler> { override val route: String = "run/admin/{runId}/viewer/list" @@ -802,7 +789,7 @@ class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf( /** * REST handler to force the viewer state of a viewer instance registered for a [RunManager]. */ -class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), +class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { override val route: String = "run/admin/{runId}/viewer/list/{viewerId}/force" @@ -845,7 +832,7 @@ class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf( } } -class OverviewRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), GetRestHandler { +class OverviewRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), GetRestHandler { override val route = "run/admin/{runId}/overview" @OpenApi( @@ -874,7 +861,7 @@ class OverviewRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(Res } -class UpdateRunPropertiesAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(RestApiRole.ADMIN)), PatchRestHandler { +class UpdateRunPropertiesAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PatchRestHandler { override val route = "run/admin/{runId}/properties" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt index 2a912a3c3..fb03cf448 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.UID @@ -15,13 +15,13 @@ import io.javalin.openapi.* abstract class AbstractCompetitionRunClientInfoHandler : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(RestApiRole.VIEWER) + override val permittedRoles: Set = setOf(ApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId())!! - fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains( - RestApiRole.ADMIN) + fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains( + ApiRole.ADMIN) fun getRelevantManagers(ctx: Context): List { val userId = userId(ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 19e0fa0bb..0bfe9d53e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.run.RunInfo import dev.dres.api.rest.types.run.RunState import dev.dres.api.rest.types.run.SubmissionInfo @@ -31,19 +31,19 @@ import java.io.IOException abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(RestApiRole.VIEWER) + override val permittedRoles: Set = setOf(ApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId())!! private fun isJudge(ctx: Context): Boolean { val roles = AccessManager.rolesOfSession(ctx.sessionId()) - return roles.contains(RestApiRole.JUDGE) && !roles.contains(RestApiRole.ADMIN) + return roles.contains(ApiRole.JUDGE) && !roles.contains(ApiRole.ADMIN) } fun isParticipant(ctx: Context): Boolean { val roles = AccessManager.rolesOfSession(ctx.sessionId()) - return roles.contains(RestApiRole.PARTICIPANT) && !roles.contains(RestApiRole.ADMIN) + return roles.contains(ApiRole.PARTICIPANT) && !roles.contains(ApiRole.ADMIN) } fun getRelevantManagers(ctx: Context): List { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 2a77a0c4f..fef109358 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.UID @@ -26,17 +26,17 @@ import io.javalin.openapi.* */ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(RestApiRole.VIEWER) + override val permittedRoles: Set = setOf(ApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId())!! /** - * Checks if the current session has the [RestApiRole.PARTICIPANT]. + * Checks if the current session has the [ApiRole.PARTICIPANT]. * * @param ctx The [Context] to check. */ - fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN) + fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) fun getRun(ctx: Context, runId: UID): InteractiveRunManager? { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt index 67877a72a..02514527e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.dbo.DAO @@ -25,7 +25,7 @@ sealed class DownloadHandler : AccessManagedRestHandler { override val apiVersion = "v1" /** The roles permitted to access the [DownloadHandler]. */ - override val permittedRoles: Set = setOf(RestApiRole.ADMIN, RestApiRole.PARTICIPANT) + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) /** * REST handler to download the competition run information. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt deleted file mode 100644 index af019176c..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/GetMediaHandler.kt +++ /dev/null @@ -1,74 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.RestApiRole -import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.errorResponse -import dev.dres.utilities.extensions.streamFile -import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiParam -import io.javalin.openapi.OpenApiResponse -import java.io.File - -class GetMediaHandler(private val itemCache: DaoIndexer>, private val collectionCache : DaoIndexer) : GetRestHandler, AccessManagedRestHandler { - - override val permittedRoles = setOf(RestApiRole.VIEWER) - override val route: String = "media/{collection}/{item}" - override val apiVersion = "v1" - - //not used - override fun doGet(ctx: Context): Any = "" - - @OpenApi(summary = "Returns a collection item", - path = "/api/v1/media/{collection}/{item}", - pathParams = [ - OpenApiParam("collection", String::class, "Collection id"), - OpenApiParam("item", String::class, "MediaItem id") - ], - tags = ["Media"], - responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], - ignore = true, - methods = [HttpMethod.GET] - ) - override fun get(ctx: Context) { - - val params = ctx.pathParamMap() - - if (!params.containsKey("collection") || !params.containsKey("item")) { - ctx.errorResponse(400, "missing parameters") - return - } - - val collectionUid = params["collection"]!!.UID() - val collection = collectionCache[collectionUid].firstOrNull() //collections.find { it.name == collectionName } - - if (collection == null) { - ctx.errorResponse(404, "collection not found") - return - } - - val itemUid = params["item"]!!.UID() - val item = itemCache[collection.id to itemUid].firstOrNull()//items.find { it.collection == collection.id && it.name == itemName } - - if (item == null) { - ctx.errorResponse(404, "item with name $itemUid found") - return - } - - val basePath = File(collection.path) - val itemFile = File(basePath, item.location) - - try{ - ctx.streamFile(itemFile) - } catch (e: org.eclipse.jetty.io.EofException) { - //is triggered by a client abruptly stopping playback, can be safely ignored - } - - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt index 450f4bc02..9db5672ba 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt @@ -1,13 +1,13 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.dbo.DAO +import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem import dev.dres.data.model.basics.media.PlayableMediaItem import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.ItemAspect @@ -16,7 +16,6 @@ import dev.dres.data.model.submissions.aspects.TextAspect import dev.dres.run.RunExecutor import dev.dres.run.RunManager import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.VoteValidator import dev.dres.utilities.extensions.UID @@ -27,7 +26,7 @@ import io.javalin.http.Context import io.javalin.openapi.* abstract class AbstractJudgementHandler : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(RestApiRole.JUDGE) + override val permittedRoles: Set = setOf(ApiRole.JUDGE) override val apiVersion = "v1" protected fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { @@ -37,12 +36,12 @@ abstract class AbstractJudgementHandler : RestHandler, AccessManagedRestHandler companion object { fun checkRunManagerAccess(ctx: Context, runManager: RunManager) { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException( + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException( 403, "No valid user.", ctx ) - if (AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN)) { + if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { return //Admins require no further check } if (userId !in runManager.description.judges) { @@ -151,20 +150,19 @@ class PostJudgementHandler : AbstractJudgementHandler(), PostRestHandler>, AccessManagedRestHandler { - override val permittedRoles = setOf(RestApiRole.VIEWER) + override val permittedRoles = setOf(ApiRole.VIEWER) override val route = "run/{runId}/judge/status" override val apiVersion = "v1" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt index b821e3b20..dfaa4f5a9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -43,7 +43,7 @@ abstract class LogHandler : PostRestHandler, AccessManagedRestHan } class QueryLogHandler : LogHandler() { - override val permittedRoles: Set = setOf(RestApiRole.ADMIN, RestApiRole.PARTICIPANT) + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) override val route = "log/query" @OpenApi(summary = "Accepts query logs from participants", @@ -61,7 +61,7 @@ class QueryLogHandler : LogHandler() { ) override fun doPost(ctx: Context): SuccessStatus { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val run = getActiveRun(userId, ctx) @@ -80,7 +80,7 @@ class QueryLogHandler : LogHandler() { } class ResultLogHandler : LogHandler() { - override val permittedRoles: Set = setOf(RestApiRole.ADMIN, RestApiRole.PARTICIPANT) + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) override val route = "log/result" @OpenApi(summary = "Accepts result logs from participants", @@ -98,7 +98,7 @@ class ResultLogHandler : LogHandler() { ) override fun doPost(ctx: Context): SuccessStatus { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val run = getActiveRun(userId, ctx) val queryResultLog = try { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt deleted file mode 100644 index a4ae6e3da..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt +++ /dev/null @@ -1,38 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.dbo.DAO -import dev.dres.run.audit.AuditLogEntry -import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource -import dev.dres.utilities.extensions.sessionId -import io.javalin.http.Context -import io.javalin.openapi.* - -class LogoutHandler(private val audit: DAO) : RestHandler, GetRestHandler { - - override val apiVersion = "v1" - - @OpenApi(summary = "Clears all user roles of the current session.", - path = "/api/v1/logout", - tags = ["User"], - queryParams = [ - OpenApiParam("session", String::class, "Session Token") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): SuccessStatus { - AccessManager.clearUserSession(ctx.sessionId()) - AuditLogger.logout(ctx.sessionId(), LogEventSource.REST) - return SuccessStatus("Logged out") - - } - - override val route = "logout" -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt index 00f18cf8d..bac53cb5f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/RestHandler.kt @@ -25,7 +25,6 @@ interface GetRestHandler : RestHandler { } fun doGet(ctx: Context): T - } interface PostRestHandler : RestHandler { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt deleted file mode 100644 index 2beb23405..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/StatusHandler.kt +++ /dev/null @@ -1,96 +0,0 @@ -package dev.dres.api.rest.handler - -import com.sun.management.OperatingSystemMXBean -import dev.dres.DRES -import dev.dres.api.rest.RestApi -import dev.dres.api.rest.RestApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse -import io.javalin.security.RouteRole -import java.lang.management.ManagementFactory - -data class CurrentTime(val timeStamp: Long = System.currentTimeMillis()) - -class CurrentTimeHandler : GetRestHandler { - - override val route = "status/time" - override val apiVersion = "v1" - - @OpenApi(summary = "Returns the current time on the server.", - path = "/api/v1/status/time", - methods = [HttpMethod.GET], - tags = ["Status"], - responses = [ - OpenApiResponse("200", [OpenApiContent(CurrentTime::class)]) - ]) - override fun doGet(ctx: Context): CurrentTime = CurrentTime() - -} - -data class DresInfo( - val version: String = DRES.VERSION, - val startTime: Long, - val uptime: Long -) - -class InfoHandler : GetRestHandler { - - override val route = "status/info" - override val apiVersion = "v1" - - private val startTime = System.currentTimeMillis() - - @OpenApi(summary = "Returns an overview of the server properties.", - path = "/api/v1/status/info", - methods = [HttpMethod.GET], - tags = ["Status"], - responses = [ - OpenApiResponse("200", [OpenApiContent(DresInfo::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ]) - override fun doGet(ctx: Context): DresInfo = DresInfo( - startTime = startTime, - uptime = System.currentTimeMillis() - startTime - ) - -} - -data class DresAdminInfo( - val version: String = DRES.VERSION, - val startTime: Long, - val uptime: Long, - val os: String = System.getProperty("os.name"), - val jvm: String = "${System.getProperty("java.version")} (${System.getProperty("java.vendor")})", - val args: String = ManagementFactory.getRuntimeMXBean().inputArguments.joinToString(), - val cores: Int = Runtime.getRuntime().availableProcessors(), - val freeMemory: Long = Runtime.getRuntime().freeMemory(), - val totalMemory: Long = Runtime.getRuntime().totalMemory(), - val load: Double = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean::class.java)?.systemCpuLoad ?: -1.0, - val availableSeverThreads: Int = RestApi.readyThreadCount -) - -class AdminInfoHandler: AccessManagedRestHandler, GetRestHandler { - - override val permittedRoles: Set = setOf(RestApiRole.ADMIN) - override val route = "status/info/admin" - override val apiVersion = "v1" - - private val startTime = System.currentTimeMillis() - - @OpenApi(summary = "Returns an extensive overview of the server properties.", - path = "/api/v1/status/info/admin", - methods = [HttpMethod.GET], - tags = ["Status"], - responses = [ - OpenApiResponse("200", [OpenApiContent(DresAdminInfo::class)]) - ]) - override fun doGet(ctx: Context): DresAdminInfo = DresAdminInfo( - startTime = startTime, - uptime = System.currentTimeMillis() - startTime - ) - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt deleted file mode 100644 index 0e2b5af69..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt +++ /dev/null @@ -1,281 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.dbo.DAO -import dev.dres.data.model.UID -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User -import dev.dres.data.model.admin.UserName -import dev.dres.mgmt.admin.UserManager -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId -import dev.dres.utilities.extensions.toSessionId -import io.javalin.http.Context -import io.javalin.http.bodyAsClass -import io.javalin.openapi.* - -data class SessionId(val sessionId: String) - -data class UserRequest(val username: String, val password: String?, val role: RestApiRole?) - -data class UserDetails(val id: String, val username: String, val role: Role, val sessionId: String? = null) { - - companion object { - fun of(user: User): UserDetails = UserDetails(user.id.string, user.username.name, user.role) - fun create(user: User, ctx: Context): UserDetails = UserDetails(user.id.string, user.username.name, user.role, ctx.sessionId()) - } -} - -abstract class UserHandler : RestHandler { - - override val apiVersion = "v1" - - protected fun getFromSessionOrDie(ctx: Context): User { - return UserManager.get(id = AccessManager.getUserIdForSession(ctx.sessionId())!!) - ?: throw ErrorStatusException(404, "User could not be found!", ctx) - } - - protected fun getIdFromPath(ctx: Context): UID { - val id = ctx.pathParam("userId").UID() - if (UserManager.exists(id = id)) { - return id - } else { - throw ErrorStatusException(404, "User ($id) not found!", ctx) - } - } - - protected fun getUserFromId(ctx: Context): User { - val id = getIdFromPath(ctx) - return UserManager.get(id = id) ?: throw ErrorStatusException(404, "User ($id) not found!", ctx) - } - - protected fun getCreateUserFromBody(ctx: Context): UserRequest { - return ctx.bodyAsClass() - } -} - - -class ListUsersHandler : UserHandler(), GetRestHandler>, AccessManagedRestHandler { - - @OpenApi( - summary = "Lists all available users.", - path = "/api/v1/user/list", - tags = ["User"], - responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context) = UserManager.list().map(UserDetails.Companion::of) - - - override val permittedRoles = setOf(RestApiRole.ADMIN) - - override val route = "user/list" -} - -class UserDetailsHandler : UserHandler(), GetRestHandler, AccessManagedRestHandler { - - - @OpenApi( - summary = "Gets details of the user with the given id", - path = "/api/v1/user/{userId}", - pathParams = [ - OpenApiParam("userId", String::class, "User's UID") - ], - tags = ["User"], - responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found"), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context) = UserDetails.of(getUserFromId(ctx)) - - override val permittedRoles = RestApiRole.values().toSet() - - override val route = "user/{userId}" -} - -class DeleteUsersHandler : UserHandler(), DeleteRestHandler, AccessManagedRestHandler { - - @OpenApi( - summary = "Deletes the specified user. Requires ADMIN privileges", - path = "/api/v1/user/{userId}", methods = [HttpMethod.DELETE], - pathParams = [OpenApiParam("userId", Long::class, "User ID")], - tags = ["User"], - responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found"), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doDelete(ctx: Context): UserDetails { - val user = getUserFromId(ctx) - if (UserManager.delete(id = user.id)) { - return UserDetails.of(user) - } else { - throw ErrorStatusException(500, "Could not delete the user (${user.id})", ctx) - } - } - - override val permittedRoles = setOf(RestApiRole.ADMIN) - - override val route = "user/{userId}" -} - - -class CreateUsersHandler : UserHandler(), PostRestHandler, AccessManagedRestHandler { - - @OpenApi( - summary = "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - path = "/api/v1/user", methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), - tags = ["User"], - responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)], description = "If the username is already taken"), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): UserDetails { - val req = getCreateUserFromBody(ctx) - val success = UserManager.create(req) - if (success) { - return UserDetails.of(UserManager.get(username = UserName(req.username))!!) - } else { - throw ErrorStatusException(400, "The request could not be fulfilled.", ctx) - } - } - - override val permittedRoles = setOf(RestApiRole.ADMIN) - - override val route = "user" -} - -class UpdateUsersHandler : UserHandler(), PatchRestHandler, AccessManagedRestHandler { - - @OpenApi( - summary = "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", - path = "/api/v1/user/{userId}", methods = [HttpMethod.PATCH], - pathParams = [OpenApiParam("userId", String::class, "User ID")], - requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), - tags = ["User"], - responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPatch(ctx: Context): UserDetails { - val id = getIdFromPath(ctx) // Id was verified that it exists - val req = getCreateUserFromBody(ctx) - val caller = getFromSessionOrDie(ctx) - when { - (caller.role == Role.ADMIN) and (caller.id != id) -> { - /* ADMIN -- Can edit anyone */ - val success = UserManager.update(id = id, user = req) - if (success) { - return UserDetails.of(UserManager.get(id = id)!!) - } else { - throw ErrorStatusException(500, "Could not update user!", ctx) - } - } - caller.id == id -> { - /* Self-Update*/ - val success = UserManager.update(id = id, user = req) - if (success) { - return UserDetails.of(UserManager.get(id = id)!!) - } else { - throw ErrorStatusException(500, "Could not update user!", ctx) - } - } - else -> throw ErrorStatusException(400, "Cannot edit user ($id) as $caller!", ctx) - } - } - - override val permittedRoles = setOf(RestApiRole.VIEWER) - - override val route = "user/{userId}" -} - -class CurrentUsersHandler : UserHandler(), GetRestHandler, AccessManagedRestHandler { - - @OpenApi( - summary = "Get information about the current user.", - path = "/api/v1/user", - tags = ["User"], - responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): UserDetails { - return UserDetails.create(getFromSessionOrDie(ctx), ctx) - } - - override val permittedRoles = setOf(RestApiRole.VIEWER) - - override val route = "user" - -} - -class CurrentUsersSessionIdHandler : UserHandler(), GetRestHandler, AccessManagedRestHandler { - - @OpenApi( - summary = "Get current sessionId", - path = "/api/v1/user/session", - tags = ["User"], - queryParams = [ - OpenApiParam("session", String::class, "Session Token") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(SessionId::class)]), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): SessionId { - return ctx.sessionId().toSessionId() - } - - override val permittedRoles = setOf(RestApiRole.VIEWER) - - override val route = "user/session" -} - -class ActiveSessionsHandler(private val users: DAO) : GetRestHandler>, AccessManagedRestHandler { - - override val permittedRoles = setOf(RestApiRole.ADMIN) - override val route = "user/session/active/list" - override val apiVersion = "v1" - - - @OpenApi( - summary = "Get details of all current user sessions", - path = "/api/v1/user/session/active/list", - tags = ["User"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - - return AccessManager.currentSessions.map { session -> - val userId = AccessManager.getUserIdForSession(session) - ?: return@map UserDetails(UID.EMPTY.string, "??", Role.VIEWER, session) - val user = users[userId] ?: return@map UserDetails(userId.string, "??", Role.VIEWER, session) - UserDetails( - user.id.string, user.username.name, user.role, session - ) - } - - } - -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt new file mode 100644 index 000000000..7afa216dc --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt @@ -0,0 +1,21 @@ +package dev.dres.api.rest.handler.audit + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.handler.collection.AbstractCollectionHandler +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Loris Sauter + * @version 2.0.0 + */ +abstract class AbstractAuditLogHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { + /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + /** All [AbstractCollectionHandler]s are part of the v1 API. */ + override val apiVersion = "v1" +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt new file mode 100644 index 000000000..578b9fd55 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt @@ -0,0 +1,30 @@ +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.audit.AbstractAuditLogHandler +import jetbrains.exodus.database.TransientEntityStore +import dev.dres.api.rest.types.audit.AuditLogInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.audit.AuditLogEntry +import io.javalin.http.Context +import io.javalin.openapi.* +import kotlinx.dnq.query.lastOrNull +import kotlinx.dnq.query.size +import kotlinx.dnq.query.sortedBy + +class GetAuditLogInfoHandler(store: TransientEntityStore) : AbstractAuditLogHandler(store), GetRestHandler { + + override val route = "audit/info" + + @OpenApi( + summary = "Gives information about the audit log. Namely size and latest timestamp of known entries.", + path = "/api/v1/audit/info", + tags = ["Audit"], + responses = [ + OpenApiResponse("200", [OpenApiContent(AuditLogInfo::class)], description = "The audit log info."), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user executes the call.") + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): AuditLogInfo = this.store.transactional(true) { + AuditLogInfo(size = AuditLogEntry.all().size(), latest = AuditLogEntry.all().sortedBy(AuditLogEntry::timestamp, true).lastOrNull()?.timestamp?.millis) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt new file mode 100644 index 000000000..b01cd74ad --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -0,0 +1,60 @@ +package dev.dres.api.rest.handler.audit + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.audit.RestAuditLogEntry +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.utilities.extensions.toPathParamKey +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.drop +import kotlinx.dnq.query.take + +/** + * [AbstractAuditLogHandler] to list all [AuditLogEntry]. Allows for pagination. + * + * @author Loris Sauter + * @version 1.0.0 + */ +class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandler(store), GetRestHandler> { + override val route = "audit/log/list/limit/${LIMIT_PARAM.toPathParamKey()}/${PAGE_INDEX_PARAM.toPathParamKey()}" + + companion object { + const val LIMIT_PARAM = "limit" + const val PAGE_INDEX_PARAM = "page" + const val DEFAULT_LIMIT = 500 + + const val TYPE_PARAM = "type" // Coupled to 1047, not yet ready + const val RELATION_PARAM = "when" + const val BEFORE_PARAM_KEY = "before" + const val UPTO_PARAM_KEY = "upto" + /* + // See https://github.com/tipsy/javalin/issues/1047 - we currently are behind + enum class TemporalRelation{ + BEFORE, + UPTO + }*/ + } + + @OpenApi( + summary = "Lists all audit logs matching the query.", + path = "/api/v1/audit/log/list/limit/{limit}/{page}", + pathParams = [ + OpenApiParam(LIMIT_PARAM, Int::class, "The maximum number of results. Default: 500"), + OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.") + ], + tags = ["Audit"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List = this.store.transactional(true) { + val limit = (ctx.pathParamMap()[LIMIT_PARAM]?.toIntOrNull() ?: DEFAULT_LIMIT).coerceAtLeast(0) + val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) + AuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { RestAuditLogEntry.convert(it) }.toList() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt new file mode 100644 index 000000000..cd07c854f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -0,0 +1,50 @@ +package dev.dres.api.rest.handler.audit + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.audit.RestAuditLogEntry +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.audit.AuditLogEntry +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import org.joda.time.DateTime + +/** + * [AbstractAuditLogHandler] to list all [AuditLogEntry] between two points in time. + * + * @author Loris Sauter + * @version 1.0.0 + */ +class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLogHandler(store), GetRestHandler> { + + override val route = "audit/log/list/since/{since}/{upto}" + + @OpenApi( + summary = "Lists all audit logs matching the query", + path = "/api/v1/audit/log/list/since/{since}/{upto}", + pathParams = [ + OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include"), + OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.") + ], + tags = ["Audit"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val since = DateTime(ctx.pathParamMap()["since"]?.toLongOrNull() ?: 0L) + val upto = DateTime(ctx.pathParamMap()["upto"]?.toLongOrNull() ?:Long.MAX_VALUE) + + if (since < upto) throw ErrorStatusException(400, "Since must be smaller or equal to upto.", ctx) + + return this.store.transactional(true) { + AuditLogEntry.query((AuditLogEntry::timestamp gt since) and (AuditLogEntry::timestamp lt upto)).asSequence().map { + RestAuditLogEntry.convert(it) + }.toList() + } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt new file mode 100644 index 000000000..932f9b7ec --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt @@ -0,0 +1,33 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.basics.media.MediaCollection +import io.javalin.http.Context +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractCollectionHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { + + /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + /** All [AbstractCollectionHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** Convenience method to extract [MediaCollection] from [Context]. */ + protected fun collectionFromContext(ctx: Context): MediaCollection { + val id = ctx.pathParamMap()["collectionId"] ?: throw ErrorStatusException(404, "Parameter 'collectionId' is missing!'", ctx) + return MediaCollection.query(MediaCollection::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Collection with ID $id not found.'", ctx) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt new file mode 100644 index 000000000..4fc6799a4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -0,0 +1,60 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.UID +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.utilities.extensions.cleanPathString +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import java.util.UUID + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler { + + override val route: String = "collection" + + @OpenApi( + summary = "Adds a new media collection.", + path = "/api/v1/collection", + tags = ["Collection"], + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val restCollection = try { + ctx.bodyAsClass(RestMediaCollection::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + if (restCollection.basePath == null) { + throw ErrorStatusException(400, "Invalid parameters, collection base path not set.", ctx) + } + + val collection = this.store.transactional { + MediaCollection.new { + this.id = UUID.randomUUID().toString() + this.name = restCollection.name + this.description = restCollection.description + this.path = restCollection.basePath.cleanPathString() + } + } + return SuccessStatus("Collection ${collection.id} added successfully.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt new file mode 100644 index 000000000..8a4b5f3e4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -0,0 +1,72 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.RestMediaItemType +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.basics.media.MediaType +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import java.util.* + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler { + + override val route: String = "mediaitem" + + @OpenApi( + summary = "Adds a media item to the specified media collection.", + path = "/api/v1/mediaItem", + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(RestMediaItem::class)]), + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + + /* Parse media item and perform sanity checks */ + val mediaItem = try { + ctx.bodyAsClass(RestMediaItem::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } catch (e: IllegalArgumentException) { + throw ErrorStatusException(400, e.message ?: "Invalid parameters. This is a programmers error!", ctx) + } + + /* Try to persist media item. */ + val collectionId = mediaItem.collectionId + return this.store.transactional { + val collection = MediaCollection.query(MediaCollection::id eq collectionId).firstOrNull() + ?: throw ErrorStatusException(400, "Invalid parameters, collection with ID $collectionId does not exist.", ctx) + if (collection.items.filter { it.name eq mediaItem.name }.isNotEmpty) { + throw ErrorStatusException(400, "Media item with name '${mediaItem.name}' already exists in collection ${collection.name}.", ctx) + } + + val item = MediaItem.new { + this.id = UUID.randomUUID().toString() + this.type = MediaType.convertApiType(mediaItem.type) + this.name = mediaItem.name + this.location = mediaItem.location + this.fps = mediaItem.fps + this.durationMs = mediaItem.durationMs + } + collection.items.add(item) + + SuccessStatus("Media item added successfully.") + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt new file mode 100644 index 000000000..9487041b7 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt @@ -0,0 +1,40 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.DeleteRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.SuccessStatus +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), DeleteRestHandler { + + @OpenApi( + summary = "Deletes a media collection identified by a collection id.", + path = "/api/v1/collection/{collectionId}", + pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], + tags = ["Collection"], + methods = [HttpMethod.DELETE], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doDelete(ctx: Context): SuccessStatus { + val status = this.store.transactional { + val collection = collectionFromContext(ctx) + collection.delete() + SuccessStatus("Collection ${collection.id} deleted successfully.") + } + return status + } + + override val route: String = "collection/{collectionId}" + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt new file mode 100644 index 000000000..7b6c439d5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -0,0 +1,51 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.DeleteRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.basics.media.MediaItem +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), DeleteRestHandler { + + override val route: String = "mediaItem/{mediaId}" + + @OpenApi( + summary = "Tries to delete a specific media item.", + path = "/api/v1/mediaItem/{mediaId}", + methods = [HttpMethod.DELETE], + pathParams = [ + OpenApiParam("mediaId", String::class, "Media item ID") + ], + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doDelete(ctx: Context): SuccessStatus { + val mediaId = ctx.pathParamMap().getOrElse("mediaId") { + throw ErrorStatusException(404, "Parameter 'mediaId' is missing!'", ctx) + } + + return this.store.transactional { + val item = MediaItem.query(MediaItem::id eq mediaId).firstOrNull() + ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) + item.delete() + SuccessStatus("Media item ${item.id} deleted successfully.") + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt new file mode 100644 index 000000000..0b67aedf8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -0,0 +1,39 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.basics.media.MediaCollection +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence + +/** + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { + + override val route: String = "collection/list" + + @OpenApi( + summary = "Lists all available media collections with basic information about their content.", + path = "/api/v1/collection/list", + tags = ["Collection"], + methods = [HttpMethod.GET], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): List { + return this.store.transactional(true) { + MediaCollection.all().asSequence().map { RestMediaCollection.fromMediaCollection(it) }.toList() + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt new file mode 100644 index 000000000..6f428d61a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -0,0 +1,59 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.Config +import io.javalin.http.Context +import io.javalin.openapi.* +import java.nio.file.FileVisitOption +import java.nio.file.Files +import java.nio.file.Paths +import kotlin.streams.toList + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +/** + * Lists and returns the media items in the external media item directory. + */ +class ListExternalItemHandler(config: Config) : GetRestHandler> { + + override val apiVersion = "v1" + + /** Path to the directory that contains the external items. */ + val path = Paths.get(config.externalPath) + + init { + /* Check if directory exists and create it, if it doesn't. */ + if (!Files.exists(this.path)) { + Files.createDirectories(this.path) + } + } + + @OpenApi( + summary = "Lists items from the external media collection whose name start with the given string.", + path = "/api/v1/external/", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("startsWith", String::class, "Name starts with.", required = false) + ], + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): Array { + val startsWith = ctx.pathParamMap()["startsWith"] ?: "" + val list = Files.walk(this.path, 1, FileVisitOption.FOLLOW_LINKS).filter { + Files.isRegularFile(it) && it.fileName.toString().startsWith(startsWith) + }.limit(50).map { it.toString() }.toList() + return list.toTypedArray() + } + + override val route: String = "external/" +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt new file mode 100644 index 000000000..ebfd40259 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -0,0 +1,49 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.basics.media.MediaItem +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.query +import kotlinx.dnq.query.startsWith +import kotlinx.dnq.query.take + +/** + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { + override val route: String = "collection/{collectionId}/{startsWith}" + + @OpenApi( + summary = "Lists media items from a given media collection whose name start with the given string.", + path = "/api/v1/collection/{collectionId}/{startsWith}", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("collectionId", String::class, "Collection ID"), + OpenApiParam("startsWith", String::class, "Name the item(s) should start with.", required = false) + ], + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): List = this.store.transactional(true) { + val collection = collectionFromContext(ctx) + val start = ctx.pathParamMap()["startsWith"] + val query = if (!start.isNullOrBlank()) { + collection.items.query(MediaItem::name startsWith start) + } else { + collection.items + } + query.take(50).asSequence().map { RestMediaItem.fromMediaItem(it) }.toList() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt new file mode 100644 index 000000000..333a0f73a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -0,0 +1,51 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.UID +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.drop +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.size +import kotlinx.dnq.query.take +import java.util.SplittableRandom + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { + + private val rand = SplittableRandom(System.currentTimeMillis()) // TODO Decide upon seed -- time based or fixed? + + override val route: String = "collection/{collectionId}/random" + + @OpenApi( + summary = "Selects and returns a random media item from a given media collection.", + path = "/api/v1/collection/{collectionId}/random", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("collectionId", String::class, "Collection ID") + ], + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(RestMediaItem::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): RestMediaItem = this.store.transactional(true) { + val collection = collectionFromContext(ctx) + val item = collection.items.drop(this.rand.nextInt(0, collection.items.size())).take(1).firstOrNull() ?: + throw ErrorStatusException(404, "Failed to ferch media item. It seems that the given collection ${collection.id} is empty.", ctx) + RestMediaItem.fromMediaItem(item) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt new file mode 100644 index 000000000..07b2e3b97 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt @@ -0,0 +1,62 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.UID +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.query +import kotlinx.dnq.query.toList + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler> { + override val route = "collection/{collectionId}/resolve" + + @OpenApi( + summary = "Resolves a list of media item names to media items", + path = "/api/v1/collection/{collectionId}/resolve", + methods = [HttpMethod.POST], + pathParams = [ + OpenApiParam("collectionId", String::class, "Collection ID") + ], + requestBody = OpenApiRequestBody([OpenApiContent(Array::class)]), + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): List { + val queriedNames = try { + ctx.bodyAsClass(Array::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters: Expected Array. This is a programmers error!", ctx) + } + + /** If no name is contained in the list, an empty result set will be returned.*/ + if (queriedNames.isEmpty()) { + return emptyList() + } + + /** Execute query. */ + return this.store.transactional(true) { + val collection = collectionFromContext(ctx) + collection.items.filter { it.name isIn(queriedNames.asIterable()) }.asSequence().map { RestMediaItem.fromMediaItem(it) }.toList() + } + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt new file mode 100644 index 000000000..131dea125 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -0,0 +1,42 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.collection.RestFullMediaCollection +import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.basics.media.MediaItem +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.query + +/** + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { + + override val route: String = "collection/{collectionId}" + + @OpenApi( + summary = "Shows the content of the specified media collection.", + path = "/api/v1/collection/{collectionId}", + pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(RestFullMediaCollection::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): RestFullMediaCollection = this.store.transactional(true) { + val collection = collectionFromContext(ctx) //also checks if collection exists + val items = MediaItem.query(MediaItem::collection eq collection).asSequence().map { RestMediaItem.fromMediaItem(it) }.toList() + RestFullMediaCollection(RestMediaCollection.fromMediaCollection(collection), items) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt new file mode 100644 index 000000000..4a1aad9d5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt @@ -0,0 +1,50 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.utilities.extensions.UID +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { + override val route: String = "mediaItem/{mediaId}" + + @OpenApi( + summary = "Selects and returns a specific media item.", + path = "/api/v1/mediaItem/{mediaId}", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("mediaId", String::class, "Media item ID") + ], + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(RestMediaItem::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): RestMediaItem { + val mediaId = ctx.pathParamMap().getOrElse("mediaId") { + throw ErrorStatusException(404, "Parameter 'mediaId' is missing!'", ctx) + } + + return this.store.transactional(true) { + val item = MediaItem.query(MediaItem::id eq mediaId).firstOrNull() + ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) + RestMediaItem.fromMediaItem(item) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt new file mode 100644 index 000000000..04365c548 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.utilities.extensions.cleanPathString +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PatchRestHandler { + + override val route: String = "collection" + + @OpenApi( + summary = "Updates a media collection", + path = "/api/v1/collection", + tags = ["Collection"], + methods = [HttpMethod.PATCH], + requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): SuccessStatus { + + val restCollection = try { + ctx.bodyAsClass(RestMediaCollection::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + val status = this.store.transactional { + val collection = MediaCollection.query(MediaCollection::id eq restCollection.id).firstOrNull() + ?: throw ErrorStatusException(400, "Invalid parameters, collection with ID ${restCollection.id} does not exist.", ctx) + collection.name = restCollection.name.trim() + collection.description = restCollection.description?.trim() ?: collection.description + collection.path = restCollection.basePath?.cleanPathString() ?: collection.path + SuccessStatus("Collection ${collection.id} updated successfully.") + } + + return status + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt new file mode 100644 index 000000000..8735b6ae3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -0,0 +1,61 @@ +package dev.dres.api.rest.handler.collection + +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.basics.media.MediaType +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PatchRestHandler { + + override val route: String = "mediaitem" + + @OpenApi( + summary = "Updates a Media Item to the specified Media Collection.", + path = "/api/v1/mediaitem", + methods = [HttpMethod.PATCH], + requestBody = OpenApiRequestBody([OpenApiContent(RestMediaItem::class)]), + tags = ["Collection"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): SuccessStatus { + /* Parse media item and perform sanity checks */ + val mediaItem = try { + ctx.bodyAsClass(RestMediaItem::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } catch (e: IllegalArgumentException) { + throw ErrorStatusException(400, e.message ?: "Invalid parameters. This is a programmers error!", ctx) + } + + return this.store.transactional { + val item = MediaItem.query(MediaItem::id eq mediaItem.id).firstOrNull() + ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.id} not found.", ctx) + + item.type = MediaType.convertApiType(mediaItem.type) + item.name = item.name + item.location = item.location + item.fps = item.fps + item.durationMs = item.durationMs + + SuccessStatus("Media item ${item.id} updated successfully.") + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt new file mode 100644 index 000000000..ceda61c8e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -0,0 +1,128 @@ +package dev.dres.api.rest.handler.preview + +import dev.dres.api.rest.RestApi +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.collection.AbstractCollectionHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.Config +import dev.dres.data.model.basics.media.CollectionId +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.basics.media.MediaType +import dev.dres.utilities.FFmpegUtil +import dev.dres.utilities.extensions.sendFile +import dev.dres.utilities.extensions.streamFile +import io.javalin.http.Context +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import java.io.FileNotFoundException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +/** + * An abstract [GetRestHandler] used to access preview images. + * + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, config: Config) : GetRestHandler, AccessManagedRestHandler { + companion object { + /** Placeholder for when image is missing. */ + private val MISSING_IMAGE = this::class.java.getResourceAsStream("/img/missing.png").use { it!!.readAllBytes() } + + /** Placeholder for when image is waiting to be loaded. */ + private val WAITING_IMAGE = this::class.java.getResourceAsStream("/img/loading.png").use { it!!.readAllBytes() } + } + + /** All [AbstractCollectionHandler]s require [ApiRole.VIEWER]. */ + override val permittedRoles = setOf(ApiRole.VIEWER) + + /** All [AbstractPreviewHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** The [Path] to the pre-calculated previews.*/ + private val cacheLocation = Paths.get(config.cachePath + "/previews") + + init { + Files.createDirectories(this.cacheLocation) + } + + /** + * Handles a request for a preview based on an [CollectionId] and a [MediaItem]'s name. Fetching of the [MediaItem] takes + * place in a transaction context. However, the (potentially) long running media processing is executed outside. + * + * @param collectionId [CollectionId] of the [MediaCollection]. + * @param itemName Name of the [MediaItem] + * @param time The exact timepoint of the [MediaItem] in ms. Only works for [MediaType.VIDEO]. + * @param ctx The request [Context] + */ + protected fun handlePreviewRequest(collectionId: CollectionId, itemName: String, time: Long?, ctx: Context) { + val item = this.store.transactional(true) { + MediaItem.query((MediaItem::name eq itemName) and (MediaItem::collection.matches(MediaCollection::id eq collectionId))).firstOrNull() + ?: throw ErrorStatusException(404, "Media item $itemName (collection = $collectionId) not found!", ctx) + } + handlePreviewRequest(item, time, ctx) + } + + /** + * + */ + protected fun handlePreviewRequest(item: MediaItem, time: Long?, ctx: Context) { + + val basePath = Paths.get(item.collection.path) + if (item.type == MediaType.IMAGE) { + //TODO scale down image if too large + ctx.header("Cache-Control", "max-age=31622400") + ctx.streamFile(basePath.resolve(item.location)) + return + } else if (item.type == MediaType.VIDEO) { + /* Prepare cache directory for item. */ + val cacheDir = this.cacheLocation.resolve("${item.collection}/${item.name}") + if (!Files.exists(cacheDir)) { + Files.createDirectories(cacheDir) + } + + /* check timestamp. */ + if (time == null) throw ErrorStatusException(400, "Timestamp unspecified or invalid.", ctx) + + val imgPath = cacheDir.resolve("${time}.jpg") + if (Files.exists(imgPath)) { //if file is available, send contents immediately + ctx.header("Cache-Control", "max-age=31622400") + ctx.sendFile(imgPath) + } else { + val future = FFmpegUtil.executeFFmpegAsync(basePath.resolve(item.location), time, imgPath) + val waitTime = if (RestApi.readyThreadCount > 500) { + 3L + } else { + 1L + } + + try { + val path = future.get(waitTime, TimeUnit.SECONDS) ?: throw FileNotFoundException() + ctx.sendFile(path.toFile()) + } catch (e: TimeoutException) { + ctx.status(408) + ctx.header("Cache-Control", "max-age=30") + ctx.contentType("image/png") + ctx.result(WAITING_IMAGE) + } catch (t: Throwable) { + ctx.status(429) + ctx.header("Cache-Control", "max-age=600") + ctx.contentType("image/png") + ctx.result(MISSING_IMAGE) + } + } + } + } +} + + + + + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt new file mode 100644 index 000000000..3a46a8282 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt @@ -0,0 +1,71 @@ +package dev.dres.api.rest.handler.preview + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.utilities.extensions.errorResponse +import dev.dres.utilities.extensions.streamFile +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiParam +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query +import java.nio.file.Paths + +/** + * An [GetRestHandler] used to access the files that belong to a specific [MediaItem]. + * + * @author Luca Rossetto + * @version 2.0.0 + */ +class GetMediaHandler(private val store: TransientEntityStore) : GetRestHandler, AccessManagedRestHandler { + override val route: String = "media/{collection}/{item}" + + /** All [GetMediaHandler] can be used by [ApiRole.VIEWER], [ApiRole.PARTICIPANT] and [ApiRole.ADMIN]. */ + override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.ADMIN) + + /** [GetMediaHandler] is part of the v1 API. */ + override val apiVersion = "v1" + + //not used + override fun doGet(ctx: Context): Any = "" + + @OpenApi(summary = "Returns a collection item", + path = "/api/v1/media/{itemId}", + pathParams = [ + OpenApiParam("itemId", String::class, "The media item ID.") + ], + tags = ["Media"], + responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], + ignore = true, + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + /* Extract parameter. */ + val itemId = ctx.pathParamMap()["itemId"] + if (itemId == null) { + ctx.errorResponse(400, "Missing parameters!") + return + } + + /* Lookup item by ID. */ + val item = this.store.transactional(true) { + MediaItem.query(MediaItem::id eq itemId).firstOrNull() + } + if (item == null) { + ctx.errorResponse(404, "item with name $itemId found") + return + } + + try{ + ctx.streamFile( Paths.get(item.collection.path).resolve(item.location)) + } catch (e: org.eclipse.jetty.io.EofException) { + //is triggered by a client abruptly stopping playback, can be safely ignored + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt new file mode 100644 index 000000000..fd54d918d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt @@ -0,0 +1,53 @@ +package dev.dres.api.rest.handler.preview + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.Config +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.utilities.extensions.errorResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * An [AbstractPreviewHandler] used to access previews of specific [MediaItem]s. + * + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class MediaPreviewHandler(store: TransientEntityStore, config: Config) : AbstractPreviewHandler(store, config) { + + override val route: String = "preview/item/{collection}/{item}/{time}" + @OpenApi( + summary = "Returns a preview image from a collection item", + path = "/api/v1/preview/item/{collection}/{item}/{time}", + pathParams = [ + OpenApiParam("collectionId", String::class, "Unique ID of the media collection."), + OpenApiParam("item", String::class, "Name of the media item-"), + OpenApiParam("time", Long::class, "Time into the video in milliseconds (for videos only).") + ], + tags = ["Media"], + responses = [OpenApiResponse( + "200", + [OpenApiContent(type = "image/png")] + ), OpenApiResponse("401"), OpenApiResponse("400")], + ignore = true, + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + try { + val params = ctx.pathParamMap() + val collectionId = params["collection"] ?: throw ErrorStatusException(400, "Collection ID not specified or invalid.", ctx) + val itemName = params["item"] ?: throw ErrorStatusException(400, "Item name not specified.", ctx) + val time = params["time"]?.toLongOrNull() + handlePreviewRequest(collectionId, itemName, time, ctx) + } catch (e: ErrorStatusException) { + ctx.errorResponse(e) + } + } + + override fun doGet(ctx: Context): Any { + throw UnsupportedOperationException("MediaPreviewHandler::doGet() is not supported and should not be executed!") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt new file mode 100644 index 000000000..8290bad1d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -0,0 +1,82 @@ +package dev.dres.api.rest.handler.preview + +import dev.dres.api.rest.handler.AbstractPreviewHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.Config +import dev.dres.data.model.UID +import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.submissions.aspects.ItemAspect +import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect +import dev.dres.data.model.submissions.aspects.TextAspect +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunExecutor +import dev.dres.utilities.extensions.UID +import dev.dres.utilities.extensions.errorResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * An [AbstractPreviewHandler] used to access previews of [MediaItem]s based on a specific [Submission]. + * + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : AbstractPreviewHandler(store, config) { + override val route: String = "preview/submission/{runId}/{submissionId}" + + @OpenApi( + summary = "Returns a preview image for a specific submission.", + path = "/api/v1/preview/submission/{runId}/{submissionId}", + pathParams = [ + OpenApiParam("runId", String::class, "Competition ID"), + OpenApiParam("submissionId", String::class, "Subission ID") + ], + tags = ["Media"], + responses = [OpenApiResponse( + "200", + [OpenApiContent(type = "image/png")] + ), OpenApiResponse("401"), OpenApiResponse("400")], + ignore = true, + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + + try { + val params = ctx.pathParamMap() + val runId = params["runId"] ?: throw ErrorStatusException(404, "Parameter 'runId' is invalid", ctx) + val submissionId = params["submissionId"]?.UID() ?: throw ErrorStatusException(404, "Parameter 'submissionId' is missing", ctx) + val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Competition Run $runId not found", ctx) + if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Competition Run $runId is not interactive", ctx) + + val submission = run.allSubmissions.find { it.uid == submissionId } + ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) + + when (submission) { + is ItemAspect -> { + handlePreviewRequest(submission.item, if (submission is TemporalSubmissionAspect) submission.start else null, ctx) + } + is TextAspect -> { + ctx.header("Cache-Control", "max-age=31622400") + ctx.contentType("image/png") + ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) + } + else -> { + ctx.header("Cache-Control", "max-age=31622400") + ctx.contentType("image/png") + ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) + } + } + } catch (e: ErrorStatusException) { + ctx.errorResponse(e) + } + + } + + + override fun doGet(ctx: Context): Any { + throw UnsupportedOperationException("SubmissionPreviewHandler::doGet() is not supported and should not be executed!") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt similarity index 95% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index 3c61dbe1c..b046007dd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -28,9 +28,9 @@ import io.javalin.security.RouteRole abstract class BatchSubmissionHandler(internal val collections: DAO, internal val itemIndex: DaoIndexer>, internal val segmentIndex: DaoIndexer) : PostRestHandler, AccessManagedRestHandler { override val apiVersion = "v1" - override val permittedRoles: Set = setOf(RestApiRole.PARTICIPANT) + override val permittedRoles: Set = setOf(ApiRole.PARTICIPANT) - internal fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + internal fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(404, "Parameter 'runId' is missing!'", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt similarity index 84% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 3433a2053..aad7bc341 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -1,18 +1,19 @@ -package dev.dres.api.rest.handler +package dev.dres.api.rest.handler.submission import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.preview.AbstractPreviewHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus -import dev.dres.data.dbo.DAO -import dev.dres.data.dbo.DaoIndexer import dev.dres.data.model.Config import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaItemSegmentList import dev.dres.data.model.basics.media.PlayableMediaItem import dev.dres.data.model.basics.time.TemporalPoint import dev.dres.data.model.competition.options.SimpleOption @@ -21,9 +22,7 @@ import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.InteractiveRunManager -import dev.dres.run.RunManager import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException @@ -32,16 +31,29 @@ import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore import org.slf4j.LoggerFactory import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -class SubmissionHandler (val collections: DAO, private val itemIndex: DaoIndexer>, private val segmentIndex: DaoIndexer, private val config: Config): GetRestHandler, AccessManagedRestHandler { - override val permittedRoles = setOf(RestApiRole.PARTICIPANT) - override val route = "submit" +/** + * An [GetRestHandler] used to process [Submission]s + * + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class SubmissionHandler (private val store: TransientEntityStore, private val config: Config): GetRestHandler, AccessManagedRestHandler { + + /** [SubmissionHandler] requires [ApiRole.PARTICIPANT]. */ + override val permittedRoles = setOf(ApiRole.PARTICIPANT) + + /** All [AbstractPreviewHandler]s are part of the v1 API. */ override val apiVersion = "v1" + override val route = "submit" + private val logger = LoggerFactory.getLogger(this.javaClass) companion object { @@ -53,26 +65,84 @@ class SubmissionHandler (val collections: DAO, private val item const val PARAMETER_NAME_TEXT = "text" } + @OpenApi(summary = "Endpoint to accept submissions", + path = "/api/v1/submit", + queryParams = [ + OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), + OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), + OpenApiParam(PARAMETER_NAME_TEXT, String::class, "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", allowEmptyValue = true, required = false), + OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), + OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), + OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), + OpenApiParam("session", String::class, "Session Token") + ], + tags = ["Submission"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), + OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val run = getEligibleRunManager(userId, ctx) + val time = System.currentTimeMillis() + val submission = toSubmission(userId, run, time, ctx) + val rac = RunActionContext.runActionContext(ctx, run) - private fun getRelevantManagers(userId: UID): Set = AccessManager.getRunManagerForUser(userId) - - private fun getActiveRun(userId: UID, ctx: Context): InteractiveRunManager { - val managers = getRelevantManagers(userId).filterIsInstance(InteractiveRunManager::class.java).filter { - val rac = RunActionContext.runActionContext(ctx, it) - it.currentTask(rac)?.isRunning == true + val result = try { + run.postSubmission(rac, submission) + } catch (e: SubmissionRejectedException) { + throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) + } catch (e: IllegalRunStateException) { + logger.info("Submission was received while Run manager not accepting submissions") + throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + } catch (e: IllegalTeamIdException) { + logger.info("Submission with unkown team id '${rac.teamId}' was received") + throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - if (managers.isEmpty()) { - throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) + + AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionId(), ctx.req().remoteAddr) + + if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { //pre-generate preview + generatePreview(submission) } - if (managers.size > 1) { - throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.description.name }}", ctx) + logger.info("submission ${submission.uid} received status $result") + + return when (result) { + SubmissionStatus.CORRECT -> SuccessfulSubmissionsStatus(SubmissionStatus.CORRECT, "Submission correct!") + SubmissionStatus.WRONG -> SuccessfulSubmissionsStatus(SubmissionStatus.WRONG, "Submission incorrect! Try again") + SubmissionStatus.INDETERMINATE -> { + ctx.status(202) /* HTTP Accepted. */ + SuccessfulSubmissionsStatus(SubmissionStatus.INDETERMINATE, "Submission received. Waiting for verdict!") + } + SubmissionStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(SubmissionStatus.UNDECIDABLE,"Submission undecidable. Try again!") } + } + + /** + * + */ + private fun getEligibleRunManager(userId: UserId, ctx: Context): InteractiveRunManager { + val managers = AccessManager.getRunManagerForUser(userId).filterIsInstance(InteractiveRunManager::class.java).filter { + val rac = RunActionContext.runActionContext(ctx, it) + it.currentTask(rac)?.isRunning == true + } + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.description.name }}", ctx) return managers.first() } - private fun toSubmission(ctx: Context, userId: UID, runManager: InteractiveRunManager, submissionTime: Long): Submission { + /** + * + */ + private fun toSubmission(userId: UserId, runManager: InteractiveRunManager, submissionTime: Long, ctx: Context): Submission { val map = ctx.queryParamMap() /* Find team that the user belongs to. */ @@ -102,7 +172,7 @@ class SubmissionHandler (val collections: DAO, private val item /* Find media item. */ val itemParam = map[PARAMETER_NAME_ITEM]?.first() ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) val item = this.itemIndex[collectionId to itemParam].firstOrNull() ?: - throw ErrorStatusException(404, "Media item '$itemParam (collection = $collectionId)' could not be found.", ctx) + throw ErrorStatusException(404, "Media item '$itemParam (collection = $collectionId)' could not be found.", ctx) val mapToSegment = runManager.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.MAP_TO_SEGMENT } return when { @@ -152,66 +222,6 @@ class SubmissionHandler (val collections: DAO, private val item } } - @OpenApi(summary = "Endpoint to accept submissions", - path = "/api/v1/submit", - queryParams = [ - OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), - OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), - OpenApiParam(PARAMETER_NAME_TEXT, String::class, "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), - OpenApiParam("session", String::class, "Session Token") - ], - tags = ["Submission"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), - OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) - val run = getActiveRun(userId, ctx) - val time = System.currentTimeMillis() - val submission = toSubmission(ctx, userId, run, time) - val rac = RunActionContext.runActionContext(ctx, run) - - val result = try { - run.postSubmission(rac, submission) - } catch (e: SubmissionRejectedException) { - throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) - } catch (e: IllegalRunStateException) { - logger.info("Submission was received while Run manager not accepting submissions") - throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) - } catch (e: IllegalTeamIdException) { - logger.info("Submission with unkown team id '${rac.teamId}' was received") - throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) - } - - AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, run.currentTask(rac)?.uid, submission, LogEventSource.REST, ctx.sessionId(), ctx.req().remoteAddr) - - if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { //pre-generate preview - generatePreview(submission) - } - - logger.info("submission ${submission.uid} received status $result") - - return when (result) { - SubmissionStatus.CORRECT -> SuccessfulSubmissionsStatus(SubmissionStatus.CORRECT, "Submission correct!") - SubmissionStatus.WRONG -> SuccessfulSubmissionsStatus(SubmissionStatus.WRONG, "Submission incorrect! Try again") - SubmissionStatus.INDETERMINATE -> { - ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(SubmissionStatus.INDETERMINATE, "Submission received. Waiting for verdict!") - } - SubmissionStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(SubmissionStatus.UNDECIDABLE,"Submission undecidable. Try again!") - } - } - private fun generatePreview(submission: Submission) { if (submission !is TemporalSubmissionAspect) { return diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt new file mode 100644 index 000000000..e9ac829f5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt @@ -0,0 +1,30 @@ +package dev.dres.api.rest.handler.system + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.system.CurrentTime +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse + +/** + * A [GetRestHandler] that returns the current server time. + * + * @author Luca Rossetto + * @version 1.0.0 + */ +class CurrentTimeHandler : GetRestHandler { + + override val route = "status/time" + override val apiVersion = "v1" + + @OpenApi(summary = "Returns the current time on the server.", + path = "/api/v1/status/time", + methods = [HttpMethod.GET], + tags = ["Status"], + responses = [ + OpenApiResponse("200", [OpenApiContent(CurrentTime::class)]) + ]) + override fun doGet(ctx: Context): CurrentTime = CurrentTime() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt new file mode 100644 index 000000000..09ecf5cde --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.system + +import com.sun.management.OperatingSystemMXBean +import dev.dres.DRES +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.RestApi +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.system.DresInfo +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse +import java.lang.management.ManagementFactory + +/** + * A [GetRestHandler] that returns general information about the DRES instance. + * + * @author Luca Rossetto + * @version 1.0.0 + */ +class InfoHandler : GetRestHandler { + + override val route = "status/info" + + override val apiVersion = "v1" + + + @OpenApi(summary = "Returns an overview of the server properties.", + path = "/api/v1/status/info", + methods = [HttpMethod.GET], + tags = ["Status"], + responses = [ + OpenApiResponse("200", [OpenApiContent(DresInfo::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ]) + override fun doGet(ctx: Context): DresInfo { + return if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { + DresInfo ( + DRES.VERSION, + ManagementFactory.getRuntimeMXBean().startTime, + ManagementFactory.getRuntimeMXBean().uptime, + System.getProperty("os.name"), + "${System.getProperty("java.version")} (${System.getProperty("java.vendor")})", + ManagementFactory.getRuntimeMXBean().inputArguments.joinToString(), + Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().freeMemory(), + Runtime.getRuntime().totalMemory(), + ManagementFactory.getPlatformMXBean(OperatingSystemMXBean::class.java)?.systemCpuLoad ?: -1.0, + RestApi.readyThreadCount) + } else { + DresInfo (DRES.VERSION, ManagementFactory.getRuntimeMXBean().startTime, ManagementFactory.getRuntimeMXBean().uptime) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt similarity index 76% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index af56b2b5d..cbc8bcb45 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -1,20 +1,27 @@ -package dev.dres.api.rest.handler +package dev.dres.api.rest.handler.system import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.admin.Password +import dev.dres.data.model.audit.AuditLogSource import dev.dres.mgmt.admin.UserManager import dev.dres.mgmt.admin.UserManager.getMatchingUser -import dev.dres.run.audit.AuditLogEntry import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource import dev.dres.utilities.extensions.sessionId import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* -class LoginHandler(private val audit: DAO) : RestHandler, PostRestHandler { +/** + * A [GetRestHandler] that handles user-requests to login. + * + * @version 1.0.0 + * @author Luca Rossetto + */ +class LoginHandler : RestHandler, PostRestHandler { override val apiVersion = "v1" @@ -44,11 +51,11 @@ class LoginHandler(private val audit: DAO) : RestHandler, PostRes val password = Password.Plain(loginRequest.password) val user = getMatchingUser(username, password) ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) - AccessManager.setUserForSession(ctx.sessionId(), user) - AuditLogger.login(loginRequest.username, ctx.sessionId(), LogEventSource.REST) + /* Begin user session. */ + AccessManager.registerUserForSession(ctx.sessionId(), user) + AuditLogger.login(user.userId, AuditLogSource.REST, ctx.sessionId()) return UserDetails.create(UserManager.get(username)!!, ctx) - } override val route = "login" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt new file mode 100644 index 000000000..bfe969ed8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -0,0 +1,44 @@ +package dev.dres.api.rest.handler.system + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * A [GetRestHandler] that handles user-requests to logout. + * + * @version 2.0.0 + * @author Luca Rossetto + */ +class LogoutHandler : RestHandler, GetRestHandler { + override val apiVersion = "v1" + + @OpenApi(summary = "Clears all user roles of the current session.", + path = "/api/v1/logout", + tags = ["User"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): SuccessStatus { + val username = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) + AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionId()) + AccessManager.deregisterUserSession(ctx.sessionId()) + return SuccessStatus("User '${username}' logged out successfully.") + } + override val route = "logout" +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt new file mode 100644 index 000000000..fcadc743a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt @@ -0,0 +1,35 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.admin.User +import dev.dres.mgmt.admin.UserManager +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context + +typealias SessionId = String + +/** + * An abstract [RestHandler] to manage [User]s + * + * @author Loris Sauter + * @version 2.0.0 + */ +abstract class AbstractUserHandler: RestHandler, AccessManagedRestHandler { + /** All [AbstractUserHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** Convenience method to extract [User] from current session. */ + protected fun userFromSession(ctx: Context): User { + return UserManager.get(id = AccessManager.userIdForSession(ctx.sessionId())!!) + ?: throw ErrorStatusException(404, "User could not be found!", ctx) + } + + /** Convenience method to extract [User] from [Context] (userId parameter). */ + protected fun userFromContext(ctx: Context): User { + val id = ctx.pathParamMap()["userId"] ?: throw ErrorStatusException(404, "Parameter 'userId' is missing!'", ctx) + return UserManager.get(id = id) ?: throw ErrorStatusException(404, "User ($id) not found!", ctx) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt new file mode 100644 index 000000000..2888eb93e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -0,0 +1,62 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.UserRequest +import dev.dres.data.model.admin.Password +import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.User +import dev.dres.mgmt.admin.UserManager +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * An [AbstractUserHandler] to create new [User]s. + * + * @author Loris Sauter + * @version 2.0.0 + */ +class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { + override val route = "user" + + /** [CreateUsersHandler] requires [ApiRole.ADMIN]. */ + override val permittedRoles = setOf(ApiRole.ADMIN) + + @OpenApi( + summary = "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + path = "/api/v1/user", methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), + tags = ["User"], + responses = [ + OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)], description = "If the username is already taken"), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): UserDetails { + val req = try { + ctx.bodyAsClass(UserRequest::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + if (req.password == null || req.password.length < User.MIN_LENGTH_PASSWORD) + throw ErrorStatusException(400, "Invalid parameters. Password must consist of at least ${User.MIN_LENGTH_PASSWORD} characters.", ctx) + if (req.username.length < User.MIN_LENGTH_USERNAME) + throw ErrorStatusException(400, "Invalid parameters. Username must consist of at least ${User.MIN_LENGTH_USERNAME} characters.", ctx) + if (req.role == null) + throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) + + val success = UserManager.create(req.username, Password.Plain(req.password), Role.convertApiRole(req.role)) + if (success) { + return UserDetails.of(UserManager.get(username = req.username)!!) + } else { + throw ErrorStatusException(400, "The request could not be fulfilled.", ctx) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt new file mode 100644 index 000000000..de0167d6d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -0,0 +1,45 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.DeleteRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.UserDetails +import dev.dres.data.model.admin.User +import dev.dres.mgmt.admin.UserManager +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * An [AbstractUserHandler] to delete [User]s. + * + * @author Loris Sauter + * @version 2.0.0 + */ +class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { + override val permittedRoles = setOf(ApiRole.ADMIN) + + /** [DeleteUsersHandler] requires [ApiRole.ADMIN]. */ + override val route = "user/{userId}" + + @OpenApi( + summary = "Deletes the specified user. Requires ADMIN privileges", + path = "/api/v1/user/{userId}", methods = [HttpMethod.DELETE], + pathParams = [OpenApiParam("userId", Long::class, "User ID")], + tags = ["User"], + responses = [ + OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found"), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doDelete(ctx: Context): UserDetails { + val user = userFromContext(ctx) + if (UserManager.delete(id = user.id)) { + return UserDetails.of(user) + } else { + throw ErrorStatusException(500, "Could not delete the user (${user.id})", ctx) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt new file mode 100644 index 000000000..dc858a81c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -0,0 +1,51 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.users.UserDetails +import dev.dres.data.model.UID +import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.User +import dev.dres.mgmt.admin.UserManager +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse + +/** + * An [AbstractUserHandler] to list all [User]s that are currently logged in. + * + * @author Loris Sauter + * @version 2.0.0 + */ +class ListActiveUsersHandler() : GetRestHandler>, AccessManagedRestHandler { + override val permittedRoles = setOf(ApiRole.ADMIN) + + /** All [UserDetailsHandler] requires [ApiRole.ADMIN]. */ + override val route = "user/session/active/list" + + override val apiVersion = "v1" + + + @OpenApi( + summary = "Get details of all current user sessions", + path = "/api/v1/user/session/active/list", + tags = ["User"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List = AccessManager.currentSessions.map { session -> + AccessManager.userIdForSession(session)?.let { + UserManager.get(id = it) + }?.let { + UserDetails.of(it) + } ?: return@map UserDetails(UID.EMPTY.string, "??", Role.VIEWER, session) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt new file mode 100644 index 000000000..be2af8291 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -0,0 +1,36 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.users.UserDetails +import dev.dres.data.model.admin.User +import dev.dres.mgmt.admin.UserManager +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse + +/** + * An [AbstractUserHandler] to list all [User]s. + * + * @author Loris Sauter + * @version 2.0.0 + */ +class ListUsersHandler: AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { + + override val route = "user/list" + + /** All [UserDetailsHandler] requires [ApiRole.ADMIN]. */ + override val permittedRoles = setOf(ApiRole.ADMIN) + + @OpenApi( + summary = "Lists all available users.", + path = "/api/v1/user/list", + tags = ["User"], + responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context) = UserManager.list().map(UserDetails.Companion::of) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt new file mode 100644 index 000000000..21f436569 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt @@ -0,0 +1,38 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.SessionId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ShowCurrentSessionHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { + override val route = "user/session" + + /** [ShowCurrentUserHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ + override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.ADMIN, ApiRole.PARTICIPANT) + + + @OpenApi( + summary = "Get current sessionId", + path = "/api/v1/user/session", + tags = ["User"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(SessionId::class)]), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): SessionId = ctx.sessionId() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt new file mode 100644 index 000000000..cc58d3660 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt @@ -0,0 +1,36 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.users.UserDetails +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ShowCurrentUserHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { + override val route = "user" + + /** [ShowCurrentUserHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ + override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.ADMIN, ApiRole.PARTICIPANT) + + @OpenApi( + summary = "Get information about the current user.", + path = "/api/v1/user", + tags = ["User"], + responses = [ + OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): UserDetails = UserDetails.create(userFromSession(ctx), ctx) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt new file mode 100644 index 000000000..f736b777e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -0,0 +1,65 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.UserRequest +import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.User +import dev.dres.mgmt.admin.UserManager +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * An [AbstractUserHandler] to update an existing [User]s. + * + * @author Loris Sauter + * @version 2.0.0 + */ +class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, AccessManagedRestHandler { + + /** [UpdateUsersHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ + override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.ADMIN, ApiRole.PARTICIPANT) + + override val route = "user/{userId}" + + @OpenApi( + summary = "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + path = "/api/v1/user/{userId}", methods = [HttpMethod.PATCH], + pathParams = [OpenApiParam("userId", String::class, "User ID")], + requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), + tags = ["User"], + responses = [ + OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): UserDetails { + val request = try { + ctx.bodyAsClass(UserRequest::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + /* Fetch existing objects. */ + val user = userFromContext(ctx) + val caller = userFromSession(ctx) + + if (caller.role == Role.ADMIN || user.id == caller.id) { + val success = UserManager.update(id = user.id, request = request) + if (success) { + return UserDetails.of(UserManager.get(id = user.id)!!) + } else { + throw ErrorStatusException(500, "Could not update user!", ctx) + } + } else { + throw ErrorStatusException(403, "You do not have permissions to edit user (${user.id}) as $caller!", ctx) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt new file mode 100644 index 000000000..37e129dc6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -0,0 +1,41 @@ +package dev.dres.api.rest.handler.users + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.users.UserDetails +import dev.dres.data.model.admin.User +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * An [AbstractUserHandler] to show [User] details. + * + * @author Loris Sauter + * @version 2.0.0 + */ +class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { + override val route = "user/{userId}" + + /** [UserDetailsHandler] requires [ApiRole.ADMIN]. */ + override val permittedRoles = ApiRole.values().toSet() + + @OpenApi( + summary = "Gets details of the user with the given id.", + path = "/api/v1/user/{userId}", + pathParams = [ + OpenApiParam("userId", String::class, "User's UID") + ], + tags = ["User"], + responses = [ + OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found."), + OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context) = UserDetails.of(userFromContext(ctx)) + + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt index e7913725a..4f54fbc31 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt @@ -31,7 +31,7 @@ value class WebSocketConnection(val context: WsContext) { /** Name of the user that generated this [WebSocketConnection]. */ val userName - get() = UserManager.get(AccessManager.getUserIdForSession(this.httpSessionId) ?: UID.EMPTY)?.username?.name ?: "UNKNOWN" + get() = UserManager.get(AccessManager.userIdForSession(this.httpSessionId) ?: UID.EMPTY)?.username?.name ?: "UNKNOWN" /** IP address of the client. */ val host: String diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt index a160f6438..063b3b5f4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt @@ -1,9 +1,7 @@ package dev.dres.api.rest.types.audit -import dev.dres.run.audit.AuditLogEntry - data class AuditLogInfo( val timestamp: Long = System.currentTimeMillis(), val size: Int, - val latest: Long + val latest: Long? = null ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt index 2bb1dce94..13c5644c5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt @@ -1,34 +1,24 @@ package dev.dres.api.rest.types.collection -import dev.dres.data.dbo.DAO -import dev.dres.data.model.UID - +import dev.dres.data.model.basics.media.CollectionId import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.TaskDescription import dev.dres.utilities.extensions.UID /** - * The RESTful API equivalent for [dres.data.model.basics.media.MediaCollection]. + * The RESTful API equivalent for [MediaCollection]. * - * @see dres.data.model.basics.media.MediaCollection + * @see MediaCollection * @author Ralph Gasser * @version 1.0 */ -data class RestMediaCollection(val id: String = UID.EMPTY.string, val name: String, val description: String? = null, val basePath: String? = null) { +data class RestMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { companion object { /** * Generates a [RestMediaItem] from a [TaskDescription] and returns it. * * @param task The [TaskDescription] to convert. */ - fun fromMediaCollection(item: MediaCollection) = RestMediaCollection(item.id.string, item.name, item.description, item.path) + fun fromMediaCollection(item: MediaCollection) = RestMediaCollection(item.id, item.name, item.description, item.path) } - - /** - * Converts this [RestMediaCollection] to the corresponding [MediaCollection] and returns it, - * by looking it up in the database. - * - * @param mediaCollections The [DAO] to perform lookups. - */ - fun toMediaCollection(mediaCollections: DAO) = mediaCollections[this.id.UID()] } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt index 57862d7a2..aeb9f40db 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt @@ -1,42 +1,34 @@ package dev.dres.api.rest.types.collection -import dev.dres.data.dbo.DAO import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.competition.TaskDescription -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.cleanPathString +import dev.dres.data.model.basics.media.MediaType /** - * The RESTful API equivalent for [dres.data.model.basics.media.MediaItem]. + * The RESTful API equivalent for [MediaItem]. * - * @see dres.data.model.basics.media.MediaItem + * @see MediaItem * @author Ralph Gasser - * @version 1.0 + * @version 1.1.0 */ data class RestMediaItem(val id: String= UID.EMPTY.string, val name: String, val type: RestMediaItemType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { companion object { /** - * Generates a [RestMediaItem] from a [TaskDescription] and returns it. + * Generates a [RestMediaItem] from a [MediaItem] and returns it. * - * @param task The [TaskDescription] to convert. + * @param item The [MediaItem] to convert. */ - fun fromMediaItem(item: MediaItem) = when (item) { - is MediaItem.ImageItem -> RestMediaItem(item.id.string, item.name, RestMediaItemType.IMAGE, item.collection.string, item.location) - is MediaItem.VideoItem -> RestMediaItem(item.id.string, item.name, RestMediaItemType.VIDEO, item.collection.string, item.location, item.durationMs, item.fps) + fun fromMediaItem(item: MediaItem) = when (item.type) { + MediaType.IMAGE -> RestMediaItem(item.id, item.name, RestMediaItemType.IMAGE, item.collection.id, item.location) + MediaType.VIDEO -> RestMediaItem(item.id, item.name, RestMediaItemType.VIDEO, item.collection.id, item.location, item.durationMs, item.fps) + else -> throw IllegalArgumentException("Unsupported media type ${item.type}.") } } - /** - * Converts this [RestMediaItem] to the corresponding [MediaItem] and returns it, - * by looking it up in the collection - * - * @param mediaItems The [DAO] to perform lookups - */ - fun lookup(mediaItems: DAO) = mediaItems[this.id.UID()] - - fun toMediaItem(): MediaItem = when(type){ - RestMediaItemType.IMAGE -> MediaItem.ImageItem(id.UID(), name.trim(), location.cleanPathString(), collectionId.UID()) - RestMediaItemType.VIDEO -> MediaItem.VideoItem(id.UID(), name.trim(), location.cleanPathString(), collectionId.UID(), durationMs!!, fps!!) + init { + if (this.type == RestMediaItemType.VIDEO) { + require(this.durationMs != null) { "Duration must be set for a video item." } + require(this.fps != null) { "Duration must be set for a video item." } + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt index bf4fbeb3d..80edf8059 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.competition import dev.dres.api.rest.handler.UserDetails -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.mgmt.admin.UserManager data class RestDetailedTeam(val name: String, val color: String, val logoId: String, val users: List) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt index be4ed2605..4309062d7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.types.competition import dev.dres.data.model.Config import dev.dres.data.model.UID -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.utilities.extensions.UID import java.awt.image.BufferedImage import java.io.ByteArrayInputStream diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt index aa48dc552..a09c906d1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.competition.TeamGroup import dev.dres.data.model.competition.TeamGroupAggregation import dev.dres.utilities.extensions.UID diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt index a85af0175..77f718586 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.run -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.run.InteractiveSynchronousCompetition /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/system/CurrentTime.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/system/CurrentTime.kt new file mode 100644 index 000000000..2b3450770 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/system/CurrentTime.kt @@ -0,0 +1,9 @@ +package dev.dres.api.rest.types.system + +/** + * The current timestamp. + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class CurrentTime(val timeStamp: Long = System.currentTimeMillis()) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/system/DresInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/system/DresInfo.kt new file mode 100644 index 000000000..72f1a7cae --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/system/DresInfo.kt @@ -0,0 +1,23 @@ +package dev.dres.api.rest.types.system + +import dev.dres.DRES + +/** + * Information about the DRES instance. + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class DresInfo( + val version: String = DRES.VERSION, + val startTime: Long, + val uptime: Long, + val os: String? = null, + val jvm: String? = null, + val args: String? = null, + val cores: Int? = null, + val freeMemory: Long? = null, + val totalMemory: Long? = null, + val load: Double? = null, + val availableSeverThreads: Int? = null +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt new file mode 100644 index 000000000..5cb7d512a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.users + +import io.javalin.security.RouteRole + +/** + * An enumeration of all roles exposed through the API. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt new file mode 100644 index 000000000..300f528fb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt @@ -0,0 +1,19 @@ +package dev.dres.api.rest.types.users + +import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.User +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context + +/** + * A response surrounding manipulation of users. + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class UserDetails(val id: String, val username: String, val role: Role, val sessionId: String? = null) { + companion object { + fun of(user: User): UserDetails = UserDetails(user.id, user.username, user.role) + fun create(user: User, ctx: Context): UserDetails = UserDetails(user.id, user.username, user.role, ctx.sessionId()) + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt new file mode 100644 index 000000000..7d3ceac1b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.users + +import dev.dres.api.rest.types.users.ApiRole + +/** + * A request surrounding manipulation of users. + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class UserRequest(val username: String, val password: String?, val role: ApiRole?) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 1c63c1096..3da07f9d7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.admin -import dev.dres.api.rest.RestApiRole +import dev.dres.api.rest.types.users.ApiRole import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -16,20 +16,38 @@ class Role(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { val VIEWER by enumField { description = "VIEWER" } val PARTICIPANT by enumField { description = "PARTICIPANT" } - val JUDGE by enumField { description = "JDUGE" } + val JUDGE by enumField { description = "JUDGE" } val ADMIN by enumField { description = "ADMIN" } /** - * Generates and returns the [Role] that corresponds to the given [RestApiRole]. + * Returns a list of all [Role] values. * - * @param role [RestApiRole] + * @return List of all [Role] values. */ - fun fromRestRole(role: RestApiRole): Role = when(role) { - RestApiRole.ANYONE, - RestApiRole.VIEWER -> VIEWER - RestApiRole.PARTICIPANT -> PARTICIPANT - RestApiRole.JUDGE -> JUDGE - RestApiRole.ADMIN -> ADMIN + fun values() = listOf(VIEWER, PARTICIPANT, JUDGE, ADMIN) + + /** + * Parses a [Role] instance from a [String]. + */ + fun parse(string: String) = when(string.uppercase()) { + "VIEWER" -> VIEWER + "PARTICIPANT" -> PARTICIPANT + "JUDGE" -> JUDGE + "ADMIN", "ADMINISTRATOR" -> ADMIN + else -> throw IllegalArgumentException("Failed to parse role '$string'.") + } + + /** + * Generates and returns the [Role] that corresponds to the given [ApiRole]. + * + * @param role [ApiRole] + */ + fun convertApiRole(role: ApiRole): Role = when(role) { + ApiRole.ANYONE, + ApiRole.VIEWER -> VIEWER + ApiRole.PARTICIPANT -> PARTICIPANT + ApiRole.JUDGE -> JUDGE + ApiRole.ADMIN -> ADMIN } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index f1c1a73b5..a5d7a6342 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -1,14 +1,17 @@ package dev.dres.data.model.admin import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.UID +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.team.Team import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.simple.length +import kotlinx.dnq.xdLink0_N import kotlinx.dnq.xdLink1 import kotlinx.dnq.xdRequiredStringProp -typealias UserId = UID +typealias UserId = String /** * A [User] in the DRES user management model. @@ -17,10 +20,21 @@ typealias UserId = UID * @version 2.0.0 */ class User(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() + companion object : XdNaturalEntityType() { + /** The minimum length of a password. */ + const val MIN_LENGTH_PASSWORD = 6 + + /** The minimum length of a username. */ + const val MIN_LENGTH_USERNAME = 4 + } + + /** The [UserId] of this [User]. */ + var userId: UserId + get() = this.id + set(value) { this.id = value } /** The name held by this [User]. Must be unique!*/ - var username by xdRequiredStringProp(unique = true, trimmed = false) { length(4, 16, "Username must consist of between 4 and 16 characters")} + var username by xdRequiredStringProp(unique = true, trimmed = false) { length(MIN_LENGTH_USERNAME, 16, "Username must consist of between 4 and 16 characters")} /** The password held by this [User]. */ var password by xdRequiredStringProp(unique = false, trimmed = false) @@ -28,6 +42,12 @@ class User(entity: Entity): PersistentEntity(entity) { /** The [Role] of this [User]. */ var role by xdLink1(Role) + /** The [Team]s this [User] belongs to. */ + val teams by xdLink0_N(Team::users, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + + /** The [CompetitionDescription]s this [User] acts as judge for. */ + val judges by xdLink0_N(CompetitionDescription::judges, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + /** * The [Password.Hashed] held by this [User]. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt new file mode 100644 index 000000000..42618905c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt @@ -0,0 +1,48 @@ +package dev.dres.data.model.audit + +import dev.dres.data.model.PersistentEntity +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.xdLink1 +import kotlinx.dnq.xdRequiredDateTimeProp +import kotlinx.dnq.xdStringProp + +class AuditLogEntry(entity: Entity): PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The type of [AuditLogEntry]. */ + var type by xdLink1(AuditLogType) + + /** The [AuditLogSource] that generated this [AuditLogEntry]. */ + var source by xdLink1(AuditLogSource) + + /** The timestamp of this [AuditLogEntry]. */ + var timestamp by xdRequiredDateTimeProp() + + /** The ID of the competition this [AuditLogEntry] belongs to. */ + var competitionId by xdStringProp() + + /** The ID of the competition this [AuditLogEntry] belongs to. */ + var taskId by xdStringProp() + + /** The ID of the submission this [AuditLogEntry] belongs to. Only valid if [type] is equal to [AuditLogType.SUBMISSION], [AuditLogType.SUBMISSION_VALIDATION] or [AuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ + var submissionId by xdStringProp() + + /** The submission verdicht this captured by this [AuditLogEntry]. Only valid if [type] is equal to [AuditLogType.SUBMISSION_VALIDATION] or [AuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ + var verdict by xdStringProp() + + /** The name of the submission validator. Only valid if [type] is equal to [AuditLogType.SUBMISSION_VALIDATION]. */ + var validatorName by xdStringProp() + + /** The user ID of the user who generated this [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ + var userId by xdStringProp() + + /** The session ID of the [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ + var session by xdStringProp() + + /** The source address of the [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ + var address by xdStringProp() + + /** Descriptive metadata for this [AuditLogEntry]. */ + var description by xdStringProp() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt new file mode 100644 index 000000000..6a1cd16e8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt @@ -0,0 +1,24 @@ +package dev.dres.data.model.audit + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * Enumeration of the source of a [AuditLogEntry]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class AuditLogSource(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val REST by enumField { description = "REST" } + val CLI by enumField { description = "CLI" } + val INTERNAL by enumField { description = "INTERNAL" } + } + + /** Name / description of the [AuditLogType]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt new file mode 100644 index 000000000..8986d5bb7 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt @@ -0,0 +1,33 @@ +package dev.dres.data.model.audit + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * The [AuditLogEntry] types currently supported by DRES. + * + * @author Luca Rossetto + * @version 2.0.0 + */ +class AuditLogType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val COMPETITION_START by enumField { description = "COMPETITION_START" } + val COMPETITION_END by enumField { description = "COMPETITION_END" } + val TASK_START by enumField { description = "TASK_START" } + val TASK_MODIFIED by enumField { description = "TASK_MODIFIED" } + val TASK_END by enumField { description = "TASK_END" } + val SUBMISSION by enumField { description = "SUBMISSION" } + val PREPARE_JUDGEMENT by enumField { description = "PREPARE_JUDGEMENT" } + val JUDGEMENT by enumField { description = "JUDGEMENT" } + val LOGIN by enumField { description = "LOGIN" } + val LOGOUT by enumField { description = "LOGOUT" } + val SUBMISSION_VALIDATION by enumField { description = "SUBMISSION_VALIDATION" } + val SUBMISSION_STATUS_OVERWRITE by enumField { description = "SUBMISSION_STATUS_OVERWRITE" } + } + + /** Name / description of the [AuditLogType]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt index a34147352..f7c6c7e12 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt @@ -2,9 +2,9 @@ package dev.dres.data.model.basics.media import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdNaturalEntityType -import kotlinx.dnq.xdRequiredStringProp -import kotlinx.dnq.xdStringProp +import kotlinx.dnq.* + +typealias CollectionId = String /** * A named media collection consisting of media items. @@ -14,9 +14,10 @@ import kotlinx.dnq.xdStringProp */ class MediaCollection(entity: Entity): PersistentEntity(entity) { companion object : XdNaturalEntityType() - val name: String by xdRequiredStringProp(unique = true, trimmed = false) - val path: String by xdRequiredStringProp(unique = true, trimmed = false) - val description: String? by xdStringProp(trimmed = false) + var name: String by xdRequiredStringProp(unique = true, trimmed = false) + var path: String by xdRequiredStringProp(unique = true, trimmed = false) + var description: String? by xdStringProp(trimmed = false) + val items by xdChildren0_N(MediaItem::collection) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt index fb137a8b7..cab245b5d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt @@ -31,8 +31,8 @@ class MediaItem(entity: Entity) : PersistentEntity(entity) { var type by xdLink1(MediaType) /** The [MediaCollection] this [MediaItem] belongs to. */ - var collection by xdLink1(MediaCollection) + var collection: MediaCollection by xdParent(MediaCollection::items) /** List of [MediaItemSegment] that this [MediaItem] contains. */ - val segments by xdLink0_N(MediaItemSegment::item) + val segments by xdChildren0_N(MediaItemSegment::item) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt index 9ffbb9619..f9d6233a9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt @@ -4,11 +4,8 @@ import dev.dres.data.model.PersistentEntity import dev.dres.data.model.basics.time.TemporalPoint import dev.dres.data.model.basics.time.TemporalRange import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.* import kotlinx.dnq.simple.min -import kotlinx.dnq.xdLink1 -import kotlinx.dnq.xdRequiredIntProp -import kotlinx.dnq.xdRequiredStringProp /** * A segment of a [MediaItem] as mostly used by items that exhibit temporal progression. @@ -23,7 +20,7 @@ class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [MediaType] of this [MediaItem]. */ - var item by xdLink1(MediaItem) + var item by xdParent(MediaItem::segments) /** The start frame number of this [MediaItemSegment]. */ var startFrame by xdRequiredIntProp() { min(0) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt index 5bd6c2016..1f27a0d42 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt @@ -1,5 +1,9 @@ package dev.dres.data.model.basics.media +import dev.dres.api.rest.ApiRole +import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.RestMediaItemType +import dev.dres.data.model.admin.Role import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -15,6 +19,16 @@ class MediaType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { val IMAGE by enumField { description = "IMAGE" } val VIDEO by enumField { description = "VIDEO" } + + /** + * Generates and returns the [MediaType] that corresponds to the given [RestMediaItemType]. + * + * @param type [RestMediaItemType] + */ + fun convertApiType(type: RestMediaItemType): MediaType = when(type) { + RestMediaItemType.IMAGE -> MediaType.IMAGE + RestMediaItemType.VIDEO -> MediaType.VIDEO + } } var description by xdRequiredStringProp(unique = true) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt index eb6090740..f87eeb21b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt @@ -1,24 +1,50 @@ package dev.dres.data.model.competition import com.fasterxml.jackson.annotation.JsonIgnore +import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID +import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserId +import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.competition.team.TeamGroupId import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.link.OnDeletePolicy +import java.nio.file.Paths -data class CompetitionDescription( - override var id: UID, - val name: String, - val description: String?, +/** + * + */ +class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ + companion object: XdNaturalEntityType() + + /** The name held by this [CompetitionDescription]. Must be unique!*/ + var name by xdRequiredStringProp(unique = true, trimmed = false) + + /** An optional description of this [CompetitionDescription]. */ + var description by xdStringProp(trimmed = false) + + /** The [TaskGroup]s that are part of this [CompetitionDescription]. */ + val taskGroups by xdChildren0_N(TaskGroup::competition) + + /** The [Team]s that are part of this [CompetitionDescription]. */ + val teams by xdChildren0_N(Team::competition) + + /** The [Team]s that are part of this [CompetitionDescription]. */ + val teamsGroups by xdChildren0_N(TeamGroup::competition) + + /** The [User]s that act as judge for this [CompetitionDescription] */ + val judges by xdLink0_N(User::judges, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + + /* val taskTypes: MutableList, - val taskGroups: MutableList, val tasks: MutableList, - val teams: MutableList, - val teamGroups: MutableList, - val judges: MutableList -) : PersistentEntity { + */ fun validate() { for (group in this.taskGroups) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt index 4363f1103..11b976afa 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt @@ -3,9 +3,4 @@ package dev.dres.data.model.competition import dev.dres.data.model.UID /** The ID of a [TaskDescription]. */ -typealias TaskDescriptionId = UID - -/** The ID of a [Team]. */ -typealias TeamId = UID - -typealias TeamGroupId = UID \ No newline at end of file +typealias TaskDescriptionId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt index ce57db8af..8b604a3d4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt @@ -5,29 +5,49 @@ import dev.dres.api.rest.types.task.TaskHint import dev.dres.api.rest.types.task.TaskTarget import dev.dres.data.dbo.DAO import dev.dres.data.model.Config +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.interfaces.SubmissionFilterFactory import dev.dres.data.model.competition.interfaces.TaskScorerFactory +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.run.interfaces.Task import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* import java.io.FileNotFoundException import java.io.IOException import java.io.PrintStream import kotlin.math.max +import kotlin.math.min /** * Basic description of a [Task] as executed in DRES. Defines basic attributes such as its name, its duration, * the [TaskDescriptionTarget] and the [TaskDescriptionHint]s, that should be presented to the user. * - * @version 1.0.2 + * @version 2.0.0 * @author Luca Rossetto & Ralph Gasser */ -class TaskDescription( +class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { + companion object: XdNaturalEntityType() - /** Internal, unique ID of this [TaskDescription]. */ + + /** The name held by this [Team]. Must be unique!*/ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [TaskGroup] this [TaskDescription] belongs to. */ + var taskGroup by xdParent(TaskGroup::tasks) + + /** The [MediaCollection] this [TaskDescription] operates upon. */ + var collection by xdLink1(MediaCollection) + + /** The duration of the [TaskDescription] in seconds. */ + var duration by xdRequiredLongProp<> { min(0L) } + + + /* /** Internal, unique ID of this [TaskDescription]. */ val id: TaskDescriptionId, /** The name of the task */ @@ -49,9 +69,7 @@ class TaskDescription( val target: TaskDescriptionTarget, /** List of [TaskDescriptionHint]s that act as clues to find the target media. */ - val hints: List - -): TaskScorerFactory, SubmissionFilterFactory { + val hints: List*/ /** * Generates a new [TaskScorer] for this [TaskDescription]. Depending diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt index c01156111..f056ad466 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt @@ -1,9 +1,29 @@ package dev.dres.data.model.competition +import dev.dres.data.model.PersistentEntity +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.xdChildren0_N +import kotlinx.dnq.xdParent +import kotlinx.dnq.xdRequiredStringProp + /** * A [TaskGroup] allows the user to specify common traits among a group of [Task]s. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0 + * @version 2.0.0 */ -data class TaskGroup constructor(val name: String, val type: String) \ No newline at end of file +class TaskGroup(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The name of this [TaskGroup]. */ + var name: String by xdRequiredStringProp(false, false) + + var type: String by xdRequiredStringProp(false, false) + + /** The [CompetitionDescription] this [TaskGroup] belongs to. */ + var competition by xdParent(CompetitionDescription::taskGroups) + + /** The [TaskDescription]s contained in this [TaskGroup]*/ + val tasks by xdChildren0_N(TaskDescription::taskGroup) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt deleted file mode 100644 index 2452fc6ba..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/Team.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dev.dres.data.model.competition - -import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.admin.UserId -import java.nio.file.Path -import java.nio.file.Paths - -/** - * Represents a [Team] that takes part in a competition managed by DRES. - * - * @author Ralph Gasser, Loris Sauter, Luca Rossetto - * @version 1.2.0 - */ -data class Team constructor(val uid: TeamId, val name: String, val color: String, val logoId: UID, val users: MutableList) { - companion object { - /** - * Generates and returns the [Path] to the team logo with the given [logoId]. - * - * @param logoId The ID of the desired logo. - * @param config The global [Config] used to construct the [Path]. - */ - fun logoPath(config: Config, logoId: UID) = Paths.get(config.cachePath, "logos", "${logoId.string}.png") - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TeamGroup.kt deleted file mode 100644 index b53a44397..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TeamGroup.kt +++ /dev/null @@ -1,67 +0,0 @@ -package dev.dres.data.model.competition - -data class TeamGroup( - val uid: TeamGroupId, - val name: String, - val teams: List, - val aggregation: TeamGroupAggregation -) { - fun newAggregator() : TeamAggregator = aggregation.aggregationFactory(teams) -} - -abstract class TeamAggregator internal constructor(teams: List) { - - private val teamIds = teams.map { it.uid }.toSet() - - var lastValue: Double = 0.0 - private set - - internal abstract fun computeAggregation(teamScores: Map): Double - - @Synchronized - fun aggregate(teamScores: Map) : Double{ - this.lastValue = computeAggregation(teamScores.filter { it.key in teamIds }) - return lastValue - } - -} - -class MaxTeamAggregator(teams: List) : TeamAggregator(teams) { - override fun computeAggregation(teamScores: Map): Double = - teamScores.map { it.value }.maxOrNull() ?: 0.0 -} - -class MinTeamAggregator(teams: List) : TeamAggregator(teams) { - override fun computeAggregation(teamScores: Map): Double = - teamScores.map { it.value }.minOrNull() ?: 0.0 -} - -class MeanTeamAggregator(teams: List) : TeamAggregator(teams) { - override fun computeAggregation(teamScores: Map): Double = - if (teamScores.isEmpty()) 0.0 else teamScores.map { it.value }.sum() / teamScores.size -} - -class LastTeamAggregator(teams: List) : TeamAggregator(teams) { - - private val lastScores = mutableListOf>() - - override fun computeAggregation(teamScores: Map): Double { - teamScores.forEach{ - if (lastScores.find { p -> p.first == it.key }?.second != it.value) { - lastScores.removeIf { p -> p.first == it.key } - lastScores.add(it.key to it.value) - } - } - - return lastScores.lastOrNull()?.second ?: 0.0 - - } - -} - -enum class TeamGroupAggregation(internal val aggregationFactory: (List) -> TeamAggregator) { - MAX(::MaxTeamAggregator), - MIN(::MinTeamAggregator), - MEAN(::MeanTeamAggregator), - LAST(::LastTeamAggregator) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt index 7c280fb28..d80b47d0c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.competition.options /** - * A concrete instance for an [Option] including named paramters. + * A concrete instance for an [Option] including named parameters. * * @author Luca Rossetto & Ralph Gasser * @version 1.1.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt new file mode 100644 index 000000000..f4440b95b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt @@ -0,0 +1,57 @@ +package dev.dres.data.model.competition.team + +import dev.dres.data.model.Config +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.admin.User +import dev.dres.data.model.competition.CompetitionDescription +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.link.OnDeletePolicy +import java.nio.file.Paths + +/** The ID of a [Team]. */ +typealias TeamId = String + +/** The ID of a [Team]'s logo. */ +typealias LogoId = String + +/** + * Represents a [Team] that takes part in a competition managed by DRES. + * + * @author Ralph Gasser, Loris Sauter, Luca Rossetto + * @version 2.0.0 + */ +class Team(entity: Entity) : PersistentEntity(entity) { + companion object: XdNaturalEntityType() { + /** + * Generates and returns the [Path] to the team logo with the given [logoId]. + * + * @param config The global [Config] used to construct the [Path]. + * @param logoId The ID of the desired logo. + */ + fun logoPath(config: Config, logoId: LogoId) = Paths.get(config.cachePath, "logos", "${logoId}.png") + } + + /** The [UserId] of this [User]. */ + var teamId: TeamId + get() = this.id + set(value) { this.id = value } + + /** The name held by this [Team]. Must be unique!*/ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The password held by this [User]. */ + var color by xdRequiredStringProp(unique = false, trimmed = false) + + /** The password held by this [User]. */ + var logoId by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [CompetitionDescription] this [Team] belongs to. */ + var competition by xdParent(CompetitionDescription::teams) + + /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ + var group by xdLink0_1(TeamGroup::teams) + + /** The [User]s that belong to this [Team]. */ + val users by xdLink0_N(User::teams, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt new file mode 100644 index 000000000..ca30630b6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt @@ -0,0 +1,25 @@ +package dev.dres.data.model.competition.team + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ +class TeamAggregator(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val MAX by enumField { description = "MAX" } + val MIN by enumField { description = "MIN" } + val MEAN by enumField { description = "MEAN" } + val LAST by enumField { description = "LAST" } + } + + /** Name / description of the [TeamAggregator]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt new file mode 100644 index 000000000..b542ed7d1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt @@ -0,0 +1,56 @@ +package dev.dres.data.model.competition.team + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +sealed class TeamAggregatorImpl constructor(teams: List) { + + private val teamIds = teams.map { it }.toSet() + + var lastValue: Double = 0.0 + private set + + internal abstract fun computeAggregation(teamScores: Map): Double + + @Synchronized + fun aggregate(teamScores: Map) : Double{ + this.lastValue = computeAggregation(teamScores.filter { it.key in teamIds }) + return lastValue + } + + class Max(teams: List) : TeamAggregatorImpl(teams) { + override fun computeAggregation(teamScores: Map): Double = + teamScores.map { it.value }.maxOrNull() ?: 0.0 + } + + class Min(teams: List) : TeamAggregatorImpl(teams) { + override fun computeAggregation(teamScores: Map): Double = + teamScores.map { it.value }.minOrNull() ?: 0.0 + } + + class Mean(teams: List) : TeamAggregatorImpl(teams) { + override fun computeAggregation(teamScores: Map): Double = + if (teamScores.isEmpty()) 0.0 else teamScores.map { it.value }.sum() / teamScores.size + } + + class Last(teams: List) : TeamAggregatorImpl(teams) { + private val lastScores = mutableListOf>() + override fun computeAggregation(teamScores: Map): Double { + teamScores.forEach{ + if (lastScores.find { p -> p.first == it.key }?.second != it.value) { + lastScores.removeIf { p -> p.first == it.key } + lastScores.add(it.key to it.value) + } + } + return lastScores.lastOrNull()?.second ?: 0.0 + } + } +} + + + + + + diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt new file mode 100644 index 000000000..f91100dd1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt @@ -0,0 +1,39 @@ +package dev.dres.data.model.competition.team + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.admin.User +import dev.dres.data.model.competition.CompetitionDescription +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* + +typealias TeamGroupId = String + +/** + * Represents a [TeamGroup] that takes part in a competition managed by DRES. + * + * Can be used to aggregate score values across [Team]s + * + * @author Luca Rossetto + * @version 1.0.0 + */ +class TeamGroup(entity: Entity) : PersistentEntity(entity) { + + companion object: XdNaturalEntityType() + + /** The [UserId] of this [User]. */ + var teamGroupId: TeamGroupId + get() = this.id + set(value) { this.id = value } + + /** The name held by this [User]. Must be unique!*/ + var name by xdRequiredStringProp(unique = true, trimmed = false) + + /** The default [TeamAggregator] to use for this [TeamGroup]. */ + var defaultAggregator by xdLink1(TeamAggregator) + + /** The [CompetitionDescription] this [Team] belongs to. */ + var competition by xdParent(CompetitionDescription::teamsGroups) + + /** The [Team]s that belong to this [TeamGroup]. */ + val teams by xdLink0_N(Team::group) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index 0d55a7700..75d6f6287 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -302,7 +302,7 @@ class InteractiveAsynchronousCompetition( /* Process Submission. */ this.submissions.add(submission) this.validator.validate(submission) - AuditLogger.validateSubmission(submission, validator) + AuditLogger.validateSubmission(submission, this.validator) } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index d0d1d515b..18cd14e33 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -153,7 +153,7 @@ class InteractiveSynchronousCompetition( /* Process Submission. */ this.submissions.add(submission) this.validator.validate(submission) - AuditLogger.validateSubmission(submission, validator) + AuditLogger.validateSubmission(submission, this.validator) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 32270d8ef..4283b1ba7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -34,8 +34,8 @@ data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: * @param runManager The [RunManager] to construct the [RunActionContext] for. */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.fromRestRole(it) }.toSet() + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) + val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.convertApiRole(it) }.toSet() val teamId = runManager.description.teams.find { it.users.contains(userId) }?.uid return RunActionContext(userId, teamId, roles) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt index b92e5ba5a..c4f43ded3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt @@ -5,6 +5,5 @@ import dev.dres.data.model.UID /** The ID of a [Task]. */ typealias TaskId = UID - /** The ID of a [Competition]. */ typealias CompetitionId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt index c37c211bf..ae4fa0ae0 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt @@ -1,13 +1,12 @@ package dev.dres.data.serializers -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.competition.TeamGroup import dev.dres.data.model.competition.TeamGroupAggregation import dev.dres.utilities.extensions.readUID import dev.dres.utilities.extensions.writeUID import org.mapdb.DataInput2 import org.mapdb.DataOutput2 -import org.mapdb.Serializer object TeamGroupSerializer { fun serialize(out: DataOutput2, value: TeamGroup) { diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt index 8524ab6d8..57930916d 100644 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt +++ b/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt @@ -1,6 +1,6 @@ package dev.dres.data.serializers -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.utilities.extensions.readUID import dev.dres.utilities.extensions.writeUID import org.mapdb.DataInput2 diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index a74c1b12a..371343f9f 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -1,6 +1,6 @@ package dev.dres.mgmt.admin -import dev.dres.api.rest.handler.UserRequest +import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.UID import dev.dres.data.model.admin.* import jetbrains.exodus.database.TransientEntityStore @@ -14,8 +14,6 @@ import kotlinx.dnq.query.* */ object UserManager { - const val MIN_LENGTH_PASSWORD = 6 - /** The [TransientEntityStore] instance used by this [UserManager]. */ private lateinit var store: TransientEntityStore @@ -59,16 +57,16 @@ object UserManager { } /** - * Updates a [User] with the given [UID], [username], [password] and [role]. + * Updates a [User] with the given [UserId], [username], [password] and [role]. * - * @param id The [UID] of the user to update. + * @param id The [UserId] of the user to update. * @param username The name of the [User]. Must be unique. * @param password The [Password.Hashed] of the user. * @param role The [Role] of the new user. */ - fun update(id: UID?, username: String?, password: Password.Hashed?, role: Role?): Boolean = this.store.transactional { + fun update(id: UserId?, username: String?, password: Password.Hashed?, role: Role?): Boolean = this.store.transactional { val user = if (id != null) { - User.query(User::id eq id.string).firstOrNull() + User.query(User::id eq id).firstOrNull() } else if (username != null) { User.query(User::username eq username).firstOrNull() } else { @@ -82,50 +80,40 @@ object UserManager { } /** - * Updates a [User] with the given [UID], [username], [password] and [role]. + * Updates a [User] with the given [UserId], [username], [password] and [role]. * - * @param id The [UID] of the user to update. + * @param id The [UserId] of the user to update. * @param username The name of the [User]. Must be unique. * @param password The [Password.Plain] of the user. * @param role The [Role] of the new user. */ - fun update(id: UID?, username: String?, password: Password.Plain?, role: Role?): Boolean + fun update(id: UserId?, username: String?, password: Password.Plain?, role: Role?): Boolean = update(id, username, password?.hash(), role) /** * Updates a [User] for the given [id] based o the [request]. * - * @param id The [UID] of the user to update. + * @param id The [UserId] of the user to update. * @param request The [UserRequest] detailing the update * @return True on success, false otherwise. */ - fun update(id: UID?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.let { Role.fromRestRole(it) }) + fun update(id: UserId?, request: UserRequest): Boolean + = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.let { Role.convertApiRole(it) }) /** - * Deletes the [User] for the given [UID]. + * Deletes the [User] for the given [UserId]. * * @param username The name of the [User] to delete. * @return True on success, false otherwise. */ - fun delete(username: String): Boolean = this.store.transactional { - val user = User.query(User::username eq username).firstOrNull() - if (user != null) { - user.delete() - true + fun delete(id: UserId? = null, username: String? = null): Boolean = this.store.transactional { + val user = if (id != null) { + User.query(User::id eq id).firstOrNull() + } else if (username != null) { + User.query(User::username eq username).firstOrNull() } else { - false + null } - } - - /** - * Deletes the [User] for the given [UID]. - * - * @param id The [UID] of the [User] to delete. - * @return True on success, false otherwise. - */ - fun delete(id: UID):Boolean = this.store.transactional { - val user = User.query(User::id eq id.string).firstOrNull() if (user != null) { user.delete() true @@ -149,18 +137,14 @@ object UserManager { * @param id [UID] to check. * @return True if [User] exists, false otherwise. */ - fun exists(id: UID): Boolean = this.store.transactional(readonly = true) { - User.query(User::id eq id.string).isNotEmpty - } - - /** - * Checks for the existence of the [User] with the given [UID]. - * - * @param username User name to check. - * @return True if [User] exists, false otherwise. - */ - fun exists(username: String): Boolean = this.store.transactional(readonly = true) { - User.query(User::username eq username).isNotEmpty + fun exists(id: UserId? = null, username: String? = null): Boolean = this.store.transactional(readonly = true) { + if (id != null) { + User.query(User::id eq id).isNotEmpty + } else if (username != null) { + User.query(User::username eq username).isNotEmpty + } else { + throw IllegalArgumentException("Either user ID or username must be non-null!") + } } /** @@ -169,25 +153,21 @@ object UserManager { * @param id The [UID] of the [User] to fetch. * @return [User] or null */ - fun get(id: UID): User? = this.store.transactional(readonly = true) { - User.query(User::id eq id.string).firstOrNull() - } - - /** - * Returns the [User] for the given [username] or null if [User] doesn't exist. - * - * @param username The name of the [User] to fetch. - * @return [User] or null - */ - fun get(username: String): User? = this.store.transactional(readonly = true) { - User.query(User::username eq username).firstOrNull() + fun get(id: UserId? = null, username: String? = null): User? = this.store.transactional(readonly = true) { + if (id != null) { + User.query(User::id eq id).firstOrNull() + } else if (username != null) { + User.query(User::username eq username).firstOrNull() + } else { + null + } } /** * Either returns a user for this username/password tuple or null */ fun getMatchingUser(username: String, password: Password.Plain) : User? { - val user = get(username) + val user = get(null, username) return if (user?.hashedPassword()?.check(password) == true) user else null } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 629df3b87..3ffc29984 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID import dev.dres.data.model.admin.Role +import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.TeamId @@ -722,7 +723,7 @@ class InteractiveAsynchronousRunManager( val task = this.run.currentTaskForTeam(teamId) ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") task.start() - AuditLogger.taskStart(this.id, task.uid, task.description, LogEventSource.INTERNAL, null) + AuditLogger.taskStart(this.id, task.uid, task.description, AuditLogSource.REST, null) this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_START), teamId) } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index e54ceda52..781be9b41 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID +import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription import dev.dres.data.model.competition.options.ConfiguredOption @@ -18,7 +19,6 @@ import dev.dres.data.model.run.RunProperties import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent import dev.dres.run.exceptions.IllegalRunStateException @@ -634,7 +634,7 @@ class InteractiveSynchronousRunManager( this.stateLock.write { this.run.currentTask!!.start() //this.status = RunManagerStatus.RUNNING_TASK - AuditLogger.taskStart(this.id, this.run.currentTask!!.uid, this.run.currentTaskDescription, LogEventSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.run.currentTask!!.uid, this.run.currentTaskDescription, AuditLogSource.INTERNAL, null) } /* Mark DAO for update. */ @@ -655,7 +655,7 @@ class InteractiveSynchronousRunManager( this.stateLock.write { task.end() //this.status = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, this.run.currentTask!!.uid, this.run.currentTaskDescription, LogEventSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, this.run.currentTask!!.uid, AuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.uid)) } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 3038c8e68..27daf41b5 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -7,19 +7,20 @@ import dev.dres.api.rest.types.run.websocket.ClientMessage import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType -import dev.dres.data.dbo.DAO import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.InteractiveAsynchronousCompetition import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.NonInteractiveCompetition import dev.dres.data.model.run.interfaces.Competition +import dev.dres.run.audit.AuditLogger import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write import io.javalin.websocket.WsConfig import io.javalin.websocket.WsContext +import jetbrains.exodus.database.TransientEntityStore import org.slf4j.LoggerFactory import java.io.File import java.util.* @@ -62,19 +63,21 @@ object RunExecutor : Consumer { /** Internal array of [Future]s for cleaning after [RunManager]s. See [RunExecutor.cleanerThread]*/ private val results = HashMap, UID>() - /** Instance of shared [DAO] used to access [InteractiveSynchronousCompetition]s. */ - lateinit var runs: DAO + /** The [TransientEntityStore] instance used by this [AuditLogger]. */ + private lateinit var store: TransientEntityStore /** * Initializes this [RunExecutor]. * - * @param runs The shared [DAO] used to access [InteractiveSynchronousCompetition]s. + * @param store The shared [TransientEntityStore]. */ - fun init(runs: DAO) { - this.runs = runs - this.runs.filter { !it.hasEnded }.forEach { //TODO needs more distinction + fun init(store: TransientEntityStore) { + this.store = store + /* TODO: Schedule runs that have not ended + * this.runs.filter { !it.hasEnded }.forEach { //TODO needs more distinction schedule(it) } + */ } fun schedule(competition: Competition) { @@ -268,7 +271,7 @@ object RunExecutor : Consumer { this.runManagerLock.read { this.connectedClients.filter { - this.observingClients[runId]?.contains(it) ?: false && AccessManager.getUserIdForSession(it.httpSessionId) in teamMembers + this.observingClients[runId]?.contains(it) ?: false && AccessManager.userIdForSession(it.httpSessionId) in teamMembers }.forEach { it.send(message) } diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt deleted file mode 100644 index ac9cd8a9e..000000000 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt +++ /dev/null @@ -1,85 +0,0 @@ -package dev.dres.run.audit - -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.UID -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus - -enum class AuditLogEntryType { - - COMPETITION_START, - COMPETITION_END, - TASK_START, - TASK_MODIFIED, - TASK_END, - SUBMISSION, - PREPARE_JUDGEMENT, - JUDGEMENT, - LOGIN, - LOGOUT, - SUBMISSION_VALIDATION, - SUBMISSION_STATUS_OVERWRITE - -} - -enum class LogEventSource { - REST, - CLI, - INTERNAL -} - -sealed class AuditLogEntry(val type: AuditLogEntryType): PersistentEntity{ - - var timestamp: Long = System.currentTimeMillis() - internal set - - -} - -data class CompetitionStartAuditLogEntry(override var id: UID, val competition: UID, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.COMPETITION_START){ - constructor(competition: UID, api: LogEventSource, user: String?): this(UID.EMPTY, competition, api, user) -} - -data class CompetitionEndAuditLogEntry(override var id: UID, val competition: UID, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.COMPETITION_END){ - constructor(competition: UID, api: LogEventSource, user: String?): this(UID.EMPTY, competition, api, user) -} - -data class TaskStartAuditLogEntry(override var id: UID, val competition: UID, val taskName: String, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.TASK_START){ - constructor(competition: UID, taskName: String, api: LogEventSource, user: String?): this(UID.EMPTY, competition, taskName, api, user) -} - -data class TaskModifiedAuditLogEntry(override var id: UID, val competition: UID, val taskName: String, val modification: String, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.TASK_MODIFIED) { - constructor(competition: UID, taskName: String, modification: String, api: LogEventSource, user: String?): this(UID.EMPTY, competition, taskName, modification, api, user) -} - -data class TaskEndAuditLogEntry(override var id: UID, val competition: UID, val taskName: String, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.TASK_END){ - constructor(competition: UID, taskName: String, api: LogEventSource, user: String?): this(UID.EMPTY, competition, taskName, api, user) -} - -data class SubmissionAuditLogEntry(override var id: UID, val competition: UID, val taskName: String, val submission: Submission, val api: LogEventSource, val user: String?, val address: String) : AuditLogEntry(AuditLogEntryType.SUBMISSION){ - constructor(competition: UID, taskName: String, submission: Submission, api: LogEventSource, user: String?, address: String): this(UID.EMPTY, competition, taskName, submission, api, user, address) -} - -data class PrepareJudgementAuditLogEntry(override var id: UID, val validator: String, val token: String, val submission: Submission) : AuditLogEntry(AuditLogEntryType.PREPARE_JUDGEMENT){ - constructor(validator: String, token: String, submission: Submission): this(UID.EMPTY, validator, token, submission) -} - -data class JudgementAuditLogEntry(override var id: UID, val competition: UID, val validator: String, val token: String, val verdict: SubmissionStatus, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.JUDGEMENT) { - constructor(competition: UID, validator: String, token: String, verdict: SubmissionStatus, api: LogEventSource, user: String?): this(UID.EMPTY, competition, validator, token, verdict, api, user) -} - -data class LoginAuditLogEntry(override var id: UID, val user: String, val session: String, val api: LogEventSource) : AuditLogEntry(AuditLogEntryType.LOGIN) { - constructor(user: String, session: String, api: LogEventSource): this(UID.EMPTY, user, session, api) -} - -data class LogoutAuditLogEntry(override var id: UID, val session: String, val api: LogEventSource) : AuditLogEntry(AuditLogEntryType.LOGOUT) { - constructor(session: String, api: LogEventSource): this(UID.EMPTY, session, api) -} - -data class SubmissionValidationAuditLogEntry(override var id: UID, val submission: Submission, val validatorName: String, val status: SubmissionStatus) : AuditLogEntry(AuditLogEntryType.SUBMISSION_VALIDATION) { - constructor(submission: Submission, validatorName: String, status: SubmissionStatus) : this(UID.EMPTY, submission, validatorName, status) -} - -data class SubmissionStatusOverwriteAuditLogEntry(override var id: UID, val competitionRunUid: UID, val submissionId: UID, val newVerdict: SubmissionStatus, val api: LogEventSource, val user: String?) : AuditLogEntry(AuditLogEntryType.SUBMISSION_STATUS_OVERWRITE) { - constructor(competitionRunUid: UID, submissionId: UID, newVerdict: SubmissionStatus, api: LogEventSource, user: String?) : this(UID.EMPTY, competitionRunUid, submissionId, newVerdict, api, user) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 9024c5fb1..eaba7a6ba 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -1,85 +1,295 @@ package dev.dres.run.audit -import dev.dres.data.dbo.DAO +import dev.dres.api.rest.handler.SessionId import dev.dres.data.model.UID +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.AuditLogType import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.eventstream.* +import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionValidator -import org.slf4j.LoggerFactory -import org.slf4j.Marker -import org.slf4j.MarkerFactory +import jetbrains.exodus.database.TransientEntityStore +import org.joda.time.DateTime +/** + * Audit logging instance of DRES. Requires one-time initialisation + * + * @author Luca Rossetto + * @version 2.0.0 + */ object AuditLogger { + /** The [TransientEntityStore] instance used by this [AuditLogger]. */ + private lateinit var store: TransientEntityStore - private val logMarker: Marker = MarkerFactory.getMarker("AUDIT") - - private val logger = LoggerFactory.getLogger(this.javaClass) - - private lateinit var dao: DAO - - fun init(dao: DAO) { - this.dao = dao + /** Initializes this [AuditLogger]. */ + fun init(store: TransientEntityStore) { + this.store = store } - private fun log(entry: AuditLogEntry){ - dao.append(entry) - logger.info(logMarker, "Audit event: $entry") + /** + * Logs the start of a DRES competition. + * + * @param description The [CompetitionDescription]. + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun competitionStart(competitionId: CompetitionId, description: CompetitionDescription, api: AuditLogSource, session: SessionId?) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.COMPETITION_START + this.source = api + this.timestamp = DateTime.now() + this.competitionId = competitionId.string + this.session = session + } + } + EventStreamProcessor.event(RunStartEvent(competitionId, description)) } - fun competitionStart(competitionRunUid: UID, competitionDescription: CompetitionDescription, api: LogEventSource, session: String?) { - log(CompetitionStartAuditLogEntry(competitionRunUid, api, session)) - EventStreamProcessor.event( - RunStartEvent(competitionRunUid, competitionDescription) - ) + /** + * Logs the end of a DRES competition. + * + * @param competitionId [UID] that identifies the competition + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun competitionEnd(competitionId: CompetitionId, api: AuditLogSource, session: SessionId?) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.COMPETITION_END + this.source = api + this.timestamp = DateTime.now() + this.competitionId = competitionId.string + this.session = session + } + } + EventStreamProcessor.event(RunEndEvent(competitionId)) } - fun competitionEnd(competitionRunUid: UID, api: LogEventSource, session: String?) { - log(CompetitionEndAuditLogEntry(competitionRunUid, api, session)) - EventStreamProcessor.event( - RunEndEvent(competitionRunUid) - ) + /** + * Logs the start of a DRES task. + * + * @param competitionId [UID] that identifies the competition + * @param taskId [UID] that identifies the task + * @param description The [TaskDescription]. + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun taskStart(competitionId: CompetitionId, taskId: TaskId, description: TaskDescription, api: AuditLogSource, session: SessionId?) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.TASK_START + this.source = api + this.timestamp = DateTime.now() + this.competitionId = competitionId.string + this.taskId = taskId.string + this.session = session + } + } + EventStreamProcessor.event(TaskStartEvent(competitionId, taskId, description)) } - fun taskStart(competitionRunUid: UID, taskId: TaskId, taskDescription: TaskDescription, api: LogEventSource, session: String?) { - log(TaskStartAuditLogEntry(competitionRunUid, taskDescription.name, api, session)) - EventStreamProcessor.event( - TaskStartEvent( - competitionRunUid, - taskId, - taskDescription - ) - ) + /** + * Logs the start of a DRES task. + * + * @param competitionId [UID] that identifies the competition + * @param taskId [UID] that identifies the task + * @param modification Description of the modification. + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun taskModified(competitionId: CompetitionId, taskId: TaskId, modification: String, api: AuditLogSource, session: String?) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.TASK_MODIFIED + this.source = api + this.timestamp = DateTime.now() + this.competitionId = competitionId.string + this.taskId = taskId.string + this.description = modification + this.session = session + } + } } - fun taskModified(competitionRunUid: UID, taskName: String, modification: String, api: LogEventSource, session: String?) = log(TaskModifiedAuditLogEntry(competitionRunUid, taskName, modification, api, session)) - - fun taskEnd(competitionRunUid: UID, taskId: TaskId, taskDescription: TaskDescription, api: LogEventSource, session: String?) { - log(TaskEndAuditLogEntry(competitionRunUid, taskDescription.name, api, session)) - EventStreamProcessor.event( - TaskEndEvent(competitionRunUid, taskId) - ) + /** + * Logs the end of a DRES task. + * + * @param competitionId [UID] that identifies the competition + * @param taskId [UID] that identifies the task + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun taskEnd(competitionId: CompetitionId, taskId: TaskId, api: AuditLogSource, session: SessionId?) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.TASK_END + this.source = api + this.timestamp = DateTime.now() + this.competitionId = competitionId.string + this.taskId = taskId.string + this.session = session + } + } + EventStreamProcessor.event(TaskEndEvent(competitionId, taskId)) } - fun submission(competitionRunUid: UID, taskName: String, taskId: TaskId?, submission: Submission, api: LogEventSource, session: String?, address: String) { - log(SubmissionAuditLogEntry(competitionRunUid, taskName, submission, api, session, address)) - EventStreamProcessor.event( - SubmissionEvent(session ?: "N/A", competitionRunUid, taskId, submission) - ) + /** + * Logs an incoming submission to DRES. + * + * @param submission The [Submission] that was registered. + * @param api The [AuditLogSource] + * @param sessionId The identifier of the user session. + * @param address The IP address of the submitter. + */ + fun submission(submission: Submission, api: AuditLogSource, sessionId: SessionId?, address: String) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.SUBMISSION + this.source = api + this.timestamp = DateTime.now() + this.submissionId = submission.uid.string + this.competitionId = submission.task?.competition?.id?.string + this.taskId = submission.task?.uid?.string + this.submissionId = submission.uid.string + this.session = sessionId + this.address = address + } + } + EventStreamProcessor.event(SubmissionEvent(sessionId ?: "na", submission.task?.competition?.id!!, submission.task?.uid, submission)) } - fun validateSubmission(submission: Submission, validator: SubmissionValidator) = log(SubmissionValidationAuditLogEntry(submission, validator::class.simpleName ?: "unknown validator", submission.status)) + /** + * Logs the validation of a [Submission] to DRES. + * + * @param submission The [Submission] the submission that was validated + * @param validator The [SubmissionValidator] instance. + */ + fun validateSubmission(submission: Submission, validator: SubmissionValidator) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.SUBMISSION_VALIDATION + this.source = AuditLogSource.INTERNAL + this.timestamp = DateTime.now() + this.submissionId = submission.uid.string + this.competitionId = submission.task?.competition?.id?.string + this.taskId = submission.task?.uid?.string + this.verdict = submission.status.toString() + this.validatorName = validator::class.simpleName + } + } + } - fun prepareJudgement(validator: String, token: String, submission: Submission) = log(PrepareJudgementAuditLogEntry(validator, token, submission)) + /** + * Logs a submission override to DRES. + * + * @param submission The [Submission] that was overriden (new snapshot). + * @param api The [AuditLogSource] + * @param sessionId The identifier of the user session. + */ + fun overrideSubmission(submission: Submission, api: AuditLogSource, sessionId: SessionId?) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.SUBMISSION_STATUS_OVERWRITE + this.source = api + this.timestamp = DateTime.now() + this.submissionId = submission.uid.string + this.competitionId = submission.task?.competition?.id?.string + this.taskId = submission.task?.uid?.string + this.verdict = submission.status.toString() + this.session = sessionId + } + } + } - fun judgement(competitionRunUid: UID, validator: String, token: String, verdict: SubmissionStatus, api: LogEventSource, session: String?) = log(JudgementAuditLogEntry(competitionRunUid, validator, token, verdict, api, session)) + /** + * Logs a submission override to DRES. + * + * @param submission The [Submission] that was overriden (new snapshot). + * @param validator The [JudgementValidator] instance. + * @param token The token generated by the judgement sub-system + */ + fun prepareJudgement(submission: Submission, validator: JudgementValidator, token: String) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.PREPARE_JUDGEMENT + this.source = AuditLogSource.INTERNAL + this.timestamp = DateTime.now() + this.submissionId = submission.uid.string + this.competitionId = submission.task?.competition?.id?.string + this.taskId = submission.task?.uid?.string + this.verdict = submission.status.toString() + this.validatorName = validator.id + this.description = "Token: $token" + } + } + } - fun login(user: String, session: String, api: LogEventSource) = log(LoginAuditLogEntry(user, session, api)) + /** + * Logs a submission override to DRES. + * + * @param competitionId [UID] that identifies the competition + * @param validator The [JudgementValidator] instance. + * @param token The token generated by the judgement sub-system + * @param verdict The [SubmissionStatus] submitted by the judge. + * @param api The [AuditLogSource] + * @param sessionId The identifier of the user session. + */ + fun judgement(competitionId: UID, validator: JudgementValidator, token: String, verdict: SubmissionStatus, api: AuditLogSource, sessionId: SessionId?) { + AuditLogEntry.new { + this.type = AuditLogType.JUDGEMENT + this.source = api + this.timestamp = DateTime.now() + this.competitionId = competitionId.string + this.verdict = verdict.toString() + this.validatorName = validator.id + this.description = "Token: $token" + this.session = sessionId + } + } - fun logout(session: String, api: LogEventSource) = log(LogoutAuditLogEntry(session, api)) + /** + * Logs a user user login event. + * + * @param userId [UID] of the user who logged out. + * @param api The [AuditLogSource] + * @param sessionId The [SessionId] + */ + fun login(userId: UserId, api: AuditLogSource, sessionId: SessionId) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.LOGIN + this.source = api + this.timestamp = DateTime.now() + this.userId = userId + this.session = sessionId + } + } + } - fun overrideSubmission(competitionRunUid: UID, submissionId: UID, newVerdict: SubmissionStatus, api: LogEventSource, session: String?) = log(SubmissionStatusOverwriteAuditLogEntry(competitionRunUid, submissionId, newVerdict, api, session)) + /** + * Logs a user logout event. + * + * @param userId [UID] of the user who logged out. + * @param api The [AuditLogSource] + * @param sessionId The [SessionId] + */ + fun logout(userId: UserId, api: AuditLogSource, sessionId: SessionId) { + this.store.transactional { + AuditLogEntry.new { + this.type = AuditLogType.LOGOUT + this.source = api + this.timestamp = DateTime.now() + this.userId = userId + this.session = sessionId + } + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 77ceb1adb..a29ee78d7 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -2,7 +2,7 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.UID import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index b29db1c3f..e149db5ee 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -1,7 +1,7 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.UID -import dev.dres.data.model.competition.Team +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 8124df52e..c9dbf3197 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -125,7 +125,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e val token = UUID.randomUUID().toString() this.waiting[token] = next this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) - AuditLogger.prepareJudgement(this.id, token, next) + AuditLogger.prepareJudgement(next, this, token) Pair(token, next) } else { null diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 0caa5f753..b45d9937d 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -39,6 +39,16 @@ fun Context.streamFile(path: Path) { this.writeSeekableStream(Files.newInputStream(path, StandardOpenOption.READ), mimeType) } +fun Context.sendFile(path: Path) { + if (!Files.exists(path)){ + this.errorResponse(404, "File $path not found!") + return + } + val mimeType = MimeTypeHelper.mimeType(path.toFile()) + this.contentType(mimeType) + this.result(Files.newInputStream(path, StandardOpenOption.READ)) +} + fun Context.sendFile(file: File) { if (!file.exists()){ this.errorResponse(404, "'${file.name}' not found") diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt index 4af43aa10..02a01e93a 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt @@ -5,13 +5,6 @@ import dev.dres.data.model.UID import java.util.* import java.util.regex.Matcher -/** - * Converts a [String] to a [SessionId]. - * - * @return [SessionId] - */ -fun String.toSessionId(): SessionId = SessionId(this) - /** * Converts a [String] to a [UID]. * From ffada32c76de3419ea451f16a5d04c36069205fd Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 28 Oct 2022 16:23:10 +0200 Subject: [PATCH 013/498] Intermediate commit (doesn't compile). --- backend/src/main/kotlin/dev/dres/DRES.kt | 66 +++++-- .../dev/dres/api/cli/CompetitionCommand.kt | 2 +- .../dev/dres/api/cli/CompetitionRunCommand.kt | 3 +- .../dres/api/cli/MediaCollectionCommand.kt | 10 +- .../kotlin/dev/dres/api/cli/UserCommand.kt | 16 +- .../kotlin/dev/dres/api/rest/AccessManager.kt | 10 +- .../api/rest/handler/CompetitionHandler.kt | 10 +- .../handler/CompetitionRunAdminHandler.kt | 8 +- .../api/rest/handler/CompetitionRunHandler.kt | 2 +- .../dres/api/rest/handler/JudgementHandler.kt | 4 +- .../handler/audit/ListAuditLogsHandler.kt | 10 +- .../audit/ListAuditLogsInRangeHandler.kt | 10 +- .../collection/AbstractCollectionHandler.kt | 2 +- .../collection/AddCollectionHandler.kt | 4 +- .../handler/collection/AddMediaItemHandler.kt | 13 +- .../collection/DeleteMediaItemHandler.kt | 2 +- .../collection/ListCollectionHandler.kt | 2 +- .../collection/ListMediaItemHandler.kt | 12 +- .../collection/RandomMediaItemHandler.kt | 13 +- .../ResolveMediaItemListByNameHandler.kt | 15 +- .../collection/ShowCollectionHandler.kt | 6 +- .../collection/ShowMediaItemHandler.kt | 13 +- .../collection/UpdateCollectionHandler.kt | 2 +- .../collection/UpdateMediaItemHandler.kt | 10 +- .../handler/preview/AbstractPreviewHandler.kt | 8 +- .../rest/handler/preview/GetMediaHandler.kt | 2 +- .../handler/preview/MediaPreviewHandler.kt | 2 +- .../preview/SubmissionPreviewHandler.kt | 4 +- .../submission/BatchSubmissionHandler.kt | 8 +- .../handler/submission/SubmissionHandler.kt | 6 +- .../rest/handler/users/CreateUsersHandler.kt | 13 +- .../rest/handler/users/DeleteUsersHandler.kt | 10 +- .../handler/users/ListActiveUsersHandler.kt | 13 +- .../rest/handler/users/ListUsersHandler.kt | 8 +- .../users/ShowCurrentSessionHandler.kt | 1 - .../handler/users/ShowCurrentUserHandler.kt | 13 +- .../rest/handler/users/UpdateUsersHandler.kt | 10 +- .../rest/handler/users/UserDetailsHandler.kt | 8 +- .../api/rest/types/audit/ApiAuditLogEntry.kt | 22 +++ .../api/rest/types/audit/ApiAuditLogSource.kt | 13 ++ .../api/rest/types/audit/ApiAuditLogType.kt | 22 +++ .../api/rest/types/audit/RestAuditLogEntry.kt | 76 -------- .../api/rest/types/collection/ApiMediaItem.kt | 21 +++ .../api/rest/types/collection/ApiMediaType.kt | 15 ++ .../collection/RestFullMediaCollection.kt | 2 +- .../types/collection/RestMediaCollection.kt | 9 +- .../rest/types/collection/RestMediaItem.kt | 34 ---- .../types/collection/RestMediaItemType.kt | 12 -- .../api/rest/types/competition/ApiTeam.kt | 11 ++ .../competition/RestCompetitionDescription.kt | 24 +-- .../types/competition/RestDetailedTeam.kt | 15 -- .../types/competition/RestTaskDescription.kt | 26 +-- .../rest/types/competition/tasks/ApiTarget.kt | 45 +++++ ...criptionTargetItem.kt => ApiTargetItem.kt} | 4 +- .../types/competition/tasks/ApiTargetType.kt | 17 ++ .../tasks/RestTaskDescriptionComponent.kt | 3 +- .../tasks/RestTaskDescriptionTarget.kt | 64 ------- .../competition/tasks/RestTemporalRange.kt | 2 + .../dres/api/rest/types/run/SubmissionInfo.kt | 6 +- .../dev/dres/api/rest/types/run/TaskInfo.kt | 2 +- .../api/rest/types/task/ApiContentElement.kt | 13 ++ .../{ContentType.kt => ApiContentType.kt} | 5 +- .../api/rest/types/task/ContentElement.kt | 13 -- .../dev/dres/api/rest/types/task/TaskHint.kt | 6 +- .../dres/api/rest/types/task/TaskTarget.kt | 4 +- .../dev/dres/api/rest/types/users/ApiRole.kt | 9 +- .../dev/dres/api/rest/types/users/ApiUser.kt | 12 ++ .../dres/api/rest/types/users/UserDetails.kt | 19 -- .../dev/dres/data/model/PersistentEntity.kt | 4 +- .../dev/dres/data/model/admin/Password.kt | 5 +- .../kotlin/dev/dres/data/model/admin/Role.kt | 23 +-- .../kotlin/dev/dres/data/model/admin/User.kt | 20 +- .../dres/data/model/audit/AuditLogEntry.kt | 19 +- .../dres/data/model/audit/AuditLogSource.kt | 9 + .../dev/dres/data/model/audit/AuditLogType.kt | 10 + .../dres/data/model/basics/media/MediaItem.kt | 38 ---- .../dres/data/model/basics/media/MediaType.kt | 36 ---- .../competition/CompetitionDescription.kt | 90 +++++---- .../dev/dres/data/model/competition/Ids.kt | 6 - .../model/competition/TaskDescriptionHint.kt | 154 --------------- .../competition/TaskDescriptionTarget.kt | 160 ---------------- .../dres/data/model/competition/TaskGroup.kt | 29 --- .../dres/data/model/competition/TaskType.kt | 41 ---- .../data/model/competition/VideoSegment.kt | 23 --- .../FileTaskDescriptionComponent.kt | 13 -- .../interfaces/SubmissionFilterFactory.kt | 1 - .../competition/options/ConfiguredOption.kt | 57 ------ .../data/model/competition/options/Option.kt | 10 - .../options/QueryComponentOption.kt | 14 -- .../competition/options/ScoringOption.kt | 25 --- .../model/competition/options/SimpleOption.kt | 13 -- .../options/SimpleOptionParameters.kt | 31 --- .../options/SubmissionFilterOption.kt | 38 ---- .../model/competition/options/TargetOption.kt | 15 -- .../dres/data/model/competition/task/Hint.kt | 130 +++++++++++++ .../data/model/competition/task/HintType.kt | 46 +++++ .../data/model/competition/task/TargetType.kt | 39 ++++ .../competition/{ => task}/TaskDescription.kt | 105 +++++------ .../competition/task/TaskDescriptionTarget.kt | 124 ++++++++++++ .../data/model/competition/task/TaskGroup.kt | 34 ++++ .../data/model/competition/task/TaskType.kt | 92 +++++++++ .../task/options/ConfiguredOption.kt | 66 +++++++ .../task/options/TaskComponentOption.kt | 27 +++ .../competition/task/options/TaskOption.kt | 26 +++ .../task/options/TaskScoreOption.kt | 38 ++++ .../task/options/TaskSubmissionOption.kt | 50 +++++ .../task/options/TaskTargetOption.kt | 27 +++ .../dres/data/model/competition/team/Team.kt | 23 ++- .../data/model/competition/team/TeamGroup.kt | 4 +- .../{basics => }/media/MediaCollection.kt | 4 +- .../dev/dres/data/model/media/MediaItem.kt | 76 ++++++++ .../{basics => }/media/MediaItemSegment.kt | 9 +- .../media/MediaItemSegmentList.kt | 2 +- .../dev/dres/data/model/media/MediaType.kt | 35 ++++ .../{basics => }/media/PlayableMediaItem.kt | 2 +- .../{basics => media}/time/TemporalPoint.kt | 6 +- .../{basics => media}/time/TemporalRange.kt | 2 +- .../data/model/run/AbstractInteractiveTask.kt | 1 + .../model/run/AbstractNonInteractiveTask.kt | 2 +- .../run/InteractiveAsynchronousCompetition.kt | 2 +- .../run/InteractiveSynchronousCompetition.kt | 2 +- .../model/run/NonInteractiveCompetition.kt | 2 +- .../dres/data/model/run/interfaces/Task.kt | 2 +- .../dres/data/model/submissions/Submission.kt | 6 +- .../model/submissions/aspects/ItemAspect.kt | 2 +- .../submissions/aspects/TemporalAspect.kt | 2 +- .../submissions/batch/ItemBatchElement.kt | 2 +- .../submissions/batch/TemporalBatchElement.kt | 6 +- .../serializers/AuditLogEntrySerializer.kt | 122 ------------ .../serializers/CompetitionRunSerializer.kt | 139 -------------- .../data/serializers/CompetitionSerializer.kt | 60 ------ .../serializers/MediaCollectionSerializer.kt | 21 --- .../serializers/MediaItemSegmentSerializer.kt | 28 --- .../data/serializers/MediaItemSerializer.kt | 41 ---- .../serializers/RunPropertiesSerializer.kt | 20 -- .../data/serializers/SubmissionSerializer.kt | 58 ------ .../serializers/TaskDescriptionSerializer.kt | 177 ------------------ .../data/serializers/TaskGroupSerializer.kt | 14 -- .../data/serializers/TaskTypeSerializer.kt | 57 ------ .../data/serializers/TeamGroupSerializer.kt | 30 --- .../dres/data/serializers/TeamSerializer.kt | 27 --- .../serializers/TemporalPointSerializer.kt | 37 ---- .../serializers/TemporalRangeSerializer.kt | 18 -- .../dres/data/serializers/UIDSerializer.kt | 16 -- .../dres/data/serializers/UserSerializer.kt | 26 --- .../run/InteractiveAsynchronousRunManager.kt | 2 +- .../dev/dres/run/InteractiveRunManager.kt | 2 +- .../run/InteractiveSynchronousRunManager.kt | 2 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 26 ++- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 2 +- .../dev/dres/run/eventstream/StreamEvent.kt | 2 +- .../handlers/ResultLogStatisticsHandler.kt | 12 +- .../handlers/TeamCombinationScoreHandler.kt | 2 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 2 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 2 +- .../MediaItemsSubmissionBatchValidator.kt | 2 +- .../MediaItemsSubmissionValidator.kt | 2 +- .../TemporalContainmentSubmissionValidator.kt | 2 +- .../TemporalOverlapSubmissionValidator.kt | 2 +- .../dres/run/validation/judged/ItemRange.kt | 2 +- .../kotlin/dev/dres/utilities/TimeUtil.kt | 4 +- .../run/score/scorer/AvsTaskScorerTest.kt | 2 +- .../run/score/scorer/KisTaskScorerTest.kt | 2 +- 163 files changed, 1485 insertions(+), 2235 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItemType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/{RestTaskDescriptionTargetItem.kt => ApiTargetItem.kt} (52%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentElement.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/task/{ContentType.kt => ApiContentType.kt} (64%) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentElement.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionHint.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/VideoSegment.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/FileTaskDescriptionComponent.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt rename backend/src/main/kotlin/dev/dres/data/model/competition/{ => task}/TaskDescription.kt (56%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt rename backend/src/main/kotlin/dev/dres/data/model/{basics => }/media/MediaCollection.kt (83%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt rename backend/src/main/kotlin/dev/dres/data/model/{basics => }/media/MediaItemSegment.kt (82%) rename backend/src/main/kotlin/dev/dres/data/model/{basics => }/media/MediaItemSegmentList.kt (90%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt rename backend/src/main/kotlin/dev/dres/data/model/{basics => }/media/PlayableMediaItem.kt (93%) rename backend/src/main/kotlin/dev/dres/data/model/{basics => media}/time/TemporalPoint.kt (95%) rename backend/src/main/kotlin/dev/dres/data/model/{basics => media}/time/TemporalRange.kt (97%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/CompetitionSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSegmentSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/RunPropertiesSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TaskDescriptionSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TaskGroupSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TemporalPointSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/TemporalRangeSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/UIDSerializer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/serializers/UserSerializer.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 18cf2be56..27e8743ae 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -7,10 +7,16 @@ import dev.dres.data.model.Config import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaItemSegment -import dev.dres.data.model.basics.media.MediaType +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.task.* +import dev.dres.data.model.competition.task.options.* +import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.competition.team.TeamAggregator +import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaItemSegment +import dev.dres.data.model.media.MediaType import dev.dres.mgmt.admin.UserManager import dev.dres.run.RunExecutor import dev.dres.run.audit.AuditLogger @@ -19,6 +25,7 @@ import dev.dres.run.eventstream.handlers.ResultLogStatisticsHandler import dev.dres.run.eventstream.handlers.SubmissionStatisticsHandler import dev.dres.run.eventstream.handlers.TeamCombinationScoreHandler import dev.dres.utilities.FFmpegUtil +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.XdModel import kotlinx.dnq.store.container.StaticStoreContainer import kotlinx.dnq.util.initMetaData @@ -27,9 +34,9 @@ import java.io.File object DRES { - const val VERSION = "1.2.2" + const val VERSION = "2.0.0" - /** Application root; shoud pe relative to JAR file or classes path. */ + /** Application root; should pe relative to JAR file or classes path. */ val rootPath = File(FFmpegUtil::class.java.protectionDomain.codeSource.location.toURI()).toPath() init { @@ -50,17 +57,7 @@ object DRES { println("Initializing...") /* Initialize Xodus based data store. */ - XdModel.registerNodes( - AuditLogSource, - AuditLogType, - AuditLogEntry, - MediaType, - MediaCollection, - MediaItem, - MediaItemSegment - ) - val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") - initMetaData(XdModel.hierarchy, store) + val store = this.prepareDatabase(config) /* Initialize UserManager. */ UserManager.init(store) @@ -92,4 +89,39 @@ object DRES { EventStreamProcessor.stop() FFmpegUtil.stop() } + + /** + * Loads and prepares the database. + * + * @param config The [Config] + */ + private fun prepareDatabase(config: Config): TransientEntityStore { + XdModel.registerNodes( + AuditLogSource, + AuditLogType, + AuditLogEntry, + MediaType, + MediaCollection, + MediaItem, + MediaItemSegment, + ConfiguredOption, + TaskComponentOption, + TaskOption, + TaskScoreOption, + TaskSubmissionOption, + TaskTargetOption, + CompetitionDescription, + TaskDescription, + Team, + TeamGroup, + TeamAggregator, + HintType, + Hint, + TargetType, + TaskDescriptionTarget + ) + val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") + initMetaData(XdModel.hierarchy, store) + return store + } } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt index cc2fcf2ea..833cf42ca 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt @@ -13,7 +13,7 @@ import com.jakewharton.picnic.table import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription import dev.dres.utilities.FFmpegUtil import java.io.File diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index a7b7737da..d30f8ffdf 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -8,9 +8,8 @@ import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.path import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter import com.jakewharton.picnic.table -import dev.dres.data.dbo.DAO import dev.dres.data.model.UID -import dev.dres.data.model.competition.TaskDescriptionTarget +import dev.dres.data.model.competition.task.TaskDescriptionTarget import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.RunActionContext diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 3a42469ca..3d74da7df 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -14,11 +14,11 @@ import com.jakewharton.picnic.table import dev.dres.data.dbo.DAO import dev.dres.data.dbo.DaoIndexer import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaItemSegment -import dev.dres.data.model.basics.media.MediaItemSegmentList -import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaItemSegment +import dev.dres.data.model.media.MediaItemSegmentList +import dev.dres.data.model.media.time.TemporalRange import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.cleanPathString diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index ef959a2e6..031de2f6a 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -29,15 +29,13 @@ sealed class UserCommand : NoOpCliktCommand(name = "user") { this.subcommands(Create(), Update(), Delete(), List(), Roles(), Export(), Import()) } - override fun aliases(): Map> { - return mapOf( - "ls" to listOf("list"), - "remove" to listOf("delete"), - "rm" to listOf("delete"), - "drop" to listOf("delete"), - "add" to listOf("create") - ) - } + override fun aliases() = mapOf( + "ls" to listOf("list"), + "remove" to listOf("delete"), + "rm" to listOf("delete"), + "drop" to listOf("delete"), + "add" to listOf("create") + ) /** * [CliktCommand] to create a new [User]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index e2fc1dfda..3854244a6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -10,6 +10,8 @@ import dev.dres.utilities.extensions.sessionId import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.http.Handler +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.flatMapDistinct import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantReadWriteLock @@ -114,11 +116,11 @@ object AccessManager { * @param runManager The [RunManager] to register. */ fun registerRunManager(runManager: RunManager) = this.locks.write { - runManager.description.teams.flatMap { t -> t.users }.forEach { - if (this.usersToRunMap.containsKey(it)) { - this.usersToRunMap[it]?.add(runManager) + runManager.description.teams.flatMapDistinct { it.users }.asSequence().forEach { + if (this.usersToRunMap.containsKey(it.id)) { + this.usersToRunMap[it.id]?.add(runManager) } else { - this.usersToRunMap[it] = mutableSetOf(runManager) + this.usersToRunMap[it.id] = mutableSetOf(runManager) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt index e4679cfdb..36b1acb48 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.competition.RestCompetitionDescription -import dev.dres.api.rest.types.competition.RestDetailedTeam +import dev.dres.api.rest.types.competition.ApiTeam import dev.dres.api.rest.types.competition.RestTaskDescription import dev.dres.api.rest.types.competition.RestTeam import dev.dres.api.rest.types.status.ErrorStatus @@ -11,7 +11,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.team.Team import dev.dres.utilities.extensions.UID @@ -161,7 +161,7 @@ class ListTeamHandler(competitions: DAO) : CompetitionHa /** * REST handler to list all teams for a [CompetitionDescription], inclusive [UserDetails] */ -class ListDetailedTeamHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler>{ +class ListDetailedTeamHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler>{ override val route: String = "competition/{competitionId}/team/list/details" @@ -171,14 +171,14 @@ class ListDetailedTeamHandler(competitions: DAO) : Compe pathParams= [OpenApiParam("competitionId", String::class, "Competition ID")], tags = ["Competition"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = competitionFromContext(ctx).teams.map{ RestDetailedTeam.of(it) } + override fun doGet(ctx: Context) = competitionFromContext(ctx).teams.map{ ApiTeam.of(it) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 4b9e8ecb9..12b3aed54 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.competition.CompetitionStartMessage import dev.dres.api.rest.types.run.* import dev.dres.api.rest.types.status.ErrorStatus @@ -11,7 +11,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.RunActionContext.Companion.runActionContext @@ -141,7 +141,7 @@ class CreateCompetitionRunAdminHandler( ) } - val segmentTasks = competitionToStart.getAllCachedVideoItems() + val segmentTasks = competitionToStart.getAllVideos() /* check videos */ segmentTasks.forEach { @@ -679,7 +679,7 @@ class ListSubmissionsPerTaskRunAdminHandler : memberName = UserManager.get(sub.memberId)?.username, status = sub.status, timestamp = sub.timestamp, - item = if (sub is ItemAspect) RestMediaItem.fromMediaItem(sub.item) else null, + item = if (sub is ItemAspect) sub.item.toApi() else null, text = if (sub is TextAspect) sub.text else null, start = if (sub is TemporalSubmissionAspect) sub.start else null, end = if (sub is TemporalSubmissionAspect) sub.end else null diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 0bfe9d53e..4e5728ff9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -13,7 +13,7 @@ import dev.dres.api.rest.types.task.TaskTarget import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.Submission diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt index 9db5672ba..2e54cc5e1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt @@ -7,8 +7,8 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.dbo.DAO import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.PlayableMediaItem +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.PlayableMediaItem import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index b01cd74ad..27b4e3307 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.audit import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.audit.RestAuditLogEntry +import dev.dres.api.rest.types.audit.ApiAuditLogEntry import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.audit.AuditLogEntry import dev.dres.utilities.extensions.toPathParamKey @@ -18,7 +18,7 @@ import kotlinx.dnq.query.take * @author Loris Sauter * @version 1.0.0 */ -class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandler(store), GetRestHandler> { +class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandler(store), GetRestHandler> { override val route = "audit/log/list/limit/${LIMIT_PARAM.toPathParamKey()}/${PAGE_INDEX_PARAM.toPathParamKey()}" companion object { @@ -47,14 +47,14 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle ], tags = ["Audit"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), + OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { + override fun doGet(ctx: Context): List = this.store.transactional(true) { val limit = (ctx.pathParamMap()[LIMIT_PARAM]?.toIntOrNull() ?: DEFAULT_LIMIT).coerceAtLeast(0) val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) - AuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { RestAuditLogEntry.convert(it) }.toList() + AuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { ApiAuditLogEntry.convert(it) }.toList() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt index cd07c854f..b67e696fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.audit import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.audit.RestAuditLogEntry +import dev.dres.api.rest.types.audit.ApiAuditLogEntry import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.audit.AuditLogEntry @@ -17,7 +17,7 @@ import org.joda.time.DateTime * @author Loris Sauter * @version 1.0.0 */ -class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLogHandler(store), GetRestHandler> { +class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLogHandler(store), GetRestHandler> { override val route = "audit/log/list/since/{since}/{upto}" @@ -30,12 +30,12 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog ], tags = ["Audit"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), + OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { + override fun doGet(ctx: Context): List { val since = DateTime(ctx.pathParamMap()["since"]?.toLongOrNull() ?: 0L) val upto = DateTime(ctx.pathParamMap()["upto"]?.toLongOrNull() ?:Long.MAX_VALUE) @@ -43,7 +43,7 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog return this.store.transactional(true) { AuditLogEntry.query((AuditLogEntry::timestamp gt since) and (AuditLogEntry::timestamp lt upto)).asSequence().map { - RestAuditLogEntry.convert(it) + ApiAuditLogEntry.convert(it) }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt index 932f9b7ec..99716856c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index 4fc6799a4..c7b990ef9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -6,8 +6,8 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 8a4b5f3e4..99bd65abe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -1,14 +1,13 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.collection.RestMediaItem -import dev.dres.api.rest.types.collection.RestMediaItemType +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaType +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -29,7 +28,7 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl summary = "Adds a media item to the specified media collection.", path = "/api/v1/mediaItem", methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaItem::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaItem::class)]), tags = ["Collection"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -40,7 +39,7 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl /* Parse media item and perform sanity checks */ val mediaItem = try { - ctx.bodyAsClass(RestMediaItem::class.java) + ctx.bodyAsClass(ApiMediaItem::class.java) } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } catch (e: IllegalArgumentException) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt index 7b6c439d5..1f2ae9aeb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt index 0b67aedf8..5493bd32e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.RestMediaCollection import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt index ebfd40259..781ed1a2d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -17,7 +17,7 @@ import kotlinx.dnq.query.take * @author Ralph Gasser * @version 1.0.0 */ -class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { +class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { override val route: String = "collection/{collectionId}/{startsWith}" @OpenApi( @@ -30,13 +30,13 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand ], tags = ["Collection"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { + override fun doGet(ctx: Context): List = this.store.transactional(true) { val collection = collectionFromContext(ctx) val start = ctx.pathParamMap()["startsWith"] val query = if (!start.isNullOrBlank()) { @@ -44,6 +44,6 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand } else { collection.items } - query.take(50).asSequence().map { RestMediaItem.fromMediaItem(it) }.toList() + query.take(50).asSequence().map { it.toApi() }.toList() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt index 333a0f73a..f3c4ca16d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -1,12 +1,9 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -21,7 +18,7 @@ import java.util.SplittableRandom * @author Ralph Gasser * @version 1.0 */ -class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { +class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { private val rand = SplittableRandom(System.currentTimeMillis()) // TODO Decide upon seed -- time based or fixed? @@ -36,16 +33,16 @@ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa ], tags = ["Collection"], responses = [ - OpenApiResponse("200", [OpenApiContent(RestMediaItem::class)]), + OpenApiResponse("200", [OpenApiContent(ApiMediaItem::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): RestMediaItem = this.store.transactional(true) { + override fun doGet(ctx: Context): ApiMediaItem = this.store.transactional(true) { val collection = collectionFromContext(ctx) val item = collection.items.drop(this.rand.nextInt(0, collection.items.size())).take(1).firstOrNull() ?: throw ErrorStatusException(404, "Failed to ferch media item. It seems that the given collection ${collection.id} is empty.", ctx) - RestMediaItem.fromMediaItem(item) + item.toApi() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt index 07b2e3b97..d3b8bf411 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt @@ -1,27 +1,22 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter -import kotlinx.dnq.query.query -import kotlinx.dnq.query.toList /** * * @author Ralph Gasser * @version 1.0 */ -class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler> { +class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler> { override val route = "collection/{collectionId}/resolve" @OpenApi( @@ -34,13 +29,13 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC requestBody = OpenApiRequestBody([OpenApiContent(Array::class)]), tags = ["Collection"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doPost(ctx: Context): List { + override fun doPost(ctx: Context): List { val queriedNames = try { ctx.bodyAsClass(Array::class.java) } catch (e: BadRequestResponse) { @@ -55,7 +50,7 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC /** Execute query. */ return this.store.transactional(true) { val collection = collectionFromContext(ctx) - collection.items.filter { it.name isIn(queriedNames.asIterable()) }.asSequence().map { RestMediaItem.fromMediaItem(it) }.toList() + collection.items.filter { it.name isIn(queriedNames.asIterable()) }.asSequence().map { it.toApi() }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index 131dea125..740ee833f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -3,9 +3,9 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.RestFullMediaCollection import dev.dres.api.rest.types.collection.RestMediaCollection -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -36,7 +36,7 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan ) override fun doGet(ctx: Context): RestFullMediaCollection = this.store.transactional(true) { val collection = collectionFromContext(ctx) //also checks if collection exists - val items = MediaItem.query(MediaItem::collection eq collection).asSequence().map { RestMediaItem.fromMediaItem(it) }.toList() + val items = MediaItem.query(MediaItem::collection eq collection).asSequence().map { it.toApi() }.toList() RestFullMediaCollection(RestMediaCollection.fromMediaCollection(collection), items) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt index 4a1aad9d5..ff4433950 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt @@ -1,11 +1,10 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.utilities.extensions.UID +import dev.dres.data.model.media.MediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -18,7 +17,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0 */ -class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { +class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { override val route: String = "mediaItem/{mediaId}" @OpenApi( @@ -30,13 +29,13 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand ], tags = ["Collection"], responses = [ - OpenApiResponse("200", [OpenApiContent(RestMediaItem::class)]), + OpenApiResponse("200", [OpenApiContent(ApiMediaItem::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): RestMediaItem { + override fun doGet(ctx: Context): ApiMediaItem { val mediaId = ctx.pathParamMap().getOrElse("mediaId") { throw ErrorStatusException(404, "Parameter 'mediaId' is missing!'", ctx) } @@ -44,7 +43,7 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand return this.store.transactional(true) { val item = MediaItem.query(MediaItem::id eq mediaId).firstOrNull() ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) - RestMediaItem.fromMediaItem(item) + item.toApi() } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt index 04365c548..087094922 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.collection.RestMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 8735b6ae3..fbf5365c5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -1,12 +1,12 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaType +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -28,7 +28,7 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa summary = "Updates a Media Item to the specified Media Collection.", path = "/api/v1/mediaitem", methods = [HttpMethod.PATCH], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaItem::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaItem::class)]), tags = ["Collection"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -38,7 +38,7 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa override fun doPatch(ctx: Context): SuccessStatus { /* Parse media item and perform sanity checks */ val mediaItem = try { - ctx.bodyAsClass(RestMediaItem::class.java) + ctx.bodyAsClass(ApiMediaItem::class.java) } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } catch (e: IllegalArgumentException) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index ceda61c8e..fe0a1380d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -7,10 +7,10 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.handler.collection.AbstractCollectionHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config -import dev.dres.data.model.basics.media.CollectionId -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaType +import dev.dres.data.model.media.CollectionId +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.extensions.sendFile import dev.dres.utilities.extensions.streamFile diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt index 3a46a8282..fb997fbbf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.preview import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.utilities.extensions.errorResponse import dev.dres.utilities.extensions.streamFile import io.javalin.http.Context diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt index fd54d918d..12fcb96f5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.preview import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.utilities.extensions.errorResponse import io.javalin.http.Context import io.javalin.openapi.* diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index 8290bad1d..09245592c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -4,8 +4,8 @@ import dev.dres.api.rest.handler.AbstractPreviewHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.data.model.submissions.aspects.TextAspect diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index b046007dd..c465d46fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -9,10 +9,10 @@ import dev.dres.api.rest.types.submission.RunResult import dev.dres.data.dbo.DAO import dev.dres.data.dbo.DaoIndexer import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaItemSegmentList -import dev.dres.data.model.basics.time.TemporalPoint +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaItemSegmentList +import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.batch.* import dev.dres.run.InteractiveRunManager diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index aad7bc341..5693f108d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -13,9 +13,9 @@ import dev.dres.data.model.Config import dev.dres.data.model.UID import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.PlayableMediaItem -import dev.dres.data.model.basics.time.TemporalPoint +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.PlayableMediaItem +import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.Submission diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index 2888eb93e..2fd273158 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -5,10 +5,9 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.ApiUser import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.Password -import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse @@ -21,7 +20,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { +class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { override val route = "user" /** [CreateUsersHandler] requires [ApiRole.ADMIN]. */ @@ -33,12 +32,12 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)], description = "If the username is already taken"), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doPost(ctx: Context): UserDetails { + override fun doPost(ctx: Context): ApiUser { val req = try { ctx.bodyAsClass(UserRequest::class.java) } catch (e: BadRequestResponse) { @@ -52,9 +51,9 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, if (req.role == null) throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) - val success = UserManager.create(req.username, Password.Plain(req.password), Role.convertApiRole(req.role)) + val success = UserManager.create(req.username, Password.Plain(req.password), req.role.role ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) if (success) { - return UserDetails.of(UserManager.get(username = req.username)!!) + return UserManager.get(username = req.username)!!.toApi() } else { throw ErrorStatusException(400, "The request could not be fulfilled.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index de0167d6d..7af9d2e9f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.User import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context @@ -17,7 +17,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { +class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.ADMIN) /** [DeleteUsersHandler] requires [ApiRole.ADMIN]. */ @@ -29,15 +29,15 @@ class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler pathParams = [OpenApiParam("userId", Long::class, "User ID")], tags = ["User"], responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found"), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doDelete(ctx: Context): UserDetails { + override fun doDelete(ctx: Context): ApiUser { val user = userFromContext(ctx) if (UserManager.delete(id = user.id)) { - return UserDetails.of(user) + return user.toApi() } else { throw ErrorStatusException(500, "Could not delete the user (${user.id})", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index dc858a81c..9d8710c1e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.UID import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User @@ -22,7 +22,7 @@ import io.javalin.openapi.OpenApiResponse * @author Loris Sauter * @version 2.0.0 */ -class ListActiveUsersHandler() : GetRestHandler>, AccessManagedRestHandler { +class ListActiveUsersHandler : GetRestHandler>, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.ADMIN) /** All [UserDetailsHandler] requires [ApiRole.ADMIN]. */ @@ -30,22 +30,21 @@ class ListActiveUsersHandler() : GetRestHandler>, AccessManage override val apiVersion = "v1" - @OpenApi( summary = "Get details of all current user sessions", path = "/api/v1/user/session/active/list", tags = ["User"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = AccessManager.currentSessions.map { session -> + override fun doGet(ctx: Context): List = AccessManager.currentSessions.map { session -> AccessManager.userIdForSession(session)?.let { UserManager.get(id = it) }?.let { - UserDetails.of(it) - } ?: return@map UserDetails(UID.EMPTY.string, "??", Role.VIEWER, session) + it.toApi() + } ?: return@map ApiUser(UID.EMPTY.string, "??", ApiRole.VIEWER, session) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt index be2af8291..32a0cedcf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.users import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.User import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context @@ -18,7 +18,7 @@ import io.javalin.openapi.OpenApiResponse * @author Loris Sauter * @version 2.0.0 */ -class ListUsersHandler: AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { +class ListUsersHandler: AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { override val route = "user/list" @@ -29,8 +29,8 @@ class ListUsersHandler: AbstractUserHandler(), GetRestHandler> summary = "Lists all available users.", path = "/api/v1/user/list", tags = ["User"], - responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], + responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = UserManager.list().map(UserDetails.Companion::of) + override fun doGet(ctx: Context) = UserManager.list().map { it.toApi() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt index 21f436569..207d2cf91 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt @@ -3,7 +3,6 @@ package dev.dres.api.rest.handler.users import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.SessionId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt index cc58d3660..b64f0afba 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt @@ -4,7 +4,8 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.ApiUser +import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi @@ -16,7 +17,7 @@ import io.javalin.openapi.OpenApiResponse * @author Ralph Gasser * @version 1.0 */ -class ShowCurrentUserHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class ShowCurrentUserHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user" /** [ShowCurrentUserHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ @@ -27,10 +28,14 @@ class ShowCurrentUserHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, AccessManagedRestHandler { /** [UpdateUsersHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.ADMIN, ApiRole.PARTICIPANT) @@ -34,13 +34,13 @@ class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doPatch(ctx: Context): UserDetails { + override fun doPatch(ctx: Context): ApiUser { val request = try { ctx.bodyAsClass(UserRequest::class.java) } catch (e: BadRequestResponse) { @@ -54,7 +54,7 @@ class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, if (caller.role == Role.ADMIN || user.id == caller.id) { val success = UserManager.update(id = user.id, request = request) if (success) { - return UserDetails.of(UserManager.get(id = user.id)!!) + return UserManager.get(id = user.id)!!.toApi() } else { throw ErrorStatusException(500, "Could not update user!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt index 37e129dc6..c3b28ef35 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.users.UserDetails +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.User import io.javalin.http.Context import io.javalin.openapi.* @@ -15,7 +15,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user/{userId}" /** [UserDetailsHandler] requires [ApiRole.ADMIN]. */ @@ -29,13 +29,13 @@ class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, A ], tags = ["User"], responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)], description = "If the user could not be found."), OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = UserDetails.of(userFromContext(ctx)) + override fun doGet(ctx: Context) = userFromContext(ctx).toApi() } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt new file mode 100644 index 000000000..3bdb0f600 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt @@ -0,0 +1,22 @@ +package dev.dres.api.rest.types.audit + +import dev.dres.data.model.audit.AuditLogEntry + +/** + * A RESTful API representation of a [AuditLogEntry] + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class ApiAuditLogEntry( + val id: String, + val type: ApiAuditLogType, + val source: ApiAuditLogSource, + val timestamp: Long, + val competitionId: String? = null, + val userId: String? = null, + val submissionId: String? = null, + val session: String? = null, + val address: String? = null, + val description: String? = null +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt new file mode 100644 index 000000000..f5ab09973 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt @@ -0,0 +1,13 @@ +package dev.dres.api.rest.types.audit + +import dev.dres.data.model.audit.AuditLogSource + +/** + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiAuditLogSource(val source: AuditLogSource) { + REST(AuditLogSource.REST), + CLI(AuditLogSource.CLI), + INTERNAL(AuditLogSource.INTERNAL) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt new file mode 100644 index 000000000..52a1d1b56 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt @@ -0,0 +1,22 @@ +package dev.dres.api.rest.types.audit + +import dev.dres.data.model.audit.AuditLogType + +/** + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiAuditLogType(val type: AuditLogType) { + COMPETITION_START(AuditLogType.COMPETITION_START), + COMPETITION_END(AuditLogType.COMPETITION_END), + TASK_START(AuditLogType.TASK_START), + TASK_MODIFIED(AuditLogType.TASK_MODIFIED), + TASK_END(AuditLogType.TASK_END), + SUBMISSION(AuditLogType.SUBMISSION), + PREPARE_JUDGEMENT(AuditLogType.PREPARE_JUDGEMENT), + JUDGEMENT(AuditLogType.JUDGEMENT), + LOGIN(AuditLogType.LOGIN), + LOGOUT(AuditLogType.LOGOUT), + SUBMISSION_VALIDATION(AuditLogType.SUBMISSION_VALIDATION), + SUBMISSION_STATUS_OVERWRITE(AuditLogType.SUBMISSION_STATUS_OVERWRITE) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt deleted file mode 100644 index e9873e314..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/RestAuditLogEntry.kt +++ /dev/null @@ -1,76 +0,0 @@ -package dev.dres.api.rest.types.audit - -import dev.dres.api.rest.types.AbstractRestEntity -import dev.dres.api.rest.types.run.SubmissionInfo -import dev.dres.data.model.UID -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.audit.* - -sealed class RestAuditLogEntry(val type: AuditLogEntryType, id: UID, val timestamp: Long) : AbstractRestEntity(id.string) { - companion object { - fun convert(log: AuditLogEntry): RestAuditLogEntry { - return when (log) { - is CompetitionStartAuditLogEntry -> RestCompetitionStartAuditLogEntry(log) - is CompetitionEndAuditLogEntry -> RestCompetitionEndAuditLogEntry(log) - is TaskStartAuditLogEntry -> RestTaskStartAuditLogEntry(log) - is TaskModifiedAuditLogEntry -> RestTaskModifiedAuditLogEntry(log) - is TaskEndAuditLogEntry -> RestTaskEndAuditLogEntry(log) - is SubmissionAuditLogEntry -> RestSubmissionAuditLogEntry(log) - is PrepareJudgementAuditLogEntry -> RestPrepareJudgementAuditLogEntry(log) - is JudgementAuditLogEntry -> RestJudgementAuditLogEntry(log) - is LoginAuditLogEntry -> RestLoginAuditLogEntry(log) - is LogoutAuditLogEntry -> RestLogoutAuditLogEntry(log) - is SubmissionValidationAuditLogEntry -> RestSubmissionValidationAuditLogEntry(log) - is SubmissionStatusOverwriteAuditLogEntry -> RestSubmissionStatusOverwriteAuditLogEntry(log) - } - } - } -} - -class RestCompetitionStartAuditLogEntry(id: UID, timestamp: Long, val competition: String, val api: LogEventSource, val user: String?) : RestAuditLogEntry(AuditLogEntryType.COMPETITION_START, id, timestamp) { - constructor(log: CompetitionStartAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.api, log.user) -} - -class RestCompetitionEndAuditLogEntry(id: UID, timestamp: Long, val competition: String, val api: LogEventSource, val user: String?) : RestAuditLogEntry(AuditLogEntryType.COMPETITION_END, id, timestamp) { - constructor(log: CompetitionEndAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.api, log.user) -} - -class RestTaskStartAuditLogEntry(id: UID, timestamp: Long, val competition: String, val taskName: String, val api: LogEventSource, val user: String?) : RestAuditLogEntry(AuditLogEntryType.TASK_START, id, timestamp) { - constructor(log: TaskStartAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.taskName, log.api, log.user) -} - -class RestTaskModifiedAuditLogEntry(id: UID, timestamp: Long, val competition: String, val taskName: String, val modification: String, val api: LogEventSource, val user: String?) : RestAuditLogEntry(AuditLogEntryType.TASK_MODIFIED, id, timestamp) { - constructor(log: TaskModifiedAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.taskName, log.modification, log.api, log.user) -} - -class RestTaskEndAuditLogEntry(id: UID, timestamp: Long, val competition: String, val taskName: String, val api: LogEventSource, val user: String?) : RestAuditLogEntry(AuditLogEntryType.TASK_END, id, timestamp) { - constructor(log: TaskEndAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.taskName, log.api, log.user) -} - -class RestSubmissionAuditLogEntry(id: UID, timestamp: Long, val competition: String, val taskName: String, val submission: SubmissionInfo, val api: LogEventSource, val user: String?, val address: String) : RestAuditLogEntry(AuditLogEntryType.SUBMISSION, id, timestamp) { - constructor(log: SubmissionAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.taskName, SubmissionInfo(log.submission), log.api, log.user, log.address) -} - -class RestPrepareJudgementAuditLogEntry(id: UID, timestamp: Long, val validator: String, val token: String, val submission: SubmissionInfo): RestAuditLogEntry(AuditLogEntryType.PREPARE_JUDGEMENT, id, timestamp){ - constructor(log: PrepareJudgementAuditLogEntry): this(log.id, log.timestamp, log.validator, log.token, SubmissionInfo(log.submission)) -} - -class RestJudgementAuditLogEntry(id: UID, timestamp: Long, val competition: String, val validator: String, val token: String, val verdict: SubmissionStatus, val api: LogEventSource, val user: String?) : RestAuditLogEntry(AuditLogEntryType.JUDGEMENT, id, timestamp) { - constructor(log: JudgementAuditLogEntry) : this(log.id, log.timestamp, log.competition.string, log.validator, log.token, log.verdict, log.api, log.user) -} - -class RestLoginAuditLogEntry(id: UID, timestamp: Long, val user: String, val session: String, val api: LogEventSource) : RestAuditLogEntry(AuditLogEntryType.LOGIN, id, timestamp) { - constructor(log: LoginAuditLogEntry) : this(log.id, log.timestamp, log.user, log.session, log.api) -} - -class RestLogoutAuditLogEntry(id: UID, timestamp: Long, val session: String, val api: LogEventSource) : RestAuditLogEntry(AuditLogEntryType.LOGOUT, id, timestamp) { - constructor(log: LogoutAuditLogEntry) : this(log.id, log.timestamp, log.session, log.api) -} - -class RestSubmissionValidationAuditLogEntry(id: UID, timestamp: Long, val submissionId: String, val validator: String, val verdict: SubmissionStatus) : RestAuditLogEntry(AuditLogEntryType.SUBMISSION_VALIDATION, id, timestamp) { - constructor(log: SubmissionValidationAuditLogEntry) : this(log.id, log.timestamp, log.submission.uid.string, log.validatorName, log.status) -} - -class RestSubmissionStatusOverwriteAuditLogEntry(id: UID, timestamp: Long, val runId: String, val submissionId: String, val newVerdict: SubmissionStatus, val session: String, val api: LogEventSource) : RestAuditLogEntry(AuditLogEntryType.SUBMISSION_STATUS_OVERWRITE, id, timestamp) { - constructor(log: SubmissionStatusOverwriteAuditLogEntry) : this(log.id, log.timestamp, log.competitionRunUid.string, log.submissionId.string, log.newVerdict, log.user?: "", log.api) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt new file mode 100644 index 000000000..4a7e4c4d7 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -0,0 +1,21 @@ +package dev.dres.api.rest.types.collection + +import dev.dres.data.model.UID +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType + +/** + * The RESTful API equivalent for [MediaItem]. + * + * @see MediaItem + * @author Ralph Gasser + * @version 1.1.0 + */ +data class ApiMediaItem(val id: String= UID.EMPTY.string, val name: String, val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { + init { + if (this.type == ApiMediaType.VIDEO) { + require(this.durationMs != null) { "Duration must be set for a video item." } + require(this.fps != null) { "Duration must be set for a video item." } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt new file mode 100644 index 000000000..a9a6d417a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -0,0 +1,15 @@ +package dev.dres.api.rest.types.collection + +import dev.dres.data.model.media.MediaType + +/** + * The RESTful API equivalent for the type of a [ApiMediaItem] + * + * @see ApiMediaItem + * @author Ralph Gasser + * @version 1.0 + */ +enum class ApiMediaType(val mediaType: MediaType) { + IMAGE(MediaType.IMAGE), + VIDEO(MediaType.VIDEO) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt index 7fa0fc90b..705e36cd8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt @@ -1,3 +1,3 @@ package dev.dres.api.rest.types.collection -data class RestFullMediaCollection (val collection:RestMediaCollection, val items: List) \ No newline at end of file +data class RestFullMediaCollection (val collection:RestMediaCollection, val items: List) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt index 13c5644c5..eca39ed8d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt @@ -1,9 +1,8 @@ package dev.dres.api.rest.types.collection -import dev.dres.data.model.basics.media.CollectionId -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.competition.TaskDescription -import dev.dres.utilities.extensions.UID +import dev.dres.data.model.media.CollectionId +import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.competition.task.TaskDescription /** * The RESTful API equivalent for [MediaCollection]. @@ -15,7 +14,7 @@ import dev.dres.utilities.extensions.UID data class RestMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { companion object { /** - * Generates a [RestMediaItem] from a [TaskDescription] and returns it. + * Generates a [ApiMediaItem] from a [TaskDescription] and returns it. * * @param task The [TaskDescription] to convert. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt deleted file mode 100644 index aeb9f40db..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItem.kt +++ /dev/null @@ -1,34 +0,0 @@ -package dev.dres.api.rest.types.collection - -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaType - -/** - * The RESTful API equivalent for [MediaItem]. - * - * @see MediaItem - * @author Ralph Gasser - * @version 1.1.0 - */ -data class RestMediaItem(val id: String= UID.EMPTY.string, val name: String, val type: RestMediaItemType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { - companion object { - /** - * Generates a [RestMediaItem] from a [MediaItem] and returns it. - * - * @param item The [MediaItem] to convert. - */ - fun fromMediaItem(item: MediaItem) = when (item.type) { - MediaType.IMAGE -> RestMediaItem(item.id, item.name, RestMediaItemType.IMAGE, item.collection.id, item.location) - MediaType.VIDEO -> RestMediaItem(item.id, item.name, RestMediaItemType.VIDEO, item.collection.id, item.location, item.durationMs, item.fps) - else -> throw IllegalArgumentException("Unsupported media type ${item.type}.") - } - } - - init { - if (this.type == RestMediaItemType.VIDEO) { - require(this.durationMs != null) { "Duration must be set for a video item." } - require(this.fps != null) { "Duration must be set for a video item." } - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItemType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItemType.kt deleted file mode 100644 index 9ee680ce2..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaItemType.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.dres.api.rest.types.collection - -/** - * The RESTful API equivalent for the type of a [RestMediaItem] - * - * @see RestMediaItem - * @author Ralph Gasser - * @version 1.0 - */ -enum class RestMediaItemType { - IMAGE, VIDEO -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt new file mode 100644 index 000000000..fd15b4808 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.competition + +import dev.dres.api.rest.types.users.ApiUser + +/** + * A RESTful API representation of a [Team] + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class ApiTeam(val name: String, val color: String, val logoId: String, val users: List = emptyList()) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt index 4dd6a1231..968ea4ca8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt @@ -3,10 +3,10 @@ package dev.dres.api.rest.types.competition import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.admin.UserId -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskGroup -import dev.dres.data.model.competition.TaskType +import dev.dres.data.model.competition.task.TaskGroup +import dev.dres.data.model.competition.task.TaskType import dev.dres.utilities.extensions.UID /** @@ -18,15 +18,15 @@ import dev.dres.utilities.extensions.UID * */ data class RestCompetitionDescription( - val id: String, - val name: String, - val description: String?, - val taskTypes: List, - val taskGroups: List, - val tasks: List, - val teams: List, - val teamGroups: List, - val judges: List + val id: String, + val name: String, + val description: String?, + val taskTypes: List, + val taskGroups: List, + val tasks: List, + val teams: List, + val teamGroups: List, + val judges: List ) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt deleted file mode 100644 index 80edf8059..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestDetailedTeam.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.api.rest.handler.UserDetails -import dev.dres.data.model.competition.team.Team -import dev.dres.mgmt.admin.UserManager - -data class RestDetailedTeam(val name: String, val color: String, val logoId: String, val users: List) { - companion object { - fun of(team: Team): RestDetailedTeam { - return RestDetailedTeam(team.name, team.color, team.logoId.string, team.users.map { - UserDetails.of(UserManager.get(it)!!) - }) - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt index 58ed61a7a..ed506d480 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt @@ -1,12 +1,14 @@ package dev.dres.api.rest.types.competition import dev.dres.api.rest.types.competition.tasks.RestTaskDescriptionComponent -import dev.dres.api.rest.types.competition.tasks.RestTaskDescriptionTarget +import dev.dres.api.rest.types.competition.tasks.ApiTarget import dev.dres.data.dbo.DAO import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.competition.* -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskGroup +import dev.dres.data.model.competition.task.TaskType import dev.dres.utilities.extensions.UID /** @@ -17,14 +19,14 @@ import dev.dres.utilities.extensions.UID * @version 1.0 */ class RestTaskDescription( - val id: String = UID().string, - val name: String, - val taskGroup: String, - val taskType: String, - val duration: Long, - val mediaCollectionId: String, - val target: RestTaskDescriptionTarget, - val components: List + val id: String = UID().string, + val name: String, + val taskGroup: String, + val taskType: String, + val duration: Long, + val mediaCollectionId: String, + val target: ApiTarget, + val components: List ) { @@ -41,7 +43,7 @@ class RestTaskDescription( task.taskType.name, task.duration, task.mediaCollectionId.string, - RestTaskDescriptionTarget.fromTarget(task.target), + ApiTarget.fromTarget(task.target), task.hints.map { RestTaskDescriptionComponent.fromComponent(it) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt new file mode 100644 index 000000000..0b7eda007 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt @@ -0,0 +1,45 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.competition.task.TaskDescriptionTarget +import dev.dres.utilities.extensions.UID + +/** + * The RESTful API equivalent for [TaskDescriptionTarget]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ +data class ApiTarget(val type: ApiTargetType, val items: List = emptyList()) { + + /** + * Converts this [ApiTarget] to the corresponding [TaskDescriptionTarget] and returns it. + * + * @param mediaItems [DAO] used to perform media item lookups. + */ + fun toTarget(mediaItems: DAO) = when(this.type){ + TargetOption.SINGLE_MEDIA_SEGMENT -> { + val item = mediaItems[this.items.first().mediaItem.UID()]!! as MediaItem.VideoItem + TaskDescriptionTarget.VideoSegmentTarget(item, this.items.first().temporalRange!!.toTemporalRange(item.fps)) + } + TargetOption.JUDGEMENT -> TaskDescriptionTarget.JudgementTaskDescriptionTarget( + this.items.map { + val item = mediaItems[it.mediaItem.UID()]!! + val fps = if (item is MediaItem.VideoItem) item.fps else 0f + item to it.temporalRange!!.toTemporalRange(fps) + } + ) + TargetOption.SINGLE_MEDIA_ITEM -> TaskDescriptionTarget.MediaItemTarget(mediaItems[this.items.first().mediaItem.UID()]!!) + TargetOption.MULTIPLE_MEDIA_ITEMS -> TaskDescriptionTarget.MultipleMediaItemTarget(this.items.map { mediaItems[it.mediaItem.UID()]!! }) + TargetOption.VOTE -> TaskDescriptionTarget.VoteTaskDescriptionTarget( + this.items.map { + val item = mediaItems[it.mediaItem.UID()]!! + val fps = if (item is MediaItem.VideoItem) item.fps else 0f + item to it.temporalRange!!.toTemporalRange(fps) + } + ) + TargetOption.TEXT -> TaskDescriptionTarget.TextTaskDescriptionTarget( + this.items.map { it.mediaItem } //TODO maybe should be renamed from 'mediaItem' to something else + ) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTargetItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt similarity index 52% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTargetItem.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt index 43281dee7..054369acd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTargetItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt @@ -3,9 +3,9 @@ package dev.dres.api.rest.types.competition.tasks /** * The RESTful API representation of a target item. * - * @see RestTaskDescriptionTarget + * @see ApiTarget * @author Ralph Gasser * @version 1.0 * */ -data class RestTaskDescriptionTargetItem(val mediaItem: String, val temporalRange: RestTemporalRange? = null) \ No newline at end of file +data class ApiTargetItem(val target: String, val temporalRange: RestTemporalRange? = null) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt new file mode 100644 index 000000000..8b32b5e08 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt @@ -0,0 +1,17 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.data.model.competition.task.TargetType + +/** + * The RESTful API equivalent for [TargetType]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.0 + */ +enum class ApiTargetType(val targetType: TargetType) { + JUDGEMENT(TargetType.JUDGEMENT), + JUDGEMENT_WITH_VOTE(TargetType.JUDGEMENT_WITH_VOTE), + MEDIA_ITEM(TargetType.MEDIA_ITEM), + MEDIA_ITEM_TEMPORAL_RANGE(TargetType.MEDIA_ITEM_TEMPORAL_RANGE), + TEXT(TargetType.TEXT) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt index f2ab823c8..5fca9e419 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt @@ -1,9 +1,10 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.data.dbo.DAO -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.competition.* import dev.dres.data.model.competition.options.QueryComponentOption +import dev.dres.data.model.competition.task.TaskDescriptionHint import dev.dres.utilities.extensions.UID import java.nio.file.Paths diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt deleted file mode 100644 index 755e839e2..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionTarget.kt +++ /dev/null @@ -1,64 +0,0 @@ -package dev.dres.api.rest.types.competition.tasks - -import dev.dres.data.dbo.DAO -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.competition.TaskDescriptionTarget -import dev.dres.data.model.competition.options.TargetOption -import dev.dres.utilities.extensions.UID - -/** - * The RESTful API equivalent for [TaskDescriptionTarget]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.0 - */ -data class RestTaskDescriptionTarget(val type: TargetOption, val mediaItems: List = emptyList()) { - - companion object { - - /** - * Generates a [RestTaskDescriptionTarget] from a [TaskDescriptionTarget] and returns it. - * - * @param target The [TaskDescriptionTarget] to convert. - */ - fun fromTarget(target: TaskDescriptionTarget) = when(target) { - is TaskDescriptionTarget.JudgementTaskDescriptionTarget -> RestTaskDescriptionTarget(TargetOption.JUDGEMENT, target.targets.map { RestTaskDescriptionTargetItem(it.first.id.string, if (it.second == null) null else RestTemporalRange(it.second!!)) }) - is TaskDescriptionTarget.VideoSegmentTarget -> RestTaskDescriptionTarget(TargetOption.SINGLE_MEDIA_SEGMENT, listOf(RestTaskDescriptionTargetItem(target.item.id.string, RestTemporalRange(target.temporalRange)))) - is TaskDescriptionTarget.MediaItemTarget -> RestTaskDescriptionTarget(TargetOption.SINGLE_MEDIA_ITEM, listOf(RestTaskDescriptionTargetItem(target.item.id.string))) - is TaskDescriptionTarget.MultipleMediaItemTarget -> RestTaskDescriptionTarget(TargetOption.MULTIPLE_MEDIA_ITEMS, target.items.map { RestTaskDescriptionTargetItem(it.id.string) }) - is TaskDescriptionTarget.VoteTaskDescriptionTarget -> RestTaskDescriptionTarget(TargetOption.VOTE, target.targets.map { RestTaskDescriptionTargetItem(it.first.id.string, if (it.second == null) null else RestTemporalRange(it.second!!)) }) - is TaskDescriptionTarget.TextTaskDescriptionTarget -> RestTaskDescriptionTarget(TargetOption.TEXT, target.targets.map { RestTaskDescriptionTargetItem(it, null) }) - } - } - - /** - * Converts this [RestTaskDescriptionTarget] to the corresponding [TaskDescriptionTarget] and returns it. - * - * @param mediaItems [DAO] used to perform media item lookups. - */ - fun toTarget(mediaItems: DAO) = when(this.type){ - TargetOption.SINGLE_MEDIA_SEGMENT -> { - val item = mediaItems[this.mediaItems.first().mediaItem.UID()]!! as MediaItem.VideoItem - TaskDescriptionTarget.VideoSegmentTarget(item, this.mediaItems.first().temporalRange!!.toTemporalRange(item.fps)) - } - TargetOption.JUDGEMENT -> TaskDescriptionTarget.JudgementTaskDescriptionTarget( - this.mediaItems.map { - val item = mediaItems[it.mediaItem.UID()]!! - val fps = if (item is MediaItem.VideoItem) item.fps else 0f - item to it.temporalRange!!.toTemporalRange(fps) - } - ) - TargetOption.SINGLE_MEDIA_ITEM -> TaskDescriptionTarget.MediaItemTarget(mediaItems[this.mediaItems.first().mediaItem.UID()]!!) - TargetOption.MULTIPLE_MEDIA_ITEMS -> TaskDescriptionTarget.MultipleMediaItemTarget(this.mediaItems.map { mediaItems[it.mediaItem.UID()]!! }) - TargetOption.VOTE -> TaskDescriptionTarget.VoteTaskDescriptionTarget( - this.mediaItems.map { - val item = mediaItems[it.mediaItem.UID()]!! - val fps = if (item is MediaItem.VideoItem) item.fps else 0f - item to it.temporalRange!!.toTemporalRange(fps) - } - ) - TargetOption.TEXT -> TaskDescriptionTarget.TextTaskDescriptionTarget( - this.mediaItems.map { it.mediaItem } //TODO maybe should be renamed from 'mediaItem' to something else - ) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt index 7295b6baa..1f7c4d675 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt @@ -1,6 +1,8 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.data.model.basics.time.* +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange data class RestTemporalRange(val start: RestTemporalPoint, val end: RestTemporalPoint) { constructor(range: TemporalRange) : this(RestTemporalPoint.fromTemporalPoint(range.start), RestTemporalPoint.fromTemporalPoint(range.end)) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt index 80e3c1e90..f00428a74 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.run -import dev.dres.api.rest.types.collection.RestMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.ItemAspect @@ -21,7 +21,7 @@ data class SubmissionInfo( val memberName: String? = null, val status: SubmissionStatus, val timestamp: Long, - val item: RestMediaItem? = null, + val item: ApiMediaItem? = null, val text: String? = null, val start: Long? = null, val end: Long? = null @@ -40,7 +40,7 @@ data class SubmissionInfo( memberId = submission.memberId.string, status = submission.status, timestamp = submission.timestamp, - item = if (submission is ItemAspect) RestMediaItem.fromMediaItem(submission.item) else null, + item = if (submission is ItemAspect) submission.item.toApi() else null, text = if (submission is TextAspect) submission.text else null, start = if (submission is TemporalSubmissionAspect) submission.start else null, end = if (submission is TemporalSubmissionAspect) submission.end else null diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt index c7db3dcea..7cd2b21d0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.run import dev.dres.data.model.UID -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.run.InteractiveSynchronousCompetition /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentElement.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentElement.kt new file mode 100644 index 000000000..5c6372c2b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentElement.kt @@ -0,0 +1,13 @@ +package dev.dres.api.rest.types.task + +/** + * Describes a [ApiContentElement], i.e., a piece of content that should be displayed to the user. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.1 + * + * @param content Encoded content; use [ApiContentType] to decode. + * @param contentType [ApiContentType] of the content held by this [ApiContentElement] + * @param offset Time in seconds after which this [ApiContentElement] should be displayed. + */ +data class ApiContentElement(val contentType: ApiContentType, val content: String? = null, val offset: Long) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentType.kt similarity index 64% rename from backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentType.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentType.kt index ac8a16020..a8269c95f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/task/ApiContentType.kt @@ -1,6 +1,9 @@ package dev.dres.api.rest.types.task -enum class ContentType(val mimeType: String, val base64: Boolean) { +/** + * + */ +enum class ApiContentType(val mimeType: String, val base64: Boolean) { EMPTY("", false), TEXT("text/plain", false), VIDEO("video/mp4", true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentElement.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentElement.kt deleted file mode 100644 index e3ebeb6f5..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/task/ContentElement.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.api.rest.types.task - -/** - * Describes a [ContentElement], i.e., a piece of content that should be displayed to the user. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.1 - * - * @param content Encoded content; use [ContentType] to decode. - * @param contentType [ContentType] of the content held by this [ContentElement] - * @param offset Time in seconds after which this [ContentElement] should be displayed. - */ -data class ContentElement(val contentType: ContentType, val content: String? = null, val offset: Long) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt index 4624c5840..6c1c80c3a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt @@ -8,7 +8,7 @@ package dev.dres.api.rest.types.task * @version 1.0 * * @param taskId of the [TaskDescription] this [TaskHint] was derived from. - * @param sequence Sequence of [ContentElement]s to display. - * @param loop Specifies if last [ContentElement] should be displayed until the end or if the entire sequence should be looped. + * @param sequence Sequence of [ApiContentElement]s to display. + * @param loop Specifies if last [ApiContentElement] should be displayed until the end or if the entire sequence should be looped. */ -data class TaskHint(val taskId: String, val sequence: List = emptyList(), val loop: Boolean = false) +data class TaskHint(val taskId: String, val sequence: List = emptyList(), val loop: Boolean = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt index 47ed5e59f..221e47df6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt @@ -8,6 +8,6 @@ package dev.dres.api.rest.types.task * @version 1.0 * * @param taskId of the [TaskDescription] this [TaskTarget] was derived from. - * @param sequence Sequence of [ContentElement]s to display. + * @param sequence Sequence of [ApiContentElement]s to display. */ -data class TaskTarget(val taskId: String, val sequence: List = emptyList()) +data class TaskTarget(val taskId: String, val sequence: List = emptyList()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt index 5cb7d512a..b4258f896 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.users +import dev.dres.data.model.admin.Role import io.javalin.security.RouteRole /** @@ -8,4 +9,10 @@ import io.javalin.security.RouteRole * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN } \ No newline at end of file +enum class ApiRole(val role: Role?) : RouteRole { + ANYONE(null), + VIEWER(Role.VIEWER), + PARTICIPANT(Role.PARTICIPANT), + JUDGE(Role.JUDGE), + ADMIN(Role.ADMIN) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt new file mode 100644 index 000000000..6c0525e1d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt @@ -0,0 +1,12 @@ +package dev.dres.api.rest.types.users + +import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.UserId + +/** + * A RESTful API representation of a [User] + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class ApiUser(val id: UserId, val username: String, val role: ApiRole, var sessionId: String? = null) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt deleted file mode 100644 index 300f528fb..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserDetails.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.dres.api.rest.types.users - -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User -import dev.dres.utilities.extensions.sessionId -import io.javalin.http.Context - -/** - * A response surrounding manipulation of users. - * - * @author Loris Sauter - * @version 1.0.0 - */ -data class UserDetails(val id: String, val username: String, val role: Role, val sessionId: String? = null) { - companion object { - fun of(user: User): UserDetails = UserDetails(user.id, user.username, user.role) - fun create(user: User, ctx: Context): UserDetails = UserDetails(user.id, user.username, user.role, ctx.sessionId()) - } -} diff --git a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt index 33bcd30bd..487a3692a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt @@ -5,9 +5,11 @@ import kotlinx.dnq.XdEntity import kotlinx.dnq.xdRequiredStringProp /** + * The root class for all DRES entities that are persisted via Xodus DNQ. * + * @author Ralph Gasser + * @version 1.0.0 */ abstract class PersistentEntity(entity: Entity): XdEntity(entity) { - /** */ var id: String by xdRequiredStringProp(unique = true, trimmed = false) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt index 82f63eb39..e5798d067 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt @@ -2,10 +2,11 @@ package dev.dres.data.model.admin import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty -import dev.dres.mgmt.admin.UserManager import org.mindrot.jbcrypt.BCrypt /** + * A [Password]. Exists in two variations [Plain] and [Hashed]. + * * * @author Ralph Gasser * @version 1.0.0 @@ -13,7 +14,7 @@ import org.mindrot.jbcrypt.BCrypt sealed class Password(val password: String) { class Plain(password: String): Password(password) { init { - require (password.length < UserManager.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${UserManager.MIN_LENGTH_PASSWORD}" } + require (password.length < User.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${User.MIN_LENGTH_PASSWORD}" } } fun hash(): Hashed = Hashed(BCrypt.hashpw(this.password, BCrypt.gensalt())) val length: Int diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 3da07f9d7..688bd9dc1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -1,6 +1,8 @@ package dev.dres.data.model.admin +import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.users.ApiUser import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -36,22 +38,17 @@ class Role(entity: Entity) : XdEnumEntity(entity) { "ADMIN", "ADMINISTRATOR" -> ADMIN else -> throw IllegalArgumentException("Failed to parse role '$string'.") } - - /** - * Generates and returns the [Role] that corresponds to the given [ApiRole]. - * - * @param role [ApiRole] - */ - fun convertApiRole(role: ApiRole): Role = when(role) { - ApiRole.ANYONE, - ApiRole.VIEWER -> VIEWER - ApiRole.PARTICIPANT -> PARTICIPANT - ApiRole.JUDGE -> JUDGE - ApiRole.ADMIN -> ADMIN - } } /** Name / description of the [Role]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [Role] to a RESTful API representation [ApiRole]. + * + * @return [ApiRole] + */ + fun toApi(): ApiRole + = ApiRole.values().find { it.role == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index a5d7a6342..3a8b7c733 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -1,13 +1,10 @@ package dev.dres.data.model.admin +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.team.Team import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdNaturalEntityType -import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.simple.length -import kotlinx.dnq.xdLink0_N import kotlinx.dnq.xdLink1 import kotlinx.dnq.xdRequiredStringProp @@ -42,16 +39,19 @@ class User(entity: Entity): PersistentEntity(entity) { /** The [Role] of this [User]. */ var role by xdLink1(Role) - /** The [Team]s this [User] belongs to. */ - val teams by xdLink0_N(Team::users, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) - - /** The [CompetitionDescription]s this [User] acts as judge for. */ - val judges by xdLink0_N(CompetitionDescription::judges, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) - /** * The [Password.Hashed] held by this [User]. */ fun hashedPassword() = Password.Hashed(this.password) override fun toString(): String = "User(id=$id, username=${username}, role=$role)" + + /** + * Converts this [User] to a RESTful API representation [ApiUser]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiUser] + */ + fun toApi() = ApiUser(id = this.userId, username = this.username, role = this.role.toApi()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt index 42618905c..e93e1ff9c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.audit +import dev.dres.api.rest.types.audit.* import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdNaturalEntityType @@ -7,6 +8,9 @@ import kotlinx.dnq.xdLink1 import kotlinx.dnq.xdRequiredDateTimeProp import kotlinx.dnq.xdStringProp +/** + * + */ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { companion object : XdNaturalEntityType() @@ -28,12 +32,6 @@ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { /** The ID of the submission this [AuditLogEntry] belongs to. Only valid if [type] is equal to [AuditLogType.SUBMISSION], [AuditLogType.SUBMISSION_VALIDATION] or [AuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ var submissionId by xdStringProp() - /** The submission verdicht this captured by this [AuditLogEntry]. Only valid if [type] is equal to [AuditLogType.SUBMISSION_VALIDATION] or [AuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ - var verdict by xdStringProp() - - /** The name of the submission validator. Only valid if [type] is equal to [AuditLogType.SUBMISSION_VALIDATION]. */ - var validatorName by xdStringProp() - /** The user ID of the user who generated this [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ var userId by xdStringProp() @@ -45,4 +43,13 @@ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { /** Descriptive metadata for this [AuditLogEntry]. */ var description by xdStringProp() + + /** + * Converts this [AuditLogEntry] to a RESTful API representation [ApiAuditLogEntry]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiAuditLogEntry] + */ + fun toApi(): ApiAuditLogEntry = ApiAuditLogEntry(this.id, this.type.toApi(), this.source.toApi(), this.timestamp.millis, this.competitionId, this.userId, this.submissionId, this.session, this.address, this.description) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt index 6a1cd16e8..b0a846e03 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.audit +import dev.dres.api.rest.types.audit.ApiAuditLogSource import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -21,4 +22,12 @@ class AuditLogSource(entity: Entity) : XdEnumEntity(entity) { /** Name / description of the [AuditLogType]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [AuditLogSource] to a RESTful API representation [ApiAuditLogSource]. + * + * @return [ApiAuditLogSource] + */ + fun toApi(): ApiAuditLogSource + = ApiAuditLogSource.values().find { it.source == this } ?: throw IllegalStateException("Audit log source ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt index 8986d5bb7..1b8f81a14 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt @@ -1,5 +1,7 @@ package dev.dres.data.model.audit +import dev.dres.api.rest.types.audit.ApiAuditLogSource +import dev.dres.api.rest.types.audit.ApiAuditLogType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -30,4 +32,12 @@ class AuditLogType(entity: Entity) : XdEnumEntity(entity) { /** Name / description of the [AuditLogType]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [AuditLogSource] to a RESTful API representation [ApiAuditLogSource]. + * + * @return [ApiAuditLogSource] + */ + fun toApi(): ApiAuditLogType + = ApiAuditLogType.values().find { it.type == this } ?: throw IllegalStateException("Audit log type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt deleted file mode 100644 index cab245b5d..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItem.kt +++ /dev/null @@ -1,38 +0,0 @@ -package dev.dres.data.model.basics.media - -import dev.dres.data.model.PersistentEntity -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* - -/** - * A media item such as a video or an image - * - * @author Ralph Gasser - * @version 2.0.0 - */ -class MediaItem(entity: Entity) : PersistentEntity(entity) { - - /** */ - companion object : XdNaturalEntityType() - - /** The name of this [MediaItem]. */ - var name by xdRequiredStringProp(unique = false, trimmed = false) - - /** The location of this [MediaItem] on disk. */ - var location by xdRequiredStringProp(unique = false, trimmed = false) - - /** Frame rate of the [MediaItem] in frames per second. Null for type without temporal development. */ - var fps by xdNullableFloatProp() - - /** Duration of the [MediaItem] in milliseconds. Null for type without temporal development. */ - var durationMs by xdNullableLongProp() - - /** The [MediaType] of this [MediaItem]. */ - var type by xdLink1(MediaType) - - /** The [MediaCollection] this [MediaItem] belongs to. */ - var collection: MediaCollection by xdParent(MediaCollection::items) - - /** List of [MediaItemSegment] that this [MediaItem] contains. */ - val segments by xdChildren0_N(MediaItemSegment::item) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt deleted file mode 100644 index 1f27a0d42..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaType.kt +++ /dev/null @@ -1,36 +0,0 @@ -package dev.dres.data.model.basics.media - -import dev.dres.api.rest.ApiRole -import dev.dres.api.rest.types.collection.RestMediaItem -import dev.dres.api.rest.types.collection.RestMediaItemType -import dev.dres.data.model.admin.Role -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdEnumEntity -import kotlinx.dnq.XdEnumEntityType -import kotlinx.dnq.xdRequiredStringProp - -/** - * A persistable media type enumeration such as a video or an image - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class MediaType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { - val IMAGE by enumField { description = "IMAGE" } - val VIDEO by enumField { description = "VIDEO" } - - /** - * Generates and returns the [MediaType] that corresponds to the given [RestMediaItemType]. - * - * @param type [RestMediaItemType] - */ - fun convertApiType(type: RestMediaItemType): MediaType = when(type) { - RestMediaItemType.IMAGE -> MediaType.IMAGE - RestMediaItemType.VIDEO -> MediaType.VIDEO - } - } - - var description by xdRequiredStringProp(unique = true) - private set -} diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt index f87eeb21b..7895c7996 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt @@ -1,36 +1,51 @@ package dev.dres.data.model.competition -import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.UID import dev.dres.data.model.admin.User -import dev.dres.data.model.admin.UserId +import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType +import dev.dres.data.model.competition.task.TaskGroup +import dev.dres.data.model.competition.task.TaskType import dev.dres.data.model.competition.team.Team import dev.dres.data.model.competition.team.TeamGroup -import dev.dres.data.model.competition.team.TeamGroupId import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.link.OnDeletePolicy -import java.nio.file.Paths +import kotlinx.dnq.query.* + +typealias CompetitionDescriptionId = String /** + * Basic description of a competitions as executed in DRES. + * + * Defines basic attributes such as its name and the [TaskType]s and [TaskGroup]s it contains. * + * @version 2.0.0 + * @author Luca Rossetto & Ralph Gasser */ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ companion object: XdNaturalEntityType() + /** The [CompetitionDescriptionId] of this [CompetitionDescription]. */ + var teamId: CompetitionDescriptionId + get() = this.id + set(value) { this.id = value } + /** The name held by this [CompetitionDescription]. Must be unique!*/ var name by xdRequiredStringProp(unique = true, trimmed = false) /** An optional description of this [CompetitionDescription]. */ var description by xdStringProp(trimmed = false) + /** The [TaskType]s defined within this [CompetitionDescription]. */ + val taskTypes by xdChildren0_N(TaskType::competition) + /** The [TaskGroup]s that are part of this [CompetitionDescription]. */ - val taskGroups by xdChildren0_N(TaskGroup::competition) + val taskGroups by xdChildren0_N(TaskGroup::competition) /** The [Team]s that are part of this [CompetitionDescription]. */ val teams by xdChildren0_N(Team::competition) @@ -41,56 +56,39 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ /** The [User]s that act as judge for this [CompetitionDescription] */ val judges by xdLink0_N(User::judges, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) - /* - val taskTypes: MutableList, - val tasks: MutableList, - */ - - fun validate() { - for (group in this.taskGroups) { - if (this.taskGroups.map { it.name }.count { it == group.name } > 1) { - throw IllegalArgumentException("Duplicate group with name '${group.name}'!") - } - } - - for (task in this.tasks) { - if (this.tasks.map { it.name }.count { it == task.name } > 1) { - throw IllegalArgumentException("Duplicate task with name '${task.name}'!") - } - } - - for (team in this.teams) { - if (this.teams.map { it.name }.count { it == team.name } > 1) { - throw IllegalArgumentException("Duplicate team with name '${team.name}'!") - } - } - - tasks.forEach { it.validate() } - } - /** - * Generates and returns the default [Scoreboard] implementations for this [CompetitionDescription] + * Generates and returns the default [Scoreboard] implementations for this [CompetitionDescription]. + * + * This is a convenience method and requires an active transaction context. * * @return List of [Scoreboard] implementations. */ fun generateDefaultScoreboards(): List { - val groupBoards = this.taskGroups.map { group -> - MaxNormalizingScoreBoard(group.name, this.teams, {task -> task.taskGroup == group}, group.name) - } + val teams = this.teams.toList() + val groupBoards = this.taskGroups.asSequence().map { group -> + MaxNormalizingScoreBoard(group.name, teams, {task -> task.taskGroup.id == group.id}, group.name) + }.toList() val aggregateScoreBoard = SumAggregateScoreBoard("sum", groupBoards) return groupBoards.plus(aggregateScoreBoard) } /** - * Generates and returns a list of all [CachedVideoItem] for this [CompetitionDescription]. + * Generates and returns a list of all [MediaItem] for this [CompetitionDescription]. * - * This is a convenience method and cannot be serialized! + * This is a convenience method and requires an active transaction context. * - * @return [List] of [CachedVideoItem]s + * @return [List] of [MediaItem]s */ - @JsonIgnore - fun getAllCachedVideoItems(): List = this.tasks - .flatMap { it.hints } - .filterIsInstance(CachedVideoItem::class.java) - .plus(tasks.map { it.target }.filterIsInstance(CachedVideoItem::class.java)) + fun getAllVideos(): List { + return (this.taskGroups.flatMapDistinct { it.tasks } + .flatMapDistinct { it.hints } + .filter { it.hintItem ne null } + .mapDistinct { it.hintItem } union + this.taskGroups.flatMapDistinct { it.tasks } + .flatMapDistinct { it.targets } + .filter { it.item ne null } + .mapDistinct { it.item }).filter { + it.type eq MediaType.VIDEO + }.toList() + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt deleted file mode 100644 index 11b976afa..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/Ids.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.dres.data.model.competition - -import dev.dres.data.model.UID - -/** The ID of a [TaskDescription]. */ -typealias TaskDescriptionId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionHint.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionHint.kt deleted file mode 100644 index e25e82ae5..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionHint.kt +++ /dev/null @@ -1,154 +0,0 @@ -package dev.dres.data.model.competition - -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.fasterxml.jackson.annotation.JsonTypeName -import dev.dres.api.rest.types.task.ContentType -import dev.dres.api.rest.types.task.ContentElement -import dev.dres.data.model.Config -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.time.TemporalRange -import dev.dres.data.model.competition.interfaces.FileTaskDescriptionComponent -import java.io.File -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Path -import java.util.* - -/** - * Represents a descriptive component of a [TaskDescription], i.e., the part that describes the - * target of the [TaskDescription]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.2.0 - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -sealed class TaskDescriptionHint { - abstract val contentType: ContentType - abstract val ordinal: Int - abstract val start: Long? - abstract val end: Long? - - internal abstract fun textDescription(): String - - /** - * Generates and returns a [ContentElement] object to be used by the RESTful interface. - * - * @param config The [Config] used of path resolution. - * @return [ContentElement] - * - * @throws FileNotFoundException - * @throws IOException - */ - internal abstract fun toQueryContentElement(config: Config): ContentElement - - /** - * A textual [TaskDescriptionHint] consisting of a simple, textual description. - */ - @JsonTypeName("TextHint") - data class TextTaskDescriptionHint(val text: String, override val start: Long?, override val end: Long?) : TaskDescriptionHint() { - @JsonIgnore - override val ordinal = 1 - - @JsonIgnore - override val contentType = ContentType.TEXT - - override fun textDescription(): String = "\"$text\" from ${start ?: "beginning"} to ${end ?: "end"}" - override fun toQueryContentElement(config: Config): ContentElement = ContentElement(ContentType.TEXT, this.text, this.start ?: 0) - } - - /** - * A visual [TaskDescriptionHint] consisting of a single image that is part of a collection maintained by DRES. - */ - @JsonTypeName("ImageHint") - data class ImageItemTaskDescriptionHint(val item: MediaItem.ImageItem, override val start: Long?, override val end: Long?): TaskDescriptionHint() { - @JsonIgnore - override val ordinal = 2 - - @JsonIgnore - override val contentType = ContentType.IMAGE - - override fun textDescription(): String = "Image ${item.name} from ${start ?: "beginning"} to ${end ?: "end"}" - override fun toQueryContentElement(config: Config): ContentElement { - val file = File(config.cachePath + "/tasks") - return FileInputStream(file).use { imageInFile -> - val fileData = ByteArray(file.length().toInt()) - imageInFile.read(fileData) - ContentElement(ContentType.IMAGE, Base64.getEncoder().encodeToString(fileData), this.start ?: 0) - } - } - } - - /** - * A visual [TaskDescriptionHint] consisting of a segment of a video that is part of a collection maintained by DRES. - */ - @JsonTypeName("VideoSegmentHint") - data class VideoItemSegmentTaskDescriptionHint(override val item: MediaItem.VideoItem, override val temporalRange: TemporalRange, override val start: Long?, override val end: Long?) : TaskDescriptionHint(), CachedVideoItem { - @JsonIgnore - override val ordinal = 3 - - @JsonIgnore - override val contentType = ContentType.VIDEO - - override fun textDescription(): String = "Video ${item.name} from ${start ?: "beginning"} to ${end ?: "end"}" - override fun toQueryContentElement(config: Config): ContentElement { - val file = File(config.cachePath + "/tasks", this.cacheItemName()) - return FileInputStream(file).use { imageInFile -> - val fileData = ByteArray(file.length().toInt()) - imageInFile.read(fileData) - ContentElement(ContentType.VIDEO, Base64.getEncoder().encodeToString(fileData), this.start ?: 0) - } - } - } - - /** - * A visual [TaskDescriptionHint] consisting of an external image provided by the user. - */ - @JsonTypeName("ExternalImageHint") - data class ExternalImageTaskDescriptionHint(val imageLocation: Path, override val start: Long?, override val end: Long?) : TaskDescriptionHint() { - @JsonIgnore - override val ordinal = 4 - - @JsonIgnore - override val contentType = ContentType.IMAGE - - override fun textDescription(): String = "External Image at $imageLocation from ${start ?: "beginning"} to ${end ?: "end"}" - override fun toQueryContentElement(config: Config): ContentElement { - return Files.newInputStream(this.imageLocation).use { imageInFile -> - val fileData = ByteArray(Files.size(this.imageLocation).toInt()) - imageInFile.read(fileData) - ContentElement(ContentType.IMAGE, Base64.getEncoder().encodeToString(fileData), this.start ?: 0) - } - } - } - - /* - * A visual [TaskDescriptionComponent] consisting of an external video provided by the user. - */ - @JsonTypeName("ExternalVideoHint") - data class ExternalVideoTaskDescriptionHint(val videoLocation: Path, override val start: Long?, override val end: Long?) : TaskDescriptionHint() { - @JsonIgnore - override val ordinal = 5 - - @JsonIgnore - override val contentType = ContentType.IMAGE - - override fun textDescription(): String = "External Video at $videoLocation from ${start ?: "beginning"} to ${end ?: "end"}" - override fun toQueryContentElement(config: Config): ContentElement { - return Files.newInputStream(this.videoLocation).use { imageInFile -> - val fileData = ByteArray(Files.size(videoLocation).toInt()) - imageInFile.read(fileData) - ContentElement(ContentType.VIDEO, Base64.getEncoder().encodeToString(fileData), this.start ?: 0) - } - } - } -} - - - - - - - diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt deleted file mode 100644 index 8f6cc0dd8..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescriptionTarget.kt +++ /dev/null @@ -1,160 +0,0 @@ -package dev.dres.data.model.competition - -import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.fasterxml.jackson.annotation.JsonTypeName -import dev.dres.api.rest.types.task.ContentElement -import dev.dres.api.rest.types.task.ContentType -import dev.dres.data.dbo.DAO -import dev.dres.data.model.Config -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.time.TemporalRange -import java.io.File -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.IOException -import java.util.* - - -/** - * Represents the target of a [TaskDescription], i.e., the media object or segment that is - * considered correct. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -sealed class TaskDescriptionTarget { - - companion object { - const val IMAGE_CONTENT_ELEMENT_DURATION_S = 3L - const val EMPTY_CONTENT_ELEMENT_DURATION_S = 0L - } - - abstract val ordinal: Int - - internal abstract fun textDescription(): String - - /** - * Generates and returns a [ContentElement] object to be used by the RESTful interface. - * - * @param config The [Config] used of path resolution. - * @return [ContentElement] - * - * @throws FileNotFoundException - * @throws IOException - */ - internal abstract fun toQueryContentElement(config: Config, collections: DAO): List - - /** - * A [TaskDescriptionTarget] that is validated by human judges. - */ - @JsonTypeName("JudgementTarget") - data class JudgementTaskDescriptionTarget(val targets: List>) : TaskDescriptionTarget() { - override val ordinal: Int = 1 - override fun textDescription() = "Judgement" - override fun toQueryContentElement(config: Config, collections: DAO): List = emptyList() - } - - /** - * A [TaskDescriptionTarget], specified by a [MediaItem]. - */ - @JsonTypeName("MediaItemTarget") - data class MediaItemTarget(val item: MediaItem) : TaskDescriptionTarget() { - override val ordinal: Int = 2 - override fun textDescription() = "Media Item ${item.name}" - override fun toQueryContentElement(config: Config, collections: DAO): List { - val collection = collections[this.item.collection]!! - val file = File(File(collection.path), this.item.location) - val contentElements = mutableListOf() - FileInputStream(file).use { imageInFile -> - val fileData = ByteArray(file.length().toInt()) - imageInFile.read(fileData) - when(this.item) { - is MediaItem.VideoItem -> { - contentElements += ContentElement(ContentType.VIDEO, Base64.getEncoder().encodeToString(fileData), 0) - contentElements += ContentElement(ContentType.EMPTY, null, Math.floorDiv(this.item.durationMs, 1000L) + 1L) - } - is MediaItem.ImageItem ->{ - contentElements += ContentElement(ContentType.IMAGE, Base64.getEncoder().encodeToString(fileData), 0) - contentElements += ContentElement(ContentType.EMPTY, null, IMAGE_CONTENT_ELEMENT_DURATION_S) - } - } - } - return contentElements - } - } - - /** - * A [TaskDescriptionTarget], specified by a [MediaItem.VideoItem] and a [TemporalRange]. - */ - @JsonTypeName("VideoSegmentTarget") - data class VideoSegmentTarget(override val item: MediaItem.VideoItem, override val temporalRange: TemporalRange) : TaskDescriptionTarget(), CachedVideoItem { - override val ordinal: Int = 3 - override fun textDescription() = "Media Item ${item.name} @ ${temporalRange.start.niceText()} - ${temporalRange.end.niceText()}" - override fun toQueryContentElement(config: Config, collections: DAO): List { - val file = File(File(config.cachePath + "/tasks"), this.cacheItemName()) - val contentElements = mutableListOf() - FileInputStream(file).use { imageInFile -> - val fileData = ByteArray(file.length().toInt()) - imageInFile.read(fileData) - contentElements += ContentElement(ContentType.VIDEO, Base64.getEncoder().encodeToString(fileData), 0) - contentElements += ContentElement(ContentType.EMPTY, null, Math.floorDiv(this.temporalRange.durationMs(), 1000L) + 1L) - } - return contentElements - } - } - - /** - * A [TaskDescriptionTarget], specified by multiple [MediaItem]s. - */ - @JsonTypeName("MultipleMediaItemTarget") - data class MultipleMediaItemTarget(val items: List) : TaskDescriptionTarget() { - override val ordinal: Int = 4 - override fun textDescription() = "Media Items" - override fun toQueryContentElement(config: Config, collections: DAO): List { - var cummulativeOffset = 0L - val contentElements = mutableListOf() - this.items.forEach { item -> - val collection = collections[item.collection]!! - val file = File(File(collection.path), item.location) - FileInputStream(file).use { imageInFile -> - val fileData = ByteArray(file.length().toInt()) - imageInFile.read(fileData) - when(item) { - is MediaItem.VideoItem -> { - contentElements += ContentElement(ContentType.VIDEO, Base64.getEncoder().encodeToString(fileData), cummulativeOffset) - cummulativeOffset += Math.floorDiv(item.durationMs, 1000L) + 1L - } - is MediaItem.ImageItem -> { - contentElements += ContentElement(ContentType.IMAGE, Base64.getEncoder().encodeToString(fileData), cummulativeOffset) - cummulativeOffset += IMAGE_CONTENT_ELEMENT_DURATION_S - } - } - - /* Add pause in between. */ - contentElements += ContentElement(ContentType.EMPTY, null, cummulativeOffset) - cummulativeOffset += EMPTY_CONTENT_ELEMENT_DURATION_S - } - } - return contentElements - } - } - - @JsonTypeName("VoteTarget") - data class VoteTaskDescriptionTarget(val targets: List>) : TaskDescriptionTarget() { - override val ordinal: Int = 5 - override fun textDescription() = "Judgement with voting" - override fun toQueryContentElement(config: Config, collections: DAO): List = emptyList() - } - - @JsonTypeName("TextTarget") - data class TextTaskDescriptionTarget(val targets: List) : TaskDescriptionTarget() { - override val ordinal: Int = 6 - override fun textDescription() = targets.joinToString(separator = ", ") - override fun toQueryContentElement(config: Config, collections: DAO): List = - listOf( - ContentElement(ContentType.TEXT, targets.joinToString(separator = ", "), 0) - ) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt deleted file mode 100644 index f056ad466..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskGroup.kt +++ /dev/null @@ -1,29 +0,0 @@ -package dev.dres.data.model.competition - -import dev.dres.data.model.PersistentEntity -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdNaturalEntityType -import kotlinx.dnq.xdChildren0_N -import kotlinx.dnq.xdParent -import kotlinx.dnq.xdRequiredStringProp - -/** - * A [TaskGroup] allows the user to specify common traits among a group of [Task]s. - * - * @author Luca Rossetto & Ralph Gasser - * @version 2.0.0 - */ -class TaskGroup(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() - - /** The name of this [TaskGroup]. */ - var name: String by xdRequiredStringProp(false, false) - - var type: String by xdRequiredStringProp(false, false) - - /** The [CompetitionDescription] this [TaskGroup] belongs to. */ - var competition by xdParent(CompetitionDescription::taskGroups) - - /** The [TaskDescription]s contained in this [TaskGroup]*/ - val tasks by xdChildren0_N(TaskDescription::taskGroup) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt deleted file mode 100644 index 1799aaca9..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskType.kt +++ /dev/null @@ -1,41 +0,0 @@ -package dev.dres.data.model.competition - -import dev.dres.data.model.competition.options.* -import dev.dres.run.filter.AllSubmissionFilter -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.filter.SubmissionFilterAggregator -import dev.dres.run.score.interfaces.TaskScorer -import dev.dres.run.validation.interfaces.SubmissionValidator - - -data class TaskType( - val name: String, - val taskDuration: Long, //in ms - val targetType: ConfiguredOption, - val components: Collection>, - val score: ConfiguredOption, - val filter: Collection>, - val options: Collection> -) { - - /** - * Generates a new [TaskScorer] for this [TaskDescription]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * @return [TaskScorer]. - */ - fun newScorer(): TaskScorer = this.score.option.scorer(this.score.parameters) - - /** - * Generates and returns a [SubmissionValidator] instance for this [TaskDescription]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * @return [SubmissionFilter] - */ - fun newFilter(): SubmissionFilter { - if (this.filter.isEmpty()) { - return AllSubmissionFilter - } - return SubmissionFilterAggregator(this.filter.map { it.option.filter(it.parameters) }) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/VideoSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/VideoSegment.kt deleted file mode 100644 index 7d5b11b04..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/VideoSegment.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.dres.data.model.competition - -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.time.TemporalRange - -interface VideoSegment { - - val item: MediaItem.VideoItem - val temporalRange: TemporalRange - -} - -interface CachedItem { - - fun cacheItemName(): String - -} - -interface CachedVideoItem: VideoSegment, CachedItem { - - override fun cacheItemName(): String = "${item.collection.string}-${item.id.string}-${temporalRange.start.toMilliseconds()}-${temporalRange.end.toMilliseconds()}.mp4" - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/FileTaskDescriptionComponent.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/FileTaskDescriptionComponent.kt deleted file mode 100644 index 16b6e3cba..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/FileTaskDescriptionComponent.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.data.model.competition.interfaces - -import java.io.File - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -interface FileTaskDescriptionComponent { - - fun file(): File -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt index 4bfccefb0..6ffdc64e3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt @@ -9,7 +9,6 @@ import dev.dres.run.filter.SubmissionFilter * @version 1.0.0 */ interface SubmissionFilterFactory { - /** * Generates and returns a [SubmissionFilter]. Depending on the implementation, the returned instance * is a new instance or can re-used. diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt deleted file mode 100644 index d80b47d0c..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ConfiguredOption.kt +++ /dev/null @@ -1,57 +0,0 @@ -package dev.dres.data.model.competition.options - -/** - * A concrete instance for an [Option] including named parameters. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 - */ -data class ConfiguredOption(val option: T, val parameters : Map = emptyMap()) { - /** - * Tries to access the named parameter. Returns null, if the parameter is not set. - * - * @param name Name of the parameter to return. - * @return [String] or null. - */ - operator fun get(name: String): String? = this.parameters[name] - - /** - * Tries to parse a named parameter as [Boolean]. Returns null, if the parameter is not set or cannot be converted. - * - * @param name Name of the parameter to return. - * @return [Boolean] or null. - */ - fun getAsBool(name: String): Boolean? = this.parameters[name]?.toBoolean() - - /** - * Tries to parse a named parameter as [Int]. Returns null, if the parameter is not set or cannot be converted. - * - * @param name Name of the parameter to return. - * @return [Int] or null. - */ - fun getAsInt(name: String): Int? = this.parameters[name]?.toIntOrNull() - - /** - * Tries to parse a named parameter as [Long]. Returns null, if the parameter is not set or cannot be converted. - * - * @param name Name of the parameter to return. - * @return [Long] or null. - */ - fun getAsLong(name: String): Long? = this.parameters[name]?.toLongOrNull() - - /** - * Tries to parse a named parameter as [Float]. Returns null, if the parameter is not set or cannot be converted. - * - * @param name Name of the parameter to return. - * @return [Float] or null. - */ - fun getAsFloat(name: String): Float? = this.parameters[name]?.toFloatOrNull() - - /** - * Tries to parse a named parameter as [Double]. Returns null, if the parameter is not set or cannot be converted. - * - * @param name Name of the parameter to return. - * @return [Double] or null. - */ - fun getAsDouble(name: String): Double? = this.parameters[name]?.toDoubleOrNull() -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt deleted file mode 100644 index 17d6e6cf9..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/Option.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.dres.data.model.competition.options - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -interface Option { - val ordinal: Int -} diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt deleted file mode 100644 index f0822bd8a..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/QueryComponentOption.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.data.model.competition.options - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -enum class QueryComponentOption: Option { - IMAGE_ITEM, //Image Media Item - VIDEO_ITEM_SEGMENT, //Part of a Video Media Item - TEXT, - EXTERNAL_IMAGE, - EXTERNAL_VIDEO -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt deleted file mode 100644 index 7639ab456..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/ScoringOption.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dev.dres.data.model.competition.options - -import dev.dres.run.score.interfaces.TaskScorer -import dev.dres.run.score.scorer.AvsTaskScorer -import dev.dres.run.score.scorer.KisTaskScorer - -/** - * An [Option] to specify the different types of [TaskScorer]s. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -enum class ScoringOption: Option { - KIS, AVS; - - /** - * Returns the [TaskScorer] for this [ScoringOption]. - * - * @param parameters The parameter [Map] used to configure the [TaskScorer] - */ - fun scorer(parameters: Map): TaskScorer = when(this) { - KIS -> KisTaskScorer(parameters) - AVS -> AvsTaskScorer() - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt deleted file mode 100644 index 3fb79e6a3..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOption.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.data.model.competition.options - -/** - * Simple [Option]s that can be applied to a task. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 - */ -enum class SimpleOption: Option { - HIDDEN_RESULTS, /** Do not show submissions while task is running. */ - MAP_TO_SEGMENT, /** Map the time of a submission to a pre-defined segment. */ - PROLONG_ON_SUBMISSION; /** Prolongs a task if a submission arrives within a certain time limit towards the end. */ -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt deleted file mode 100644 index b99ce6f0e..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SimpleOptionParameters.kt +++ /dev/null @@ -1,31 +0,0 @@ -package dev.dres.data.model.competition.options - -/** - * Named parameters and defaults for [SimpleOption]s. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -object SimpleOptionParameters { - /** - * Key for [SimpleOption.PROLONG_ON_SUBMISSION]; [Int] value expected. The parameter determines how many milliseconds - * before the end of a task a [SimpleOption.PROLONG_ON_SUBMISSION] [Option] should be applied. If a task has a total duration - * of 500s, then a value of 5s means that submissions arriving between 495s and 500s may trigger prolongation of the task. - */ - const val PROLONG_ON_SUBMISSION_LIMIT_PARAM = "limit" - - /** Default value for [PROLONG_ON_SUBMISSION_LIMIT_PARAM]. Defaults to the rule being applied during the final 5 seconds. */ - const val PROLONG_ON_SUBMISSION_LIMIT_DEFAULT = 5 - - /** Key for [SimpleOption.PROLONG_ON_SUBMISSION]; [Int] value expected. Determines by how many seconds a task should be prolonged. */ - const val PROLONG_ON_SUBMISSION_BY_PARAM = "prolong_by" - - /** Default value for [PROLONG_ON_SUBMISSION_BY_PARAM]. Defaults to a prolongation of 5 seconds. */ - const val PROLONG_ON_SUBMISSION_BY_DEFAULT = 5 - - /** Key for [SimpleOption.PROLONG_ON_SUBMISSION]; [Int] value expected. Determines by how many seconds a task should be prolonged. */ - const val PROLONG_ON_SUBMISSION_CORRECT_PARAM = "correct_only" - - /** Default value for [PROLONG_ON_SUBMISSION_BY_PARAM]. Defaults to a prolongation of 5 seconds. */ - const val PROLONG_ON_SUBMISSION_CORRECT_DEFAULT = false -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt deleted file mode 100644 index 1447efb00..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/SubmissionFilterOption.kt +++ /dev/null @@ -1,38 +0,0 @@ -package dev.dres.data.model.competition.options - -import dev.dres.run.filter.* - -/** - * [Option] that can be applied to a task. Configures [SubmissionFilter]s that should be used. - * - * @author Luca Rossetto & Ralph Gasser & Loris Sauter - * @version 1.2.0 - */ -enum class SubmissionFilterOption : Option { - NO_DUPLICATES, - LIMIT_CORRECT_PER_TEAM, - LIMIT_WRONG_PER_TEAM, - LIMIT_TOTAL_PER_TEAM, - LIMIT_CORRECT_PER_MEMBER, - TEMPORAL_SUBMISSION, - TEXTUAL_SUBMISSION, - ITEM_SUBMISSION, - MINIMUM_TIME_GAP; - - /** - * Returns the [SubmissionFilter] for this [SubmissionFilterOption] and the given [parameters]. - * - * @param parameters The parameter [Map] used to configure the [SubmissionFilter] - */ - fun filter(parameters: Map) = when (this) { - NO_DUPLICATES -> DuplicateSubmissionFilter() - LIMIT_CORRECT_PER_TEAM -> CorrectPerTeamFilter(parameters) - LIMIT_WRONG_PER_TEAM -> MaximumWrongPerTeamFilter(parameters) - LIMIT_TOTAL_PER_TEAM -> MaximumTotalPerTeamFilter(parameters) - LIMIT_CORRECT_PER_MEMBER -> CorrectPerTeamMemberFilter(parameters) - TEMPORAL_SUBMISSION -> TemporalSubmissionFilter() - TEXTUAL_SUBMISSION -> TextualSubmissionFilter() - ITEM_SUBMISSION -> ItemSubmissionFilter() - MINIMUM_TIME_GAP -> SubmissionRateFilter(parameters) - } -} diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt deleted file mode 100644 index 693598bee..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/options/TargetOption.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.dres.data.model.competition.options - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -enum class TargetOption : Option { - SINGLE_MEDIA_ITEM, // Whole Media Item - SINGLE_MEDIA_SEGMENT, //Part of a Media Item - MULTIPLE_MEDIA_ITEMS, //Multiple Media Items - JUDGEMENT, //Judgement - VOTE, //Judgement with audience voting - TEXT -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt new file mode 100644 index 000000000..f784a76ec --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt @@ -0,0 +1,130 @@ +package dev.dres.data.model.competition.task + +import dev.dres.api.rest.types.task.ApiContentElement +import dev.dres.data.model.Config +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.min +import kotlinx.dnq.simple.requireIf +import java.io.FileNotFoundException +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardOpenOption +import java.util.* + + +/** + * Represents the hint given by a [TaskDescription], e.g., a media item or text that is shown. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class Hint(entity: Entity) : XdEntity(entity) { + companion object : XdNaturalEntityType() + + /** The [HintType] of this [Hint]. */ + var type by xdLink1(HintType) + + /** The start of a (potential) range. */ + var start by xdNullableLongProp() { min(0L) } + + /** The start of a (potential) range. */ + var end by xdNullableLongProp { min(this@Hint.start ?: 0L) } + + /** The parent [TaskDescription] this [Hint] belongs to. */ + var task by xdParent(TaskDescription::hints) + + /** The[MediaItem] shown as part of the [Hint]. Can be null. */ + var hintItem by xdLink0_1(MediaItem) + + /** The target text. Can be null. */ + var hintText by xdStringProp() { requireIf { type == HintType.TEXT }} + + /** The target text. Can be null. */ + var hintExternalLocation by xdStringProp() + + /** The start of a (potential) range. */ + var temporalRangeStart by xdNullableIntProp { requireIf { type == HintType.VIDEO } } + + /** The start of a (potential) range. */ + var temporalRangeEnd by xdNullableIntProp { requireIf { type == HintType.VIDEO } } + + /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ + val range: TemporalRange? + get() { + return if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { + return TemporalRange(TemporalPoint.Frame(this.temporalRangeStart!!, this.hintItem?.fps ?: 1.0f), TemporalPoint.Frame(this.temporalRangeEnd!!, this.hintItem?.fps ?: 1.0f)) + } else { + null + } + } + + /** + * Generates and returns a textual description of this [Hint]. + * + * @return Text + */ + fun textDescription(): String = when (this.type) { + HintType.TEXT -> "\"${this.hintText}\" from ${this.start ?: "beginning"} to ${end ?: "end"}" + HintType.VIDEO -> { + if (this.hintItem != null) { + "Image ${this.hintItem!!.name} from ${start ?: "beginning"} to ${end ?: "end"}" + } else { + "Image ${this.hintExternalLocation} from ${start ?: "beginning"} to ${end ?: "end"}" + } + } + HintType.IMAGE -> { + if (this.hintItem != null) { + "Image ${this.hintItem!!.name}" + } else { + "Image ${this.hintExternalLocation}" + } + } + HintType.EMPTY -> "Empty item" + else -> throw IllegalStateException("The task hint type ${this.type.description} is currently not supported.") + } + + /** + * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. + * + * @param config The [Config] used of path resolution. + * @return [ApiContentElement] + * + * @throws FileNotFoundException + * @throws IOException + */ + fun toQueryContentElement(config: Config): ApiContentElement { + val content = when (this.type) { + HintType.IMAGE, + HintType.VIDEO -> { + val path = if (this.hintItem != null) { + this.hintItem!!.pathToCachedItem(config, this.temporalRangeStart, this.temporalRangeEnd) + } else if (this.hintExternalLocation != null) { + Paths.get(this.hintExternalLocation!!) + } else { + throw IllegalArgumentException("A hint of type ${this.type.description} must have a valid media item or external path.") + } + val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> + stream.readAllBytes() + } + Base64.getEncoder().encodeToString(data) + } + HintType.EMPTY -> "" + HintType.TEXT -> this.hintText ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") + else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") + } + return ApiContentElement(contentType = this.type.toApi(), content = content, offset = this.start ?: 0L) + } +} + + + + + + + diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt new file mode 100644 index 000000000..ef0324903 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt @@ -0,0 +1,46 @@ +package dev.dres.data.model.competition.task + +import dev.dres.api.rest.types.task.ApiContentType +import dev.dres.data.model.competition.task.options.TaskScoreOption +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdBooleanProp +import kotlinx.dnq.xdRequiredStringProp + +class HintType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val EMPTY by enumField { description = "EMPTY"; mimeType = ""; suffix = ""; base64 = true } + val TEXT by enumField { description = "TEXT"; mimeType = "text/plain"; base64 = false } + val VIDEO by enumField { description = "VIDEO"; mimeType = "video/mp4"; base64 = true } + val IMAGE by enumField { description = "IMAGE"; mimeType = "image/jpg"; base64 = true } + + } + + /** Name / description of the [TaskScoreOption]. */ + var description by xdRequiredStringProp(unique = true) + private set + + /** Name / description of the [TaskScoreOption]. */ + var mimeType by xdRequiredStringProp(unique = true) + private set + + /** Name / description of the [TaskScoreOption]. */ + var suffix by xdRequiredStringProp(unique = true) + + /** Name / description of the [TaskScoreOption]. */ + var base64 by xdBooleanProp() + + /** + * Converts this [HintType] to the RESTful API representation [ApiContentType]. + * + * @return [ApiContentType] equivalent to this [HintType]. + */ + fun toApi() = when(this) { + EMPTY -> ApiContentType.EMPTY + TEXT -> ApiContentType.TEXT + VIDEO -> ApiContentType.VIDEO + IMAGE -> ApiContentType.IMAGE + else -> throw IllegalStateException("The content type ${this.description} is not supported.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt new file mode 100644 index 000000000..6d917beda --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt @@ -0,0 +1,39 @@ +package dev.dres.data.model.competition.task + +import dev.dres.api.rest.types.task.ApiContentType +import dev.dres.data.model.competition.task.options.TaskScoreOption +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * + */ +class TargetType(entity: Entity): XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val JUDGEMENT by enumField { description = "JUDGEMENT" } + val JUDGEMENT_WITH_VOTE by enumField { description = "JUDGEMENT_WITH_VOTE" } + val MEDIA_ITEM by enumField { description = "MEDIA_ITEM" } + val MEDIA_ITEM_TEMPORAL_RANGE by enumField { description = "MEDIA_ITEM_TEMPORAL_RANGE" } + val TEXT by enumField { description = "EXTERNAL_IMAGE" } + } + + /** Name / description of the [TaskScoreOption]. */ + var description by xdRequiredStringProp(unique = true) + private set + + /** + * Converts this [HintType] to the RESTful API representation [ApiContentType]. + * + * @return [ApiContentType] equivalent to this [HintType]. + */ + fun toApi() = when(this) { + JUDGEMENT, + JUDGEMENT_WITH_VOTE -> ApiContentType.EMPTY + MEDIA_ITEM -> ApiContentType.IMAGE + MEDIA_ITEM_TEMPORAL_RANGE -> ApiContentType.VIDEO + TEXT -> ApiContentType.TEXT + else -> throw IllegalStateException("The target type ${this.description} is not supported.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt similarity index 56% rename from backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt rename to backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt index 8b604a3d4..f0b5d2df4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt @@ -1,13 +1,12 @@ -package dev.dres.data.model.competition +package dev.dres.data.model.competition.task -import dev.dres.api.rest.types.task.ContentElement +import dev.dres.api.rest.types.task.ApiContentElement +import dev.dres.api.rest.types.task.ApiContentType import dev.dres.api.rest.types.task.TaskHint import dev.dres.api.rest.types.task.TaskTarget -import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaCollection +import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.interfaces.SubmissionFilterFactory import dev.dres.data.model.competition.interfaces.TaskScorerFactory import dev.dres.data.model.competition.team.Team @@ -17,15 +16,17 @@ import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.simple.min import java.io.FileNotFoundException import java.io.IOException -import java.io.PrintStream -import kotlin.math.max -import kotlin.math.min +import java.lang.Long.max + +typealias TaskDescriptionId = String /** * Basic description of a [Task] as executed in DRES. Defines basic attributes such as its name, its duration, - * the [TaskDescriptionTarget] and the [TaskDescriptionHint]s, that should be presented to the user. + * the [TaskDescriptionTarget] and the [Hint]s, that should be presented to the user. * * @version 2.0.0 * @author Luca Rossetto & Ralph Gasser @@ -33,6 +34,10 @@ import kotlin.math.min class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { companion object: XdNaturalEntityType() + /** The [TaskDescriptionId] of this [TaskDescription]. */ + var teamId: TaskDescriptionId + get() = this.id + set(value) { this.id = value } /** The name held by this [Team]. Must be unique!*/ var name by xdRequiredStringProp(unique = false, trimmed = false) @@ -44,32 +49,13 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact var collection by xdLink1(MediaCollection) /** The duration of the [TaskDescription] in seconds. */ - var duration by xdRequiredLongProp<> { min(0L) } - - - /* /** Internal, unique ID of this [TaskDescription]. */ - val id: TaskDescriptionId, - - /** The name of the task */ - val name: String, - - /** The [TaskGroup] this [TaskDescription] belongs to. */ - val taskGroup: TaskGroup, - - /** The [TaskType] this [TaskDescription] belongs to. */ - val taskType: TaskType, - - /** The duration of the [TaskDescription] in seconds. */ - val duration: Long, + var duration by xdRequiredLongProp { min(0L) } - /** The id of the relevant media collection for this task, if not otherwise specified */ - val mediaCollectionId: UID, + /** The [TaskDescriptionTarget]s that identify the target. Multiple entries indicate the existence of multiple targets. */ + val targets by xdChildren1_N(TaskDescriptionTarget::task) - /** The [TaskDescriptionTarget] that identifies the target media. */ - val target: TaskDescriptionTarget, - - /** List of [TaskDescriptionHint]s that act as clues to find the target media. */ - val hints: List*/ + /** The [Hint]s that act as clues to find the target media. */ + val hints by xdChildren0_N(Hint::task) /** * Generates a new [TaskScorer] for this [TaskDescription]. Depending @@ -77,7 +63,7 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact * * @return [TaskScorer]. */ - override fun newScorer(): TaskScorer = this.taskType.newScorer() + override fun newScorer(): TaskScorer = this.taskGroup.type.newScorer() /** @@ -86,11 +72,13 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact * * @return [SubmissionFilter] */ - override fun newFilter(): SubmissionFilter = this.taskType.newFilter() + override fun newFilter(): SubmissionFilter = this.taskGroup.type.newFilter() /** * Generates and returns a [TaskHint] object to be used by the RESTful interface. * + * Requires a valid transaction. + * * @param config The [Config] used of path resolution. * @return [TaskHint] * @@ -98,63 +86,68 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact * @throws IOException */ fun toTaskHint(config: Config): TaskHint { - val sequence = this.hints.groupBy { it.contentType }.flatMap { group -> + val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> var index = 0 group.value.sortedBy { it.start ?: 0 }.flatMap { val ret = mutableListOf(it.toQueryContentElement(config)) if (it.end != null) { if (index == (group.value.size - 1)) { - ret.add(ContentElement(contentType = ret.first().contentType, offset = it.end!!)) + ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) } else if ((group.value[index+1].start ?: 0) > it.end!!) { - ret.add(ContentElement(contentType = ret.first().contentType, offset = it.end!!)) + ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) } } index += 1 ret } } - return TaskHint(this.id.string, sequence, false) + return TaskHint(this.id, sequence, false) } /** * Generates and returns a [TaskTarget] object to be used by the RESTful interface. * + * Requires a valid transaction. + * * @param config The [Config] used of path resolution. * @return [TaskTarget] * * @throws FileNotFoundException * @throws IOException */ - fun toTaskTarget(config: Config, collections: DAO): TaskTarget = this.target.toQueryContentElement(config, collections).let { TaskTarget(this.id.string, it) } - - /** Produces a Textual description of the content of the task if possible */ - fun textualDescription(): String = hints.filterIsInstance(TaskDescriptionHint.TextTaskDescriptionHint::class.java) - .maxByOrNull { it.start ?: 0 }?.text ?: name - - /** Prints an overview of the task to a provided stream */ - fun printOverview(out: PrintStream) { - out.println("$name: ${taskGroup.name} (${taskType.name})") - out.println("Target: ${target.textDescription()}") - out.println("Components: (${hints.size})") - hints.sortedBy { it.start ?: 0}.forEach { - out.println(it.textDescription()) - } - out.println() + fun toTaskTarget(config: Config): TaskTarget { + var cummulativeOffset = 0L + val sequence = this.targets.asSequence().flatMap { + cummulativeOffset += Math.floorDiv(it.item!!.durationMs!!, 1000L) + 1L + listOf( + it.toQueryContentElement(config), + ApiContentElement(ApiContentType.EMPTY, null, cummulativeOffset) + ) + }.toList() + return TaskTarget(this.id, sequence) } + /** + * Produces a Textual description of the content of the [TaskDescription] if possible + * + * @return Textual description of this [TaskDescription]'s content, + */ + fun textualDescription(): String = this.hints.asSequence().filter { it.type == HintType.TEXT }.maxByOrNull { it.start ?: 0 }?.hintText ?: name + /** * Checks if no components of the same type overlap * * @throws IllegalArgumentException */ + fun validate() { - this.hints.groupBy { it.contentType }.forEach { group -> + this.hints.asSequence().groupBy { it.type }.forEach { group -> var end = 0L group.value.sortedBy { it.start ?: 0 }.forEach { if((it.start ?: end) < end){ throw IllegalArgumentException("Overlapping component of type ${group.key} in task $name") } - end = max(end, it.end ?: ((it.start ?: 0) + 1)) + end = max(end, it.end ?: ((it.start ?: 0L) + 1L)) } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt new file mode 100644 index 000000000..9cedac25a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt @@ -0,0 +1,124 @@ +package dev.dres.data.model.competition.task + +import dev.dres.api.rest.types.competition.tasks.ApiTarget +import dev.dres.api.rest.types.competition.tasks.ApiTargetItem +import dev.dres.api.rest.types.competition.tasks.RestTemporalRange +import dev.dres.api.rest.types.task.ApiContentElement +import dev.dres.data.model.Config +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.requireIf +import java.io.FileNotFoundException +import java.io.IOException +import java.nio.file.Files +import java.nio.file.StandardOpenOption +import java.util.* + +/** + * Represents the target of a [TaskDescription], i.e., the [MediaItem] or part thereof that is considered correct. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { + companion object: XdNaturalEntityType() + + /** The [TargetType] of this [TaskDescriptionTarget]. */ + var type by xdLink1(TargetType) + + /** The parent [TaskDescription] this [TaskDescriptionTarget] belongs to. */ + var task by xdParent(TaskDescription::targets) + + /** The targeted [MediaItem]. Can be null. */ + var item by xdLink0_1(MediaItem) + + /** The target text. Can be null. */ + var text by xdStringProp() { requireIf { item == null }} + + /** The start of a (potential) range. */ + var temporalRangeStart by xdNullableIntProp { requireIf { item?.type == MediaType.VIDEO } } + + /** The start of a (potential) range. */ + var temporalRangeEnd by xdNullableIntProp { requireIf { item?.type == MediaType.VIDEO } } + + /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ + val range: TemporalRange? + get() { + return if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { + return TemporalRange(TemporalPoint.Frame(this.temporalRangeStart!!, this.item!!.fps ?: 1.0f), TemporalPoint.Frame(this.temporalRangeEnd!!, this.item!!.fps ?: 1.0f)) + } else { + null + } + } + + /** + * Generates and returns a textual description of this [TaskDescriptionTarget]. + * + * @return Text + */ + fun textDescription(): String = when (this.type) { + TargetType.JUDGEMENT -> "Judgement" + TargetType.JUDGEMENT_WITH_VOTE -> "Judgement with vote" + TargetType.MEDIA_ITEM -> "Media item ${this.item?.name}" + TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.temporalRangeStart} - ${this.temporalRangeEnd}" + TargetType.TEXT -> "Text: ${this.text}" + else -> throw IllegalStateException("The task description type ${this.type.description} is currently not supported.") + } + + /** + * + */ + fun toApi() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { + ApiTarget(this.type.toApi(), ApiTargetItem(this.item.id)) + } else { + ApiTarget(this.type.toApi(), ApiTargetItem(this.item.id)) + } + /** + * Generates a [ApiTarget] from a [TaskDescriptionTarget] and returns it. + * + * @param target The [TaskDescriptionTarget] to convert. + */ + fun fromTarget(target: TaskDescriptionTarget) = when(target.type) { + is TaskDescriptionTarget.JudgementTaskDescriptionTarget -> ApiTarget(TargetOption.JUDGEMENT, target.targets.map { ApiTargetItem(it.first.id.string, if (it.second == null) null else RestTemporalRange(it.second!!)) }) + is TaskDescriptionTarget.VideoSegmentTarget -> ApiTarget(TargetOption.SINGLE_MEDIA_SEGMENT, listOf( + ApiTargetItem(target.item.id.string, RestTemporalRange(target.temporalRange)) + )) + is TaskDescriptionTarget.MediaItemTarget -> ApiTarget(TargetOption.SINGLE_MEDIA_ITEM, listOf(ApiTargetItem(target.item.id.string))) + is TaskDescriptionTarget.MultipleMediaItemTarget -> ApiTarget(TargetOption.MULTIPLE_MEDIA_ITEMS, target.items.map { ApiTargetItem(it.id.string) }) + is TaskDescriptionTarget.VoteTaskDescriptionTarget -> ApiTarget(TargetOption.VOTE, target.targets.map { ApiTargetItem(it.first.id.string, if (it.second == null) null else RestTemporalRange(it.second!!)) }) + is TaskDescriptionTarget.TextTaskDescriptionTarget -> ApiTarget(TargetOption.TEXT, target.targets.map { ApiTargetItem(it, null) }) + } + + /** + * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. + * + * @param config The [Config] used of path resolution. + * @return [ApiContentElement] + * + * @throws FileNotFoundException + * @throws IOException + */ + fun toQueryContentElement(config: Config): ApiContentElement { + val content = when (this.type) { + TargetType.JUDGEMENT, + TargetType.JUDGEMENT_WITH_VOTE -> null + TargetType.MEDIA_ITEM -> { + val path = this.item?.pathToCachedItem(config, this.temporalRangeStart, this.temporalRangeEnd) + ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") + val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> + stream.readAllBytes() + } + Base64.getEncoder().encodeToString(data) + } + TargetType.TEXT -> this.text + else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") + } + return ApiContentElement(contentType = this.type.toApi(), content = content, offset = 0L) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt new file mode 100644 index 000000000..643b671ce --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt @@ -0,0 +1,34 @@ +package dev.dres.data.model.competition.task + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.team.Team +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* + +/** + * A [TaskGroup] allows the user to specify common traits among a group of [Task]s. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class TaskGroup(entity: Entity) : XdEntity(entity) { + companion object : XdNaturalEntityType() { + /** Combination of [TaskGroup] name / competition must be unique. */ + override val compositeIndices = listOf( + listOf(TaskGroup::name, TaskGroup::competition) + ) + } + + /** The name of this [TaskGroup]. */ + var name: String by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [TaskType] this [TaskGroup] belongs to.*/ + var type by xdLink1(TaskType) + + /** The [CompetitionDescription] this [TaskGroup] belongs to. */ + var competition by xdParent(CompetitionDescription::taskGroups) + + /** The [TaskDescription]s contained in this [TaskGroup]*/ + val tasks by xdChildren0_N(TaskDescription::taskGroup) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt new file mode 100644 index 000000000..946584c49 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt @@ -0,0 +1,92 @@ +package dev.dres.data.model.competition.task + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.task.options.* +import dev.dres.data.model.competition.task.options.ConfiguredOption +import dev.dres.run.filter.AllSubmissionFilter +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.filter.SubmissionFilterAggregator +import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.validation.interfaces.SubmissionValidator +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.isEmpty +import kotlinx.dnq.query.query +import kotlinx.dnq.simple.min + +/** + * Specifies the type of a [TaskDescription] and allows for many aspects of its configuration. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class TaskType(entity: Entity) : XdEntity(entity) { + /** Combination of [TaskType] name / competition must be unique. */ + companion object: XdNaturalEntityType() { + override val compositeIndices = listOf( + listOf(TaskType::name, TaskType::competition) + ) + } + + /** The name of this [TaskType]. */ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [CompetitionDescription] this [TaskType] belongs to. */ + var competition by xdParent(CompetitionDescription::taskTypes) + + /** The (default) duration of this [TaskType] in seconds. */ + var duration by xdRequiredLongProp() { min(0L) } + + /** The [TaskTargetOption] for this [TaskType]. Specifies the type of target. */ + var target by xdLink1(TaskTargetOption) + + /** The [TaskScoreOption] for this [TaskType]. Specifies the type of scorer that should be used. */ + var score by xdLink1(TaskScoreOption) + + /** The [TaskComponentOption]s that make-up this [TaskType]. */ + val components by xdLink0_N(TaskComponentOption) + + /** The [TaskSubmissionOption]s for this [TaskType]. */ + val submission by xdLink0_N(TaskSubmissionOption) + + /** The [TaskOption]s for this [TaskType]. */ + val options by xdLink0_N(TaskOption) + + /** [ConfiguredOption]s registered for this [TaskDescription]. */ + val configurations by xdChildren0_N(ConfiguredOption::task) + + /** + * Generates a new [TaskScorer] for this [TaskDescription]. Depending + * on the implementation, the returned instance is a new instance or being re-use. + * + * Calling this method requires an ongoing transaction! + * + * @return [TaskScorer]. + */ + fun newScorer(): TaskScorer { + val parameters = this.configurations.query(ConfiguredOption::key eq this.score.description) + .asSequence().map { it.key to it.value }.toMap() + return this.score.scorer(parameters) + } + + /** + * Generates and returns a [SubmissionValidator] instance for this [TaskDescription]. Depending + * on the implementation, the returned instance is a new instance or being re-use. + * + * Calling this method requires an ongoing transaction! + * + * @return [SubmissionFilter] + */ + fun newFilter(): SubmissionFilter { + if (this.submission.isEmpty) return AllSubmissionFilter + val filters = this.submission.asSequence().map { option -> + val parameters = this.configurations.query(ConfiguredOption::key eq this.score.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() + return SubmissionFilterAggregator(filters) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt new file mode 100644 index 000000000..6e183d5a4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt @@ -0,0 +1,66 @@ +package dev.dres.data.model.competition.task.options + +import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskType +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* + +/** + * A helper class that can be used to configure options in a [TaskType]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ConfiguredOption(entity: Entity) : XdEntity(entity) { + + companion object: XdNaturalEntityType() + + /** The key for this [ConfiguredOption]. Identifies the option. */ + val key by xdRequiredStringProp() + + /** The conifgured value for this [ConfiguredOption]. */ + val value by xdRequiredStringProp() + + /** The [TaskDescription] this [ConfiguredOption] belongs to. */ + val task by xdParent(TaskType::configurations) + + /** + * Tries to parse a named parameter as [Boolean]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Boolean] or null. + */ + fun getAsBool(name: String): Boolean? = this.value.toBooleanStrictOrNull() + + /** + * Tries to parse a named parameter as [Int]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Int] or null. + */ + fun getAsInt(name: String): Int? = this.value.toIntOrNull() + + /** + * Tries to parse a named parameter as [Long]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Long] or null. + */ + fun getAsLong(name: String): Long? = this.value.toLongOrNull() + + /** + * Tries to parse a named parameter as [Float]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Float] or null. + */ + fun getAsFloat(name: String): Float? = this.value.toFloatOrNull() + + /** + * Tries to parse a named parameter as [Double]. Returns null, if the parameter is not set or cannot be converted. + * + * @param name Name of the parameter to return. + * @return [Double] or null. + */ + fun getAsDouble(name: String): Double? = this.value.toDoubleOrNull() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt new file mode 100644 index 000000000..c5cc68adb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt @@ -0,0 +1,27 @@ +package dev.dres.data.model.competition.task.options + +import dev.dres.data.model.competition.task.TaskDescription +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * An enumeration of potential options for [TaskDescription] targets. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class TaskComponentOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val IMAGE_ITEM by enumField { description = "IMAGE_ITEM" } /** An image [MediaItem]. */ + val VIDEO_ITEM_SEGMENT by enumField { description = "VIDEO_ITEM_SEGMENT" } /** Part of a video [MediaItem]. */ + val TEXT by enumField { description = "TEXT" } /** A text snippet. */ + val EXTERNAL_IMAGE by enumField { description = "EXTERNAL_IMAGE" } /** An external image that is not part of a collection. */ + val EXTERNAL_VIDEO by enumField { description = "EXTERNAL_VIDEO" } /** An external video that is not part of a collection. */ + } + + /** Name / description of the [TaskScoreOption]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt new file mode 100644 index 000000000..88df462c0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt @@ -0,0 +1,26 @@ +package dev.dres.data.model.competition.task.options + +import dev.dres.data.model.competition.task.TaskDescription +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + + +/** + * An enumeration of potential general options for [TaskDescription]. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class TaskOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val HIDDEN_RESULTS by enumField { description = "HIDDEN_RESULTS" } /** Do not show submissions while task is running. */ + val MAP_TO_SEGMENT by enumField { description = "MAP_TO_SEGMENT" } /** Map the time of a submission to a pre-defined segment. */ + val PROLONG_ON_SUBMISSION by enumField { description = "PROLONG_ON_SUBMISSION" } /** Prolongs a task if a submission arrives within a certain time limit towards the end. */ + } + + /** Name / description of the [TaskScoreOption]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt new file mode 100644 index 000000000..f49f820b6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt @@ -0,0 +1,38 @@ +package dev.dres.data.model.competition.task.options + +import dev.dres.data.model.competition.options.ScoringOption +import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.KisTaskScorer +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * An enumeration of potential options for [TaskDescription] scorers. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class TaskScoreOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val KIS by enumField { description = "KIS" } + val AVS by enumField { description = "AVS" } + } + + /** Name / description of the [TaskScoreOption]. */ + var description by xdRequiredStringProp(unique = true) + private set + + /** + * Returns the [TaskScorer] for this [ScoringOption]. + * + * @param parameters The parameter [Map] used to configure the [TaskScorer] + */ + fun scorer(parameters: Map): TaskScorer = when(this) { + KIS -> KisTaskScorer(parameters) + AVS -> AvsTaskScorer() + else -> throw IllegalStateException("The task score option ${this.description} is currently not supported.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt new file mode 100644 index 000000000..73e0a9b93 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt @@ -0,0 +1,50 @@ +package dev.dres.data.model.competition.task.options + +import dev.dres.data.model.competition.options.SubmissionFilterOption +import dev.dres.run.filter.* +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * An enumeration of potential options for [TaskDescription] submission settings. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class TaskSubmissionOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val NO_DUPLICATES by enumField { description = "NO_DUPLICATES" } + val LIMIT_CORRECT_PER_TEAM by enumField { description = "LIMIT_CORRECT_PER_TEAM" } + val LIMIT_WRONG_PER_TEAM by enumField { description = "LIMIT_WRONG_PER_TEAM" } + val LIMIT_TOTAL_PER_TEAM by enumField { description = "LIMIT_TOTAL_PER_TEAM" } + val LIMIT_CORRECT_PER_MEMBER by enumField { description = "LIMIT_CORRECT_PER_MEMBER" } + val TEMPORAL_SUBMISSION by enumField { description = "TEMPORAL_SUBMISSION" } + val TEXTUAL_SUBMISSION by enumField { description = "TEXTUAL_SUBMISSION" } + val ITEM_SUBMISSION by enumField { description = "ITEM_SUBMISSION" } + val MINIMUM_TIME_GAP by enumField { description = "MINIMUM_TIME_GAP" } + } + + /** Name / description of the [TaskScoreOption]. */ + var description by xdRequiredStringProp(unique = true) + private set + + /** + * Returns the [SubmissionFilter] for this [SubmissionFilterOption] and the given [parameters]. + * + * @param parameters The parameter [Map] used to configure the [SubmissionFilter] + */ + fun newFilter(parameters: Map) = when (this) { + NO_DUPLICATES -> DuplicateSubmissionFilter() + LIMIT_CORRECT_PER_TEAM -> CorrectPerTeamFilter(parameters) + LIMIT_WRONG_PER_TEAM -> MaximumWrongPerTeamFilter(parameters) + LIMIT_TOTAL_PER_TEAM -> MaximumTotalPerTeamFilter(parameters) + LIMIT_CORRECT_PER_MEMBER -> CorrectPerTeamMemberFilter(parameters) + TEMPORAL_SUBMISSION -> TemporalSubmissionFilter() + TEXTUAL_SUBMISSION -> TextualSubmissionFilter() + ITEM_SUBMISSION -> ItemSubmissionFilter() + MINIMUM_TIME_GAP -> SubmissionRateFilter(parameters) + else -> throw IllegalStateException("The task filter option ${this.description} is currently not supported.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt new file mode 100644 index 000000000..286d38bc6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt @@ -0,0 +1,27 @@ +package dev.dres.data.model.competition.task.options + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp +/** + * An enumeration of potential options for [TaskDescription] targets. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class TaskTargetOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val SINGLE_MEDIA_ITEM by enumField { description = "SINGLE_MEDIA_ITEM" } + val SINGLE_MEDIA_SEGMENT by enumField { description = "SINGLE_MEDIA_SEGMENT" } + val MULTIPLE_MEDIA_ITEMS by enumField { description = "MULTIPLE_MEDIA_ITEMS" } + val JUDGEMENT by enumField { description = "JUDGEMENT" } + val VOTE by enumField { description = "VOTE" } + val TEXT by enumField { description = "TEXT" } + + } + + /** Name / description of the [TaskTargetOption]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt index f4440b95b..293ccfb8e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt @@ -1,5 +1,7 @@ package dev.dres.data.model.competition.team +import dev.dres.api.rest.types.competition.ApiTeam +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User @@ -7,6 +9,7 @@ import dev.dres.data.model.competition.CompetitionDescription import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.link.OnDeletePolicy +import kotlinx.dnq.query.asSequence import java.nio.file.Paths /** The ID of a [Team]. */ @@ -23,6 +26,11 @@ typealias LogoId = String */ class Team(entity: Entity) : PersistentEntity(entity) { companion object: XdNaturalEntityType() { + /** Combination of [Team] name / competition must be unique. */ + override val compositeIndices = listOf( + listOf(Team::name, Team::competition) + ) + /** * Generates and returns the [Path] to the team logo with the given [logoId]. * @@ -32,7 +40,7 @@ class Team(entity: Entity) : PersistentEntity(entity) { fun logoPath(config: Config, logoId: LogoId) = Paths.get(config.cachePath, "logos", "${logoId}.png") } - /** The [UserId] of this [User]. */ + /** The [TeamId] of this [Team]. */ var teamId: TeamId get() = this.id set(value) { this.id = value } @@ -50,8 +58,17 @@ class Team(entity: Entity) : PersistentEntity(entity) { var competition by xdParent(CompetitionDescription::teams) /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ - var group by xdLink0_1(TeamGroup::teams) + var group by xdLink0_1(TeamGroup::teams) /** The [User]s that belong to this [Team]. */ - val users by xdLink0_N(User::teams, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + val users by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + + /** + * Converts this [Team] to a RESTful API representation [ApiTeam]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiTeam] + */ + fun toApi() = ApiTeam(this.name, this.color, this.logoId, this.users.asSequence().map { it.toApi() }.toList()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt index f91100dd1..9ae89cf36 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt @@ -26,13 +26,13 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { set(value) { this.id = value } /** The name held by this [User]. Must be unique!*/ - var name by xdRequiredStringProp(unique = true, trimmed = false) + var name by xdRequiredStringProp(unique = false, trimmed = false) /** The default [TeamAggregator] to use for this [TeamGroup]. */ var defaultAggregator by xdLink1(TeamAggregator) /** The [CompetitionDescription] this [Team] belongs to. */ - var competition by xdParent(CompetitionDescription::teamsGroups) + var competition by xdParent(CompetitionDescription::teamsGroups) /** The [Team]s that belong to this [TeamGroup]. */ val teams by xdLink0_N(Team::group) diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt similarity index 83% rename from backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt index f7c6c7e12..ba9efa8ae 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.basics.media +package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity @@ -17,7 +17,7 @@ class MediaCollection(entity: Entity): PersistentEntity(entity) { var name: String by xdRequiredStringProp(unique = true, trimmed = false) var path: String by xdRequiredStringProp(unique = true, trimmed = false) var description: String? by xdStringProp(trimmed = false) - val items by xdChildren0_N(MediaItem::collection) + val items by xdChildren0_N(MediaItem::collection) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt new file mode 100644 index 000000000..656948e09 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -0,0 +1,76 @@ +package dev.dres.data.model.media + +import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.data.model.Config +import dev.dres.data.model.PersistentEntity +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.requireIf +import java.nio.file.Path +import java.nio.file.Paths + +/** + * A media item such as a video or an image + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class MediaItem(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() { + /** Combination of [MediaItem] name / competition must be unique. */ + override val compositeIndices = listOf( + listOf(MediaItem::name, MediaItem::collection) + ) + } + + /** The name of this [MediaItem]. */ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [MediaType] of this [MediaItem]. */ + var type by xdLink1(MediaType) + + /** The location of this [MediaItem] on disk. */ + var location by xdRequiredStringProp(unique = false, trimmed = false) + + /** Frame rate of the [MediaItem] in frames per second. Null for type without temporal development. */ + var fps by xdNullableFloatProp() { requireIf { this.type == MediaType.VIDEO } } + + /** Duration of the [MediaItem] in milliseconds. Null for type without temporal development. */ + var durationMs by xdNullableLongProp() { requireIf { this.type == MediaType.VIDEO } } + + /** The [MediaCollection] this [MediaItem] belongs to. */ + var collection by xdParent(MediaCollection::items) + + /** List of [MediaItemSegment] that this [MediaItem] contains. */ + val segments by xdChildren0_N(MediaItemSegment::item) + + /** + * Generates a [ApiMediaItem] this [MediaItem] and returns it. + * + * @return [ApiMediaItem] + */ + fun toApi(): ApiMediaItem + = ApiMediaItem(this.id, this.name, this.type.toApi(), this.collection.id, this.location, this.durationMs, this.fps) + + + /** + * Returns the [Path] to the original file for this [MediaItem]. + * + * @return [Path] + */ + fun pathToOriginal(): Path = Paths.get(this.collection.path).resolve(this.location) + + /** + * Returns the [Path] to the cached file for this [MediaItem]. + * + * @param config + * @param start + * @param end + * @return [Path] + */ + fun pathToCachedItem(config: Config, start: Int? = null, end: Int? = null) = if (start != null && end != null) { + Paths.get(config.cachePath).resolve("media").resolve("${this.collection.name}-${this.id}-$start-$end.${this.type.suffix}") + } else { + Paths.get(config.cachePath).resolve("media").resolve("${this.collection.name}-${this.id}.${this.type.suffix}") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt similarity index 82% rename from backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt index f9d6233a9..d423957c3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt @@ -1,8 +1,8 @@ -package dev.dres.data.model.basics.media +package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.basics.time.TemporalPoint -import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.simple.min @@ -20,7 +20,7 @@ class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [MediaType] of this [MediaItem]. */ - var item by xdParent(MediaItem::segments) + var item by xdParent(MediaItem::segments) /** The start frame number of this [MediaItemSegment]. */ var startFrame by xdRequiredIntProp() { min(0) } @@ -31,5 +31,4 @@ class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { /** Returns the [range] of this [MediaItemSegment] as [TemporalRange]. */ val range: TemporalRange get() = TemporalRange(TemporalPoint.Frame(this.startFrame, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.endFrame, this.item.fps ?: 1.0f)) - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt similarity index 90% rename from backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt index f5b618939..dc3ed0037 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/MediaItemSegmentList.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.basics.media +package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity import dev.dres.data.model.UID diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt new file mode 100644 index 000000000..29fcd420d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt @@ -0,0 +1,35 @@ +package dev.dres.data.model.media + +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.api.rest.types.competition.ApiTeam +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * A persistable media type enumeration such as a video or an image + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class MediaType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val IMAGE by enumField { description = "IMAGE"; suffix = "mp4"; } + val VIDEO by enumField { description = "VIDEO"; suffix = "jpg"; } + } + + var description by xdRequiredStringProp(unique = true) + private set + + /** The default suffix used for this [MediaType]. */ + var suffix by xdRequiredStringProp(unique = true) + + /** + * Converts this [MediaType] to a RESTful API representation [ApiMediaType]. + * + * This is a convenience method and requires an active transaction context. + */ + fun toApi(): ApiMediaType + = ApiMediaType.values().find { it.mediaType == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/media/PlayableMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt similarity index 93% rename from backend/src/main/kotlin/dev/dres/data/model/basics/media/PlayableMediaItem.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt index 61fe94567..f739f2fca 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/media/PlayableMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.basics.media +package dev.dres.data.model.media import com.fasterxml.jackson.annotation.JsonIgnore import java.time.Duration diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/time/TemporalPoint.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt similarity index 95% rename from backend/src/main/kotlin/dev/dres/data/model/basics/time/TemporalPoint.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt index 4f50b1daa..75fa98ee2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/time/TemporalPoint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt @@ -1,9 +1,9 @@ -package dev.dres.data.model.basics.time +package dev.dres.data.model.media.time import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonTypeName -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.PlayableMediaItem +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.PlayableMediaItem import java.lang.IllegalArgumentException /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/basics/time/TemporalRange.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt similarity index 97% rename from backend/src/main/kotlin/dev/dres/data/model/basics/time/TemporalRange.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt index 2d23e6d51..c90f9761b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/basics/time/TemporalRange.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.basics.time +package dev.dres.data.model.media.time import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.utilities.TimeUtil diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index 410da4e45..a854eb942 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -2,6 +2,7 @@ package dev.dres.data.model.run import dev.dres.data.model.competition.* import dev.dres.data.model.competition.options.TargetOption +import dev.dres.data.model.competition.task.TaskDescriptionTarget import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index fb8059728..f03de0abf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.TaskDescriptionTarget +import dev.dres.data.model.competition.task.TaskDescriptionTarget import dev.dres.data.model.competition.options.TargetOption import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.aspects.OriginAspect diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index 75d6f6287..d38c566fc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -3,7 +3,7 @@ package dev.dres.data.model.run import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index 18cd14e33..bc4fc87d2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnoreProperties import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.run.InteractiveSynchronousCompetition.Task import dev.dres.data.model.run.interfaces.Competition diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt index fb338b7c3..9c9bb94a7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt @@ -2,7 +2,7 @@ package dev.dres.data.model.run import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.interfaces.Competition diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt index 7993181e4..d97c4fdd2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task import dev.dres.run.TaskRunStatus import dev.dres.run.score.interfaces.TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index efd0703f1..cb7e87712 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -2,9 +2,9 @@ package dev.dres.data.model.submissions import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.time.TemporalPoint -import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.submissions.aspects.BaseSubmissionAspect import dev.dres.data.model.submissions.aspects.ItemAspect diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt index d1a792b8f..50762f418 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions.aspects -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem /** * diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt index f7740cb46..25e231bec 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions.aspects -import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.media.time.TemporalRange /** * diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt index cc17e396d..3e4bef769 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions.batch -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.SubmissionStatus /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt index b75e4f498..b534e86dc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt @@ -1,8 +1,8 @@ package dev.dres.data.model.submissions.batch -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.time.TemporalPoint -import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalAspect diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt deleted file mode 100644 index e29d4ccc2..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/AuditLogEntrySerializer.kt +++ /dev/null @@ -1,122 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.audit.* -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object AuditLogEntrySerializer: Serializer { - - override fun serialize(out: DataOutput2, value: AuditLogEntry) { - out.writeUID(value.id) - out.packLong(value.timestamp) - out.packInt(value.type.ordinal) - when(value.type){ - AuditLogEntryType.COMPETITION_START -> { - val competitionStart = value as CompetitionStartAuditLogEntry - out.writeUID(competitionStart.competition) - out.packInt(competitionStart.api.ordinal) - out.writeUTF(competitionStart.user ?: "") - } - AuditLogEntryType.COMPETITION_END -> { - val competitionEnd = value as CompetitionEndAuditLogEntry - out.writeUID(competitionEnd.competition) - out.packInt(competitionEnd.api.ordinal) - out.writeUTF(competitionEnd.user ?: "") - } - AuditLogEntryType.TASK_START -> { - val taskStart = value as TaskStartAuditLogEntry - out.writeUID(taskStart.competition) - out.writeUTF(taskStart.taskName) - out.packInt(taskStart.api.ordinal) - out.writeUTF(taskStart.user ?: "") - } - AuditLogEntryType.TASK_MODIFIED -> { - val taskmod = value as TaskModifiedAuditLogEntry - out.writeUID(taskmod.competition) - out.writeUTF(taskmod.taskName) - out.writeUTF(taskmod.modification) - out.packInt(taskmod.api.ordinal) - out.writeUTF(taskmod.user ?: "") - } - AuditLogEntryType.TASK_END -> { - val taskend = value as TaskEndAuditLogEntry - out.writeUID(taskend.competition) - out.writeUTF(taskend.taskName) - out.packInt(taskend.api.ordinal) - out.writeUTF(taskend.user ?: "") - } - AuditLogEntryType.SUBMISSION -> { - val submission = value as SubmissionAuditLogEntry - out.writeUID(submission.competition) - out.writeUTF(submission.taskName) - SubmissionSerializer.serialize(out, submission.submission) - out.packInt(submission.api.ordinal) - out.writeUTF(submission.user ?: "") - out.writeUTF(submission.address) - } - AuditLogEntryType.PREPARE_JUDGEMENT -> { - val judgement = value as PrepareJudgementAuditLogEntry - out.writeUTF(judgement.validator) - out.writeUTF(judgement.token) - SubmissionSerializer.serialize(out, judgement.submission) - } - AuditLogEntryType.JUDGEMENT -> { - val judgement = value as JudgementAuditLogEntry - out.writeUID(judgement.competition) - out.writeUTF(judgement.validator) - out.writeUTF(judgement.token) - out.packInt(judgement.verdict.ordinal) - out.packInt(judgement.api.ordinal) - out.writeUTF(judgement.user ?: "") - } - AuditLogEntryType.LOGIN -> { - val login = value as LoginAuditLogEntry - out.writeUTF(login.user) - out.writeUTF(login.session) - out.packInt(login.api.ordinal) - } - AuditLogEntryType.LOGOUT -> { - val logout = value as LogoutAuditLogEntry - out.writeUTF(logout.session) - out.packInt(logout.api.ordinal) - } - AuditLogEntryType.SUBMISSION_VALIDATION -> { - val validate = value as SubmissionValidationAuditLogEntry - SubmissionSerializer.serialize(out, validate.submission) - out.writeUTF(validate.validatorName) - out.packInt(validate.status.ordinal) - } - AuditLogEntryType.SUBMISSION_STATUS_OVERWRITE -> { - val overwrite = value as SubmissionStatusOverwriteAuditLogEntry - out.writeUID(overwrite.competitionRunUid) - out.writeUID(overwrite.submissionId) - out.packInt(overwrite.newVerdict.ordinal) - out.packInt(overwrite.api.ordinal) - out.writeUTF(overwrite.user ?: "") - } - } - } - - override fun deserialize(input: DataInput2, available: Int): AuditLogEntry { - val id = input.readUID() - val timestamp = input.unpackLong()//out.packLong(value.timestamp) - return when(AuditLogEntryType.values()[input.unpackInt()]){ - AuditLogEntryType.COMPETITION_START -> CompetitionStartAuditLogEntry(id, input.readUID(), LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.COMPETITION_END -> CompetitionEndAuditLogEntry(id, input.readUID(), LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.TASK_START -> TaskStartAuditLogEntry(id, input.readUID(), input.readUTF(), LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.TASK_MODIFIED -> TaskModifiedAuditLogEntry(id, input.readUID(), input.readUTF(), input.readUTF(), LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.TASK_END -> TaskEndAuditLogEntry(id, input.readUID(), input.readUTF(), LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.SUBMISSION -> SubmissionAuditLogEntry(id, input.readUID(), input.readUTF(), SubmissionSerializer.deserialize(input, available), LogEventSource.values()[input.unpackInt()], input.readUTF(), input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.PREPARE_JUDGEMENT -> PrepareJudgementAuditLogEntry(id, input.readUTF(), input.readUTF(), SubmissionSerializer.deserialize(input, available)).also { it.timestamp = timestamp } - AuditLogEntryType.JUDGEMENT -> JudgementAuditLogEntry(id, input.readUID(), input.readUTF(), input.readUTF(), SubmissionStatus.values()[input.unpackInt()], LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - AuditLogEntryType.LOGIN -> LoginAuditLogEntry(id, input.readUTF(), input.readUTF(), LogEventSource.values()[input.unpackInt()]).also { it.timestamp = timestamp } - AuditLogEntryType.LOGOUT -> LogoutAuditLogEntry(id, input.readUTF(), LogEventSource.values()[input.unpackInt()]).also { it.timestamp = timestamp } - AuditLogEntryType.SUBMISSION_VALIDATION -> SubmissionValidationAuditLogEntry(id, SubmissionSerializer.deserialize(input, available), input.readUTF(), SubmissionStatus.values()[input.unpackInt()]).also { it.timestamp = timestamp } - AuditLogEntryType.SUBMISSION_STATUS_OVERWRITE -> SubmissionStatusOverwriteAuditLogEntry(id, input.readUID(), input.readUID(), SubmissionStatus.values()[input.unpackInt()], LogEventSource.values()[input.unpackInt()], input.readUTF()).also { it.timestamp = timestamp } - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt deleted file mode 100644 index 6f22acc55..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionRunSerializer.kt +++ /dev/null @@ -1,139 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.InteractiveAsynchronousCompetition -import dev.dres.data.model.run.InteractiveSynchronousCompetition -import dev.dres.data.model.run.NonInteractiveCompetition -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.model.submissions.Submission -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -class CompetitionRunSerializer(private val competitionSerializer: CompetitionSerializer): Serializer { - override fun serialize(out: DataOutput2, value: Competition) { - when(value) { - is InteractiveSynchronousCompetition -> out.packInt(1) - is NonInteractiveCompetition -> out.packInt(2) - is InteractiveAsynchronousCompetition -> out.packInt(3) - } - out.writeUID(value.id) - out.writeUTF(value.name) - this.competitionSerializer.serialize(out, value.description) - RunPropertiesSerializer.serialize(out, value.properties) - out.writeLong(value.started ?: -1) - out.writeLong(value.ended ?: -1) - out.writeInt(value.tasks.size) - - when(value){ - is InteractiveSynchronousCompetition -> { - for (taskRun in value.tasks) { - out.writeUID(taskRun.uid) - out.writeUID(taskRun.taskDescriptionId) - out.writeLong(taskRun.started ?: -1) - out.writeLong(taskRun.ended ?: -1) - out.writeInt(taskRun.submissions.size) - for (submission in taskRun.submissions) { - SubmissionSerializer.serialize(out, submission) - } - } - } - is NonInteractiveCompetition -> { - //TODO - } - is InteractiveAsynchronousCompetition -> { - for (taskRun in value.tasks) { - out.writeUID(taskRun.uid) - out.writeUID(taskRun.teamId) - out.writeUID(taskRun.descriptionId) - out.writeLong(taskRun.started ?: -1) - out.writeLong(taskRun.ended ?: -1) - out.writeInt(taskRun.submissions.size) - for (submission in taskRun.submissions) { - SubmissionSerializer.serialize(out, submission) - } - } - value.permutation.forEach { teamId, indices -> - out.writeUID(teamId) - indices.forEach { out.packInt(it) } - } - } - } - - - } - - override fun deserialize(input: DataInput2, available: Int): Competition { - return when(val type = input.unpackInt()) { - 1 -> { - val run = InteractiveSynchronousCompetition(input.readUTF().UID(), input.readUTF(), competitionSerializer.deserialize(input, available), RunPropertiesSerializer.deserialize(input, available), input.readLong(), input.readLong()) - for (i in 0 until input.readInt()) { - val taskRun = run.Task(input.readUID(), input.readUID(), input.readLong(), input.readLong()) - for (j in 0 until input.readInt()) { - taskRun.submissions.add(SubmissionSerializer.deserialize(input,available)) - } - } - run - } - 2 -> { - TODO() - } - 3 -> { - - val id = input.readUID() - val name = input.readUTF() - val description = competitionSerializer.deserialize(input, available) - val properties = RunPropertiesSerializer.deserialize(input, available) - val start = input.readLong() - val end = input.readLong() - - val tasks = mutableListOf() - - for (i in 0 until input.readInt()) { - val taskContainer = TaskContainer(input.readUID(), input.readUID(), input.readUID(), input.readLong(), input.readLong()) - for (j in 0 until input.readInt()) { - taskContainer.submissions.add(SubmissionSerializer.deserialize(input,available)) - } - tasks.add(taskContainer) - } - - val permutations = (0 until description.teams.size).associate { - val teamId = input.readUID() - val indices = (0 until description.tasks.size).map { - input.unpackInt() - } - teamId to indices - } - - val run = InteractiveAsynchronousCompetition(id, name, description, properties, start, end, permutations) - - tasks.forEach { - val taskRun = run.Task(it.runId, it.teamId, it.descriptionId, it.start, it.end) - it.submissions.forEach { s -> - taskRun.submissions.add(s) - } - } - - run.reconstructNavigationMap() - run - } - else -> throw IllegalArgumentException("Unknown CompetitionRun type: $type") - } - - } - - data class TaskContainer( - val runId: UID, - val teamId: TeamId, - val descriptionId: TaskDescriptionId, - val start: Long, - val end: Long, - val submissions: MutableList = mutableListOf() - ) - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionSerializer.kt deleted file mode 100644 index cd959888b..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/CompetitionSerializer.kt +++ /dev/null @@ -1,60 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.dbo.DAO -import dev.dres.data.model.admin.UserId -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -class CompetitionSerializer(private val mediaItems: DAO): Serializer { - override fun serialize(out: DataOutput2, value: CompetitionDescription) { - val taskDescriptionSerializer = TaskDescriptionSerializer(value.taskGroups, value.taskTypes, this.mediaItems) - out.writeUID(value.id) - out.writeUTF(value.name) - out.writeUTF(value.description ?: "") - out.packInt(value.taskTypes.size) - for (type in value.taskTypes) { - TaskTypeSerializer.serialize(out, type) - } - out.packInt(value.taskGroups.size) - for (task in value.taskGroups) { - TaskGroupSerializer.serialize(out, task) - } - out.packInt(value.tasks.size) - for (task in value.tasks) { - taskDescriptionSerializer.serialize(out, task) - } - out.packInt(value.teams.size) - for (team in value.teams) { - TeamSerializer.serialize(out, team) - } - out.packInt(value.teamGroups.size) - for (teamGroup in value.teamGroups) { - TeamGroupSerializer.serialize(out, teamGroup) - } - out.packInt(value.judges.size) - for (judge in value.judges) { - out.writeUID(judge) - } - //out.writeBoolean(value.participantCanView) - //out.writeBoolean(value.shuffleTasks) - } - - override fun deserialize(input: DataInput2, available: Int): CompetitionDescription { - val id = input.readUID() - val name = input.readUTF() - val description = input.readUTF() - val taskTypes = (0 until input.unpackInt()).map { TaskTypeSerializer.deserialize(input, available) }.toMutableList() - val taskGroups = (0 until input.unpackInt()).map { TaskGroupSerializer.deserialize(input, available) }.toMutableList() - val taskDescriptionSerializer = TaskDescriptionSerializer(taskGroups, taskTypes, this.mediaItems) - val tasks = (0 until input.unpackInt()).map { taskDescriptionSerializer.deserialize(input,available) }.toMutableList() - val teams = (0 until input.unpackInt()).map { TeamSerializer.deserialize(input, available) }.toMutableList() - val teamGroups = (0 until input.unpackInt()).map { TeamGroupSerializer.deserialize(input, teams) }.toMutableList() - val judges = (0 until input.unpackInt()).map { input.readUID() }.toMutableList() - return CompetitionDescription(id, name, description, taskTypes, taskGroups, tasks, teams, teamGroups, judges) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt deleted file mode 100644 index 7dc854a7f..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/MediaCollectionSerializer.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.basics.media.MediaCollection -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object MediaCollectionSerializer: Serializer { - override fun serialize(out: DataOutput2, value: MediaCollection) { - out.writeUID(value.id) - out.writeUTF(value.name) - out.writeUTF(value.description ?: "") - out.writeUTF(value.path) - } - - override fun deserialize(input: DataInput2, available: Int): MediaCollection { - return MediaCollection(input.readUID(), input.readUTF(), input.readUTF(), input.readUTF()) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSegmentSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSegmentSerializer.kt deleted file mode 100644 index 1fdf29af3..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSegmentSerializer.kt +++ /dev/null @@ -1,28 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.basics.media.MediaItemSegment -import dev.dres.data.model.basics.media.MediaItemSegmentList -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object MediaItemSegmentSerializer: Serializer { - override fun serialize(out: DataOutput2, value: MediaItemSegmentList) { - out.writeUID(value.id) - out.writeUID(value.mediaItemId) - out.packInt(value.segments.size) - value.segments.forEach { - out.writeUTF(it.name) - TemporalRangeSerializer.serialize(out, it.range) - } - } - - override fun deserialize(input: DataInput2, available: Int): MediaItemSegmentList { - val id = input.readUID() - val mediaItemId = input.readUID() - val segments = (0 until input.unpackInt()).map {MediaItemSegment(mediaItemId, input.readUTF(), TemporalRangeSerializer.deserialize(input, available))}.toMutableList() - return MediaItemSegmentList(id, mediaItemId, segments) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSerializer.kt deleted file mode 100644 index 1095b1262..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/MediaItemSerializer.kt +++ /dev/null @@ -1,41 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID - -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer -import java.lang.IllegalStateException - - -object MediaItemSerializer: Serializer { - override fun serialize(out: DataOutput2, value: MediaItem) = when (value) { - is MediaItem.VideoItem -> { - out.writeInt(0) - out.writeUID(value.id) - out.writeUTF(value.name) - out.writeUTF(value.location) - out.writeUID(value.collection) - out.packLong(value.durationMs) - out.writeFloat(value.fps) - } - is MediaItem.ImageItem -> { - out.writeInt(1) - out.writeUID(value.id) - out.writeUTF(value.name) - out.writeUTF(value.location) - out.writeUID(value.collection) - } - } - - override fun deserialize(input: DataInput2, available: Int): MediaItem { - val i = input.readInt() - return when (i) { - 0 -> MediaItem.VideoItem(input.readUID(), input.readUTF(), input.readUTF(), input.readUID(), input.unpackLong(), input.readFloat()) - 1 -> MediaItem.ImageItem(input.readUID(), input.readUTF(), input.readUTF(), input.readUID()) - else -> throw IllegalStateException("Unsupported MediaItem $i type detected upon deserialization.") - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/RunPropertiesSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/RunPropertiesSerializer.kt deleted file mode 100644 index edf244de2..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/RunPropertiesSerializer.kt +++ /dev/null @@ -1,20 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.run.RunProperties -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object RunPropertiesSerializer : Serializer { - override fun serialize(out: DataOutput2, value: RunProperties) { - out.writeBoolean(value.participantCanView) - out.writeBoolean(value.shuffleTasks) - out.writeBoolean(value.allowRepeatedTasks) - } - - override fun deserialize(input: DataInput2, available: Int): RunProperties = RunProperties( - input.readBoolean(), - input.readBoolean(), - input.readBoolean() - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt deleted file mode 100644 index eb684f45e..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/SubmissionSerializer.kt +++ /dev/null @@ -1,58 +0,0 @@ -package dev.dres.data.serializers - - -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object SubmissionSerializer : Serializer { - override fun serialize(out: DataOutput2, value: Submission) { - out.writeUID(value.uid) - out.writeUID(value.teamId) - out.writeUID(value.memberId) - out.packLong(value.timestamp) - - out.packInt(value.status.ordinal) - when(value){ - is Submission.Item -> { - out.packInt(0) - MediaItemSerializer.serialize(out, value.item) - } - is Submission.Temporal -> { - out.packInt(1) - MediaItemSerializer.serialize(out, value.item) - out.packLong(value.start) - out.packLong(value.end) - } - is Submission.Text -> { - out.packInt(2) - out.writeUTF(value.text) - } - } - } - - override fun deserialize(input: DataInput2, available: Int): Submission { - val id = input.readUID() - val teamId = input.readUID() - val memberId = input.readUID() - val timestamp = input.unpackLong() - val status = SubmissionStatus.values()[input.unpackInt()] - - return when(input.unpackInt()) { - 0 -> { - val item = MediaItemSerializer.deserialize(input, available) - Submission.Item(teamId, memberId, timestamp, item, id).apply { this.status = status } - } - 1 -> { - val item = MediaItemSerializer.deserialize(input, available) - Submission.Temporal(teamId, memberId, timestamp, item, input.unpackLong(), input.unpackLong(), id).apply { this.status = status } - } - 2 -> Submission.Text(teamId, memberId, timestamp, input.readUTF(), id).apply { this.status = status } - else -> throw IllegalStateException("Unknown Submission Type") - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TaskDescriptionSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TaskDescriptionSerializer.kt deleted file mode 100644 index a82c68385..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TaskDescriptionSerializer.kt +++ /dev/null @@ -1,177 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.dbo.DAO -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.competition.* -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer -import java.nio.file.Paths - -class TaskDescriptionSerializer(val taskGroups: List, val taskTypes: List, val mediaItems: DAO): Serializer { - - /** - * Serializes [TaskDescription] - */ - override fun serialize(out: DataOutput2, value: TaskDescription) { - out.writeUID(value.id) - out.writeUTF(value.name) - out.writeUTF(value.taskGroup.name) - out.writeUTF(value.taskType.name) - out.packLong(value.duration) - out.writeUID(value.mediaCollectionId) - writeTaskDescriptionTarget(out, value.target) - writeTaskDescriptionHints(out, value.hints) - } - - /** - * Deserializes [TaskDescription] - */ - override fun deserialize(input: DataInput2, available: Int): TaskDescription = TaskDescription( - input.readUID(), - input.readUTF(), - input.readUTF().let { n -> taskGroups.first { it.name == n } }, - input.readUTF().let { n -> taskTypes.first { it.name == n } }, - input.unpackLong(), - input.readUID(), - readTaskDescriptionTarget(input, available, this.mediaItems), - readTaskDescriptionComponents(input, available, this.mediaItems) - ) - - /** - * Part of serialization of [TaskDescription]. Writes [TaskDescriptionTarget] - * - * @param out [DataOutput2] to write to. - * @param out [TaskDescriptionTarget] to serialize. - */ - private fun writeTaskDescriptionTarget(out: DataOutput2, target: TaskDescriptionTarget) { - out.packInt(target.ordinal) - when(target) { - is TaskDescriptionTarget.JudgementTaskDescriptionTarget -> { - out.packInt(target.targets.size) - target.targets.forEach { - out.writeUID(it.first.id) - out.writeBoolean(it.second != null) - if (it.second != null){ - TemporalRangeSerializer.serialize(out, it.second!!) - } - } - } - is TaskDescriptionTarget.VoteTaskDescriptionTarget-> { - out.packInt(target.targets.size) - target.targets.forEach { - out.writeUID(it.first.id) - out.writeBoolean(it.second != null) - if (it.second != null){ - TemporalRangeSerializer.serialize(out, it.second!!) - } - } - } - is TaskDescriptionTarget.VideoSegmentTarget -> { - out.writeUID(target.item.id) - TemporalRangeSerializer.serialize(out, target.temporalRange) - } - is TaskDescriptionTarget.MediaItemTarget -> { - out.writeUID(target.item.id) - } - is TaskDescriptionTarget.MultipleMediaItemTarget -> { - out.packInt(target.items.size) - target.items.forEach { out.writeUID(it.id) } - } - is TaskDescriptionTarget.TextTaskDescriptionTarget -> { - out.packInt(target.targets.size) - target.targets.forEach { out.writeUTF(it) } - } - } - } - - /** - * Part of serialization of [TaskDescription]. Writes [TaskDescriptionHint]s - * - * @param out [DataOutput2] to write to. - * @param out [TaskDescriptionHint]s to serialize. - */ - private fun writeTaskDescriptionHints(out: DataOutput2, hints: List){ - out.packInt(hints.size) - hints.forEach { - out.packLong(it.start ?: -1L) - out.packLong(it.end ?: -1L) - out.packInt(it.ordinal) - when(it) { - is TaskDescriptionHint.TextTaskDescriptionHint -> { - out.writeUTF(it.text) - } - is TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint -> { - out.writeUID(it.item.id) - TemporalRangeSerializer.serialize(out, it.temporalRange) - } - is TaskDescriptionHint.ImageItemTaskDescriptionHint -> { - out.writeUID(it.item.id) - } - is TaskDescriptionHint.ExternalImageTaskDescriptionHint -> { - out.writeUTF(it.imageLocation.toString()) - } - is TaskDescriptionHint.ExternalVideoTaskDescriptionHint -> { - out.writeUTF(it.videoLocation.toString()) - } - } - } - } - - /** - * Part of deserialization of [TaskDescription]. Reads [TaskDescriptionTarget] - * - * @param input [DataInput2] to read from. - * @param mediaItems [DAO] to lookup [MediaItem]s - * - * @return Deserialized [TaskDescriptionTarget]. - */ - private fun readTaskDescriptionTarget(input: DataInput2, available: Int, mediaItems: DAO) : TaskDescriptionTarget { - return when(val ordinal = input.unpackInt()) { - 1 -> TaskDescriptionTarget.JudgementTaskDescriptionTarget( - (0 until input.unpackInt()).map { - Pair(mediaItems[input.readUID()]!!, if (input.readBoolean()) { - TemporalRangeSerializer.deserialize(input, available) - } else null) - } - ) - 2 -> TaskDescriptionTarget.MediaItemTarget(mediaItems[input.readUID()] ?: MediaItem.ImageItem.EMPTY) - 3 -> TaskDescriptionTarget.VideoSegmentTarget((mediaItems[input.readUID()] ?: MediaItem.VideoItem.EMPTY) as MediaItem.VideoItem, TemporalRangeSerializer.deserialize(input, available)) - 4 -> TaskDescriptionTarget.MultipleMediaItemTarget((0 until input.unpackInt()).map { mediaItems[input.readUID()]!! }) - 5 -> TaskDescriptionTarget.VoteTaskDescriptionTarget( - (0 until input.unpackInt()).map { - Pair(mediaItems[input.readUID()]!!, if (input.readBoolean()) { - TemporalRangeSerializer.deserialize(input, available) - } else null) - } - ) - 6 -> TaskDescriptionTarget.TextTaskDescriptionTarget( - (0 until input.unpackInt()).map { input.readUTF() } - ) - else -> throw IllegalStateException("Failed to deserialize Task Description Target for ordinal $ordinal; not implemented.") - } - } - - /** - * Part of deserialization of [TaskDescription]. Reads [TaskDescriptionHint]s - * - * @param out [DataInput2] to read from. - * @param mediaItems [DAO] to lookup [MediaItem]s - * - * @return Deserialized [TaskDescriptionTarget]s. - */ - private fun readTaskDescriptionComponents(input: DataInput2, available: Int, mediaItems: DAO) : List = (0 until input.unpackInt()).map { - val start = input.unpackLong().let { if (it == -1L) null else it } - val end = input.unpackLong().let { if (it == -1L) null else it } - when(val ordinal = input.unpackInt()) { - 1 -> TaskDescriptionHint.TextTaskDescriptionHint(input.readUTF(), start, end) - 2 -> TaskDescriptionHint.ImageItemTaskDescriptionHint((mediaItems[input.readUID()] ?: MediaItem.ImageItem.EMPTY) as MediaItem.ImageItem, start, end) - 3 -> TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint((mediaItems[input.readUID()] ?: MediaItem.VideoItem.EMPTY) as MediaItem.VideoItem, TemporalRangeSerializer.deserialize(input, available), start, end) - 4 -> TaskDescriptionHint.ExternalImageTaskDescriptionHint(Paths.get(input.readUTF()), start, end) - 5 -> TaskDescriptionHint.ExternalVideoTaskDescriptionHint(Paths.get(input.readUTF()), start, end) - else -> throw IllegalArgumentException("Failed to deserialize Task Description Hint for ordinal $ordinal; not implemented.") - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TaskGroupSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TaskGroupSerializer.kt deleted file mode 100644 index e984709d9..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TaskGroupSerializer.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.competition.TaskGroup -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object TaskGroupSerializer: Serializer { - override fun serialize(out: DataOutput2, value: TaskGroup) { - out.writeUTF(value.name) - out.writeUTF(value.type) - } - override fun deserialize(input: DataInput2, available: Int): TaskGroup = TaskGroup(input.readUTF(), input.readUTF()) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt deleted file mode 100644 index 5e1d32f03..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TaskTypeSerializer.kt +++ /dev/null @@ -1,57 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.competition.TaskType -import dev.dres.data.model.competition.options.* -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object TaskTypeSerializer : Serializer { - - override fun serialize(out: DataOutput2, value: TaskType) { - out.writeUTF(value.name) - out.packLong(value.taskDuration) - //out.packInt(value.targetType.ordinal) - serializeOption(value.targetType, out) - out.packInt(value.components.size) - value.components.forEach { - //out.packInt(it.ordinal) - serializeOption(it, out) - } - //out.packInt(value.score.ordinal) - serializeOption(value.score, out) - out.packInt(value.filter.size) - value.filter.forEach { - //out.packInt(it.ordinal) - serializeOption(it, out) - } - out.packInt(value.options.size) - value.options.forEach { - //out.packInt(it.ordinal) - serializeOption(it, out) - } - } - - override fun deserialize(input: DataInput2, available: Int): TaskType = - TaskType( - input.readUTF(), - input.unpackLong(), - ConfiguredOption(TargetOption.values()[input.unpackInt()], deserializeMap(input)), - (0 until input.unpackInt()).map { ConfiguredOption(QueryComponentOption.values()[input.unpackInt()], deserializeMap(input)) }, - ConfiguredOption(ScoringOption.values()[input.unpackInt()], deserializeMap(input)), - (0 until input.unpackInt()).map { ConfiguredOption(SubmissionFilterOption.values()[input.unpackInt()], deserializeMap(input)) }, - (0 until input.unpackInt()).map { ConfiguredOption(SimpleOption.values()[input.unpackInt()], deserializeMap(input)) } - ) - - - private fun serializeOption(option: ConfiguredOption<*>, out: DataOutput2) { - out.packInt(option.option.ordinal) - out.packInt(option.parameters.size) - option.parameters.forEach { (k, v) -> - out.writeUTF(k) - out.writeUTF(v) - } - } - - private fun deserializeMap(input: DataInput2): Map = (0 until input.unpackInt()).map { input.readUTF() to input.readUTF() }.toMap() -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt deleted file mode 100644 index ae4fa0ae0..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TeamGroupSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.TeamGroup -import dev.dres.data.model.competition.TeamGroupAggregation -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 - -object TeamGroupSerializer { - fun serialize(out: DataOutput2, value: TeamGroup) { - out.writeUID(value.uid) - out.writeUTF(value.name) - out.packInt(value.teams.size) - value.teams.forEach { out.writeUID(it.uid) } - out.writeUTF(value.aggregation.name) - } - - fun deserialize(input: DataInput2, allTeams: List): TeamGroup { - val uid = input.readUID() - val name = input.readUTF() - val teams = (0 until input.unpackInt()).mapNotNull { - val teamId = input.readUID() - allTeams.find { it.uid == teamId } - } - val aggregation = TeamGroupAggregation.valueOf(input.readUTF()) - return TeamGroup(uid, name, teams, aggregation) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt deleted file mode 100644 index 57930916d..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TeamSerializer.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.competition.team.Team -import dev.dres.utilities.extensions.readUID -import dev.dres.utilities.extensions.writeUID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object TeamSerializer : Serializer { - override fun serialize(out: DataOutput2, value: Team) { - out.writeUID(value.uid) - out.writeUTF(value.name) - out.writeUTF(value.color) - out.writeUID(value.logoId) - out.packInt(value.users.size) - value.users.forEach { out.writeUID(it) } - } - - override fun deserialize(input: DataInput2, available: Int): Team = Team( - input.readUID(), - input.readUTF(), - input.readUTF(), - input.readUID(), - (0 until input.unpackInt()).map { input.readUID() }.toMutableList() - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TemporalPointSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TemporalPointSerializer.kt deleted file mode 100644 index 129dac9e9..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TemporalPointSerializer.kt +++ /dev/null @@ -1,37 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.basics.time.TemporalPoint -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object TemporalPointSerializer: Serializer { - override fun serialize(out: DataOutput2, value: TemporalPoint) { - when(value){ - is TemporalPoint.Frame -> { - out.packInt(1) - out.packInt(value.frame) - out.writeFloat(value.fps) - } - is TemporalPoint.Millisecond -> { - out.packInt(2) - out.packLong(value.millisecond) - } - is TemporalPoint.Timecode -> { - out.packInt(3) - out.writeUTF(value.timecode) - out.writeFloat(value.fps) - } - } - - } - - override fun deserialize(input: DataInput2, available: Int): TemporalPoint = - when(input.unpackInt()){ - 1 -> TemporalPoint.Frame(input.unpackInt(), input.readFloat()) - 2 -> TemporalPoint.Millisecond(input.unpackLong()) - 3 -> TemporalPoint.Timecode(input.readUTF(), input.readFloat()) - else -> throw IllegalArgumentException("Unknown type of TemporalPoint") - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/TemporalRangeSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/TemporalRangeSerializer.kt deleted file mode 100644 index 12681e8ca..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/TemporalRangeSerializer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.basics.time.TemporalRange -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object TemporalRangeSerializer: Serializer { - override fun serialize(out: DataOutput2, value: TemporalRange) { - TemporalPointSerializer.serialize(out, value.start) - TemporalPointSerializer.serialize(out, value.end) - } - - override fun deserialize(input: DataInput2, available: Int): TemporalRange = TemporalRange( - TemporalPointSerializer.deserialize(input, available), - TemporalPointSerializer.deserialize(input, available) - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/UIDSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/UIDSerializer.kt deleted file mode 100644 index 2c3dda1eb..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/UIDSerializer.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.UID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object UIDSerializer : Serializer { - override fun serialize(out: DataOutput2, value: UID) { - out.writeUTF(value.string) - } - - override fun deserialize(input: DataInput2, available: Int): UID { - return UID(input.readUTF()) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/serializers/UserSerializer.kt b/backend/src/main/kotlin/dev/dres/data/serializers/UserSerializer.kt deleted file mode 100644 index 63b0151e5..000000000 --- a/backend/src/main/kotlin/dev/dres/data/serializers/UserSerializer.kt +++ /dev/null @@ -1,26 +0,0 @@ -package dev.dres.data.serializers - -import dev.dres.data.model.admin.HashedPassword -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User -import dev.dres.data.model.admin.UserName -import dev.dres.utilities.extensions.UID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 -import org.mapdb.Serializer - -object UserSerializer : Serializer { - override fun serialize(out: DataOutput2, value: User) { - out.writeUTF(value.id.string) - out.writeUTF(value.username.name) - out.writeUTF(value.password.hash) - out.writeInt(value.role.ordinal) - } - - override fun deserialize(input: DataInput2, available: Int): User = User( - input.readUTF().UID(), - UserName(input.readUTF()), - HashedPassword(input.readUTF()), - Role.values()[input.readInt()] - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 3ffc29984..d4fe651ec 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -9,7 +9,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.admin.Role import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.Task diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 8bc4394df..c8e94d859 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -2,7 +2,7 @@ package dev.dres.run import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.Task import dev.dres.data.model.submissions.Submission diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 781be9b41..1e18a2fd7 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.options.ConfiguredOption import dev.dres.data.model.competition.options.Option import dev.dres.data.model.competition.options.SimpleOption diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 27daf41b5..1e744ef15 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousCompetition import dev.dres.data.model.run.InteractiveSynchronousCompetition import dev.dres.data.model.run.NonInteractiveCompetition @@ -21,6 +21,7 @@ import dev.dres.utilities.extensions.write import io.javalin.websocket.WsConfig import io.javalin.websocket.WsContext import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.io.File import java.util.* @@ -223,7 +224,9 @@ object RunExecutor : Consumer { } /* Register [RunManager] with AccessManager. */ - AccessManager.registerRunManager(manager) + this.store.transactional(true) { + AccessManager.registerRunManager(manager) + } /* Setup all the required data structures. */ this.runManagers[manager.id] = manager @@ -266,14 +269,17 @@ object RunExecutor : Consumer { * @param message The [ServerMessage] that should be broadcast. */ fun broadcastWsMessage(runId: UID, teamId: TeamId, message: ServerMessage) = this.clientLock.read { - - val teamMembers = managerForId(runId)?.description?.teams?.find { it.uid == teamId }?.users ?: return@read - - this.runManagerLock.read { - this.connectedClients.filter { - this.observingClients[runId]?.contains(it) ?: false && AccessManager.userIdForSession(it.httpSessionId) in teamMembers - }.forEach { - it.send(message) + val manager = managerForId(runId) + if (manager != null) { + val teamMembers = this.store.transactional(true) { + manager.description.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() + } + this.runManagerLock.read { + this.connectedClients.filter { + this.observingClients[runId]?.contains(it) ?: false && AccessManager.userIdForSession(it.httpSessionId) in teamMembers + }.forEach { + it.send(message) + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index eaba7a6ba..2bbde7d1b 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -7,7 +7,7 @@ import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index e6a19d6b3..9cacc5469 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -3,7 +3,7 @@ package dev.dres.run.eventstream import com.fasterxml.jackson.annotation.JsonTypeInfo import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog import dev.dres.data.model.submissions.Submission diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt index a34154049..e66c0f4fc 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt @@ -2,12 +2,12 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.dbo.DaoIndexer import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.basics.media.MediaItemSegmentList -import dev.dres.data.model.basics.time.TemporalPoint -import dev.dres.data.model.basics.time.TemporalRange -import dev.dres.data.model.competition.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionTarget +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaItemSegmentList +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskDescriptionTarget import dev.dres.run.eventstream.QueryResultLogEvent import dev.dres.run.eventstream.StreamEvent import dev.dres.run.eventstream.StreamEventHandler diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 63657a416..871cbdc8d 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -1,7 +1,7 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.UID -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* import dev.dres.run.score.TaskContext diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index a29ee78d7..3fd61e0bf 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,7 +1,7 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.UID -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.team.Team import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 9400f6f05..d7b60803b 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -1,7 +1,7 @@ package dev.dres.run.score.scorer import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.competition.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt index 9c0521fae..9fc0c1f0e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.batch.ResultBatch import dev.dres.run.validation.interfaces.SubmissionBatchValidator diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index fd523eff9..bce4b8baf 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.ItemAspect diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 4ef6a370f..9fcb5cfb0 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.VideoSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 8068f6a3d..8c1e2724f 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.competition.TaskDescription +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.VideoSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 7c27c7c58..0923acd55 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect diff --git a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt index 9e8338a6c..7f4d1579c 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt @@ -1,7 +1,7 @@ package dev.dres.utilities -import dev.dres.data.model.basics.media.MediaItemSegmentList -import dev.dres.data.model.basics.time.TemporalRange +import dev.dres.data.model.media.MediaItemSegmentList +import dev.dres.data.model.media.time.TemporalRange import kotlin.math.abs object TimeUtil { diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 6e731d809..7459abb80 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -1,7 +1,7 @@ package dres.run.score.scorer import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.TaskContext diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 11c621e9f..fef671ae4 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -1,7 +1,7 @@ package dres.run.score.scorer import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.TaskContext From f07cb36d66e19eea0aafb7c2aa17240ecb29762c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 29 Oct 2022 10:54:44 +0200 Subject: [PATCH 014/498] Updated to javalin5 openapi plugin 5.1.2 --- backend/build.gradle | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 2b379cd9e..4740b564d 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -46,6 +46,7 @@ compileTestKotlin { dependencies { def javalin = '5.1.2' + def javalinOpenApi = '5.1.3-SNAPSHOT' def log4jVersion = '2.17.1' ///// Frontend files (produced by sub-project). @@ -59,10 +60,10 @@ dependencies { ////// Javalin implementation group: 'io.javalin', name: 'javalin', version: javalin - kapt("io.javalin.community.openapi:openapi-annotation-processor:5.1.2-SNAPSHOT") + kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin") - implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "5.1.2-SNAPSHOT" - implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "5.1.2-SNAPSHOT" + implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: javalin + implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: javalin implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: javalin implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.4' From 3c6eb2c951d333e2d1aea67408678c3a675b2e81 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 31 Oct 2022 05:43:12 +0100 Subject: [PATCH 015/498] Continued migration to Xodus storge; adjusted another batch of commands (CompetitionDescription) --- backend/build.gradle | 84 +- backend/src/main/kotlin/dev/dres/DRES.kt | 8 +- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 6 +- .../dev/dres/api/cli/CompetitionCommand.kt | 241 ++--- .../dres/api/cli/MediaCollectionCommand.kt | 944 ++++++++---------- .../kotlin/dev/dres/api/cli/UserCommand.kt | 10 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 15 +- .../api/rest/handler/CompetitionHandler.kt | 303 ------ .../handler/CompetitionRunAdminHandler.kt | 3 +- .../api/rest/handler/CompetitionRunHandler.kt | 16 +- .../handler/audit/ListAuditLogsHandler.kt | 2 +- .../audit/ListAuditLogsInRangeHandler.kt | 2 +- .../handler/collection/AddMediaItemHandler.kt | 2 +- .../collection/UpdateMediaItemHandler.kt | 3 +- .../AbstractCompetitionDescriptionHandler.kt | 45 + .../description/CreateCompetitionHandler.kt | 57 ++ .../description/DeleteCompetitionHandler.kt | 41 + .../handler/description/GetTeamLogoHandler.kt | 71 ++ .../description/ListCompetitionHandler.kt | 43 + .../handler/description/ListTasksHandler.kt | 42 + .../handler/description/ListTeamHandler.kt | 41 + .../description/ShowCompetitionHandler.kt | 37 + .../description/UpdateCompetitionHandler.kt | 179 ++++ .../preview/SubmissionPreviewHandler.kt | 3 - .../api/rest/handler/system/LoginHandler.kt | 13 +- .../api/rest/types/WebSocketConnection.kt | 22 +- .../api/rest/types/collection/ApiMediaType.kt | 4 +- .../types/collection/time/ApiTemporalPoint.kt | 27 + .../types/collection/time/ApiTemporalRange.kt | 14 + .../types/collection/time/ApiTemporalUnit.kt | 13 + .../competition/ApiCompetitionDescription.kt | 25 + .../competition/ApiCompetitionOverview.kt | 11 + ...eateMessage.kt => ApiCreateCompetition.kt} | 2 +- .../api/rest/types/competition/ApiTeam.kt | 11 - .../competition/RestCompetitionDescription.kt | 75 -- .../types/competition/RestTaskDescription.kt | 72 -- .../api/rest/types/competition/RestTeam.kt | 90 -- .../rest/types/competition/RestTeamGroup.kt | 23 - .../rest/types/competition/tasks/ApiHint.kt | 77 ++ .../types/competition/tasks/ApiHintContent.kt | 16 + .../types/competition/tasks/ApiHintType.kt | 16 + .../rest/types/competition/tasks/ApiTarget.kt | 37 +- .../competition/tasks/ApiTargetContent.kt | 15 + .../types/competition/tasks/ApiTargetType.kt | 2 +- .../competition/tasks/ApiTaskDescription.kt | 23 + .../types/competition/tasks/ApiTaskGroup.kt | 11 + .../types/competition/tasks/ApiTaskType.kt | 21 + .../tasks/RestTaskDescriptionComponent.kt | 114 --- .../competition/tasks/RestTemporalRange.kt | 35 - .../tasks/options/ApiComponentOption.kt | 17 + .../tasks/options/ApiScoreOption.kt | 14 + .../tasks/options/ApiSubmissionOption.kt | 21 + .../tasks/options/ApiTargetOption.kt | 18 + .../tasks/options/ApiTaskOption.kt | 15 + .../rest/types/competition/team/ApiTeam.kt | 20 + .../types/competition/team/ApiTeamGroup.kt | 12 + .../dev/dres/api/rest/types/task/TaskHint.kt | 14 - .../dres/api/rest/types/task/TaskTarget.kt | 13 - .../dres/api/rest/types/users/UserRequest.kt | 2 - .../dev/dres/data/migration/Migration.kt | 8 +- .../competition/CompetitionDescription.kt | 46 +- .../dres/data/model/competition/task/Hint.kt | 79 +- .../data/model/competition/task/HintType.kt | 20 +- .../data/model/competition/task/TargetType.kt | 22 +- .../model/competition/task/TaskDescription.kt | 43 +- .../competition/task/TaskDescriptionTarget.kt | 64 +- .../data/model/competition/task/TaskGroup.kt | 15 +- .../data/model/competition/task/TaskType.kt | 39 +- .../task/options/ConfiguredOption.kt | 4 +- .../{TaskComponentOption.kt => HintOption.kt} | 14 +- .../{TaskScoreOption.kt => ScoreOption.kt} | 15 +- ...ubmissionOption.kt => SubmissionOption.kt} | 15 +- .../{TaskTargetOption.kt => TargetOption.kt} | 15 +- .../competition/task/options/TaskOption.kt | 11 +- .../dres/data/model/competition/team/Team.kt | 54 +- .../data/model/competition/team/TeamGroup.kt | 14 +- .../dev/dres/data/model/media/MediaItem.kt | 12 +- .../dres/data/model/media/MediaItemSegment.kt | 13 +- .../dev/dres/data/model/media/MediaType.kt | 3 +- .../data/model/media/time/TemporalPoint.kt | 8 +- .../data/model/media/time/TemporalRange.kt | 7 +- .../dev/dres/data/model/run/interfaces/Ids.kt | 3 - .../dres/data/model/run/interfaces/Task.kt | 5 +- .../model/submissions/aspects/OriginAspect.kt | 3 +- .../run/exceptions/IllegalTeamIdException.kt | 3 +- .../kotlin/dev/dres/run/score/ScoreEntry.kt | 5 - .../kotlin/dev/dres/run/score/TaskContext.kt | 2 +- .../dres/run/score/interfaces/TaskScorer.kt | 10 +- .../run/score/interfaces/TeamTaskScorer.kt | 12 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 16 +- .../scoreboard/SumAggregateScoreBoard.kt | 6 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 21 +- .../scorer/InferredAveragePrecisionScorer.kt | 4 +- .../dres/run/score/scorer/KisTaskScorer.kt | 25 +- .../dres/run/updatables/ScoresUpdatable.kt | 6 +- .../TemporalContainmentSubmissionValidator.kt | 10 +- ...TemporalOverlapSubmissionBatchValidator.kt | 16 +- .../TemporalOverlapSubmissionValidator.kt | 10 +- .../dres/run/validation/judged/ItemRange.kt | 8 +- .../kotlin/dev/dres/utilities/FFmpegUtil.kt | 40 +- .../kotlin/dev/dres/utilities/ReadyLatch.kt | 14 +- .../extensions/DataInputOutputExtensions.kt | 9 - .../utilities/extensions/StringExtensions.kt | 9 - gradle.properties | 18 +- 104 files changed, 2061 insertions(+), 1828 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalPoint.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalRange.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalUnit.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/{CompetitionCreateMessage.kt => ApiCreateCompetition.kt} (73%) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintContent.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetContent.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt rename backend/src/main/kotlin/dev/dres/data/model/competition/task/options/{TaskComponentOption.kt => HintOption.kt} (67%) rename backend/src/main/kotlin/dev/dres/data/model/competition/task/options/{TaskScoreOption.kt => ScoreOption.kt} (67%) rename backend/src/main/kotlin/dev/dres/data/model/competition/task/options/{TaskSubmissionOption.kt => SubmissionOption.kt} (79%) rename backend/src/main/kotlin/dev/dres/data/model/competition/task/options/{TaskTargetOption.kt => TargetOption.kt} (61%) delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt delete mode 100644 backend/src/main/kotlin/dev/dres/utilities/extensions/DataInputOutputExtensions.kt diff --git a/backend/build.gradle b/backend/build.gradle index 0953736a4..061fa26d1 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -1,14 +1,13 @@ import org.apache.tools.ant.taskdefs.condition.Os buildscript { - ext.kotlinVersion = '1.7.20' repositories { mavenCentral() } dependencies { - classpath "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-stdlib:$version_kotlin" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$version_kotlin" + classpath "org.jetbrains.kotlin:kotlin-serialization:$version_kotlin" } } @@ -45,76 +44,71 @@ compileTestKotlin { } dependencies { - def javalin = '5.1.1' - def xodusVersion = '2.0.1' - def log4jVersion = '2.17.0' - ///// Frontend files (produced by sub-project). - implementation frontendClasspath(project(path: ":frontend", configuration: 'frontendFiles')) + //implementation frontendClasspath(project(path: ":frontend", configuration: 'frontendFiles')) ///// FFMpeg dependency implementation files("$buildDir/ext") {builtBy 'setupFFMpeg'} ///// Xodus & Xodus DNQ - implementation group: 'org.jetbrains.xodus', name: 'xodus-openAPI', version: xodusVersion - implementation group: 'org.jetbrains.xodus', name: 'xodus-environment', version: xodusVersion - implementation group: 'org.jetbrains.xodus', name: 'xodus-entity-store', version: xodusVersion - implementation group: 'org.jetbrains.xodus', name: 'xodus-vfs', version: xodusVersion - implementation group: 'org.jetbrains.xodus', name: 'dnq', version: '2.0.0' + implementation group: 'org.jetbrains.xodus', name: 'xodus-openAPI', version: version_xodus + implementation group: 'org.jetbrains.xodus', name: 'xodus-environment', version: version_xodus + implementation group: 'org.jetbrains.xodus', name: 'xodus-entity-store', version: version_xodus + implementation group: 'org.jetbrains.xodus', name: 'xodus-vfs', version: version_xodus + implementation group: 'org.jetbrains.xodus', name: 'dnq', version: version_xodus_dnq ////// Javalin - implementation group: 'io.javalin', name: 'javalin', version: "$javalin" - kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalin") - - implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "5.1.2-SNAPSHOT" - implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "5.1.2-SNAPSHOT" - implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: "$javalin" - - implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.0' - + implementation group: 'io.javalin', name: 'javalin', version: "$version_javalin" + kapt("io.javalin.community.openapi:openapi-annotation-processor:$version_javalin") + implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: version_javalin + implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: version_javalin + implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: version_javalin - - - ////// bcrypt - implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4' + ////// Bcrypt + implementation group: 'org.mindrot', name: 'jbcrypt', version: version_bcrypt ////// JLine 3, Clikt & Picnic for optimal terminal experience :-) - implementation group: 'com.github.ajalt', name: 'clikt', version: '2.8.0' - implementation group: 'org.jline', name: 'jline-terminal', version: '3.20.0' - implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.20.0' - implementation group: 'org.jline', name: 'jline-reader', version: '3.20.0' - implementation group: 'org.jline', name: 'jline-builtins', version: '3.20.0' - implementation group: 'com.jakewharton.picnic', name: 'picnic', version: '0.5.0' + implementation group: 'org.jline', name: 'jline-terminal', version: version_jline3 + implementation group: 'org.jline', name: 'jline-terminal-jna', version: version_jline3 + implementation group: 'org.jline', name: 'jline-reader', version: version_jline3 + implementation group: 'org.jline', name: 'jline-builtins', version: version_jline3 + implementation group: 'com.github.ajalt', name: 'clikt', version: version_clikt + implementation group: 'com.jakewharton.picnic', name: 'picnic', version: version_picnic ///// Fuel - implementation group: 'com.github.kittinunf.fuel', name: 'fuel', version: '2.3.1' + implementation group: 'com.github.kittinunf.fuel', name: 'fuel', version: version_fuel ////// CSV - implementation group: 'com.github.doyaaaaaken', name: 'kotlin-csv-jvm', version: '0.7.3' + implementation group: 'com.github.doyaaaaaken', name: 'kotlin-csv-jvm', version: version_kotlin_csv ////// Jaffree ffmpeg wrapper - implementation group: 'com.github.kokorin.jaffree', name: 'jaffree', version: '0.9.3' + implementation group: 'com.github.kokorin.jaffree', name: 'jaffree', version: version_jaffree ////// Cache implementation group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.8.6' ////// Log4J - implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4jVersion - implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4jVersion - implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: log4jVersion - implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: log4jVersion + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: version_log4j + implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: version_log4j + implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: version_log4j + implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: version_log4j + + ////// FastUtil + implementation group: 'it.unimi.dsi', name: 'fastutil', version: version_fastutil ///// JUnit 5 testImplementation "org.jetbrains.kotlin:kotlin-test" testImplementation "org.jetbrains.kotlin:kotlin-test-junit5" + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: version_junit + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: version_junit + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: version_junit - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.7.0' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0' + ///// Jackson / Kotlin + implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.13.0' ///// Kotlin - implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlinVersion - implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion + implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: version_kotlin + implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: version_kotlin } kapt { diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 27e8743ae..e3f8c2d81 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -105,11 +105,11 @@ object DRES { MediaItem, MediaItemSegment, ConfiguredOption, - TaskComponentOption, + HintOption, TaskOption, - TaskScoreOption, - TaskSubmissionOption, - TaskTargetOption, + ScoreOption, + SubmissionOption, + TargetOption, CompetitionDescription, TaskDescription, Team, diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 7ebd8be3f..43fe44413 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -23,7 +23,7 @@ import kotlin.system.exitProcess /** * This is a singleton instance of the [Cli]. * - * @version 1.0.0 + * @version 1.0.1 * @author Loris Sauter */ object Cli { @@ -90,10 +90,10 @@ object Cli { while (true) { try { val line = lineReader.readLine(PROMPT).trim() - if (line.toLowerCase() == "exit" || line.lowercase() == "quit") { + if (line.lowercase() == "exit" || line.lowercase() == "quit") { break } - if (line.toLowerCase() == "help") { + if (line.lowercase() == "help") { println(clikt.getFormattedHelp()) //TODO overwrite with something more useful in a cli context continue } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt index 833cf42ca..d82efef6f 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt @@ -10,71 +10,121 @@ import com.github.ajalt.clikt.parameters.options.required import com.github.ajalt.clikt.parameters.options.validate import com.github.ajalt.clikt.parameters.types.path import com.jakewharton.picnic.table -import dev.dres.data.dbo.DAO import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription import dev.dres.utilities.FFmpegUtil -import java.io.File +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths import java.nio.file.StandardOpenOption +import java.util.* /** - * A collection of [CliktCommand]s for user management + * A collection of [CliktCommand]s for [CompetitionDescription] management. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 */ -class CompetitionCommand(internal val competitions: DAO, internal val collections: DAO, config: Config) : NoOpCliktCommand(name = "competition") { +class CompetitionCommand(private val store: TransientEntityStore, config: Config) : NoOpCliktCommand(name = "competition") { init { - this.subcommands(CreateCompetitionCommand(), ListCompetitionCommand(), ShowCompetitionCommand(), PrepareCompetitionCommand(), DeleteCompetitionCommand(), CopyCompetitionCommand(), ExportCompetitionCommand(), ImportCompetitionCommand()) + this.subcommands( + Create(), + ListCompetitionCommand(), + ShowCompetitionCommand(), + PrepareCompetitionCommand(), + DeleteCompetitionCommand(), + CopyCompetitionCommand(), + ExportCompetitionCommand(), + ImportCompetitionCommand() + ) } override fun aliases(): Map> { return mapOf( - "ls" to listOf("list"), - "remove" to listOf("delete"), - "drop" to listOf("delete"), - "add" to listOf("create") + "ls" to listOf("list"), + "remove" to listOf("delete"), + "drop" to listOf("delete"), + "add" to listOf("create") ) } - private val taskCacheLocation = File(config.cachePath + "/tasks") + /** The cache location [Paths]. */ + private val cacheLocation = Paths.get(config.cachePath, "tasks") abstract inner class AbstractCompetitionCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { - private val id: String? by option("-i", "--id") - private val competition: String? by option("-c", "--competition") - protected val competitionId: UID - get() = when { - this.id != null -> UID(this.id!!) - this.competition != null -> this@CompetitionCommand.competitions.find { c -> c.name == this.competition!! }?.id - ?: UID.EMPTY - else -> UID.EMPTY - } + protected val id: String? by option("-i", "--id") + protected val name: String? by option("-c", "--competition") } - inner class CreateCompetitionCommand : CliktCommand(name = "create", help = "Creates a new Competition") { - private val name: String by option("-n", "--name", help = "Name of the new Competition") - .required() - .validate { require(it.isNotEmpty()) { "Competition name must be non empty." } } + /** + * [CliktCommand] to create a new [CompetitionDescription]. + */ + inner class Create : CliktCommand(name = "create", help = "Creates a new Competition") { + + private val name: String by option("-c", "--competition", help = "Name of the new Competition") + .required() + .validate { require(it.isNotEmpty()) { "Competition description must be non empty." } } private val description: String by option("-d", "--description", help = "Description of the new Competition") .required() .validate { require(it.isNotEmpty()) { "Competition description must be non empty." } } + override fun run() { + val newCompetition = this@CompetitionCommand.store.transactional { + CompetitionDescription.new { + this.id = UUID.randomUUID().toString() + this.name = this@Create.name + this.description = this@Create.description + } + } + println("New competition '$newCompetition' created with ID = ${newCompetition.id}.") + } + } + + /** + * [CliktCommand] to delete a [CompetitionDescription]. + */ + inner class DeleteCompetitionCommand : AbstractCompetitionCommand(name = "delete", help = "Deletes a competition") { override fun run() { - val newCompetition = CompetitionDescription(id = UID.EMPTY, name = name, description = description, taskTypes = mutableListOf(), taskGroups = mutableListOf(), teams = mutableListOf(), teamGroups = mutableListOf(), judges = mutableListOf(), tasks = mutableListOf()) - val id = this@CompetitionCommand.competitions.append(newCompetition) - println("New competition '$newCompetition' created with ID=${id.string}.") + this@CompetitionCommand.store.transactional { + val competition = CompetitionDescription.query((CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name)).firstOrNull() + if (competition == null) { + println("Could not find competition to delete.") + return@transactional + } + competition.delete() + } + println("Successfully deleted competition description.") } } + /** + * [CliktCommand] to copy a [CompetitionDescription]. + */ + inner class CopyCompetitionCommand : AbstractCompetitionCommand(name = "copy", help = "Copies a Competition") { + override fun run() { + this@CompetitionCommand.store.transactional { + val competition = CompetitionDescription.query((CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name)).firstOrNull() + if (competition == null) { + println("Could not find competition to copy.") + return@transactional + } + + /* TODO: Copy competition. */ + } + println("Successfully copied competition.") + } + } + /** + * [CliktCommand] to list all [CompetitionDescription]s. + */ inner class ListCompetitionCommand : CliktCommand(name = "list", help = "Lists an overview of all Competitions") { - override fun run() { + override fun run() = this@CompetitionCommand.store.transactional(true) { var no = 0 println(table { cellStyle { @@ -86,8 +136,8 @@ class CompetitionCommand(internal val competitions: DAO, row("name", "id", "# teams", "# tasks", "description", ) } body { - this@CompetitionCommand.competitions.forEach { - row(it.name, it.id.string, it.teams.size, it.tasks.size, it.description).also { no++ } + CompetitionDescription.all().asSequence().forEach { c -> + row(c.name, c.id, c.teams.size(), c.taskGroups.flatMapDistinct { it.tasks }.size(), c.description).also { no++ } } } }) @@ -95,105 +145,67 @@ class CompetitionCommand(internal val competitions: DAO, } } + /** + * [CliktCommand] to show a specific [CompetitionDescription]. + */ inner class ShowCompetitionCommand : AbstractCompetitionCommand(name = "show", help = "Shows details of a Competition") { + override fun run() = this@CompetitionCommand.store.transactional(true) { + val competition = CompetitionDescription.query( + (CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name) + ).firstOrNull() - override fun run() { - // TODO fancification - val competition = this@CompetitionCommand.competitions[competitionId]!! + if (competition == null) { + println("Could not find specified competition description.") + return@transactional + } println("${competition.name}: ${competition.description}") println("Teams:") - competition.teams.forEach(::println) + competition.teams.asSequence().forEach(::println) println() println("Tasks:") - competition.tasks.forEach { - it.printOverview(System.out) + competition.taskGroups.flatMapDistinct { it.tasks }.asSequence().forEach { _ -> + /* TODO: it.printOverview(System.out) */ println() } - println() } } + /** + * [CliktCommand] to prepare a specific [CompetitionDescription]. + */ inner class PrepareCompetitionCommand : AbstractCompetitionCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { - override fun run() { - val competition = this@CompetitionCommand.competitions[competitionId]!! - - - val segmentTasks = competition.getAllCachedVideoItems() - - segmentTasks.forEach { - val item = it.item - val collection = this@CompetitionCommand.collections[item.collection] - - if (collection == null) { - println("ERROR: collection ${item.collection} not found") - return - } + override fun run() = this@CompetitionCommand.store.transactional(true) { + val competition = CompetitionDescription.query( + (CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name) + ).firstOrNull() - val videoFile = File(File(collection.path), item.location) + if (competition == null) { + println("Could not find specified competition description.") + return@transactional + } - if (!videoFile.exists()) { - println("ERROR: file ${videoFile.absolutePath} not found for item ${item.name}") + /* Fetch all videos in the competition. */ + val videos = competition.getAllVideos() + videos.forEach { item -> + val path = item.first.pathToOriginal() + if (!Files.exists(path)) { + println("ERROR: Media file $path not found for item ${item.first.name}") return@forEach } - println("rendering ${it.item} at ${it.temporalRange}") - FFmpegUtil.prepareMediaSegmentTask(it, collection.path, this@CompetitionCommand.taskCacheLocation) - - } - - } - - } - - inner class DeleteCompetitionCommand : AbstractCompetitionCommand(name = "delete", help = "Deletes a Competition") { - - override fun run() { - val competition = this@CompetitionCommand.competitions.delete(competitionId) - - if (competition != null) { - println("Successfully deleted $competition") - } else { - println("Could not find competition to delete") //should not happen + println("Rendering ${item.first.name}$ at ${item.second}") + FFmpegUtil.extractSegment(item.first, item.second, this@CompetitionCommand.cacheLocation) } - } - } - inner class CopyCompetitionCommand : AbstractCompetitionCommand(name = "copy", help = "Copies a Competition") { - - private val name: String by option("-n", "--name", help = "Name of the copied Competition") - .required() - .validate { require(it.isNotEmpty()) { "Competition name must be non empty." } } - - - override fun run() { - - if (this@CompetitionCommand.competitions.any { it.name == name }) { - println("Competition with name '$name' already exists") - return - } - - val competition = this@CompetitionCommand.competitions[competitionId]!! - val newCompetition = competition.copy(id = UID.EMPTY, name = name) - - this@CompetitionCommand.competitions.append(newCompetition) - - println("Copied") - println(competition) - println("to") - println(newCompetition) - - } - - } /** * Exports a specific competition to a JSON file. @@ -206,12 +218,16 @@ class CompetitionCommand(internal val competitions: DAO, /** Flag indicating whether export should be pretty printed.*/ private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) - override fun run() { - val competition = this@CompetitionCommand.competitions[this.competitionId] + override fun run() = this@CompetitionCommand.store.transactional(true) { + val competition = CompetitionDescription.query( + (CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name) + ).firstOrNull() + if (competition == null) { - println("Competition ${this.competitionId} does not seem to exist.") - return + println("Could not find specified competition description.") + return@transactional } + val mapper = jacksonObjectMapper() Files.newBufferedWriter(this.path, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE).use { val writer = if (this.pretty) { @@ -238,10 +254,9 @@ class CompetitionCommand(internal val competitions: DAO, private val path: Path by option("-i", "--in", help = "The file to import the competition from.").path().required() override fun run() { - - /* Read competition from file. */ - - val reader = jacksonObjectMapper().readerFor(CompetitionDescription::class.java) + /* TODO: Probably won't work this way. */ + /* Read competition from file */ + /*val reader = jacksonObjectMapper().readerFor(CompetitionDescription::class.java) val competition = try { Files.newBufferedReader(this.path).use { val tree = reader.readTree(it) @@ -269,7 +284,7 @@ class CompetitionCommand(internal val competitions: DAO, } } else { println("Could not import competition from $path: Unknown format.") - } + }*/ } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 3d74da7df..ab3f9fdff 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -11,407 +11,366 @@ import com.github.ajalt.clikt.parameters.types.long import com.github.doyaaaaaken.kotlincsv.dsl.csvReader import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter import com.jakewharton.picnic.table -import dev.dres.data.dbo.DAO -import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.UID -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaItemSegment -import dev.dres.data.model.media.MediaItemSegmentList -import dev.dres.data.model.media.time.TemporalRange +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.data.model.media.* import dev.dres.utilities.FFmpegUtil -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.cleanPathString +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory import java.io.File -import java.io.FileOutputStream -import java.io.OutputStream +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardOpenOption +import java.util.* import java.util.concurrent.TimeUnit - -class MediaCollectionCommand(val collections: DAO, val items: DAO, val itemPathIndex: DaoIndexer, val mediaItemCollectionIndex: DaoIndexer, val segments: DAO) : - NoOpCliktCommand(name = "collection") { +import kotlin.io.path.extension +import kotlin.io.path.nameWithoutExtension +import kotlin.io.path.relativeTo + +/** + * A collection of [CliktCommand]s for [MediaItem], [MediaCollection] and [MediaItemSegment] management. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpCliktCommand(name = "collection") { private val logMarker = MarkerFactory.getMarker("CLI") private val logger = LoggerFactory.getLogger(this.javaClass) - override fun aliases(): Map> { + init { + this.subcommands(Create(), Delete(), Update(), List(), Show(), Check(), Scan(), AddItem(), DeleteItem(), Export(), Import(), ImportSegments()) + } + + override fun aliases(): Map> { return mapOf( - "ls" to listOf("list"), - "remove" to listOf("delete"), - "drop" to listOf("delete") + "ls" to listOf("list"), + "remove" to listOf("delete"), + "drop" to listOf("delete") ) } companion object { enum class SortField { - ID, - NAME, - LOCATION, - DURATION, - FPS + ID, NAME, LOCATION, DURATION, FPS } } - init { - this.subcommands(CreateCollectionCommand(),UpdateCollectionCommand(), ListCollectionsCommand(), ShowCollectionCommand(), CheckCollectionCommand(), ScanCollectionCommand(), AddMediaItemCommand(), DeleteItemCommand(), ExportCollectionCommand(), ImportCollectionCommand(), DeleteCollectionCommand(), ImportMediaSegmentsCommand()) - } - + /** + * + */ abstract inner class AbstractCollectionCommand(name: String, help: String) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = true) { - private val collectionNameInput: String? by option("-c", "--collection", help = "Name of the Collection") - private val collectionIdInput: UID? by option("-i", "--id", help = "Id of the Collection").convert { it.UID() } + /** The [CollectionId] of the [MediaCollection] affected by this [AbstractCollectionCommand]. */ + protected val id: CollectionId? by option("-i", "--id", help = "ID of a media collection.") - fun actualCollectionId(): UID? = this.collectionIdInput ?: this.collectionNameInput?.let { - this@MediaCollectionCommand.collections.find { c -> c.name == it }?.id - } + /** The name of the [MediaCollection] affected by this [AbstractCollectionCommand]. */ + protected val name: String? by option("-c", "--collection", help = "Name of a media collection.") + + /** + * Loads and returns the [MediaCollection] for the specified parameters. + * This is a convenience method and requires a transaction context. + * + * @return [MediaCollection] or null + */ + protected fun getCollection(): MediaCollection? + = MediaCollection.query((MediaCollection::id eq this.id).or(MediaCollection::name eq this.name)).firstOrNull() } - inner class CreateCollectionCommand : CliktCommand(name = "create", help = "Creates a new Collection", printHelpOnEmptyArgs = true) { + /** + * [CliktCommand] to create a new [MediaCollection]. + */ + inner class Create: CliktCommand(name = "create", help = "Creates a new media collection.", printHelpOnEmptyArgs = true) { - private val name: String by option("-n", "--name", help = "Name of the Collection to be created") - .required() - .validate { require(!this@MediaCollectionCommand.collections.any { c -> c.name == it }) { "collection with name '$it' already exists" } } + /** The name of the new [MediaCollection]. */ + private val name: String by option("-n", "--name", help = "Name of the Collection to be created").required() - private val description: String by option("-d", "--description", help = "Description of the Collection to be created") - .default("") + /** A description of the new [MediaCollection]. */ + private val description: String by option("-d", "--description", help = "Description of the Collection to be created").default("") - private val basePath: String by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to") - .required() + /** The base path to the new [MediaCollection]. */ + private val basePath: String by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to").required() override fun run() { - this@MediaCollectionCommand.collections.append( - MediaCollection(name = name.trim(), description = description, basePath = basePath.cleanPathString()) - ) - println("Successfully added collection") + if (!Files.exists(Paths.get(this.basePath))) { + this@MediaCollectionCommand.logger.warn("Collection base path ${this.basePath} does not exist!") + } + this@MediaCollectionCommand.store.transactional { + MediaCollection.new { + this.id = UUID.randomUUID().toString() + this.name = this@Create.name + this.description = this@Create.description + this.path = this@Create.basePath + } + } + println("Successfully added new media collection.") } } - inner class UpdateCollectionCommand : AbstractCollectionCommand(name = "update", help = "Updates an existing Collection") { - - private val name: String? by option("-n", "--name", help = "The new name of the collection") - .validate { require(!this@MediaCollectionCommand.collections.any { c -> c.name == it }) { "collection with name '$it' already exists" } } + /** + * [CliktCommand] to create a new [MediaCollection]. + */ + inner class Delete: AbstractCollectionCommand("delete", help = "Deletes a media collection.") { + override fun run() { + this@MediaCollectionCommand.store.transactional { + val collection = this.getCollection() + if (collection == null) { + println("Failed to delete collection; specified collection not found.") + return@transactional + } + collection.delete() + } + println("Collection deleted successfully.") + } + } - private val description: String? by option("-d", "--description", help = "Description of the Collection to be created") + /** + * [CliktCommand] to create a new [MediaCollection]. + */ + inner class Update : AbstractCollectionCommand(name = "update", help = "Updates an existing Collection") { + /** The new name for the [MediaCollection]. */ + private val newName: String? by option("-n", "--name", help = "The new name of the collection") - private val basePath: String? by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to") + /** The new description for the [MediaCollection]. */ + private val newDescription: String? by option("-d", "--description", help = "Description of the Collection to be created") + /** The new path for the [MediaCollection]. */ + private val newPath: String? by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to") override fun run() { - val id = actualCollectionId() - if (id == null) { - System.err.println("Couldn't find the given collection") - return - } - var dirty = false - val collection = this@MediaCollectionCommand.collections[id]!! - val newName = if(name != null){ - dirty = true - name!! - }else{ - collection.name - } - val newDesc =if(description != null){ - dirty = true - description - }else{ - collection.description ?: "" - } - val newBasePath = if(basePath != null){ - dirty = true - basePath!! - }else{ - collection.path - } + this@MediaCollectionCommand.store.transactional { + val collection = this.getCollection() + if (collection == null) { + println("Failed to update collection; specified collection not found.") + return@transactional + } + + /* Update collection. */ + collection.name = (this.newName ?: collection.name) + collection.description = (this.newDescription ?: collection.path) + collection.path = (this.newPath ?: collection.path) - if(dirty){ - this@MediaCollectionCommand.collections.delete(id) - val newCollection = MediaCollection(id, newName, newDesc, newBasePath) - this@MediaCollectionCommand.collections.append(newCollection) - println("Updated collection with id ${id.string}") - }else{ - println("Done. Nothing to update") } + println("Successfully updated media collection.") } - } - - inner class ListCollectionsCommand : CliktCommand(name = "list", help = "Lists all Collections") { + /** + * [CliktCommand] to list all [MediaCollection]s. + */ + inner class List : CliktCommand(name = "list", help = "Lists all media collections.") { val plain by option("-p", "--plain", help = "Plain print: No fancy table presentation for machine readable output").flag(default = false) - override fun run() { - println("Available media collections ${this@MediaCollectionCommand.collections.toSet().size}") - if (plain) { - this@MediaCollectionCommand.collections.forEach { - println(it) - } + override fun run() = this@MediaCollectionCommand.store.transactional(true) { + println("Available media collections ${MediaCollection.all().size()}") + if (this.plain) { + MediaCollection.all().asSequence().forEach { println(it) } } else { println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "name", "description", "basePath", "# items") - } - body { - this@MediaCollectionCommand.collections.forEach { - row(it.id.string, it.name, it.description ?: "", it.path, this@MediaCollectionCommand.mediaItemCollectionIndex.filter { uid -> it.id.equals(uid) }.size) - } + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "name", "description", "basePath", "# items") + } + body { + MediaCollection.all().asSequence().forEach { c -> + row(c.id, c.name, c.description ?: "", c.path, c.items.size()) } } + } ) } } } - inner class ShowCollectionCommand : AbstractCollectionCommand("show", help = "Shows the content of a Collection") { - val sort by option("-s", "--sort", help = "Chose which sorting to use").enum(ignoreCase = true).defaultLazy { SortField.NAME } - val plain by option("-p", "--plain", help = "Plain formatting. No fancy tables").flag(default = false) + /** + * [CliktCommand] to show a [MediaCollection]'s [MediaItem]s in detail. + */ + inner class Show : AbstractCollectionCommand("show", help = "Lists the content of a media collection.") { - override fun run() { + /** The property of the [MediaItem]s to sort by. */ + private val sort by option("-s", "--sort", help = "Chose which sorting to use").enum(ignoreCase = true).defaultLazy { SortField.NAME } + + private val plain by option("-p", "--plain", help = "Plain formatting. No fancy tables").flag(default = false) - val collectionId = this.actualCollectionId() - if (collectionId == null) { + override fun run() = this@MediaCollectionCommand.store.transactional(true) { + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { println("Collection not found.") - return + return@transactional } - val collectionItems = this@MediaCollectionCommand - .items.filter { it.collection == collectionId } - // First sort reversed by type (i.e. Video before Image), then by name - .sortedWith(compareBy { it.javaClass.name }.reversed().thenBy { - when (sort) { - SortField.ID -> it.id.string - SortField.NAME -> it.name - SortField.LOCATION -> it.location - /* Small hack as images do not have these properties */ - SortField.DURATION -> if (it is MediaItem.VideoItem) { - it.durationMs - } else { - it.name - } - SortField.FPS -> if (it is MediaItem.VideoItem) { - it.fps - } else { - it.name - } - } - }) - - if (plain) { - collectionItems.forEach { - println(it) + /* Query for items.. */ + val query = collection.items.sortedBy( + when (this.sort) { + SortField.ID -> MediaItem::id + SortField.NAME -> MediaItem::name + SortField.LOCATION -> MediaItem::location + SortField.DURATION -> MediaItem::durationMs + SortField.FPS -> MediaItem::fps } + ) + /* Print items. */ + if (this.plain) { + query.asSequence().forEach { println(it) } } else { println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "name", "location", "type", "durationMs", "fps") - } - body { - collectionItems.forEach { - row { - cell(it.id.string) - cell(it.name) - cell(it.location) - when (it) { - is MediaItem.ImageItem -> { - cell("image") { - columnSpan = 3 - } - } - is MediaItem.VideoItem -> { - cell("video") - cell(it.durationMs) - cell(it.fps) - } - } - } - } + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "name", "location", "type", "durationMs", "fps") + } + body { + query.asSequence().forEach { + row(it.id, it.name, it.location, it.type.description, it.durationMs ?: "n/a", it.fps ?: "n/a") } } + } ) } - - println("listed ${collectionItems.size} Media Items") - - } } - inner class CheckCollectionCommand : AbstractCollectionCommand("check", help = "Checks if all the files in a collection are present and accessible") { - override fun run() { - - val collectionId = this.actualCollectionId() - if (collectionId == null) { + /** + * [CliktCommand] to validate a [MediaCollection]'s [MediaItem]s. + */ + inner class Check : AbstractCollectionCommand("check", help = "Checks if all the files in a media collection are present and accessible.") { + override fun run() = this@MediaCollectionCommand.store.transactional(true) { + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { println("Collection not found.") - return + return@transactional } - val collection = this@MediaCollectionCommand.collections[collectionId]!! - - val collectionItems = this@MediaCollectionCommand.items.filter { it.collection == collectionId } - - val baseFile = File(collection.path) - + /* Check items. */ var counter = 0 - - collectionItems.forEach { - - val file = File(baseFile, it.location) - - if (file.exists()) { - - if (file.canRead()) { - ++counter + for (item in collection.items.asSequence()) { + val path = item.pathToOriginal() + if (Files.exists(path)) { + if (Files.isReadable(path)) { + counter++ } else { - println("item ${it.name} at ${file.absolutePath} not readable") + println("Item ${item.name} at $path not readable.") } - } else { - println("item ${it.name} at ${file.absolutePath} not found") + println("Item ${item.name} at $path not found.") } - } - - println("successfully checked $counter of ${collectionItems.size} Media Items") - - + println("Successfully checked $counter of ${collection.items.size()} media itemss") } } - inner class ScanCollectionCommand : AbstractCollectionCommand("scan", help = "Scans a collection directory and adds found items") { - - val imageTypes by option("-it", "--imageType", help = "Image file types (endings) to be considered in the scan").convert { it.toLowerCase() }.multiple() - val videoTypes by option("-vt", "--videoType", help = "Video file types (endings) to be considered in the scan").convert { it.toLowerCase() }.multiple() + /** + * [CliktCommand] to validate a [MediaCollection]'s [MediaItem]s. + */ + inner class Scan : AbstractCollectionCommand("scan", help = "Scans a collection directory and adds found items") { - override fun run() { - - val collectionId = this.actualCollectionId() - if (collectionId == null) { - println("Collection not found.") - return - } + /** The file suffices that should be considered as images. */ + private val imageTypes by option("-it", "--imageType", help = "Image file types (endings) to be considered in the scan").convert { it.lowercase() }.multiple() /** The file suffices that should be considered as images. */ - val collection = this@MediaCollectionCommand.collections[collectionId]!! + /** The file suffices that should be considered as videos. */ + private val videoTypes by option("-vt", "--videoType", help = "Video file types (endings) to be considered in the scan").convert { it.lowercase() }.multiple() + override fun run() = this@MediaCollectionCommand.store.transactional { + /* Sanity cehck. */ if (imageTypes.isEmpty() && videoTypes.isEmpty()) { println("No file types specified.") - return + return@transactional } - val base = File(collection.path) - - if (!base.exists()) { - println("Cannot scan collection, '${collection.path}' does not exist.") - return + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional } - if (!base.isDirectory) { - println("Cannot scan collection, '${collection.path}' is no directory.") - return + val base = Paths.get(collection.path) + if (!Files.exists(base)) { + println("Failed to scan collection; '${collection.path}' does not exist.") + return@transactional } - if (!base.canRead()) { - println("Cannot scan collection, '${collection.path}' is not readable.") - return + if (!Files.isReadable(base)) { + println("Failed to scan collection; '${collection.path}' is not readable.") + return@transactional } - val files = base.walkTopDown().filter { it.isFile && (it.extension.toLowerCase() in imageTypes || it.extension in videoTypes) } - - val buffer = mutableListOf() - - val issues = mutableMapOf() + if (!Files.isDirectory(base)) { + println("Failed to scan collection; '${collection.path}' is no directory.") + return@transactional + } + /* Now scan directory. */ + val issues = mutableMapOf() var fileCounter = 0 - - files.forEach { file -> - - println("found ${file.absolutePath}") - - val relativePath = file.relativeTo(base).path - - val existing = this@MediaCollectionCommand.itemPathIndex[relativePath].find { it.collection == collectionId } - try { - when (file.extension.toLowerCase()) { - in imageTypes -> { - - if (existing == null) { //add - val newItem = MediaItem.ImageItem(UID.EMPTY, file.nameWithoutExtension, relativePath, collection.id) - buffer.add(newItem) - } else { //skip - println("Image ${existing.name} already present") + Files.walk(base).filter { + Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) + }.forEach { + val relativePath = it.relativeTo(base) + val exists = MediaItem.query((MediaItem::collection eq collection) and (MediaItem::location eq relativePath.toString())).isNotEmpty + if (!exists) { + try { + when (it.extension.lowercase()) { + in this.imageTypes -> { + println("Found image $it; analyzing...") + collection.items.add(MediaItem.new { + this.id = UUID.randomUUID().toString() + this.type = MediaType.IMAGE + this.name = it.fileName.nameWithoutExtension + this.location = relativePath.toString() + }) } - - - } - in videoTypes -> { - - println("Analyzing ${file.absolutePath}") - - val result = FFmpegUtil.analyze(file.toPath()).streams.first() - val fps = (result.rFrameRate ?: result.avgFrameRate!!).toFloat() - val duration = result.getDuration(TimeUnit.MILLISECONDS).let { - if (it != null) { - it - } else { - println("Cannot read duration from file, counting frames") - val analysis = FFmpegUtil.analyze(file.toPath(), countFrames = true) - val frames = analysis.streams.first().nbReadFrames - println("Counted $frames frames") - ((frames * 1000) / fps).toLong() + in videoTypes -> { + println("Found video $it; analyzing...") + val result = FFmpegUtil.analyze(it).streams.first() + val fps = (result.rFrameRate ?: result.avgFrameRate!!).toFloat() + val duration = result.getDuration(TimeUnit.MILLISECONDS).let { duration -> + if (duration != null) { + duration + } else { + println("Cannot read duration from file, counting frames") + val analysis = FFmpegUtil.analyze(it, countFrames = true) + val frames = analysis.streams.first().nbReadFrames + println("Counted $frames frames") + ((frames * 1000) / fps).toLong() + } } - } - - println("Found frame rate to be $fps frames per seconds and duration $duration ms") - if (existing == null) { //add - val newItem = MediaItem.VideoItem(UID.EMPTY, file.nameWithoutExtension, relativePath, collection.id, duration, fps) - buffer.add(newItem) - } else { //skip - val newItem = MediaItem.VideoItem(existing.id, existing.name, relativePath, collection.id, duration, fps) - this@MediaCollectionCommand.items.update(newItem) - println("Updated Video ${newItem.name}") + println("Found frame rate to be $fps frames per seconds and duration $duration ms") + collection.items.add(MediaItem.new { + this.id = UUID.randomUUID().toString() + this.type = MediaType.VIDEO + this.name = it.fileName.nameWithoutExtension + this.location = relativePath.toString() + this.durationMs = duration + this.fps = fps + }) } - } + } catch(e:Throwable) { + this@MediaCollectionCommand.logger.error(this@MediaCollectionCommand.logMarker, "An error occurred with $it. Noting and skipping...") + println("An error occurred with $it. Noting and skipping...") + issues[it] = e.stackTraceToString() } - }catch(e:Throwable){ - this@MediaCollectionCommand.logger.error(this@MediaCollectionCommand.logMarker, "An error occurred with $file. Noting and skipping...") - println("An error occurred with $file. Noting and skipping...") - issues[file.path] = e.stackTraceToString() } - println() - - if (buffer.size >= 1000) { - println() - print("Storing buffer...") - this@MediaCollectionCommand.items.batchAppend(buffer) - buffer.clear() - println("done") - } - ++fileCounter - } - - println() - if (buffer.isNotEmpty()) { - print("Storing buffer...") - this@MediaCollectionCommand.items.batchAppend(buffer) - println("done") - } - if(issues.isNotEmpty()){ + if (issues.isNotEmpty()) { val file = File("issues-scan-${collection.name}-${System.currentTimeMillis()}.json") println("There have been ${issues.size} issues while scanning. You might want to check them at ${file.path}") val om = jacksonObjectMapper() @@ -419,293 +378,234 @@ class MediaCollectionCommand(val collections: DAO, val items: D println("done") } println("\nAdded $fileCounter elements to collection") - - } - - } - inner class DeleteCollectionCommand : AbstractCollectionCommand("delete", help = "Deletes a Collection") { - override fun run() { - - val collectionId = this.actualCollectionId() - if (collectionId == null) { - println("Collection not found.") - return - } - print("looking up Media Items...") - val itemIds = this@MediaCollectionCommand.items.filter { it.collection == collectionId }.map { it.id } - println("done, found ${itemIds.size} Items") - - print("looking up Media Item Segments...") - val itemIdSet = itemIds.toSet() - val segmentIds = this@MediaCollectionCommand.segments.filter { itemIdSet.contains(it.mediaItemId) }.map { it.id } - println("done, found ${segmentIds.size} Segments") - - print("Deleting Media Item Segments...") - this@MediaCollectionCommand.segments.batchDelete(segmentIds) - println("done") - - print("Deleting Media Items...") - this@MediaCollectionCommand.items.batchDelete(itemIds) - println("done") - - print("Deleting Collection...") - this@MediaCollectionCommand.collections.delete(collectionId) - println("done") - - } - } + /** + * [CliktCommand] to delete [MediaItem]s. + */ + inner class DeleteItem : AbstractCollectionCommand("deleteItem", help = "Deletes media item(s).") { + /** The item ID matching the name of the [MediaItem] to delete. */ + private val itemId: MediaId? by option("-ii", "--itemId", help = "ID of the media item.") - inner class DeleteItemCommand : AbstractCollectionCommand("deleteItem", help = "Deletes Media Item(s)") { + /** The name of the [MediaItem] to delete. */ + private val itemName: String? by option("-in", "--itemName", help = "The exact name of the media item.") - private val itemName: String by option("-in", "--itemName", help = "Name of the Item").default("") - private val itemIdInput: UID? by option("-ii", "--itemId", help = "Id of the Item").convert { it.UID() } - private val nameRegex: Regex? by option("-e", "--regex", help="Regex for item names").convert { it.toRegex() } + /** A RegEx matching the name of the [MediaItem] to delete. */ + private val itemNameRegex: Regex? by option("-e", "--regex", help="Regex for item names").convert { it.toRegex() } - override fun run() { - val collectionId = this.actualCollectionId() - if (collectionId == null && itemIdInput == null) { - println("Collection not found.") - return - } - if ((itemName.isBlank() && itemIdInput == null) && nameRegex == null) { + override fun run() = this@MediaCollectionCommand.store.transactional { + /* Sanity check. */ + if (itemName == null && itemId == null && itemNameRegex == null) { println("Item(s) not specified.") - return + return@transactional } - if(itemName.isNotBlank() || itemIdInput != null){ - val itemId = itemIdInput - ?: this@MediaCollectionCommand.items.find { it.collection == collectionId && it.name == itemName }?.id - if (itemId == null) { - println("Item not found.") - return - } + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional + } - this@MediaCollectionCommand.items.delete(itemId) - println("Item '${itemId.string}' deleted") - }else if(nameRegex != null){ - val regex = nameRegex!! - val ids = this@MediaCollectionCommand.items.filter { it.collection == collectionId && regex.matches(it.name) }.map{it.id} - if(ids.isEmpty()){ - println("No items found for regex $regex") - return + var counter = 0 + if (this.itemId != null || this.itemName != null) { + collection.items.filter { (it.id eq itemId).or(it.name eq itemName) }.asSequence().forEach { + it.delete() + counter += 1 } - ids.forEach { - this@MediaCollectionCommand.items.delete(it) - println("Item '$it' deleted") + } else if (this.itemNameRegex != null) { + collection.items.asSequence().forEach { + if (this.itemNameRegex!!.matches(it.name)) { + it.delete() + counter += 1 + } } - }else{ - println("Nothing was specified, hence no deletion occured") } - + println("$counter media items deleted successfully.") } } - inner class AddMediaItemCommand : NoOpCliktCommand(name = "add", help = "Adds a Media Item to a Collection") { - - init { - this.subcommands(AddImageCommand(), AddVideoCommand()) - } - + /** + * [CliktCommand] to delete [MediaItem]s. + */ + inner class AddItem : AbstractCollectionCommand(name = "add", help = "Adds a media item to a media collection.") { - inner class AddImageCommand : AbstractCollectionCommand(name = "image", help = "Adds a new Image Media Item") { + /** The [ApiMediaType] of the new [MediaItem]. */ + private val type: ApiMediaType by option("-t", "--type", help = "Type of the new media item.").enum().required() - private val name: String by option("-n", "--name", help = "Name of the Item").required() - private val path: String by option("-p", "--path", help = "Path of the Item relative to the Collection base path") - .required() + /** The relative path of the new [MediaItem]. */ + private val path: String by option("-p", "--path", help = "Path of the new media item. relative to the collection base path").required() - override fun run() { + /** The duration of the new [MediaItem]. */ + private val duration: Long? by option("-d", "--duration", help = "video duration in seconds").long() - val collectionId = this.actualCollectionId() - if (collectionId == null) { - println("Collection not found.") - return - } + /** The fps rate of the new [MediaItem]. */ + private val fps: Float? by option("-f", "--fps").float().required() - val existing = this@MediaCollectionCommand.items.filterIsInstance().find { it.collection == collectionId && it.name == name } - if (existing != null) { - println("item with name '$name' already exists in collection:") - println(existing) - return - } - this@MediaCollectionCommand.items.append(MediaItem.ImageItem(name = name.trim(), location = path.cleanPathString(), collection = collectionId, id = UID.EMPTY)) - println("item added") + override fun run() = this@MediaCollectionCommand.store.transactional { + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional } - } - - inner class AddVideoCommand : AbstractCollectionCommand(name = "video", help = "Adds a new Video Media Item") { - - private val name: String by option("-n", "--name", help = "Name of the Item").required() - private val path: String by option("-p", "--path", help = "Path of the Item relative to the Collection base path") - .required() - - private val duration: Long by option("-d", "--duration", help = "video duration in seconds").long().required() - private val fps: Float by option("-f", "--fps").float().required() - - override fun run() { - val collectionId = this.actualCollectionId() - if (collectionId == null) { - println("Collection not found.") - return - } - - val existing = this@MediaCollectionCommand.items.filterIsInstance().find { it.collection == collectionId && it.name == name } - if (existing != null) { - println("item with name '$name' already exists in collection:") - println(existing) - return - } - this@MediaCollectionCommand.items.append(MediaItem.VideoItem(name = name, location = path, collection = collectionId, durationMs = duration, fps = fps, id = UID.EMPTY)) - println("item added") + /* Check if file exists. */ + val fullPath = Paths.get(collection.path, this@AddItem.path) + if (!Files.exists(fullPath)) { + println("Warning: Media item $fullPath doesn't seem to exist. Continuing anyway...") } - } - - } - - inner class ExportCollectionCommand : AbstractCollectionCommand("export", help = "Exports a Collection to a CSV file") { - - private fun fileOutputStream(file: String): OutputStream = FileOutputStream(file) - - private val outputStream: OutputStream by option("-f", "--file", help = "Path of the file the Collection is to be exported to") - .convert { fileOutputStream(it) } - .default(System.out) + /* Add new media item. */ + collection.items.add(MediaItem.new { + this.id = UUID.randomUUID().toString() + this.type = this@AddItem.type.type + this.name = Paths.get(this@AddItem.path).nameWithoutExtension + this.location = this@AddItem.path + this.durationMs = this@AddItem.duration + this.fps = this@AddItem.fps + }) - private fun toRow(item: MediaItem): List = when (item) { - is MediaItem.ImageItem -> listOf("image", item.name, item.location, null, null) - is MediaItem.VideoItem -> listOf("video", item.name, item.location, item.duration.toMillis().toString(), item.fps.toString()) - } - - private val header = listOf("itemType", "name", "location", "duration", "fps") - - override fun run() { - csvWriter().open(outputStream) { - writeRow(header) - this@MediaCollectionCommand.items.forEach { - writeRow(toRow(it)) - } - } + println("Media item added successfully.") } } - inner class ImportCollectionCommand : AbstractCollectionCommand("import", help = "Imports a Collection from a CSV file") { + /** + * [CliktCommand] to export a [MediaCollection]. + */ + inner class Export : AbstractCollectionCommand("export", help = "Exports a media collection into a CSV file.") { - private val inputFile: File by option("-f", "--file", help = "Path of the file the Collection is to be imported from") - .convert { File(it) } - .required() - .validate { require(it.exists()) { "Input File not found" } } + /** The output path for the export.. */ + private val output: Path by option("-o", "--output", help = "Path of the file the media collection should to be exported to.").convert { Paths.get(it)}.required() - private fun fromRow(map: Map, collectionId: UID): MediaItem? { + /** The header of an exported CSV file. */ + private val header = listOf("itemType", "name", "location", "duration", "fps") - if (!map.containsKey("itemType") || !map.containsKey("name") || !map.containsKey("location")) { - return null + override fun run() = this@MediaCollectionCommand.store.transactional(true) { + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional } - return when (map.getValue("itemType")) { - "image" -> MediaItem.ImageItem(UID.EMPTY, map.getValue("name"), map.getValue("location"), collectionId) - "video" -> { - if (map.containsKey("duration") && map.containsKey("fps")) { - return MediaItem.VideoItem(UID.EMPTY, map.getValue("name"), map.getValue("location"), collectionId, map.getValue("duration").toLong(), map.getValue("fps").toFloat()) + Files.newOutputStream(this.output, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW).use { os -> + csvWriter().open(os) { + writeRow(this@Export.header) + collection.items.asSequence().forEach { + writeRow(listOf(it.type.description, it.name, it.location, it.durationMs?.toString(), it.fps?.toString())) } - return null } - else -> null } } + } + + /** + * [CliktCommand] to import a [MediaCollection]. + */ + inner class Import : AbstractCollectionCommand("import", help = "Imports a media collection from a CSV file.") { + + /** [Path] to the input file. */ + private val input: Path by option("-i", "--input", help = "Path of the file the media collection should be imported from.") + .convert { Paths.get(it) }.required() override fun run() { - val collectionId = this.actualCollectionId() - if (collectionId == null) { - println("Collection not found.") + var inserted = 0 + + /* Check for input file's existence. */ + if (!Files.exists(this.input)) { + println("Input file not found.") return } - val rows: List> = csvReader().readAllWithHeader(inputFile) - val itemsFromFile = rows.mapNotNull { this.fromRow(it, collectionId) } + /* Load file. */ + this@MediaCollectionCommand.store.transactional { + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional + } - val collectionItems = this@MediaCollectionCommand.items.filter { it.collection == collectionId }.toList() + Files.newInputStream(this.input, StandardOpenOption.READ).use { ips -> + val rows: kotlin.collections.List> = csvReader().readAllWithHeader(ips) + for (row in rows) { + inserted += 1 + collection.items.add(MediaItem.new { + this.id = UUID.randomUUID().toString() + this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).type + this.name = row.getValue("name") + this.location = row.getValue("location") + this.durationMs = row["duration"]?.toLongOrNull() + this.fps = row["fps"]?.toFloatOrNull() + }) + } + } - val itemsToInsert = itemsFromFile.filter { item -> - collectionItems.none { it.name == item.name && it.location == item.location } } - - this@MediaCollectionCommand.items.batchAppend(itemsToInsert) - println("Successfully imported ${itemsToInsert.size} of ${rows.size} rows") + println("Successfully imported $inserted media items.") } - } - inner class ImportMediaSegmentsCommand : AbstractCollectionCommand("importSegments", "Imports the Segment information for the Items in a Collection from a CSV file") { - private val inputFile: File by option("-f", "--file", help = "Path of the file the Segments are to be imported from") - .convert { File(it) } - .required() - .validate { require(it.exists()) { "Input File not found" } } + /** + * [CliktCommand] to import a [MediaItemSegment]s. + * + * Uses the VBS format. + */ + inner class ImportSegments : AbstractCollectionCommand("importSegments", "Imports the Segment information for the Items in a Collection from a CSV file") { + + /** [Path] to the input file. */ + private val input: Path by option("-i", "--input", help = "Path of the file the media segments should be imported from.") + .convert { Paths.get(it) }.required() override fun run() { + var inserted = 0 - val collectionId = this.actualCollectionId() - if (collectionId == null) { - println("Collection not found.") + /* Check for input file's existence. */ + if (!Files.exists(this.input)) { + println("Input file not found.") return } - print("loading collection information...") - val itemIds = this@MediaCollectionCommand.items.filter { it.collection == collectionId }.map { it.name to it.id }.toMap() - val mediaItemIds = itemIds.values.toSet() - print(".") - val existingSegments = this@MediaCollectionCommand.segments - .filter { mediaItemIds.contains(it.mediaItemId) }.flatMap { it.segments.map { s -> it.mediaItemId to s.name } }.toMutableSet() - println("done") - - print("reading input file...") - val rows: List> = csvReader().readAllWithHeader(inputFile) - println("done, read ${rows.size} rows") - - print("analyzing segments...") - val segments = rows.map { - val video = it["video"] ?: return@map null - val name = it["name"] ?: return@map null - val start = it["start"]?.toLong() ?: return@map null - val end = it["end"]?.toLong() ?: return@map null - - val videoId = itemIds[video] ?: return@map null - - //check for duplicates - val pair = videoId to name - if (existingSegments.contains(pair)) { - return@map null + /* Load file. */ + this@MediaCollectionCommand.store.transactional { + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional } - existingSegments.add(pair) - - MediaItemSegment(videoId, name, TemporalRange(start, end)) - }.filterNotNull() - println("done, generated ${segments.size} valid, non-duplicate segments") - - val grouped = segments.groupBy { it.mediaItemId } - val affected = this@MediaCollectionCommand.segments.filter { grouped.keys.contains(it.mediaItemId) } - - affected.forEach { - it.segments.addAll(grouped[it.mediaItemId] ?: emptyList()) - } - - val affectedMediaItemIds = affected.map { it.mediaItemId }.toSet() - - val new = grouped.filter { !affectedMediaItemIds.contains(it.key) }.map { MediaItemSegmentList(UID.EMPTY, it.key, it.value.toMutableList()) } + Files.newInputStream(this.input, StandardOpenOption.READ).use { ips -> + print("Reading input file...") + val rows: kotlin.collections.List> = csvReader().readAllWithHeader(ips) + println("Done! Reading ${rows.size} rows") + for (row in rows) { + val videoName = row["name"] ?: continue + val start = row["start"]?.toIntOrNull() ?: continue + val end = row["end"]?.toIntOrNull() ?: continue + val videoItem = collection.items.filter { it.name eq videoName }.firstOrNull() + if (videoItem != null) { + inserted += 1 + videoItem.segments.add( + MediaItemSegment.new { + this.id = UUID.randomUUID().toString() + this.name = videoName + this.start = start + this.end = end + } + ) + } - print("storing segments...") - affected.forEach { - this@MediaCollectionCommand.segments.update(it) + } + println("Done! Read $inserted valid segments.") + } } - this@MediaCollectionCommand.segments.batchAppend(new) - println("done") + println("Done! Inserted $inserted valid segments.") } - } } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 031de2f6a..8e5c8014b 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -18,13 +18,12 @@ import java.nio.file.Paths import java.nio.file.StandardOpenOption /** - * A collection of [CliktCommand]s for user management + * A collection of [CliktCommand]s for [User] management * * @author Ralph Gasser * @version 2.0.0 */ -sealed class UserCommand : NoOpCliktCommand(name = "user") { - +class UserCommand : NoOpCliktCommand(name = "user") { init { this.subcommands(Create(), Update(), Delete(), List(), Roles(), Export(), Import()) } @@ -40,7 +39,7 @@ sealed class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to create a new [User]. */ - inner class Create : CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { + inner class Create: CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { /** The name of the newly created user. */ private val username: String by option("-u", "--username", help = "Username of at least $MIN_LENGTH_USERNAME characters length. Must be unique!") .required() @@ -59,7 +58,6 @@ sealed class UserCommand : NoOpCliktCommand(name = "user") { val successful = UserManager.create(username = this.username, password = this.password, role = role) if (successful) { println("New user '${UserManager.get(username = this.username)}' created.") - } else { println("Could not create user '${this.username}' because a user with that name already exists.") @@ -70,7 +68,7 @@ sealed class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to update an existing [User]. */ - inner class Update : CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { + inner class Update: CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id") /** The new username. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 5ed99edea..7fb7f3e0f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,10 +1,12 @@ package dev.dres.api.rest import GetAuditLogInfoHandler +import dev.dres.api.cli.Cli import dev.dres.api.rest.handler.* import dev.dres.api.rest.handler.audit.ListAuditLogsHandler import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler import dev.dres.api.rest.handler.collection.* +import dev.dres.api.rest.handler.description.* import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.MediaPreviewHandler import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler @@ -35,6 +37,12 @@ import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory import java.io.File +/** + * This is a singleton instance of the RESTful API + * + * @version 1.0.1 + * @author Luca Rossetto + */ object RestApi { private var javalin: Javalin? = null @@ -93,12 +101,11 @@ object RestApi { ListCompetitionHandler(store), CreateCompetitionHandler(store), UpdateCompetitionHandler(store, config), - GetCompetitionHandler(store), + ShowCompetitionHandler(store), DeleteCompetitionHandler(store), ListTeamHandler(store), - ListDetailedTeamHandler(store), - ListTaskHandler(store), - GetTeamLogoHandler(config), + ListTasksHandler(store), + GetTeamLogoHandler(store), // Submission SubmissionHandler(store, config), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt deleted file mode 100644 index 36b1acb48..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionHandler.kt +++ /dev/null @@ -1,303 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.competition.RestCompetitionDescription -import dev.dres.api.rest.types.competition.ApiTeam -import dev.dres.api.rest.types.competition.RestTaskDescription -import dev.dres.api.rest.types.competition.RestTeam -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.dbo.DAO -import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.team.Team -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.errorResponse -import io.javalin.http.BadRequestResponse -import io.javalin.http.Context -import io.javalin.openapi.* -import io.javalin.security.RouteRole -import java.io.IOException -import java.nio.file.Files - -abstract class CompetitionHandler(protected val competitions: DAO) : RestHandler, AccessManagedRestHandler { - - override val permittedRoles: Set = setOf(ApiRole.ADMIN) - override val apiVersion = "v1" - - private fun competitionId(ctx: Context): UID = - ctx.pathParamMap().getOrElse("competitionId") { - throw ErrorStatusException(404, "Parameter 'competitionId' is missing!'", ctx) - }.UID() - - protected fun competitionById(id: UID, ctx: Context): CompetitionDescription = - competitions[id] ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) - - protected fun competitionFromContext(ctx: Context): CompetitionDescription = competitionById(competitionId(ctx), ctx) - -} - -data class CompetitionOverview(val id: String, val name: String, val description: String, val taskCount: Int, val teamCount: Int) { - companion object { - fun of(competitionDescription: CompetitionDescription): CompetitionOverview = CompetitionOverview(competitionDescription.id.string, competitionDescription.name, competitionDescription.description - ?: "", competitionDescription.tasks.size, competitionDescription.teams.size) - } -} - -/** - * Data class for creation of competition - */ -data class CompetitionCreate(val name: String, val description: String) - -class GetTeamLogoHandler(val config: Config) : AbstractCompetitionRunRestHandler(), GetRestHandler { - - override val route = "competition/logo/{logoId}" - override val apiVersion = "v1" - - //not used - override fun doGet(ctx: Context): Any = "" - - @OpenApi( - summary = "Returns the logo for the given logo ID.", - path = "/api/v1/competition/logo/{logoId}", - tags = ["Competition Run", "Media"], - pathParams = [OpenApiParam("logoId", String::class, "The ID of the logo.")], - responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], - ignore = true, - methods = [HttpMethod.GET] - ) - override fun get(ctx: Context) { - - /* Extract logoId. */ - val logoId = try { - ctx.pathParamMap().getOrElse("logoId") { - ctx.errorResponse(ErrorStatusException(400, "Parameter 'logoId' is missing!'", ctx)) - return@get - }.UID() - }catch (ex: java.lang.IllegalArgumentException){ - ctx.errorResponse(ErrorStatusException(400, "Could not deserialise logoId '${ctx.pathParamMap()["logoId"]}'", ctx)) - return - } - - - /* Load image and return it. */ - try { - val image = Files.newInputStream(Team.logoPath(this.config, logoId)).use { - it.readAllBytes() - } - ctx.contentType("image/png") - ctx.result(image) - } catch (e: IOException) { - ctx.status(404) - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) - //ctx.errorResponse(ErrorStatusException(404, "Logo file for team $logoId could not be read!", ctx)) - } - } -} - -class ListCompetitionHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler> { - - @OpenApi( - summary = "Lists an overview of all available competitions with basic information about their content.", - path = "/api/v1/competition/list", - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context) = competitions.map { CompetitionOverview.of(it) } - - override val route: String = "competition/list" -} - -class GetCompetitionHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler { - - @OpenApi( - summary = "Loads the detailed definition of a specific competition.", - path = "/api/v1/competition/{competitionId}", - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(RestCompetitionDescription::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context) = RestCompetitionDescription.fromCompetition(competitionFromContext(ctx)) - - override val route: String = "competition/{competitionId}" -} - -class ListTeamHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler> { - - override val route: String = "competition/{competitionId}/team/list" - - @OpenApi( - summary = "Lists the Teams of a specific competition.", - path = "/api/v1/competition/{competitionId}/team/list", - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context) = competitionFromContext(ctx).teams.map { RestTeam(it) } - -} - - -/** - * REST handler to list all teams for a [CompetitionDescription], inclusive [UserDetails] - */ -class ListDetailedTeamHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler>{ - - override val route: String = "competition/{competitionId}/team/list/details" - - @OpenApi( - summary="Lists the teams with their user details", - path = "/api/v1/competition/{competitionId}/team/list/details", - pathParams= [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context) = competitionFromContext(ctx).teams.map{ ApiTeam.of(it) } - -} - -class ListTaskHandler(competitions: DAO) : CompetitionHandler(competitions), GetRestHandler> { - - override val route: String = "competition/{competitionId}/task/list" - - @OpenApi( - summary = "Lists the Tasks of a specific competition.", - path = "/api/v1/competition/{competitionId}/task/list", - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - - override fun doGet(ctx: Context) = competitionFromContext(ctx).tasks.map { RestTaskDescription.fromTask(it) } - -} - -class CreateCompetitionHandler(competitions: DAO) : CompetitionHandler(competitions), PostRestHandler { - @OpenApi( - summary = "Creates a new competition.", - path = "/api/v1/competition", - methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(CompetitionCreate::class)]), - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val createRequest = try { - ctx.bodyAsClass(CompetitionCreate::class.java) - }catch (e: BadRequestResponse){ - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val competition = CompetitionDescription(UID.EMPTY, createRequest.name, createRequest.description, mutableListOf(), mutableListOf(), mutableListOf(), mutableListOf(), mutableListOf(), mutableListOf()) - val competitionId = this.competitions.append(competition) - return SuccessStatus("Competition with ID ${competitionId.string} was created.") - } - - override val route: String = "competition" -} - -class UpdateCompetitionHandler(competitions: DAO, val config: Config, val mediaItems: DAO) : CompetitionHandler(competitions), PatchRestHandler { - @OpenApi( - summary = "Updates an existing competition.", - path = "/api/v1/competition", - methods = [HttpMethod.PATCH], - requestBody = OpenApiRequestBody([OpenApiContent(RestCompetitionDescription::class)]), - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPatch(ctx: Context): SuccessStatus { - val restCompetitionDescription = try { - ctx.bodyAsClass(RestCompetitionDescription::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val competition = restCompetitionDescription.toCompetitionDescription(this.config, mediaItems) - - if (!this.competitions.exists(competition.id)) { - throw ErrorStatusException(404, "Competition with ID ${competition.id.string} does not exist.", ctx) - } - - try { - competition.validate() - }catch (e: IllegalArgumentException) { - throw ErrorStatusException(400, e.message!!, ctx) - } - - this.competitions.update(competition) - return SuccessStatus("Competition with ID ${competition.id.string} was updated.") - } - - override val route: String = "competition" -} - - -class DeleteCompetitionHandler(competitions: DAO) : CompetitionHandler(competitions), DeleteRestHandler { - @OpenApi( - summary = "Deletes the competition with the given competition ID.", - path = "/api/v1/competition/{competitionId}", - methods = [HttpMethod.DELETE], - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doDelete(ctx: Context): SuccessStatus { - val competitionToDelete = competitionFromContext(ctx) - val competition = this.competitions.delete(competitionToDelete.id) - return if (competition != null) { - SuccessStatus("Competition with ID ${competitionToDelete.id.string} was deleted.") - } else { - throw ErrorStatusException(404, "Competition with ID ${competitionToDelete.id.string} not found.", ctx) - } - } - - override val route: String = "competition/{competitionId}" -} - diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 12b3aed54..e7f2467b2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -2,7 +2,6 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.competition.CompetitionStartMessage import dev.dres.api.rest.types.run.* import dev.dres.api.rest.types.status.ErrorStatus @@ -159,7 +158,7 @@ class CreateCompetitionRunAdminHandler( val outputFile = File(cacheLocation, it.cacheItemName()) if (!outputFile.exists()) { logger.warn("Query video file for item ${it.item} not found, rendering to ${outputFile.absolutePath}") - FFmpegUtil.prepareMediaSegmentTask(it, collection.path, cacheLocation) + FFmpegUtil.extractSegment(it, collection.path, cacheLocation) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 4e5728ff9..ed8208100 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -8,8 +8,8 @@ import dev.dres.api.rest.types.run.SubmissionInfo import dev.dres.api.rest.types.run.TaskInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.task.TaskHint -import dev.dres.api.rest.types.task.TaskTarget +import dev.dres.api.rest.types.competition.tasks.ApiHintContent +import dev.dres.api.rest.types.competition.tasks.ApiTargetContent import dev.dres.data.dbo.DAO import dev.dres.data.model.Config import dev.dres.data.model.UID @@ -218,7 +218,7 @@ class CurrentTaskInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandl } class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRunRestHandler(), - GetRestHandler { + GetRestHandler { override val route = "run/{runId}/hint/{taskId}" @@ -231,14 +231,14 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu OpenApiParam("taskId", String::class, "Task Description ID") ], responses = [ - OpenApiResponse("200", [OpenApiContent(TaskHint::class)]), + OpenApiResponse("200", [OpenApiContent(ApiHintContent::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): TaskHint { + override fun doGet(ctx: Context): ApiHintContent { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) @@ -274,7 +274,7 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu } class CurrentTaskTargetHandler(private val config: Config, private val collections: DAO) : - AbstractCompetitionRunRestHandler(), GetRestHandler { + AbstractCompetitionRunRestHandler(), GetRestHandler { override val route = "run/{runId}/target/{taskId}" @@ -287,14 +287,14 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio OpenApiParam("taskId", String::class, "Task Description ID") ], responses = [ - OpenApiResponse("200", [OpenApiContent(TaskTarget::class)]), + OpenApiResponse("200", [OpenApiContent(ApiTargetContent::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): TaskTarget { + override fun doGet(ctx: Context): ApiTargetContent { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index 27b4e3307..f28658c02 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -55,6 +55,6 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle override fun doGet(ctx: Context): List = this.store.transactional(true) { val limit = (ctx.pathParamMap()[LIMIT_PARAM]?.toIntOrNull() ?: DEFAULT_LIMIT).coerceAtLeast(0) val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) - AuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { ApiAuditLogEntry.convert(it) }.toList() + AuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt index b67e696fd..9506d4ed4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -43,7 +43,7 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog return this.store.transactional(true) { AuditLogEntry.query((AuditLogEntry::timestamp gt since) and (AuditLogEntry::timestamp lt upto)).asSequence().map { - ApiAuditLogEntry.convert(it) + it.toApi() }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 99bd65abe..1208e2e6f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -57,7 +57,7 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl val item = MediaItem.new { this.id = UUID.randomUUID().toString() - this.type = MediaType.convertApiType(mediaItem.type) + this.type = mediaItem.type.type this.name = mediaItem.name this.location = mediaItem.location this.fps = mediaItem.fps diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index fbf5365c5..36ef1ccc6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -6,7 +6,6 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -49,7 +48,7 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa val item = MediaItem.query(MediaItem::id eq mediaItem.id).firstOrNull() ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.id} not found.", ctx) - item.type = MediaType.convertApiType(mediaItem.type) + item.type = mediaItem.type.type item.name = item.name item.location = item.location item.fps = item.fps diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt new file mode 100644 index 000000000..f82fe1465 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt @@ -0,0 +1,45 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.handler.collection.AbstractCollectionHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.CompetitionDescriptionId +import io.javalin.http.Context +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * An abstract [RestHandler] used to access and manipulate [CompetitionDescription]s. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @author Loris Sauter + * @version 1.0.0 + */ +abstract class AbstractCompetitionDescriptionHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { + + /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + /** All [AbstractCollectionHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** Convenience method to extract [CompetitionDescription]'s ID from [Context]. */ + private fun competitionId(ctx: Context): CompetitionDescriptionId = + ctx.pathParamMap().getOrElse("competitionId") { + throw ErrorStatusException(404, "Parameter 'competitionId' is missing!'", ctx) + } + + /** Convenience method to extract [CompetitionDescription] from [Context]. */ + protected fun competitionFromContext(ctx: Context): CompetitionDescription = competitionById(competitionId(ctx), ctx) + + /** Convenience method to extract [CompetitionDescription] by ID. */ + protected fun competitionById(id: CompetitionDescriptionId, ctx: Context): CompetitionDescription + = CompetitionDescription.query(CompetitionDescription::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt new file mode 100644 index 000000000..56d6db52f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt @@ -0,0 +1,57 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.competition.ApiCreateCompetition +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.competition.CompetitionDescription +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import java.util.* + +/** + * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [CompetitionDescription]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class CreateCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), PostRestHandler { + + override val route: String = "competition" + + @OpenApi( + summary = "Creates a new competition description.", + path = "/api/v1/competition", + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(ApiCreateCompetition::class)]), + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val createRequest = try { + ctx.bodyAsClass(ApiCreateCompetition::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + val newId = UUID.randomUUID().toString() + this.store.transactional { + CompetitionDescription.new { + id = newId + name = createRequest.name + description = createRequest.description + } + } + return SuccessStatus("Competition description with ID $newId was created successfully.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt new file mode 100644 index 000000000..121dff4c9 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt @@ -0,0 +1,41 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.DeleteRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.competition.CompetitionDescription +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [AbstractCompetitionDescriptionHandler] that can be used to delete an existing [CompetitionDescription]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class DeleteCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), DeleteRestHandler { + override val route: String = "competition/{competitionId}" + + @OpenApi( + summary = "Deletes the competition description with the given competition ID.", + path = "/api/v1/competition/{competitionId}", + methods = [HttpMethod.DELETE], + pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doDelete(ctx: Context): SuccessStatus = this.store.transactional { + val competitionToDelete = competitionFromContext(ctx) + competitionToDelete.delete() + SuccessStatus("Competition with ID ${competitionToDelete.id} was deleted successfully.") + } +} + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt new file mode 100644 index 000000000..3b0f1846b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt @@ -0,0 +1,71 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.AbstractCompetitionRunRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.competition.team.Team +import dev.dres.utilities.extensions.UID +import dev.dres.utilities.extensions.errorResponse +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiParam +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore +import java.io.IOException +import java.nio.file.Files + +/** + * A [AbstractCompetitionRunRestHandler] that can be used to list all [Team] logos. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 1.0.0 + */ +class GetTeamLogoHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { + + override val route = "competition/logo/{logoId}" + override val apiVersion = "v1" + + //not used + override fun doGet(ctx: Context): Any = "" + + @OpenApi( + summary = "Returns the logo for the given logo ID.", + path = "/api/v1/competition/logo/{logoId}", + tags = ["Competition Run", "Media"], + pathParams = [OpenApiParam("logoId", String::class, "The ID of the logo.")], + responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], + ignore = true, + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + + /* Extract logoId. */ + val logoId = try { + ctx.pathParamMap().getOrElse("logoId") { + ctx.errorResponse(ErrorStatusException(400, "Parameter 'logoId' is missing!'", ctx)) + return@get + }.UID() + }catch (ex: java.lang.IllegalArgumentException){ + ctx.errorResponse(ErrorStatusException(400, "Could not deserialise logoId '${ctx.pathParamMap()["logoId"]}'", ctx)) + return + } + + + /* Load image and return it. */ + try { + val image = Files.newInputStream(Team.logoPath(this.config, logoId)).use { + it.readAllBytes() + } + ctx.contentType("image/png") + ctx.result(image) + } catch (e: IOException) { + ctx.status(404) + ctx.contentType("image/png") + ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) + //ctx.errorResponse(ErrorStatusException(404, "Logo file for team $logoId could not be read!", ctx)) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt new file mode 100644 index 000000000..d18aef501 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt @@ -0,0 +1,43 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.competition.ApiCompetitionOverview +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.competition.CompetitionDescription +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.flatMapDistinct +import kotlinx.dnq.query.size + +/** + * A [GetRestHandler] that can be used to list all [CompetitionDescription]s. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ListCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { + override val route: String = "competition/list" + + @OpenApi( + summary = "Lists an overview of all available competitions with basic information about their content.", + path = "/api/v1/competition/list", + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context) = this.store.transactional(true) { + CompetitionDescription.all().asSequence().map { + ApiCompetitionOverview(it.id, it.name, it.description, it.taskGroups.flatMapDistinct { g -> g.tasks }.size(), it.teams.size()) + }.toList() + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt new file mode 100644 index 000000000..6a381f373 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt @@ -0,0 +1,42 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.competition.tasks.ApiTaskDescription +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.competition.task.TaskDescription +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.flatMapDistinct + +/** + * A [AbstractCompetitionDescriptionHandler] that can be used to list all [TaskDescription]s. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { + + override val route: String = "competition/{competitionId}/task/list" + + @OpenApi( + summary = "Lists the tasks of a specific competition.", + path = "/api/v1/competition/{competitionId}/task/list", + pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + + override fun doGet(ctx: Context) = this.store.transactional(true) { + competitionFromContext(ctx).taskGroups.flatMapDistinct { it.tasks }.asSequence().map { it.toApi() }.toList() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt new file mode 100644 index 000000000..906506583 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt @@ -0,0 +1,41 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.competition.team.ApiTeam +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.competition.team.Team +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence + +/** + * A [AbstractCompetitionDescriptionHandler] that can be used to list all [Team]s. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ListTeamHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { + + override val route: String = "competition/{competitionId}/team/list" + + @OpenApi( + summary = "Lists all the teams of a specific competition.", + path = "/api/v1/competition/{competitionId}/team/list", + pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + + override fun doGet(ctx: Context) = this.store.transactional(true) { + competitionFromContext(ctx).teams.asSequence().map { it.toApi() }.toList() + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt new file mode 100644 index 000000000..4adf697ea --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt @@ -0,0 +1,37 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.competition.ApiCompetitionDescription +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.competition.CompetitionDescription +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [AbstractCompetitionDescriptionHandler] that can be used to show an existing [CompetitionDescription]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ShowCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { + @OpenApi( + summary = "Loads the detailed definition of a specific competition.", + path = "/api/v1/competition/{competitionId}", + pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiCompetitionDescription::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context)= this.store.transactional(true) { + competitionFromContext(ctx).toApi() + } + override val route: String = "competition/{competitionId}" +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt new file mode 100644 index 000000000..94fbf22d9 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt @@ -0,0 +1,179 @@ +package dev.dres.api.rest.handler.description + +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.types.competition.ApiCompetitionDescription +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.Config +import dev.dres.data.model.admin.User +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.task.* +import dev.dres.data.model.competition.task.options.ConfiguredOption +import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.media.MediaItem +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.creator.findOrNew +import kotlinx.dnq.query.* +import java.util.* + +/** + * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [CompetitionDescription]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) : AbstractCompetitionDescriptionHandler(store), PatchRestHandler { + + override val route: String = "competition" + + @OpenApi( + summary = "Updates an existing competition.", + path = "/api/v1/competition", + methods = [HttpMethod.PATCH], + requestBody = OpenApiRequestBody([OpenApiContent(ApiCompetitionDescription::class)]), + tags = ["Competition"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): SuccessStatus { + val apiValue = try { + ctx.bodyAsClass(ApiCompetitionDescription::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + /* Store change. */ + this.store.transactional { + val existing = this.competitionById(apiValue.id, ctx) + + /* Update core information. */ + existing.name = apiValue.name + existing.description = apiValue.description + + /* Update task type information. */ + val taskTypes = apiValue.taskTypes.map { it.name }.toTypedArray() + existing.taskTypes.removeAll(TaskType.query(TaskType::competition eq existing and not(TaskType::name.containsIn(*taskTypes)))) + for (type in apiValue.taskTypes) { + val t = TaskType.findOrNew { + (TaskType::name eq type.name) and (TaskType::competition eq existing) + } + t.name = type.name + t.duration = type.duration + t.score = type.scoreOption.option + t.targets.clear() + t.targets.addAll(type.targetOptions.map { it.option }) + t.hints.clear() + t.hints.addAll(type.hintOptions.map { it.option }) + t.submission.clear() + t.submission.addAll(type.submissionOptions.map { it.option }) + t.options.clear() + t.options.addAll(type.taskOptions.map { it.option }) + t.configurations.clear() + t.configurations.addAll(type.configuration.entries.map { + ConfiguredOption.new { + this.key = it.key + this.value = it.value + } + }) + } + + /* Update task group information. */ + val taskGroups = apiValue.taskGroups.map { it.name }.toTypedArray() + existing.taskGroups.removeAll(TaskGroup.query(TaskGroup::competition eq existing and not(TaskGroup::name.containsIn(*taskGroups)))) + for (group in apiValue.taskGroups) { + val g = TaskGroup.findOrNew { + (TaskGroup::name eq type.name) and (TaskGroup::competition eq existing) + } + g.name = group.name + g.type = TaskType.query((TaskType::name eq group.name) and (TaskGroup::competition eq existing)).first() + + /* Update task information. */ + val taskIds = group.tasks.mapNotNull { it.id }.toTypedArray() + g.tasks.removeAll(TaskDescription.query(TaskDescription::taskGroup eq g and not(TaskDescription::id.containsIn(*taskIds)))) + for (task in group.tasks) { + val t = if (task.id != null) { + g.tasks.filter { it.id eq task.id }.first() + } else { + val desc = TaskDescription.new { this.id = UUID.randomUUID().toString() } + g.tasks.add(desc) + desc + } + t.name = task.name + t.duration = task.duration + + /* Update task targets. */ + t.targets.clear() + for (target in task.targets) { + val item = MediaItem.query(MediaItem::id eq target.target).first() + t.targets.add(TaskDescriptionTarget.new { + this.item = item + this.type = target.type.type + this.temporalRangeStart = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() + this.temporalRangeEnd = target.range?.end?.toTemporalPoint(item.fps?: 0.0f)?.toMilliseconds() + }) + } + + /* Update task hints. */ + t.hints.clear() + for (hint in task.hints) { + val item = MediaItem.query(MediaItem::id eq hint.mediaItem).firstOrNull() + t.hints.add(Hint.new { + this.type = hint.type.type + this.item = item + this.text = hint.description + this.path = hint.path + this.start = hint.start + this.end = hint.end + this.temporalRangeStart = hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + this.temporalRangeEnd = hint.range?.end?.toTemporalPoint(item?.fps?: 0.0f)?.toMilliseconds() + }) + } + } + } + + /* Update team information. */ + val teamIds = apiValue.teams.map { it.teamId }.toTypedArray() + existing.teams.removeAll(Team.query(Team::competition eq existing and not(Team::id.containsIn(*teamIds)))) + for (team in apiValue.teams) { + val t = Team.findOrNew { + (Team::name eq team.name) and (Team::competition eq existing) + } + t.name = team.name + t.color = team.color + t.logoId = team.logoId + t.users.clear() + t.users.addAll(User.query(User::id.containsIn(*team.users.map { it.id }.toTypedArray()))) + } + + /* Update teamGroup information */ + val teamGroupIds = apiValue.teamGroups.map { it.id }.toTypedArray() + existing.teamsGroups.removeAll(TeamGroup.query(TeamGroup::competition eq existing and not(TeamGroup::id.containsIn(*teamGroupIds)))) + for (teamGroup in apiValue.teamGroups) { + val t = TeamGroup.findOrNew { + (Team::name eq teamGroup.name) and (Team::competition eq existing) + } + t.name = teamGroup.name + t.teams.clear() + t.teams.addAll(Team.query(Team::id.containsIn(*teamGroup.teams.map { it.teamId }.toTypedArray()))) + } + + /* Update judge information */ + existing.judges.clear() + existing.judges.addAll(User.query(User::id.containsIn(*apiValue.judges.toTypedArray()))) + } + return SuccessStatus("Competition with ID ${apiValue.id} was updated successfully.") + } +} + + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index 09245592c..a3db59959 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -1,10 +1,7 @@ package dev.dres.api.rest.handler.preview -import dev.dres.api.rest.handler.AbstractPreviewHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index cbc8bcb45..47e33201a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Password import dev.dres.data.model.audit.AuditLogSource import dev.dres.mgmt.admin.UserManager @@ -16,12 +17,12 @@ import io.javalin.http.Context import io.javalin.openapi.* /** - * A [GetRestHandler] that handles user-requests to login. + * A [PostRestHandler] that handles user-requests to login. * * @version 1.0.0 * @author Luca Rossetto */ -class LoginHandler : RestHandler, PostRestHandler { +class LoginHandler : RestHandler, PostRestHandler { override val apiVersion = "v1" @@ -33,12 +34,12 @@ class LoginHandler : RestHandler, PostRestHandler { tags = ["User"], requestBody = OpenApiRequestBody([OpenApiContent(LoginRequest::class)]), responses = [ - OpenApiResponse("200", [OpenApiContent(UserDetails::class)]), + OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doPost(ctx: Context) : UserDetails{ + override fun doPost(ctx: Context): ApiUser { val loginRequest = try { ctx.bodyAsClass(LoginRequest::class.java) @@ -55,7 +56,9 @@ class LoginHandler : RestHandler, PostRestHandler { AccessManager.registerUserForSession(ctx.sessionId(), user) AuditLogger.login(user.userId, AuditLogSource.REST, ctx.sessionId()) - return UserDetails.create(UserManager.get(username)!!, ctx) + val ret = UserManager.get(username)!!.toApi() + ret.sessionId = ctx.sessionId() + return ret } override val route = "login" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt index 4f54fbc31..061a29675 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt @@ -2,7 +2,6 @@ package dev.dres.api.rest.types import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.AccessManager -import dev.dres.data.model.UID import dev.dres.mgmt.admin.UserManager import io.javalin.websocket.WsContext import org.eclipse.jetty.server.session.Session @@ -12,7 +11,7 @@ import java.nio.ByteBuffer * Wraps a [WsContext] and gives access to specific information regarding the user owning that [WsContext] * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.1 */ @JvmInline value class WebSocketConnection(val context: WsContext) { @@ -31,7 +30,7 @@ value class WebSocketConnection(val context: WsContext) { /** Name of the user that generated this [WebSocketConnection]. */ val userName - get() = UserManager.get(AccessManager.userIdForSession(this.httpSessionId) ?: UID.EMPTY)?.username?.name ?: "UNKNOWN" + get() = UserManager.get(AccessManager.userIdForSession(this.httpSessionId))?.username ?: "UNKNOWN" /** IP address of the client. */ val host: String @@ -45,7 +44,24 @@ value class WebSocketConnection(val context: WsContext) { } + /** + * Sends an object through this [WebSocketConnection]. The object is serialized before sending. + * + * @param message The message to send. + */ fun send(message: Any) = this.context.send(jsonMapper.writeValueAsString(message)) + + /** + * Sends a plain [String] message through this [WebSocketConnection]. + * + * @param message The [String] to send. + */ fun send(message: String) = this.context.send(message) + + /** + * Sends a plain [ByteBuffer] message through this [WebSocketConnection]. + * + * @param message The [ByteBuffer] to send. + */ fun send(message: ByteBuffer) = this.context.send(message) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt index a9a6d417a..f0d0b4252 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -7,9 +7,9 @@ import dev.dres.data.model.media.MediaType * * @see ApiMediaItem * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ -enum class ApiMediaType(val mediaType: MediaType) { +enum class ApiMediaType(val type: MediaType) { IMAGE(MediaType.IMAGE), VIDEO(MediaType.VIDEO) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalPoint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalPoint.kt new file mode 100644 index 000000000..9dd6aea19 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalPoint.kt @@ -0,0 +1,27 @@ +package dev.dres.api.rest.types.collection.time + +import dev.dres.data.model.media.time.TemporalPoint + +/** + * RESTful API representation of a [TemporalPoint]. + * + * @version 1.0.0 + * @author Ralph Gasser + */ +data class ApiTemporalPoint(val value: String, val unit: ApiTemporalUnit) { + + companion object{ + fun fromTemporalPoint(temporalPoint: TemporalPoint): ApiTemporalPoint = when(temporalPoint){ + is TemporalPoint.Frame -> ApiTemporalPoint(temporalPoint.frame.toString(), ApiTemporalUnit.FRAME_NUMBER) + is TemporalPoint.Millisecond -> ApiTemporalPoint(temporalPoint.millisecond.toString(), ApiTemporalUnit.MILLISECONDS) + is TemporalPoint.Timecode -> ApiTemporalPoint(temporalPoint.toMilliseconds().toString(), ApiTemporalUnit.MILLISECONDS) + } + } + + fun toTemporalPoint(fps: Float): TemporalPoint = when(this.unit){ + ApiTemporalUnit.FRAME_NUMBER -> TemporalPoint.Frame(value.toDouble().toInt(), fps) + ApiTemporalUnit.SECONDS -> TemporalPoint.Millisecond((value.toDouble() * 1000).toLong()) + ApiTemporalUnit.MILLISECONDS -> TemporalPoint.Millisecond(value.toDouble().toLong()) + ApiTemporalUnit.TIMECODE -> TemporalPoint.Timecode(value, fps) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalRange.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalRange.kt new file mode 100644 index 000000000..cf6c4e35d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalRange.kt @@ -0,0 +1,14 @@ +package dev.dres.api.rest.types.collection.time + +import dev.dres.data.model.media.time.TemporalRange + +/** + * RESTful API representation of a [TemporalRange]. + * + * @version 1.0.0 + * @author Ralph Gasser + */ +data class ApiTemporalRange(val start: ApiTemporalPoint, val end: ApiTemporalPoint) { + constructor(range: TemporalRange) : this(ApiTemporalPoint.fromTemporalPoint(range.start), ApiTemporalPoint.fromTemporalPoint(range.end)) + fun toTemporalRange(fps: Float): TemporalRange = TemporalRange(start.toTemporalPoint(fps), end.toTemporalPoint(fps)) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalUnit.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalUnit.kt new file mode 100644 index 000000000..7266c2a96 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/time/ApiTemporalUnit.kt @@ -0,0 +1,13 @@ +package dev.dres.api.rest.types.collection.time + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +enum class ApiTemporalUnit { + FRAME_NUMBER, + SECONDS, + MILLISECONDS, + TIMECODE +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt new file mode 100644 index 000000000..5f6ec598a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt @@ -0,0 +1,25 @@ +package dev.dres.api.rest.types.competition + +import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup +import dev.dres.api.rest.types.competition.tasks.ApiTaskType +import dev.dres.api.rest.types.competition.team.ApiTeam +import dev.dres.api.rest.types.competition.team.ApiTeamGroup +import dev.dres.data.model.competition.CompetitionDescription +/** + * The RESTful API equivalent for [CompetitionDescription]. + * + * @see CompetitionDescription + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +data class ApiCompetitionDescription( + val id: String, + val name: String, + val description: String?, + val taskTypes: List, + val taskGroups: List, + val teams: List, + val teamGroups: List, + val judges: List +) + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt new file mode 100644 index 000000000..f3528704f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.competition + +import dev.dres.data.model.competition.CompetitionDescription + +/** + * An overview over a [CompetitionDescription]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiCompetitionOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionCreateMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateCompetition.kt similarity index 73% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionCreateMessage.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateCompetition.kt index 73da71dad..c37a6362c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionCreateMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateCompetition.kt @@ -6,4 +6,4 @@ package dev.dres.api.rest.types.competition * @author Ralph Gasser * @version 1.0 */ -data class CompetitionCreateMessage(val name: String, val description: String) \ No newline at end of file +data class ApiCreateCompetition(val name: String, val description: String) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt deleted file mode 100644 index fd15b4808..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiTeam.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.api.rest.types.users.ApiUser - -/** - * A RESTful API representation of a [Team] - * - * @author Loris Sauter - * @version 1.0.0 - */ -data class ApiTeam(val name: String, val color: String, val logoId: String, val users: List = emptyList()) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt deleted file mode 100644 index 968ea4ca8..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestCompetitionDescription.kt +++ /dev/null @@ -1,75 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.data.dbo.DAO -import dev.dres.data.model.Config -import dev.dres.data.model.admin.UserId -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskGroup -import dev.dres.data.model.competition.task.TaskType -import dev.dres.utilities.extensions.UID - -/** - * The RESTful API equivalent for [CompetitionDescription]. - * - * @see CompetitionDescription - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.1 - * - */ -data class RestCompetitionDescription( - val id: String, - val name: String, - val description: String?, - val taskTypes: List, - val taskGroups: List, - val tasks: List, - val teams: List, - val teamGroups: List, - val judges: List -) { - - - companion object { - /** - * Generates a [RestCompetitionDescription] from a [CompetitionDescription] and returns it. - * - * @param competition The [CompetitionDescription] to convert. - */ - fun fromCompetition(competition: CompetitionDescription) = RestCompetitionDescription( - competition.id.string, - competition.name, - competition.description, - competition.taskTypes, - competition.taskGroups, - competition.tasks.map { RestTaskDescription.fromTask(it) }, - competition.teams.map { RestTeam(it) }, - competition.teamGroups.map { RestTeamGroup(it) }, - competition.judges.map { it.string } - ) - } - - /** - * Converts this [RestCompetitionDescription] to the corresponding [CompetitionDescription] and returns it. - * - * @param config The global [Config] object used during conversion. - * @param mediaItems [DAO] used to perform media item lookups. - */ - fun toCompetitionDescription(config: Config, mediaItems: DAO) : CompetitionDescription { - - val teams = this.teams.map { it.toTeam(config) }.toMutableList() - - return CompetitionDescription( - this.id.UID(), - this.name, - this.description, - this.taskTypes.toMutableList(), - this.taskGroups.toMutableList(), - this.tasks.map { it.toTaskDescription(this.taskGroups, this.taskTypes, mediaItems) }.toMutableList(), - teams, - this.teamGroups.map { it.toTeamGroup(teams) }.toMutableList(), - this.judges.map { UserId(it) }.toMutableList() - ) - } -} - diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt deleted file mode 100644 index ed506d480..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTaskDescription.kt +++ /dev/null @@ -1,72 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.api.rest.types.competition.tasks.RestTaskDescriptionComponent -import dev.dres.api.rest.types.competition.tasks.ApiTarget -import dev.dres.data.dbo.DAO -import dev.dres.data.model.UID -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.competition.* -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskGroup -import dev.dres.data.model.competition.task.TaskType -import dev.dres.utilities.extensions.UID - -/** - * The RESTful API equivalent for [TaskDescription]. - * - * @see TaskDescription - * @author Luca Rossetto & Ralph Gasser - * @version 1.0 - */ -class RestTaskDescription( - val id: String = UID().string, - val name: String, - val taskGroup: String, - val taskType: String, - val duration: Long, - val mediaCollectionId: String, - val target: ApiTarget, - val components: List - -) { - - companion object { - /** - * Generates a [RestTaskDescription] from a [TaskDescription] and returns it. - * - * @param task The [TaskDescription] to convert. - */ - fun fromTask(task: TaskDescription) = RestTaskDescription( - task.id.string, - task.name, - task.taskGroup.name, - task.taskType.name, - task.duration, - task.mediaCollectionId.string, - ApiTarget.fromTarget(task.target), - task.hints.map { - RestTaskDescriptionComponent.fromComponent(it) - } - ) - } - - - - /** - * Converts this [RestTaskDescription] to the corresponding [TaskDescription] and returns it. - * - * @param taskGroups List of [TaskGroup] used to perform lookups. - * @param taskTypes List of [TaskType] used to perform lookups. - * @param mediaItems [DAO] used to perform media item lookups. - */ - fun toTaskDescription(taskGroups: List, taskTypes: List, mediaItems: DAO) = TaskDescription( - this.id.UID(), - this.name, - taskGroups.find { it.name == this.taskGroup }!!, - taskTypes.find { it.name == this.taskType }!!, - this.duration, - this.mediaCollectionId.UID(), - this.target.toTarget(mediaItems), - this.components.map { it.toComponent(mediaItems) } - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt deleted file mode 100644 index 4309062d7..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeam.kt +++ /dev/null @@ -1,90 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.competition.team.Team -import dev.dres.utilities.extensions.UID -import java.awt.image.BufferedImage -import java.io.ByteArrayInputStream -import java.nio.file.Files -import java.util.* -import javax.imageio.ImageIO - - -data class RestTeam(val uid: String? = null, - val name: String, - val color: String, - val logoData: String?, - val logoId: String?, - val users: List) { - - constructor(team: Team) : this( - uid = team.uid.string, - name = team.name, - color = team.color, - logoData = null, - logoId = team.logoId.string, - users = team.users.map { it.string } - ) - - - companion object { - /** - * Stores the given image data to disk. - * - * @param config The [Config] object with global configuration. - * @param data The Base64 encoded image data. - * @param logoId The [UID] of the logo to store. - * - * @return The UID of the image. - */ - fun storeLogo(config: Config, data: String, logoId: UID = UID()): UID { - /* Parse image data. */ - val base64Image: String = data.substringAfter(",") - val imageBytes = Base64.getDecoder().decode(base64Image) - val image = ByteArrayInputStream(imageBytes).use { - val original = ImageIO.read(it) - if (original.width <= config.logoMaxSize && original.height <= config.logoMaxSize) { - original - } else { - val target = if (original.width > original.height) { - Pair(config.logoMaxSize, (original.height * (config.logoMaxSize.toDouble() / original.width)).toInt()) - } else { - Pair((original.width * (config.logoMaxSize.toDouble() / original.height)).toInt(), config.logoMaxSize) - } - val resizedImage = BufferedImage(target.first, target.second, BufferedImage.TYPE_INT_ARGB) - val graphics2D = resizedImage.createGraphics() - graphics2D.drawImage(original, 0, 0, target.first, target.second, null) - graphics2D.dispose() - resizedImage - } - } - - /* Generate UID and prepare file path. */ - val path = Team.logoPath(config, logoId) - if (!Files.exists(path.parent)) { - Files.createDirectories(path.parent) - } - - /* Generate UID and write image to disk. */ - Files.newOutputStream(path).use { - ImageIO.write(image, "PNG", it) - } - return logoId - } - } - - /** - * Converts this [RestTeam] to a [Team] object. - * - * @param config The [Config] object with global configuration. - * @return [Team] - */ - fun toTeam(config: Config): Team { - val logoId = this.logoId?.UID() ?: UID() - if (this.logoData != null) { - storeLogo(config, this.logoData, logoId) - } - return Team(this.uid?.UID() ?: UID(), this.name, this.color, logoId, this.users.map { it.UID() }.toMutableList()) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt deleted file mode 100644 index a09c906d1..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/RestTeamGroup.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.TeamGroup -import dev.dres.data.model.competition.TeamGroupAggregation -import dev.dres.utilities.extensions.UID - -data class RestTeamGroup( - val uid: String? = null, - val name: String, - val teams: List, - val aggregation: String -) { - fun toTeamGroup(teams: MutableList): TeamGroup = TeamGroup( - this.uid!!.UID(), - this.name, - teams.filter { it.uid.string in this.teams }, - TeamGroupAggregation.valueOf(this.aggregation) - ) - - constructor(teamGroup: TeamGroup) : this(teamGroup.uid.string, teamGroup.name, teamGroup.teams.map { it.uid.string }, teamGroup.aggregation.name) - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt new file mode 100644 index 000000000..53246504b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt @@ -0,0 +1,77 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.api.rest.types.collection.time.ApiTemporalRange +import dev.dres.data.model.competition.* + +/** + * The RESTful API equivalent for [TaskDescriptionHint]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +data class ApiHint( + /** + * The type of this component + */ + val type: ApiHintType, + + /** + * Start time in seconds of when this component is active (Task time) + * Must be a positive integer, including zero (0) (indicates the component is available from the start) + */ + val start: Long? = null, + + /** + * End time in seconds of when this component is not active anymore (Task time) + * Must be a positive integer, greater than [start] + */ + val end: Long? = null, + + /** + * In case [type] is [TaskType.QueryComponentType.TEXT] + * + * This is the actual description + */ + val description: String? = null, + + /** + * In case [type] is + * + * - [TaskType.QueryComponentType.EXTERNAL_IMAGE] + * - [TaskType.QueryComponentType.EXTERNAL_VIDEO] + * + * This is the path to the data. + */ + val path: String? = null, + + /** + * In case [type] is + * + * - [TaskType.QueryComponentType.EXTERNAL_IMAGE] + * - [TaskType.QueryComponentType.EXTERNAL_VIDEO] + * + * This is the data type of the payload. + * TODO: Is there a standard to use here? Could be URI or base64 actual data... + */ + val dataType: String? = null, + + /** + * In case [type] is + * + * - [TaskType.QueryComponentType.IMAGE_ITEM] + * - [TaskType.QueryComponentType.VIDEO_ITEM_SEGMENT] + * + * This is the reference to the media item + */ + val mediaItem: String? = null, + + /** + * In case [type] is + * + * - [TaskType.QueryComponentType.VIDEO_ITEM_SEGMENT] + * - [TaskType.QueryComponentType.EXTERNAL_VIDEO] TBD In case of blob this wouldn't be necessary + * + * This is the actual temporal range in video time + */ + val range: ApiTemporalRange? = null +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintContent.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintContent.kt new file mode 100644 index 000000000..5f1f087fe --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintContent.kt @@ -0,0 +1,16 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.api.rest.types.task.ApiContentElement + +/** + * Describes a [ApiHintContent] that should be displayed the user as hint for a task run. This is a representation + * used in the RESTful API to transfer information regarding the query. Always derived from a [TaskDescription]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0 + * + * @param taskId of the [TaskDescription] this [ApiHintContent] was derived from. + * @param sequence Sequence of [ApiContentElement]s to display. + * @param loop Specifies if last [ApiContentElement] should be displayed until the end or if the entire sequence should be looped. + */ +data class ApiHintContent(val taskId: String, val sequence: List = emptyList(), val loop: Boolean = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt new file mode 100644 index 000000000..a55ea2713 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt @@ -0,0 +1,16 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.data.model.competition.task.HintType + +/** + * The RESTful API equivalent for [HintType]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiHintType(val type: HintType) { + EMPTY(HintType.EMPTY), + TEXT(HintType.TEXT), + VIDEO(HintType.VIDEO), + IMAGE(HintType.IMAGE) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt index 0b7eda007..eaf021eff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt @@ -1,8 +1,7 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.media.MediaItem +import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.data.model.competition.task.TaskDescriptionTarget -import dev.dres.utilities.extensions.UID /** * The RESTful API equivalent for [TaskDescriptionTarget]. @@ -10,36 +9,4 @@ import dev.dres.utilities.extensions.UID * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -data class ApiTarget(val type: ApiTargetType, val items: List = emptyList()) { - - /** - * Converts this [ApiTarget] to the corresponding [TaskDescriptionTarget] and returns it. - * - * @param mediaItems [DAO] used to perform media item lookups. - */ - fun toTarget(mediaItems: DAO) = when(this.type){ - TargetOption.SINGLE_MEDIA_SEGMENT -> { - val item = mediaItems[this.items.first().mediaItem.UID()]!! as MediaItem.VideoItem - TaskDescriptionTarget.VideoSegmentTarget(item, this.items.first().temporalRange!!.toTemporalRange(item.fps)) - } - TargetOption.JUDGEMENT -> TaskDescriptionTarget.JudgementTaskDescriptionTarget( - this.items.map { - val item = mediaItems[it.mediaItem.UID()]!! - val fps = if (item is MediaItem.VideoItem) item.fps else 0f - item to it.temporalRange!!.toTemporalRange(fps) - } - ) - TargetOption.SINGLE_MEDIA_ITEM -> TaskDescriptionTarget.MediaItemTarget(mediaItems[this.items.first().mediaItem.UID()]!!) - TargetOption.MULTIPLE_MEDIA_ITEMS -> TaskDescriptionTarget.MultipleMediaItemTarget(this.items.map { mediaItems[it.mediaItem.UID()]!! }) - TargetOption.VOTE -> TaskDescriptionTarget.VoteTaskDescriptionTarget( - this.items.map { - val item = mediaItems[it.mediaItem.UID()]!! - val fps = if (item is MediaItem.VideoItem) item.fps else 0f - item to it.temporalRange!!.toTemporalRange(fps) - } - ) - TargetOption.TEXT -> TaskDescriptionTarget.TextTaskDescriptionTarget( - this.items.map { it.mediaItem } //TODO maybe should be renamed from 'mediaItem' to something else - ) - } -} \ No newline at end of file +data class ApiTarget(val type: ApiTargetType, val target: String? = null, val range: ApiTemporalRange? = null) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetContent.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetContent.kt new file mode 100644 index 000000000..55393b1c2 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetContent.kt @@ -0,0 +1,15 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.api.rest.types.task.ApiContentElement + +/** + * Describes a [ApiTargetContent] that should be displayed the user after the task run has finished. This is a representation + * used in the RESTful API to transfer information regarding the query. Always derived from a [TaskDescription]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0 + * + * @param taskId of the [TaskDescription] this [ApiTargetContent] was derived from. + * @param sequence Sequence of [ApiContentElement]s to display. + */ +data class ApiTargetContent(val taskId: String, val sequence: List = emptyList()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt index 8b32b5e08..e2886f619 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt @@ -8,7 +8,7 @@ import dev.dres.data.model.competition.task.TargetType * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -enum class ApiTargetType(val targetType: TargetType) { +enum class ApiTargetType(val type: TargetType) { JUDGEMENT(TargetType.JUDGEMENT), JUDGEMENT_WITH_VOTE(TargetType.JUDGEMENT_WITH_VOTE), MEDIA_ITEM(TargetType.MEDIA_ITEM), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt new file mode 100644 index 000000000..1c8c9470d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt @@ -0,0 +1,23 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskDescriptionId +import dev.dres.data.model.media.CollectionId + +/** + * The RESTful API equivalent for [TaskDescription]. + * + * @see TaskDescription + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class ApiTaskDescription( + val id: TaskDescriptionId? = null, + val name: String, + val taskGroup: String, + val taskType: String, + val duration: Long, + val collectionId: CollectionId, + val targets: List, + val hints: List +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt new file mode 100644 index 000000000..69ca4be97 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.data.model.competition.task.TaskGroup + +/** + * The RESTful API equivalent of a [TaskGroup]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiTaskGroup(val name: String, val type: String, val tasks: List) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt new file mode 100644 index 000000000..25da79d9e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt @@ -0,0 +1,21 @@ +package dev.dres.api.rest.types.competition.tasks + +import dev.dres.api.rest.types.competition.tasks.options.* +import dev.dres.data.model.competition.task.TaskType + +/** + * The RESTful API equivalent of a [TaskType]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiTaskType( + val name: String, + val duration: Long, + val targetOptions: List, + val hintOptions: List, + val submissionOptions: List, + val taskOptions: List, + val scoreOption: ApiScoreOption, + val configuration: Map +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt deleted file mode 100644 index 5fca9e419..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTaskDescriptionComponent.kt +++ /dev/null @@ -1,114 +0,0 @@ -package dev.dres.api.rest.types.competition.tasks - -import dev.dres.data.dbo.DAO -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.competition.* -import dev.dres.data.model.competition.options.QueryComponentOption -import dev.dres.data.model.competition.task.TaskDescriptionHint -import dev.dres.utilities.extensions.UID -import java.nio.file.Paths - -/** - * The RESTful API equivalent for [TaskDescriptionHint]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0 - */ -data class RestTaskDescriptionComponent( - /** - * The type of this component - */ - val type: QueryComponentOption, - - /** - * Start time in seconds of when this component is active (Task time) - * Must be a positive integer, including zero (0) (indicates the component is available from the start) - */ - val start: Long? = null, - - /** - * End time in seconds of when this component is not active anymore (Task time) - * Must be a positive integer, greater than [start] - */ - val end: Long? = null, - - /** - * In case [type] is [TaskType.QueryComponentType.TEXT] - * - * This is the actual description - */ - val description: String? = null, - - /** - * In case [type] is - * - * - [TaskType.QueryComponentType.EXTERNAL_IMAGE] - * - [TaskType.QueryComponentType.EXTERNAL_VIDEO] - * - * This is the path to the data. - */ - val path: String? = null, - - /** - * In case [type] is - * - * - [TaskType.QueryComponentType.EXTERNAL_IMAGE] - * - [TaskType.QueryComponentType.EXTERNAL_VIDEO] - * - * This is the data type of the payload. - * TODO: Is there a standard to use here? Could be URI or base64 actual data... - */ - val dataType: String? = null, - - /** - * In case [type] is - * - * - [TaskType.QueryComponentType.IMAGE_ITEM] - * - [TaskType.QueryComponentType.VIDEO_ITEM_SEGMENT] - * - * This is the reference to the media item - */ - val mediaItem: String? = null, - - /** - * In case [type] is - * - * - [TaskType.QueryComponentType.VIDEO_ITEM_SEGMENT] - * - [TaskType.QueryComponentType.EXTERNAL_VIDEO] TBD In case of blob this wouldn't be necessary - * - * This is the actual temporal range in video time - */ - val range: RestTemporalRange? = null -) { - - companion object { - /** - * Generates a [RestTaskDescriptionComponent] from a [TaskDescriptionHint] and returns it. - * - * @param hint The [TaskDescriptionHint] to convert. - */ - fun fromComponent(hint: TaskDescriptionHint) = when(hint) { - is TaskDescriptionHint.TextTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.TEXT, start = hint.start, end = hint.end, description = hint.text) - is TaskDescriptionHint.ImageItemTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.IMAGE_ITEM, start = hint.start, end = hint.end, mediaItem = hint.item.id.string) - is TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.VIDEO_ITEM_SEGMENT, start = hint.start, end = hint.end, mediaItem = hint.item.id.string, range = RestTemporalRange(hint.temporalRange)) - is TaskDescriptionHint.ExternalImageTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.EXTERNAL_IMAGE, path = hint.imageLocation.toString(), start = hint.start, end = hint.end) - is TaskDescriptionHint.ExternalVideoTaskDescriptionHint -> RestTaskDescriptionComponent(type = QueryComponentOption.EXTERNAL_VIDEO, path = hint.videoLocation.toString(), start = hint.start, end = hint.end) - } - } - - /** - * Converts this [RestTaskDescriptionComponent] to the corresponding [TaskDescriptionHint] and returns it. - * - * @param mediaItems [DAO] used to perform media item lookups. - */ - fun toComponent(mediaItems: DAO) = when(this.type){ - QueryComponentOption.IMAGE_ITEM -> TaskDescriptionHint.ImageItemTaskDescriptionHint(mediaItems[this.mediaItem!!.UID()] as MediaItem.ImageItem, this.start, this.end) - QueryComponentOption.VIDEO_ITEM_SEGMENT -> { - val item = mediaItems[this.mediaItem!!.UID()] as MediaItem.VideoItem - TaskDescriptionHint.VideoItemSegmentTaskDescriptionHint(item, this.range!!.toTemporalRange(item.fps), this.start, this.end) - } - QueryComponentOption.TEXT -> TaskDescriptionHint.TextTaskDescriptionHint(this.description ?: "", this.start, this.end) - QueryComponentOption.EXTERNAL_IMAGE -> TaskDescriptionHint.ExternalImageTaskDescriptionHint(Paths.get(this.path ?: throw IllegalArgumentException("Field 'payload' is not specified but required for external image item.")), this.start, this.end) - QueryComponentOption.EXTERNAL_VIDEO -> TaskDescriptionHint.ExternalVideoTaskDescriptionHint(Paths.get(this.path ?: throw IllegalArgumentException("Field 'payload' is not specified but required for external video item.")), this.start, this.end) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt deleted file mode 100644 index 1f7c4d675..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/RestTemporalRange.kt +++ /dev/null @@ -1,35 +0,0 @@ -package dev.dres.api.rest.types.competition.tasks - -import dev.dres.data.model.basics.time.* -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange - -data class RestTemporalRange(val start: RestTemporalPoint, val end: RestTemporalPoint) { - constructor(range: TemporalRange) : this(RestTemporalPoint.fromTemporalPoint(range.start), RestTemporalPoint.fromTemporalPoint(range.end)) - fun toTemporalRange(fps: Float): TemporalRange = TemporalRange(start.toTemporalPoint(fps), end.toTemporalPoint(fps)) -} - -data class RestTemporalPoint(val value: String, val unit: RestTemporalUnit) { - - companion object{ - fun fromTemporalPoint(temporalPoint: TemporalPoint): RestTemporalPoint = when(temporalPoint){ - is TemporalPoint.Frame -> RestTemporalPoint(temporalPoint.frame.toString(), RestTemporalUnit.FRAME_NUMBER) - is TemporalPoint.Millisecond -> RestTemporalPoint(temporalPoint.millisecond.toString(), RestTemporalUnit.MILLISECONDS) - is TemporalPoint.Timecode -> RestTemporalPoint(temporalPoint.toMilliseconds().toString(), RestTemporalUnit.MILLISECONDS) - } - } - - fun toTemporalPoint(fps: Float): TemporalPoint = when(this.unit){ - RestTemporalUnit.FRAME_NUMBER -> TemporalPoint.Frame(value.toDouble().toInt(), fps) - RestTemporalUnit.SECONDS -> TemporalPoint.Millisecond((value.toDouble() * 1000).toLong()) - RestTemporalUnit.MILLISECONDS -> TemporalPoint.Millisecond(value.toDouble().toLong()) - RestTemporalUnit.TIMECODE -> TemporalPoint.Timecode(value, fps) - } -} - -enum class RestTemporalUnit { - FRAME_NUMBER, - SECONDS, - MILLISECONDS, - TIMECODE -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt new file mode 100644 index 000000000..37610dceb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt @@ -0,0 +1,17 @@ +package dev.dres.api.rest.types.competition.tasks.options + +import dev.dres.data.model.competition.task.options.HintOption + +/** + * A RESTful API representation of [HintOption]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiComponentOption(val option: HintOption) { + IMAGE_ITEM(HintOption.IMAGE_ITEM), + VIDEO_ITEM_SEGMENT(HintOption.VIDEO_ITEM_SEGMENT), + TEXT(HintOption.TEXT), + EXTERNAL_IMAGE(HintOption.EXTERNAL_IMAGE), + EXTERNAL_VIDEO(HintOption.EXTERNAL_VIDEO) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt new file mode 100644 index 000000000..8de044608 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt @@ -0,0 +1,14 @@ +package dev.dres.api.rest.types.competition.tasks.options + +import dev.dres.data.model.competition.task.options.ScoreOption + +/** + * A RESTful API representation of [ScoreOption]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiScoreOption(val option: ScoreOption) { + KIS(ScoreOption.KIS), + AVS(ScoreOption.AVS) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt new file mode 100644 index 000000000..9148dd9ae --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt @@ -0,0 +1,21 @@ +package dev.dres.api.rest.types.competition.tasks.options + +import dev.dres.data.model.competition.task.options.SubmissionOption + +/** + * A RESTful API representation of [SubmissionOption]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiSubmissionOption(val option: SubmissionOption) { + NO_DUPLICATES(SubmissionOption.NO_DUPLICATES), + LIMIT_CORRECT_PER_TEAM(SubmissionOption.LIMIT_CORRECT_PER_TEAM), + LIMIT_WRONG_PER_TEAM(SubmissionOption.LIMIT_WRONG_PER_TEAM), + LIMIT_TOTAL_PER_TEAM(SubmissionOption.LIMIT_TOTAL_PER_TEAM), + LIMIT_CORRECT_PER_MEMBER(SubmissionOption.LIMIT_CORRECT_PER_MEMBER), + TEMPORAL_SUBMISSION(SubmissionOption.TEMPORAL_SUBMISSION), + TEXTUAL_SUBMISSION(SubmissionOption.TEXTUAL_SUBMISSION), + ITEM_SUBMISSION(SubmissionOption.ITEM_SUBMISSION), + MINIMUM_TIME_GAP(SubmissionOption.MINIMUM_TIME_GAP) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt new file mode 100644 index 000000000..3e1d22494 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt @@ -0,0 +1,18 @@ +package dev.dres.api.rest.types.competition.tasks.options + +import dev.dres.data.model.competition.task.options.TargetOption + +/** + * A RESTful API representation of [TargetOption]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiTargetOption(val option: TargetOption){ + SINGLE_MEDIA_ITEM(TargetOption.SINGLE_MEDIA_ITEM), + SINGLE_MEDIA_SEGMENT(TargetOption.SINGLE_MEDIA_SEGMENT), + MULTIPLE_MEDIA_ITEMS(TargetOption.MULTIPLE_MEDIA_ITEMS), + JUDGEMENT(TargetOption.JUDGEMENT), + VOTE(TargetOption.VOTE), + TEXT(TargetOption.TEXT) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt new file mode 100644 index 000000000..63b5d6322 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt @@ -0,0 +1,15 @@ +package dev.dres.api.rest.types.competition.tasks.options + +import dev.dres.data.model.competition.task.options.TaskOption + +/** + * A RESTful API representation of [TaskOption]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiTaskOption(val option: TaskOption) { + HIDDEN_RESULTS(TaskOption.HIDDEN_RESULTS), + MAP_TO_SEGMENT(TaskOption.MAP_TO_SEGMENT), + PROLONG_ON_SUBMISSION(TaskOption.PROLONG_ON_SUBMISSION) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt new file mode 100644 index 000000000..8a552ce68 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt @@ -0,0 +1,20 @@ +package dev.dres.api.rest.types.competition.team + +import dev.dres.api.rest.types.users.ApiUser +import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.competition.team.TeamId + +/** + * A RESTful API representation of a [Team] + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class ApiTeam( + val teamId: TeamId, + val name: String, + val color: String, + val logoId: String, + val users: List = emptyList(), + var logoData: String? = null +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt new file mode 100644 index 000000000..4b5a9d91b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt @@ -0,0 +1,12 @@ +package dev.dres.api.rest.types.competition.team + +import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.competition.team.TeamGroupId + +/** + * A RESTful API representation of a [TeamGroup] + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class ApiTeamGroup(val id: TeamGroupId, val name: String, val teams: List, val aggregation: String) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt deleted file mode 100644 index 6c1c80c3a..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskHint.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.api.rest.types.task - -/** - * Describes a [TaskHint] that should be displayed the user as hint for a task run. This is a representation - * used in the RESTful API to transfer information regarding the query. Always derived from a [TaskDescription]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0 - * - * @param taskId of the [TaskDescription] this [TaskHint] was derived from. - * @param sequence Sequence of [ApiContentElement]s to display. - * @param loop Specifies if last [ApiContentElement] should be displayed until the end or if the entire sequence should be looped. - */ -data class TaskHint(val taskId: String, val sequence: List = emptyList(), val loop: Boolean = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt deleted file mode 100644 index 221e47df6..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/task/TaskTarget.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.api.rest.types.task - -/** - * Describes a [TaskTarget] that should be displayed the user after the task run has finished. This is a representation - * used in he RESTful API to transfer information regarding the query. Always derived from a [TaskDescription]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0 - * - * @param taskId of the [TaskDescription] this [TaskTarget] was derived from. - * @param sequence Sequence of [ApiContentElement]s to display. - */ -data class TaskTarget(val taskId: String, val sequence: List = emptyList()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt index 7d3ceac1b..8d3611292 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/UserRequest.kt @@ -1,7 +1,5 @@ package dev.dres.api.rest.types.users -import dev.dres.api.rest.types.users.ApiRole - /** * A request surrounding manipulation of users. * diff --git a/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt b/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt index d3281d2e5..de9640a46 100644 --- a/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt +++ b/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt @@ -1,7 +1,7 @@ package dev.dres.data.migration -import dev.dres.data.dbo.DataAccessLayer import dev.dres.data.model.Config +import jetbrains.exodus.database.TransientEntityStore /** * General interface implemented by data migration scripts. @@ -9,14 +9,14 @@ import dev.dres.data.model.Config * TODO: Extend * * @author Ralph Gasser - * @version 1.0 + * @version 1.1.0 */ interface Migration { /** * Performs data migration. * * @param config The global [Config] used for the data migration. - * @param data The [DataAccessLayer] used for data access. + * @param store The [TransientEntityStore] used for data access. */ - fun migrate(config: Config, data: DataAccessLayer) + fun migrate(config: Config, store: TransientEntityStore) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt index 7895c7996..2c95c6d2f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt @@ -1,14 +1,15 @@ package dev.dres.data.model.competition +import dev.dres.api.rest.types.competition.ApiCompetitionDescription import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User -import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaType import dev.dres.data.model.competition.task.TaskGroup import dev.dres.data.model.competition.task.TaskType import dev.dres.data.model.competition.team.Team import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.media.time.TemporalRange import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard @@ -54,7 +55,25 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ val teamsGroups by xdChildren0_N(TeamGroup::competition) /** The [User]s that act as judge for this [CompetitionDescription] */ - val judges by xdLink0_N(User::judges, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + val judges by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + + /** + * Converts this [CompetitionDescription] to a RESTful API representation [ApiCompetitionDescription]. + * + * This is a convenience method and it requires and active transaction context. + * + * @return [ApiCompetitionDescription] + */ + fun toApi(): ApiCompetitionDescription = ApiCompetitionDescription( + id = this.id, + name = this.name, + description = this.description, + taskTypes = this.taskTypes.asSequence().map { it.toApi() }.toList(), + taskGroups = this.taskGroups.asSequence().map { it.toApi() }.toList(), + teams = this.teams.asSequence().map { it.toApi() }.toList(), + teamGroups = this.teamsGroups.asSequence().map { it.toApi() }.toList(), + judges = this.judges.asSequence().map { it.id }.toList() + ) /** * Generates and returns the default [Scoreboard] implementations for this [CompetitionDescription]. @@ -66,7 +85,7 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ fun generateDefaultScoreboards(): List { val teams = this.teams.toList() val groupBoards = this.taskGroups.asSequence().map { group -> - MaxNormalizingScoreBoard(group.name, teams, {task -> task.taskGroup.id == group.id}, group.name) + MaxNormalizingScoreBoard(group.name, teams, {task -> task.taskGroup.name == group.name}, group.name) }.toList() val aggregateScoreBoard = SumAggregateScoreBoard("sum", groupBoards) return groupBoards.plus(aggregateScoreBoard) @@ -79,16 +98,19 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ * * @return [List] of [MediaItem]s */ - fun getAllVideos(): List { - return (this.taskGroups.flatMapDistinct { it.tasks } + fun getAllVideos(): List> { + val hints = this.taskGroups.flatMapDistinct { it.tasks } .flatMapDistinct { it.hints } - .filter { it.hintItem ne null } - .mapDistinct { it.hintItem } union - this.taskGroups.flatMapDistinct { it.tasks } + .filter { (it.item ne null) and (it.item!!.type eq MediaType.VIDEO) }.asSequence().map { + it.item!!to it.range!! + } + + val targets = this.taskGroups.flatMapDistinct { it.tasks } .flatMapDistinct { it.targets } - .filter { it.item ne null } - .mapDistinct { it.item }).filter { - it.type eq MediaType.VIDEO - }.toList() + .filter { (it.item ne null) and (it.item!!.type eq MediaType.VIDEO) }.asSequence().map { + it.item!! to it.range!! + } + + return (hints + targets).toList() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt index f784a76ec..09afc3069 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt @@ -1,8 +1,10 @@ package dev.dres.data.model.competition.task +import dev.dres.api.rest.types.collection.time.ApiTemporalRange +import dev.dres.api.rest.types.competition.tasks.ApiHint import dev.dres.api.rest.types.task.ApiContentElement +import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config -import dev.dres.data.model.PersistentEntity import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange @@ -40,49 +42,65 @@ class Hint(entity: Entity) : XdEntity(entity) { var task by xdParent(TaskDescription::hints) /** The[MediaItem] shown as part of the [Hint]. Can be null. */ - var hintItem by xdLink0_1(MediaItem) + var item by xdLink0_1(MediaItem) /** The target text. Can be null. */ - var hintText by xdStringProp() { requireIf { type == HintType.TEXT }} + var text by xdStringProp() { requireIf { type == HintType.TEXT }} /** The target text. Can be null. */ - var hintExternalLocation by xdStringProp() + var path by xdStringProp() /** The start of a (potential) range. */ - var temporalRangeStart by xdNullableIntProp { requireIf { type == HintType.VIDEO } } + var temporalRangeStart by xdNullableLongProp { requireIf { type == HintType.VIDEO } } /** The start of a (potential) range. */ - var temporalRangeEnd by xdNullableIntProp { requireIf { type == HintType.VIDEO } } + var temporalRangeEnd by xdNullableLongProp { requireIf { type == HintType.VIDEO } } /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ val range: TemporalRange? - get() { - return if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { - return TemporalRange(TemporalPoint.Frame(this.temporalRangeStart!!, this.hintItem?.fps ?: 1.0f), TemporalPoint.Frame(this.temporalRangeEnd!!, this.hintItem?.fps ?: 1.0f)) - } else { - null - } + get() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { + TemporalRange(TemporalPoint.Millisecond(this.temporalRangeStart!!), TemporalPoint.Millisecond(this.temporalRangeEnd!!)) + } else { + null } + + /** + * Converts this [Hint] to a RESTful API representation [ApiHint]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiHint] + */ + fun toApi(): ApiHint = ApiHint( + type = this.type.toApi(), + start = this.start, + end = this.end, + mediaItem = this.item?.id, + dataType = this.type.mimeType, + path = this.path, + range = this.range?.let { ApiTemporalRange(it) } + ) + /** * Generates and returns a textual description of this [Hint]. * * @return Text */ fun textDescription(): String = when (this.type) { - HintType.TEXT -> "\"${this.hintText}\" from ${this.start ?: "beginning"} to ${end ?: "end"}" + HintType.TEXT -> "\"${this.text}\" from ${this.start ?: "beginning"} to ${end ?: "end"}" HintType.VIDEO -> { - if (this.hintItem != null) { - "Image ${this.hintItem!!.name} from ${start ?: "beginning"} to ${end ?: "end"}" + if (this.item != null) { + "Image ${this.item!!.name} from ${start ?: "beginning"} to ${end ?: "end"}" } else { - "Image ${this.hintExternalLocation} from ${start ?: "beginning"} to ${end ?: "end"}" + "Image ${this.path} from ${start ?: "beginning"} to ${end ?: "end"}" } } HintType.IMAGE -> { - if (this.hintItem != null) { - "Image ${this.hintItem!!.name}" + if (this.item != null) { + "Image ${this.item!!.name}" } else { - "Image ${this.hintExternalLocation}" + "Image ${this.path}" } } HintType.EMPTY -> "Empty item" @@ -102,10 +120,10 @@ class Hint(entity: Entity) : XdEntity(entity) { val content = when (this.type) { HintType.IMAGE, HintType.VIDEO -> { - val path = if (this.hintItem != null) { - this.hintItem!!.pathToCachedItem(config, this.temporalRangeStart, this.temporalRangeEnd) - } else if (this.hintExternalLocation != null) { - Paths.get(this.hintExternalLocation!!) + val path = if (this.item != null) { + this.item!!.cachedItemName(config, this.temporalRangeStart, this.temporalRangeEnd) + } else if (this.path != null) { + Paths.get(this.path!!) } else { throw IllegalArgumentException("A hint of type ${this.type.description} must have a valid media item or external path.") } @@ -115,10 +133,19 @@ class Hint(entity: Entity) : XdEntity(entity) { Base64.getEncoder().encodeToString(data) } HintType.EMPTY -> "" - HintType.TEXT -> this.hintText ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") - else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") + HintType.TEXT -> this.text ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") + else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") } - return ApiContentElement(contentType = this.type.toApi(), content = content, offset = this.start ?: 0L) + + val contentType = when (this.type) { + HintType.IMAGE -> ApiContentType.IMAGE + HintType.VIDEO -> ApiContentType.VIDEO + HintType.TEXT -> ApiContentType.TEXT + HintType.EMPTY -> ApiContentType.EMPTY + else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") + } + + return ApiContentElement(contentType = contentType, content = content, offset = this.start ?: 0L) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt index ef0324903..6a0761173 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt @@ -1,7 +1,8 @@ package dev.dres.data.model.competition.task +import dev.dres.api.rest.types.competition.tasks.ApiHintType import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.competition.task.options.TaskScoreOption +import dev.dres.data.model.competition.task.options.ScoreOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -17,18 +18,18 @@ class HintType(entity: Entity) : XdEnumEntity(entity) { } - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var mimeType by xdRequiredStringProp(unique = true) private set - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var suffix by xdRequiredStringProp(unique = true) - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var base64 by xdBooleanProp() /** @@ -36,11 +37,6 @@ class HintType(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiContentType] equivalent to this [HintType]. */ - fun toApi() = when(this) { - EMPTY -> ApiContentType.EMPTY - TEXT -> ApiContentType.TEXT - VIDEO -> ApiContentType.VIDEO - IMAGE -> ApiContentType.IMAGE - else -> throw IllegalStateException("The content type ${this.description} is not supported.") - } + fun toApi(): ApiHintType + = ApiHintType.values().find { it.type == this } ?: throw IllegalStateException("Hint type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt index 6d917beda..607ba42ce 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt @@ -1,14 +1,14 @@ package dev.dres.data.model.competition.task -import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.competition.task.options.TaskScoreOption +import dev.dres.api.rest.types.competition.tasks.ApiTargetType +import dev.dres.data.model.competition.task.options.ScoreOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * + * The type of target for a */ class TargetType(entity: Entity): XdEnumEntity(entity) { companion object : XdEnumEntityType() { @@ -19,21 +19,15 @@ class TargetType(entity: Entity): XdEnumEntity(entity) { val TEXT by enumField { description = "EXTERNAL_IMAGE" } } - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [HintType] to the RESTful API representation [ApiContentType]. + * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. * - * @return [ApiContentType] equivalent to this [HintType]. + * @return [ApiTargetType] */ - fun toApi() = when(this) { - JUDGEMENT, - JUDGEMENT_WITH_VOTE -> ApiContentType.EMPTY - MEDIA_ITEM -> ApiContentType.IMAGE - MEDIA_ITEM_TEMPORAL_RANGE -> ApiContentType.VIDEO - TEXT -> ApiContentType.TEXT - else -> throw IllegalStateException("The target type ${this.description} is not supported.") - } + fun toApi(): ApiTargetType + = ApiTargetType.values().find { it.type == this } ?: throw IllegalStateException("Target type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt index f0b5d2df4..8d4ae4cf4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt @@ -1,9 +1,9 @@ package dev.dres.data.model.competition.task +import dev.dres.api.rest.types.competition.tasks.* +import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.api.rest.types.task.TaskHint -import dev.dres.api.rest.types.task.TaskTarget import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.media.MediaCollection @@ -35,7 +35,7 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact companion object: XdNaturalEntityType() /** The [TaskDescriptionId] of this [TaskDescription]. */ - var teamId: TaskDescriptionId + var taskId: TaskDescriptionId get() = this.id set(value) { this.id = value } @@ -74,18 +74,19 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact */ override fun newFilter(): SubmissionFilter = this.taskGroup.type.newFilter() + /** - * Generates and returns a [TaskHint] object to be used by the RESTful interface. + * Generates and returns a [ApiHintContent] object to be used by the RESTful interface. * * Requires a valid transaction. * * @param config The [Config] used of path resolution. - * @return [TaskHint] + * @return [ApiHintContent] * * @throws FileNotFoundException * @throws IOException */ - fun toTaskHint(config: Config): TaskHint { + fun toTaskHint(config: Config): ApiHintContent { val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> var index = 0 group.value.sortedBy { it.start ?: 0 }.flatMap { @@ -101,21 +102,21 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact ret } } - return TaskHint(this.id, sequence, false) + return ApiHintContent(this.id, sequence, false) } /** - * Generates and returns a [TaskTarget] object to be used by the RESTful interface. + * Generates and returns a [ApiTargetContent] object to be used by the RESTful interface. * * Requires a valid transaction. * * @param config The [Config] used of path resolution. - * @return [TaskTarget] + * @return [ApiTargetContent] * * @throws FileNotFoundException * @throws IOException */ - fun toTaskTarget(config: Config): TaskTarget { + fun toTaskTarget(config: Config): ApiTargetContent { var cummulativeOffset = 0L val sequence = this.targets.asSequence().flatMap { cummulativeOffset += Math.floorDiv(it.item!!.durationMs!!, 1000L) + 1L @@ -124,7 +125,7 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact ApiContentElement(ApiContentType.EMPTY, null, cummulativeOffset) ) }.toList() - return TaskTarget(this.id, sequence) + return ApiTargetContent(this.id, sequence) } /** @@ -132,7 +133,25 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact * * @return Textual description of this [TaskDescription]'s content, */ - fun textualDescription(): String = this.hints.asSequence().filter { it.type == HintType.TEXT }.maxByOrNull { it.start ?: 0 }?.hintText ?: name + fun textualDescription(): String = this.hints.asSequence().filter { it.type == HintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name + + /** + * Converts this [TaskDescription] to a RESTful API representation [ApiTaskDescription]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiTeam] + */ + fun toApi(): ApiTaskDescription = ApiTaskDescription( + this.id, + this.name, + this.taskGroup.name, + this.taskGroup.type.name, + this.duration, + this.collection.id, + this.targets.asSequence().map { it.toApi() }.toList(), + this.hints.asSequence().map { it.toApi() }.toList() + ) /** * Checks if no components of the same type overlap diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt index 9cedac25a..a6a32e4bf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt @@ -1,12 +1,10 @@ package dev.dres.data.model.competition.task +import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.api.rest.types.competition.tasks.ApiTarget -import dev.dres.api.rest.types.competition.tasks.ApiTargetItem -import dev.dres.api.rest.types.competition.tasks.RestTemporalRange import dev.dres.api.rest.types.task.ApiContentElement +import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.competition.team.Team import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaType import dev.dres.data.model.media.time.TemporalPoint @@ -42,21 +40,20 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { var text by xdStringProp() { requireIf { item == null }} /** The start of a (potential) range. */ - var temporalRangeStart by xdNullableIntProp { requireIf { item?.type == MediaType.VIDEO } } + var temporalRangeStart by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } /** The start of a (potential) range. */ - var temporalRangeEnd by xdNullableIntProp { requireIf { item?.type == MediaType.VIDEO } } + var temporalRangeEnd by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ val range: TemporalRange? - get() { - return if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { - return TemporalRange(TemporalPoint.Frame(this.temporalRangeStart!!, this.item!!.fps ?: 1.0f), TemporalPoint.Frame(this.temporalRangeEnd!!, this.item!!.fps ?: 1.0f)) - } else { - null - } + get() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { + TemporalRange(TemporalPoint.Millisecond(this.temporalRangeStart!!), TemporalPoint.Millisecond(this.temporalRangeEnd!!)) + } else { + null } + /** * Generates and returns a textual description of this [TaskDescriptionTarget]. * @@ -74,26 +71,14 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { /** * */ - fun toApi() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { - ApiTarget(this.type.toApi(), ApiTargetItem(this.item.id)) - } else { - ApiTarget(this.type.toApi(), ApiTargetItem(this.item.id)) + fun toApi(): ApiTarget = when(this.type) { + TargetType.JUDGEMENT, + TargetType.JUDGEMENT_WITH_VOTE -> ApiTarget(this.type.toApi(), null) + TargetType.MEDIA_ITEM, + TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget(this.type.toApi(), this.item?.id, this.range?.let { ApiTemporalRange(it) }) + TargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) + else -> throw IllegalStateException("Task description of type ${this.type.description} is not supported.") } - /** - * Generates a [ApiTarget] from a [TaskDescriptionTarget] and returns it. - * - * @param target The [TaskDescriptionTarget] to convert. - */ - fun fromTarget(target: TaskDescriptionTarget) = when(target.type) { - is TaskDescriptionTarget.JudgementTaskDescriptionTarget -> ApiTarget(TargetOption.JUDGEMENT, target.targets.map { ApiTargetItem(it.first.id.string, if (it.second == null) null else RestTemporalRange(it.second!!)) }) - is TaskDescriptionTarget.VideoSegmentTarget -> ApiTarget(TargetOption.SINGLE_MEDIA_SEGMENT, listOf( - ApiTargetItem(target.item.id.string, RestTemporalRange(target.temporalRange)) - )) - is TaskDescriptionTarget.MediaItemTarget -> ApiTarget(TargetOption.SINGLE_MEDIA_ITEM, listOf(ApiTargetItem(target.item.id.string))) - is TaskDescriptionTarget.MultipleMediaItemTarget -> ApiTarget(TargetOption.MULTIPLE_MEDIA_ITEMS, target.items.map { ApiTargetItem(it.id.string) }) - is TaskDescriptionTarget.VoteTaskDescriptionTarget -> ApiTarget(TargetOption.VOTE, target.targets.map { ApiTargetItem(it.first.id.string, if (it.second == null) null else RestTemporalRange(it.second!!)) }) - is TaskDescriptionTarget.TextTaskDescriptionTarget -> ApiTarget(TargetOption.TEXT, target.targets.map { ApiTargetItem(it, null) }) - } /** * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. @@ -105,20 +90,25 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { * @throws IOException */ fun toQueryContentElement(config: Config): ApiContentElement { - val content = when (this.type) { + val (content, type) = when (this.type) { TargetType.JUDGEMENT, - TargetType.JUDGEMENT_WITH_VOTE -> null + TargetType.JUDGEMENT_WITH_VOTE -> null to ApiContentType.EMPTY TargetType.MEDIA_ITEM -> { - val path = this.item?.pathToCachedItem(config, this.temporalRangeStart, this.temporalRangeEnd) + val type = when (this.item?.type) { + MediaType.VIDEO -> ApiContentType.VIDEO + MediaType.IMAGE -> ApiContentType.IMAGE + else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") + } + val path = this.item?.cachedItemName(config, this.temporalRangeStart, this.temporalRangeEnd) ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> stream.readAllBytes() } - Base64.getEncoder().encodeToString(data) + Base64.getEncoder().encodeToString(data) to type } - TargetType.TEXT -> this.text + TargetType.TEXT -> this.text to ApiContentType.TEXT else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") } - return ApiContentElement(contentType = this.type.toApi(), content = content, offset = 0L) + return ApiContentElement(contentType = type, content = content, offset = 0L) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt index 643b671ce..9ed576fa8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt @@ -1,10 +1,11 @@ package dev.dres.data.model.competition.task -import dev.dres.data.model.PersistentEntity +import dev.dres.api.rest.types.competition.tasks.ApiTargetType +import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.team.Team import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence /** * A [TaskGroup] allows the user to specify common traits among a group of [Task]s. @@ -31,4 +32,14 @@ class TaskGroup(entity: Entity) : XdEntity(entity) { /** The [TaskDescription]s contained in this [TaskGroup]*/ val tasks by xdChildren0_N(TaskDescription::taskGroup) + + /** + * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. + * + * This is a convenience method and it requires and active transaction context. + * + * @return [ApiTargetType] + */ + fun toApi(): ApiTaskGroup + = ApiTaskGroup(this.name,this.type.name,this.tasks.asSequence().map { it.toApi() }.toList()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt index 946584c49..54bd31539 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.competition.task -import dev.dres.data.model.PersistentEntity +import dev.dres.api.rest.types.competition.tasks.ApiTaskType import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.options.* import dev.dres.data.model.competition.task.options.ConfiguredOption @@ -11,10 +11,7 @@ import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.isEmpty -import kotlinx.dnq.query.query +import kotlinx.dnq.query.* import kotlinx.dnq.simple.min /** @@ -40,17 +37,17 @@ class TaskType(entity: Entity) : XdEntity(entity) { /** The (default) duration of this [TaskType] in seconds. */ var duration by xdRequiredLongProp() { min(0L) } - /** The [TaskTargetOption] for this [TaskType]. Specifies the type of target. */ - var target by xdLink1(TaskTargetOption) + /** The [TargetOption] for this [TaskType]. Specifies the type of target. */ + val targets by xdLink1_N(TargetOption) - /** The [TaskScoreOption] for this [TaskType]. Specifies the type of scorer that should be used. */ - var score by xdLink1(TaskScoreOption) + /** The [HintOption]s that make-up this [TaskType]. */ + val hints by xdLink0_N(HintOption) - /** The [TaskComponentOption]s that make-up this [TaskType]. */ - val components by xdLink0_N(TaskComponentOption) + /** The [SubmissionOption]s for this [TaskType]. */ + val submission by xdLink0_N(SubmissionOption) - /** The [TaskSubmissionOption]s for this [TaskType]. */ - val submission by xdLink0_N(TaskSubmissionOption) + /** The [ScoreOption] for this [TaskType]. Specifies the type of scorer that should be used. */ + var score by xdLink1(ScoreOption) /** The [TaskOption]s for this [TaskType]. */ val options by xdLink0_N(TaskOption) @@ -58,6 +55,22 @@ class TaskType(entity: Entity) : XdEntity(entity) { /** [ConfiguredOption]s registered for this [TaskDescription]. */ val configurations by xdChildren0_N(ConfiguredOption::task) + /** + * Converts this [TaskType] to a RESTful API representation [ApiTaskType]. + * + * @return [ApiTaskType] + */ + fun toApi(): ApiTaskType = ApiTaskType( + name = this.name, + duration = this.duration, + targetOptions = this.targets.asSequence().map { it.toApi() }.toList(), + hintOptions = this.hints.asSequence().map { it.toApi() }.toList(), + submissionOptions = this.submission.asSequence().map { it.toApi() }.toList(), + taskOptions = this.options.asSequence().map { it.toApi() }.toList(), + scoreOption = this.score.toApi(), + configuration = this.configurations.asSequence().map { it.key to it.value }.toMap() + ) + /** * Generates a new [TaskScorer] for this [TaskDescription]. Depending * on the implementation, the returned instance is a new instance or being re-use. diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt index 6e183d5a4..53af2140b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt @@ -16,10 +16,10 @@ class ConfiguredOption(entity: Entity) : XdEntity(entity) { companion object: XdNaturalEntityType() /** The key for this [ConfiguredOption]. Identifies the option. */ - val key by xdRequiredStringProp() + var key by xdRequiredStringProp() /** The conifgured value for this [ConfiguredOption]. */ - val value by xdRequiredStringProp() + var value by xdRequiredStringProp() /** The [TaskDescription] this [ConfiguredOption] belongs to. */ val task by xdParent(TaskType::configurations) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/HintOption.kt similarity index 67% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/competition/task/options/HintOption.kt index c5cc68adb..0d1ddfb39 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskComponentOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/HintOption.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.competition.task.options +import dev.dres.api.rest.types.competition.tasks.options.ApiComponentOption import dev.dres.data.model.competition.task.TaskDescription import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity @@ -12,8 +13,8 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class TaskComponentOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class HintOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val IMAGE_ITEM by enumField { description = "IMAGE_ITEM" } /** An image [MediaItem]. */ val VIDEO_ITEM_SEGMENT by enumField { description = "VIDEO_ITEM_SEGMENT" } /** Part of a video [MediaItem]. */ val TEXT by enumField { description = "TEXT" } /** A text snippet. */ @@ -21,7 +22,14 @@ class TaskComponentOption(entity: Entity) : XdEnumEntity(entity) { val EXTERNAL_VIDEO by enumField { description = "EXTERNAL_VIDEO" } /** An external video that is not part of a collection. */ } - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [HintOption] to a RESTful API representation [ApiComponentOption]. + * + * @return [ApiComponentOption] + */ + fun toApi() = ApiComponentOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ScoreOption.kt similarity index 67% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ScoreOption.kt index f49f820b6..65714a282 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ScoreOption.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.competition.task.options -import dev.dres.data.model.competition.options.ScoringOption +import dev.dres.api.rest.types.competition.tasks.options.ApiScoreOption import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.KisTaskScorer @@ -15,13 +15,13 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class TaskScoreOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class ScoreOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val KIS by enumField { description = "KIS" } val AVS by enumField { description = "AVS" } } - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set @@ -35,4 +35,11 @@ class TaskScoreOption(entity: Entity) : XdEnumEntity(entity) { AVS -> AvsTaskScorer() else -> throw IllegalStateException("The task score option ${this.description} is currently not supported.") } + + /** + * Converts this [HintOption] to a RESTful API representation [ApiScoreOption]. + * + * @return [ApiScoreOption] + */ + fun toApi() = ApiScoreOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/SubmissionOption.kt similarity index 79% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/competition/task/options/SubmissionOption.kt index 73e0a9b93..4c71313a4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskSubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/SubmissionOption.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.competition.task.options -import dev.dres.data.model.competition.options.SubmissionFilterOption +import dev.dres.api.rest.types.competition.tasks.options.ApiSubmissionOption import dev.dres.run.filter.* import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity @@ -13,8 +13,8 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class TaskSubmissionOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class SubmissionOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val NO_DUPLICATES by enumField { description = "NO_DUPLICATES" } val LIMIT_CORRECT_PER_TEAM by enumField { description = "LIMIT_CORRECT_PER_TEAM" } val LIMIT_WRONG_PER_TEAM by enumField { description = "LIMIT_WRONG_PER_TEAM" } @@ -26,7 +26,7 @@ class TaskSubmissionOption(entity: Entity) : XdEnumEntity(entity) { val MINIMUM_TIME_GAP by enumField { description = "MINIMUM_TIME_GAP" } } - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set @@ -47,4 +47,11 @@ class TaskSubmissionOption(entity: Entity) : XdEnumEntity(entity) { MINIMUM_TIME_GAP -> SubmissionRateFilter(parameters) else -> throw IllegalStateException("The task filter option ${this.description} is currently not supported.") } + + /** + * Converts this [HintOption] to a RESTful API representation [ApiSubmissionOption]. + * + * @return [ApiSubmissionOption] + */ + fun toApi() = ApiSubmissionOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt similarity index 61% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt index 286d38bc6..93effe4fa 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskTargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.competition.task.options +import dev.dres.api.rest.types.competition.tasks.options.ApiTargetOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -10,18 +11,24 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class TaskTargetOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class TargetOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val SINGLE_MEDIA_ITEM by enumField { description = "SINGLE_MEDIA_ITEM" } val SINGLE_MEDIA_SEGMENT by enumField { description = "SINGLE_MEDIA_SEGMENT" } val MULTIPLE_MEDIA_ITEMS by enumField { description = "MULTIPLE_MEDIA_ITEMS" } val JUDGEMENT by enumField { description = "JUDGEMENT" } val VOTE by enumField { description = "VOTE" } val TEXT by enumField { description = "TEXT" } - } - /** Name / description of the [TaskTargetOption]. */ + /** Name / description of the [TargetOption]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [HintOption] to a RESTful API representation [ApiTargetOption]. + * + * @return [ApiTargetOption] + */ + fun toApi() = ApiTargetOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt index 88df462c0..3ed0f9a84 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt @@ -1,12 +1,12 @@ package dev.dres.data.model.competition.task.options +import dev.dres.api.rest.types.competition.tasks.options.ApiTaskOption import dev.dres.data.model.competition.task.TaskDescription import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp - /** * An enumeration of potential general options for [TaskDescription]. * @@ -20,7 +20,14 @@ class TaskOption(entity: Entity) : XdEnumEntity(entity) { val PROLONG_ON_SUBMISSION by enumField { description = "PROLONG_ON_SUBMISSION" } /** Prolongs a task if a submission arrives within a certain time limit towards the end. */ } - /** Name / description of the [TaskScoreOption]. */ + /** Name / description of the [ScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [HintOption] to a RESTful API representation [ApiTaskOption]. + * + * @return [ApiTaskOption] + */ + fun toApi() = ApiTaskOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt index 293ccfb8e..f2dc16608 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.competition.team -import dev.dres.api.rest.types.competition.ApiTeam -import dev.dres.api.rest.types.users.ApiUser +import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User @@ -10,7 +9,12 @@ import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.query.asSequence +import java.awt.image.BufferedImage +import java.io.ByteArrayInputStream +import java.nio.file.Files import java.nio.file.Paths +import java.util.* +import javax.imageio.ImageIO /** The ID of a [Team]. */ typealias TeamId = String @@ -31,6 +35,50 @@ class Team(entity: Entity) : PersistentEntity(entity) { listOf(Team::name, Team::competition) ) + /** + * Stores the given image data to disk. + * + * @param config The [Config] object with global configuration. + * @param data The Base64 encoded image data. + * @param logoId The [LogoId] of the logo to store. + * + * @return The UID of the image. + */ + fun storeLogo(config: Config, data: String, logoId: LogoId): LogoId { + /* Parse image data. */ + val base64Image: String = data.substringAfter(",") + val imageBytes = Base64.getDecoder().decode(base64Image) + val image = ByteArrayInputStream(imageBytes).use { + val original = ImageIO.read(it) + if (original.width <= config.logoMaxSize && original.height <= config.logoMaxSize) { + original + } else { + val target = if (original.width > original.height) { + Pair(config.logoMaxSize, (original.height * (config.logoMaxSize.toDouble() / original.width)).toInt()) + } else { + Pair((original.width * (config.logoMaxSize.toDouble() / original.height)).toInt(), config.logoMaxSize) + } + val resizedImage = BufferedImage(target.first, target.second, BufferedImage.TYPE_INT_ARGB) + val graphics2D = resizedImage.createGraphics() + graphics2D.drawImage(original, 0, 0, target.first, target.second, null) + graphics2D.dispose() + resizedImage + } + } + + /* Generate UID and prepare file path. */ + val path = Team.logoPath(config, logoId) + if (!Files.exists(path.parent)) { + Files.createDirectories(path.parent) + } + + /* Generate UID and write image to disk. */ + Files.newOutputStream(path).use { + ImageIO.write(image, "PNG", it) + } + return logoId + } + /** * Generates and returns the [Path] to the team logo with the given [logoId]. * @@ -70,5 +118,5 @@ class Team(entity: Entity) : PersistentEntity(entity) { * * @return [ApiTeam] */ - fun toApi() = ApiTeam(this.name, this.color, this.logoId, this.users.asSequence().map { it.toApi() }.toList()) + fun toApi() = ApiTeam(this.teamId, this.name, this.color, this.logoId, this.users.asSequence().map { it.toApi() }.toList()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt index 9ae89cf36..8dcc9e935 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt @@ -1,10 +1,12 @@ package dev.dres.data.model.competition.team +import dev.dres.api.rest.types.competition.team.ApiTeamGroup import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User import dev.dres.data.model.competition.CompetitionDescription import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence typealias TeamGroupId = String @@ -32,8 +34,18 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { var defaultAggregator by xdLink1(TeamAggregator) /** The [CompetitionDescription] this [Team] belongs to. */ - var competition by xdParent(CompetitionDescription::teamsGroups) + var competition by xdParent(CompetitionDescription::teamsGroups) /** The [Team]s that belong to this [TeamGroup]. */ val teams by xdLink0_N(Team::group) + + + /** + * Converts this [TeamGroup] to a RESTful API representation [ApiTeamGroup]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiTeamGroup] + */ + fun toApi() = ApiTeamGroup(this.teamGroupId, this.name, this.teams.asSequence().map { it.toApi() }.toList(), this.defaultAggregator.name) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index 656948e09..174b5cd4f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -9,6 +9,8 @@ import kotlinx.dnq.simple.requireIf import java.nio.file.Path import java.nio.file.Paths +typealias MediaId = String + /** * A media item such as a video or an image * @@ -52,25 +54,23 @@ class MediaItem(entity: Entity) : PersistentEntity(entity) { fun toApi(): ApiMediaItem = ApiMediaItem(this.id, this.name, this.type.toApi(), this.collection.id, this.location, this.durationMs, this.fps) - /** * Returns the [Path] to the original file for this [MediaItem]. * * @return [Path] */ - fun pathToOriginal(): Path = Paths.get(this.collection.path).resolve(this.location) + fun pathToOriginal(): Path = Paths.get(this.collection.path, this.location) /** * Returns the [Path] to the cached file for this [MediaItem]. * - * @param config * @param start * @param end * @return [Path] */ - fun pathToCachedItem(config: Config, start: Int? = null, end: Int? = null) = if (start != null && end != null) { - Paths.get(config.cachePath).resolve("media").resolve("${this.collection.name}-${this.id}-$start-$end.${this.type.suffix}") + fun cachedItemName(start: Long? = null, end: Long? = null) = if (start != null && end != null) { + "${this.collection.name}-${this.id}-$start-$end.${this.type.suffix}" } else { - Paths.get(config.cachePath).resolve("media").resolve("${this.collection.name}-${this.id}.${this.type.suffix}") + "${this.collection.name}-${this.id}.${this.type.suffix}" } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt index d423957c3..e01927606 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt @@ -14,7 +14,12 @@ import kotlinx.dnq.simple.min * @version 2.0.0 */ class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() + companion object : XdNaturalEntityType() { + /** Combination of [MediaItemSegment] name / item must be unique. */ + override val compositeIndices = listOf( + listOf(MediaItemSegment::name, MediaItemSegment::item) + ) + } /** The name of this [MediaItemSegment]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) @@ -23,12 +28,12 @@ class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { var item by xdParent(MediaItem::segments) /** The start frame number of this [MediaItemSegment]. */ - var startFrame by xdRequiredIntProp() { min(0) } + var start by xdRequiredIntProp { min(0L) } /** The end frame number of this [MediaItemSegment]. */ - var endFrame by xdRequiredIntProp() { min(0) } + var end by xdRequiredIntProp { min(0L) } /** Returns the [range] of this [MediaItemSegment] as [TemporalRange]. */ val range: TemporalRange - get() = TemporalRange(TemporalPoint.Frame(this.startFrame, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.endFrame, this.item.fps ?: 1.0f)) + get() = TemporalRange(TemporalPoint.Frame(this.start, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.end, this.item.fps ?: 1.0f)) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt index 29fcd420d..ab4f03619 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.media import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.api.rest.types.competition.ApiTeam import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -31,5 +30,5 @@ class MediaType(entity: Entity) : XdEnumEntity(entity) { * This is a convenience method and requires an active transaction context. */ fun toApi(): ApiMediaType - = ApiMediaType.values().find { it.mediaType == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") + = ApiMediaType.values().find { it.type == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt index 75fa98ee2..b21c99a47 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt @@ -1,7 +1,5 @@ package dev.dres.data.model.media.time -import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.fasterxml.jackson.annotation.JsonTypeName import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.PlayableMediaItem import java.lang.IllegalArgumentException @@ -9,10 +7,9 @@ import java.lang.IllegalArgumentException /** * Notion of a [TemporalPoint] within a [MediaItem] that exhibits temporal development (e.g. [VideoItem]). * - * @version 2.1.0 + * @version 2.2.0 * @author Luca Rossetto & Ralph Gasser */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") sealed class TemporalPoint { abstract fun niceText(): String abstract fun toMilliseconds(): Long @@ -20,7 +17,6 @@ sealed class TemporalPoint { /** * A [TemporalPoint] represented by a frame number and a fps value. */ - @JsonTypeName("Frame") data class Frame(val frame: Int, val fps: Float) : TemporalPoint(){ companion object { @@ -35,7 +31,6 @@ sealed class TemporalPoint { /** * A [TemporalPoint] represented by a millsecond value. */ - @JsonTypeName("Millisecond") data class Millisecond(val millisecond: Long) : TemporalPoint() { override fun niceText(): String = "MilliSecondTemporalPoint($millisecond)" @@ -45,7 +40,6 @@ sealed class TemporalPoint { /** * A [TemporalPoint] represented by a timecode and a fps value. */ - @JsonTypeName("Timecode") data class Timecode(val timecode: String, val fps: Float) : TemporalPoint(){ companion object { diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt index c90f9761b..8739d29e3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalRange.kt @@ -1,12 +1,9 @@ package dev.dres.data.model.media.time -import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.utilities.TimeUtil - /** * Notion of a [TemporalRange] within a [MediaItem] that exhibits temporal development (e.g. [VideoItem]. * - * @version 2.0 + * @version 2.1.0 * * @param start The start of the [TemporalRange] * @param end The end of the [TemporalRange] @@ -41,7 +38,5 @@ data class TemporalRange constructor(val start: TemporalPoint, val end: Temporal fun toMilliseconds(): Pair = Pair(start.toMilliseconds(), end.toMilliseconds()) val center - @JsonIgnore get() = (end.toMilliseconds() - start.toMilliseconds()) / 2 - } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt index c4f43ded3..3c19ad285 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt @@ -2,8 +2,5 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.UID -/** The ID of a [Task]. */ -typealias TaskId = UID - /** The ID of a [Competition]. */ typealias CompetitionId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt index d97c4fdd2..284ab8337 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskDescriptionId import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task import dev.dres.run.TaskRunStatus import dev.dres.run.score.interfaces.TaskScorer @@ -13,7 +14,7 @@ import dev.dres.run.score.interfaces.TaskScorer */ interface Task: Run { /** The unique [TaskId] that identifies this [Task]. Used by the persistence layer. */ - val uid: TaskId + val uid: TaskDescriptionId /** Reference to the [Competition] this [Task] belongs to. */ val competition: Competition @@ -29,6 +30,4 @@ interface Task: Run { /** The current status of this [Task]. */ val status: TaskRunStatus - - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt index 76fb132e6..cf950dbf6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.submissions.aspects import dev.dres.data.model.UID +import dev.dres.data.model.competition.team.TeamId /** * @@ -9,6 +10,6 @@ import dev.dres.data.model.UID */ interface OriginAspect { val uid: UID - val teamId: UID + val teamId: TeamId val memberId: UID } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt index 55b4b0b8f..b1cdbf8f8 100644 --- a/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt +++ b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt @@ -1,10 +1,11 @@ package dev.dres.run.exceptions -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId import dev.dres.run.RunManager /** * An [IllegalStateException] that gets thrown whenever a [RunManager] or a dependent class does not know a [TeamId] it is supposed to process. + * * Errors like this are usually linked to bad user input. * * @author Ralph Gasser diff --git a/backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt b/backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt deleted file mode 100644 index 31a4c5c00..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/ScoreEntry.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.dres.run.score - -import dev.dres.data.model.competition.TeamId - -typealias ScoreEntry = Triple \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt index 6fc50b58e..30436c512 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt @@ -1,5 +1,5 @@ package dev.dres.run.score -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId data class TaskContext(val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt index bbf9a7192..ae18d3480 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt @@ -1,12 +1,15 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.UID -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.run.interfaces.Task -import dev.dres.run.score.ScoreEntry + +/** Type alias for a */ +typealias ScoreEntry = Triple /** - * A scorer for a [Task]. A score is a [Double] value that captures a teams performance. + * A scorer for a [Task]. A score is a [Double] value that captures a [Team] performance. * The [TaskScorer] calculates and tracks these scores per [TeamId]. * * @version 1.1.0 @@ -19,5 +22,4 @@ interface TaskScorer { * @return Map of team [UID] to team score */ fun scores(): List - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt index 4d781bd6c..2b38b9da9 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt @@ -1,9 +1,13 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.competition.TeamId - +import dev.dres.data.model.competition.team.TeamId + +/** + * A [TaskScorer] that aggregates scores per [TeamId]. + * + * @author Luca Rossetto + * @versio 1.0.0 + */ interface TeamTaskScorer : TaskScorer { - fun teamScoreMap() : Map - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 3fd61e0bf..4b17e75d6 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,21 +1,21 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.UID import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskDescriptionId import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (TaskDescription) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 100.0) : Scoreboard { - private val scorePerTaskMap = ConcurrentHashMap>() + private val scorePerTaskMap = ConcurrentHashMap>() - private val teamIds = teams.map { it.uid } + private val teamIds = teams.map { it.teamId } - private fun overallScoreMap(): Map { + private fun overallScoreMap(): Map { val scoreSums = scorePerTaskMap.values .flatMap {it.entries} //all team to score pairs independent of task .groupBy { it.key } //individual scores per team @@ -29,13 +29,13 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri override fun scores(): List { val scores = overallScoreMap() - return this.teamIds.map { Score(it.string, scores[it] ?: 0.0) } + return this.teamIds.map { Score(it, scores[it] ?: 0.0) } } - override fun score(teamId: UID) = overallScoreMap()[teamId] ?: 0.0 + override fun score(teamId: TeamId) = overallScoreMap()[teamId] ?: 0.0 - override fun update(scorers: Map) { + override fun update(scorers: Map) { this.scorePerTaskMap.clear() this.scorePerTaskMap.putAll(scorers.map { it.key to it.value.scores().groupBy { it.first }.mapValues { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index 5290deaca..2b159bd4b 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -1,8 +1,8 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.task.TaskDescriptionId +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskScorer class SumAggregateScoreBoard(override val name: String, private val boards: List, private val taskGroupName: String? = null) : Scoreboard { @@ -18,7 +18,7 @@ class SumAggregateScoreBoard(override val name: String, private val boards: List //since calls are delegated, nothing needs to be done here } - override fun update(scorers: Map) { + override fun update(scorers: Map) { //since calls are delegated, nothing needs to be done here } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index d7b60803b..b9e8d2310 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -1,14 +1,13 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.UID -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.media.MediaType import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.run.score.ScoreEntry import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import dev.dres.run.score.interfaces.ScoreEntry import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.utilities.TimeUtil import java.util.concurrent.locks.ReentrantReadWriteLock @@ -20,8 +19,7 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(submissions: Collection, context: TaskContext): Map { - + override fun computeScores(submissions: Collection, context: TaskContext): Map { val correctSubmissions = submissions.filter { it.status == SubmissionStatus.CORRECT } val wrongSubmissions = submissions.filter { it.status == SubmissionStatus.WRONG } @@ -44,20 +42,19 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { (countQuantized(correctSubs).toDouble() / totalCorrectQuantized) }.toMap() } - - return teamScoreMap() } private fun countQuantized(submissions: Collection): Int { return submissions.filterIsInstance().groupBy { it.item }.map { - when(it.key) { - is MediaItem.ImageItem -> 1 - is MediaItem.VideoItem -> { + when(it.key.type) { + MediaType.IMAGE -> 1 + MediaType.VIDEO -> { val ranges = it.value.map { s -> (s as Submission.Temporal).temporalRange } TimeUtil.merge(ranges, overlap = 1).size } + else -> throw IllegalStateException("Unsupported media type ${it.key.type} for AVS task scorer.") } }.sum() @@ -68,6 +65,4 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { override fun scores(): List = this.lastScoresLock.read { this.lastScores.map { ScoreEntry(it.key, null, it.value) } } - - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index 6734a0872..c6be4f665 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,12 +1,12 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.StatusAspect import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.score.ScoreEntry import dev.dres.run.score.interfaces.ResultBatchTaskScorer +import dev.dres.run.score.interfaces.ScoreEntry import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 09e49591b..0382d01db 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -1,12 +1,11 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.UID -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.run.score.ScoreEntry import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import dev.dres.run.score.interfaces.ScoreEntry import dev.dres.run.score.interfaces.TeamTaskScorer import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -34,29 +33,29 @@ class KisTaskScorer( private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { + override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("no task start time specified") val taskDuration = context.taskDuration ?: throw IllegalArgumentException("no task duration specified") val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition - this.lastScores = context.teamIds.map { teamId -> - val sbs = submissions.filter { it.teamId == teamId && (it.status == SubmissionStatus.CORRECT || it.status == SubmissionStatus.WRONG) }.sortedBy { it.timestamp } + this.lastScores = context.teamIds.associateWith { teamId -> + val sbs = submissions.filter { it.teamId == teamId && (it.status == SubmissionStatus.CORRECT || it.status == SubmissionStatus.WRONG) }.sortedBy { it.timestamp } val firstCorrect = sbs.indexOfFirst { it.status == SubmissionStatus.CORRECT } val score = if (firstCorrect > -1) { val timeFraction = 1.0 - (sbs[firstCorrect].timestamp - taskStartTime) / tDur - - max(0.0, - maxPointsAtTaskEnd + - ((maxPointsPerTask - maxPointsAtTaskEnd) * timeFraction) - - (firstCorrect * penaltyPerWrongSubmission) //index of first correct submission is the same as number of not correct submissions + max( + 0.0, + maxPointsAtTaskEnd + + ((maxPointsPerTask - maxPointsAtTaskEnd) * timeFraction) - + (firstCorrect * penaltyPerWrongSubmission) //index of first correct submission is the same as number of not correct submissions ) } else { 0.0 } - teamId to score - }.toMap() + score + } return this.lastScores } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 12a2e2d14..e404c3959 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -14,11 +14,11 @@ import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import java.util.* /** - * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated; - * it update the scores for the respective [InteractiveSynchronousCompetition.Task]. + * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated and + * updates the scores for the respective [InteractiveSynchronousCompetition.Task]. * * @author Ralph Gasser - * @version 1.1.0 + * @version 1.2.0 */ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdatable, val messageQueueUpdatable: MessageQueueUpdatable, val daoUpdatable: DAOUpdatable<*>): Updatable { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 9fcb5cfb0..c98eb54ee 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,20 +1,20 @@ package dev.dres.run.validation import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.VideoSegment +import dev.dres.data.model.media.MediaItemSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.validation.interfaces.SubmissionValidator /** - * A validator class that checks, if a submission is correct based on the target segment and the + * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the * complete containment of the [Submission] within the provided [MediaSegmentTaskDescription]. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0 + * @version 1.0.1 */ -class TemporalContainmentSubmissionValidator(private val task: VideoSegment) : SubmissionValidator { +class TemporalContainmentSubmissionValidator(private val task: MediaItemSegment) : SubmissionValidator { /** * Validates a [Submission] based on the target segment and the temporal overlap of the @@ -31,7 +31,7 @@ class TemporalContainmentSubmissionValidator(private val task: VideoSegment) : S submission.start > submission.end -> SubmissionStatus.WRONG submission.item != task.item -> SubmissionStatus.WRONG else -> { - val outer = this.task.temporalRange.toMilliseconds() + val outer = this.task.range.toMilliseconds() if (outer.first <= submission.start && outer.second >= submission.end) { SubmissionStatus.CORRECT } else { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt index c7a253d94..2e35a3073 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt @@ -1,15 +1,22 @@ package dev.dres.run.validation -import dev.dres.data.model.competition.VideoSegment +import dev.dres.data.model.media.MediaItemSegment +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalAspect import dev.dres.data.model.submissions.batch.ResultBatch import dev.dres.run.validation.interfaces.SubmissionBatchValidator -class TemporalOverlapSubmissionBatchValidator(private val targetSegment: VideoSegment) : SubmissionBatchValidator { +/** + * A [SubmissionBatchValidator] class that checks, if a submission is correct based on the target segment and the + * temporal overlap of the [Submission] with the provided [MediaSegmentTaskDescription]. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.0.1 + */ +class TemporalOverlapSubmissionBatchValidator(private val targetSegment: MediaItemSegment) : SubmissionBatchValidator { override fun validate(batch: ResultBatch<*>) { - batch.results.forEach { if (it is TemporalAspect){ it.status = when { @@ -17,7 +24,7 @@ class TemporalOverlapSubmissionBatchValidator(private val targetSegment: VideoSe it.item != targetSegment.item -> SubmissionStatus.WRONG else -> { val outer = - this.targetSegment.temporalRange.toMilliseconds() + this.targetSegment.range.toMilliseconds() if ((outer.first <= it.start && outer.second >= it.start) || (outer.first <= it.end && outer.second >= it.end)) { SubmissionStatus.CORRECT } else { @@ -29,6 +36,5 @@ class TemporalOverlapSubmissionBatchValidator(private val targetSegment: VideoSe it.status = SubmissionStatus.WRONG } } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 8c1e2724f..cff2cb3b5 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -1,20 +1,20 @@ package dev.dres.run.validation import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.VideoSegment +import dev.dres.data.model.media.MediaItemSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect import dev.dres.run.validation.interfaces.SubmissionValidator /** - * A validator class that checks, if a submission is correct based on the target segment and the + * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the * temporal overlap of the [Submission] with the provided [MediaSegmentTaskDescription]. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0 + * @version 1.0.1 */ -class TemporalOverlapSubmissionValidator(private val targetSegment: VideoSegment) : SubmissionValidator { +class TemporalOverlapSubmissionValidator(private val targetSegment: MediaItemSegment) : SubmissionValidator { /** * Validates a [Submission] based on the target segment and the temporal overlap of the @@ -31,7 +31,7 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: VideoSegment submission.start > submission.end -> SubmissionStatus.WRONG submission.item != targetSegment.item -> SubmissionStatus.WRONG else -> { - val outer = this.targetSegment.temporalRange.toMilliseconds() + val outer = this.targetSegment.range.toMilliseconds() if ((outer.first <= submission.start && outer.second >= submission.start) || (outer.first <= submission.end && outer.second >= submission.end)) { SubmissionStatus.CORRECT } else { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 0923acd55..c7dae01ea 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -8,11 +8,11 @@ import dev.dres.data.model.submissions.aspects.TextAspect /** Helper class to store submission information independent of source */ data class ItemRange(val element: String, val start: Long, val end: Long){ - constructor(submission: TemporalSubmissionAspect): this(submission.item.id.string, submission.start, submission.end) + constructor(submission: TemporalSubmissionAspect): this(submission.item.id, submission.start, submission.end) constructor(submission: TextAspect): this(submission.text, 0, 0) constructor(submission: ItemAspect): this(submission.item) - constructor(item: MediaItem): this(item.id.string, 0, 0) - constructor(item: MediaItem, start: Long, end: Long): this(item.id.string, start, end) + constructor(item: MediaItem): this(item.id, 0, 0) + constructor(item: MediaItem, start: Long, end: Long): this(item.id, start, end) constructor(submission: Submission): this(fromSubmission(submission), if (submission is TemporalSubmissionAspect) submission.start else 0, if (submission is TemporalSubmissionAspect) submission.end else 0) @@ -20,7 +20,7 @@ data class ItemRange(val element: String, val start: Long, val end: Long){ companion object { private fun fromSubmission(submission: Submission): String { return when (submission){ - is ItemAspect -> submission.item.id.string + is ItemAspect -> submission.item.id is TextAspect -> submission.text else -> throw IllegalStateException("Submission contains neither item nor text") } diff --git a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt index 6a26aa7f7..c8e967659 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt @@ -2,18 +2,15 @@ package dev.dres.utilities import com.github.kokorin.jaffree.StreamType import com.github.kokorin.jaffree.ffmpeg.FFmpeg -import com.github.kokorin.jaffree.ffmpeg.FFmpegResult import com.github.kokorin.jaffree.ffmpeg.UrlInput import com.github.kokorin.jaffree.ffmpeg.UrlOutput import com.github.kokorin.jaffree.ffprobe.FFprobe import com.github.kokorin.jaffree.ffprobe.FFprobeResult import dev.dres.DRES -import dev.dres.data.model.competition.CachedVideoItem +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalRange import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory -import java.io.File -import java.io.FileNotFoundException -import java.io.InputStream import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -171,25 +168,20 @@ object FFmpegUtil { } } - fun prepareMediaSegmentTask( - description: CachedVideoItem, - collectionBasePath: String, - cacheLocation: File - ) { - - cacheLocation.mkdirs() - - val input = File(File(collectionBasePath), description.item.location).toPath() - val output = File(cacheLocation, description.cacheItemName()).toPath() - val range = description.temporalRange.toMilliseconds() - - extractSegment( - input, - toMillisecondTimeStamp(range.first), - toMillisecondTimeStamp(range.second), - output - ) - + /** + * Extracts and renders the previews for a [MediaItem]. + * + * @param item The [MediaItem] to handle. + * @param range The [TemporalRange] within the [MediaItem] to handle. + * @param cacheLocation The cache location [Path] + */ + fun extractSegment(item: MediaItem, range: TemporalRange, cacheLocation: Path) { + Files.createDirectories(cacheLocation) + val start = range.start.toMilliseconds() + val end = range.end.toMilliseconds() + val input = item.pathToOriginal() + val output = cacheLocation.resolve(item.cachedItemName(start, end)) + extractSegment(input, toMillisecondTimeStamp(start), toMillisecondTimeStamp(end), output) } fun analyze(videoPath: Path, countFrames: Boolean = false): FFprobeResult = diff --git a/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt b/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt index cf907804f..3c185bee0 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt @@ -2,7 +2,7 @@ package dev.dres.utilities import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write -import org.eclipse.collections.impl.map.mutable.primitive.ObjectBooleanHashMap +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap import java.util.HashMap import java.util.concurrent.locks.StampedLock @@ -15,7 +15,7 @@ import java.util.concurrent.locks.StampedLock class ReadyLatch { /** Internal map that maps object of type [T] to its ready state. */ - private val map = ObjectBooleanHashMap() + private val map = Object2BooleanOpenHashMap() /** Internal lock to mediate access to map. */ private val lock = StampedLock() @@ -37,7 +37,7 @@ class ReadyLatch { * @param o The object [T] to unregister. */ fun unregister(o: T) = this.lock.write { - this.map.remove(o) + this.map.removeBoolean(o) } /** @@ -46,9 +46,7 @@ class ReadyLatch { * @return Current state of this [ReadyLatch]. */ fun state() = this.lock.read { - val map = HashMap() - this.map.forEachKeyValue { k, v -> map[k] = v} - map + HashMap(this.map) } /** @@ -77,7 +75,7 @@ class ReadyLatch { * @param timeout specifies an optional timeout in seconds after which [allReadyOrTimedOut] is considered to be true in any case */ fun reset(timeout: Long? = null) = this.lock.write { - this.map.updateValues { _, _ -> false } + this.map. { _, _ -> false } this.timeout = if (timeout != null) (1000L * timeout) + System.currentTimeMillis() else null } @@ -92,7 +90,7 @@ class ReadyLatch { * Returns true if and only if all objects registered with this [ReadyLatch] are in the ready state. */ fun allReady() = this.lock.read { - this.map.allSatisfy { it } + this.map.all { it.value } } /** diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/DataInputOutputExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/DataInputOutputExtensions.kt deleted file mode 100644 index 8e31cee27..000000000 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/DataInputOutputExtensions.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.dres.utilities.extensions - -import dev.dres.data.model.UID -import org.mapdb.DataInput2 -import org.mapdb.DataOutput2 - -fun DataOutput2.writeUID(uid: UID) = this.writeUTF(uid.string) - -fun DataInput2.readUID() : UID = this.readUTF().UID() \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt index 02a01e93a..03878621f 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt @@ -1,17 +1,8 @@ package dev.dres.utilities.extensions -import dev.dres.api.rest.handler.SessionId import dev.dres.data.model.UID -import java.util.* import java.util.regex.Matcher -/** - * Converts a [String] to a [UID]. - * - * @return [UID] - */ -fun String.UID(): UID = UID(UUID.fromString(this)) - /** * Converts a [String] to a [UID]. * diff --git a/gradle.properties b/gradle.properties index 2278016d6..67e1d1a96 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,20 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xms64m -Xmx512m \ No newline at end of file +org.gradle.jvmargs=-Xms64m -Xmx512m + +# Dependency versions +version_bcrypt=0.4 +version_clikt=2.8.0 +version_fastutil=8.5.9 +version_fuel=2.3.1 +version_jaffree=0.11.0 +version_javalin=5.1.2 +version_jline3=3.21.0 +version_junit=5.9.1 +version_kotlin=1.7.20 +version_kotlin_csv=1.6.0 +version_log4j=2.18.0 +version_picnic=0.6.0 +version_xodus=2.0.1 +version_xodus_dnq=2.0.0 \ No newline at end of file From 9778e9c94e9d7a6f9af7e63f6cde63a5d6548413 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 31 Oct 2022 10:31:17 +0100 Subject: [PATCH 016/498] Addressed some of @lucaro's comments. --- .../dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt | 6 ------ .../api/rest/handler/collection/RandomMediaItemHandler.kt | 2 +- .../rest/handler/description/CreateCompetitionHandler.kt | 3 ++- .../dev/dres/api/rest/types/collection/ApiMediaItem.kt | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index f28658c02..06734bdb6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -30,12 +30,6 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle const val RELATION_PARAM = "when" const val BEFORE_PARAM_KEY = "before" const val UPTO_PARAM_KEY = "upto" - /* - // See https://github.com/tipsy/javalin/issues/1047 - we currently are behind - enum class TemporalRelation{ - BEFORE, - UPTO - }*/ } @OpenApi( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt index f3c4ca16d..d2e62b478 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -20,7 +20,7 @@ import java.util.SplittableRandom */ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { - private val rand = SplittableRandom(System.currentTimeMillis()) // TODO Decide upon seed -- time based or fixed? + private val rand = SplittableRandom(System.currentTimeMillis()) override val route: String = "collection/{collectionId}/random" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt index 56d6db52f..0cbc7cd68 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.competition.CompetitionDescription import io.javalin.http.BadRequestResponse import io.javalin.http.Context +import io.javalin.http.bodyAsClass import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import java.util.* @@ -39,7 +40,7 @@ class CreateCompetitionHandler(store: TransientEntityStore) : AbstractCompetitio ) override fun doPost(ctx: Context): SuccessStatus { val createRequest = try { - ctx.bodyAsClass(ApiCreateCompetition::class.java) + ctx.bodyAsClass() } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 4a7e4c4d7..4e55b2682 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -14,7 +14,7 @@ import dev.dres.data.model.media.MediaType data class ApiMediaItem(val id: String= UID.EMPTY.string, val name: String, val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { init { if (this.type == ApiMediaType.VIDEO) { - require(this.durationMs != null) { "Duration must be set for a video item." } + require(this.durationMs != null) { "Duration must be set for a video item." } require(this.fps != null) { "Duration must be set for a video item." } } } From 1ade902a6e55ffb3760e4b37ef0c8876c6781b07 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 31 Oct 2022 10:35:05 +0100 Subject: [PATCH 017/498] Bugfix. --- backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt b/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt index 3c185bee0..10c4a2b5f 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt @@ -10,7 +10,7 @@ import java.util.concurrent.locks.StampedLock * A simple latch that tracks for all object it contains whether they are ready (true) or not (false). * * @author Ralph Gasser - * @version 1.1 + * @version 1.1.1 */ class ReadyLatch { @@ -75,7 +75,9 @@ class ReadyLatch { * @param timeout specifies an optional timeout in seconds after which [allReadyOrTimedOut] is considered to be true in any case */ fun reset(timeout: Long? = null) = this.lock.write { - this.map. { _, _ -> false } + for (e in this.map.keys) { + this.map[e] = false + } this.timeout = if (timeout != null) (1000L * timeout) + System.currentTimeMillis() else null } @@ -96,5 +98,6 @@ class ReadyLatch { /** * Equivalent to [allReady] in case no timeout was set in [reset] */ - fun allReadyOrTimedOut() = allReady() || if (timeout != null) timeout ?: Long.MAX_VALUE <= System.currentTimeMillis() else false + fun allReadyOrTimedOut() + = allReady() || (this.timeout ?: Long.MAX_VALUE) <= System.currentTimeMillis() } \ No newline at end of file From e4864069da48d87a37a8440194a4ab434e167a68 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 31 Oct 2022 11:58:42 +0100 Subject: [PATCH 018/498] Started migration of Submission and SubmissionHandler --- backend/build.gradle | 2 +- .../handler/submission/SubmissionHandler.kt | 241 ++++++++++-------- .../dres/api/rest/types/audit/AuditLogPage.kt | 14 +- .../competition/CompetitionStartMessage.kt | 11 +- .../types/competition/tasks/ApiTargetItem.kt | 11 - .../dres/data/model/competition/task/Hint.kt | 3 +- .../competition/task/TaskDescriptionTarget.kt | 3 +- .../dres/data/model/submissions/Submission.kt | 84 +++--- .../model/submissions/SubmissionStatus.kt | 42 ++- .../data/model/submissions/SubmissionType.kt | 39 +++ .../kotlin/dev/dres/utilities/TimeUtil.kt | 23 +- 11 files changed, 279 insertions(+), 194 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt diff --git a/backend/build.gradle b/backend/build.gradle index 061fa26d1..82db5ff0e 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -60,7 +60,7 @@ dependencies { ////// Javalin implementation group: 'io.javalin', name: 'javalin', version: "$version_javalin" kapt("io.javalin.community.openapi:openapi-annotation-processor:$version_javalin") - implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: version_javalin + implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: version_javalin implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: version_javalin implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: version_javalin diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 5693f108d..84c6fba5b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -1,6 +1,5 @@ package dev.dres.api.rest.handler.submission - import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler @@ -10,17 +9,16 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus import dev.dres.data.model.Config -import dev.dres.data.model.UID +import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.PlayableMediaItem +import dev.dres.data.model.competition.task.options.TaskOption +import dev.dres.data.model.media.* import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect +import dev.dres.data.model.submissions.SubmissionType import dev.dres.run.InteractiveRunManager import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -32,10 +30,11 @@ import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.nio.file.Files -import java.nio.file.Path import java.nio.file.Paths +import java.util.* /** * An [GetRestHandler] used to process [Submission]s @@ -71,9 +70,9 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), OpenApiParam(PARAMETER_NAME_TEXT, String::class, "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g. video).", allowEmptyValue = true, required = false), + OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g., video).", allowEmptyValue = true, required = false), + OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g., video).", allowEmptyValue = true, required = false), + OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g,. video).", allowEmptyValue = true, required = false), OpenApiParam("session", String::class, "Session Token") ], tags = ["Submission"], @@ -88,33 +87,35 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) - val run = getEligibleRunManager(userId, ctx) - val time = System.currentTimeMillis() - val submission = toSubmission(userId, run, time, ctx) - val rac = RunActionContext.runActionContext(ctx, run) - - val result = try { - run.postSubmission(rac, submission) - } catch (e: SubmissionRejectedException) { - throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) - } catch (e: IllegalRunStateException) { - logger.info("Submission was received while Run manager not accepting submissions") - throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) - } catch (e: IllegalTeamIdException) { - logger.info("Submission with unkown team id '${rac.teamId}' was received") - throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) - } + val (s,r) = this.store.transactional { + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val run = getEligibleRunManager(userId, ctx) + val time = System.currentTimeMillis() + val submission = toSubmission(userId, run, time, ctx) + val rac = RunActionContext.runActionContext(ctx, run) - AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionId(), ctx.req().remoteAddr) + val result = try { + run.postSubmission(rac, submission) + } catch (e: SubmissionRejectedException) { + throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) + } catch (e: IllegalRunStateException) { + logger.info("Submission was received while run manager was not accepting submissions.") + throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + } catch (e: IllegalTeamIdException) { + logger.info("Submission with unknown team id '${rac.teamId}' was received.") + throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) + } - if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { //pre-generate preview - generatePreview(submission) + AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionId(), ctx.req().remoteAddr) + if (run.currentTaskDescription(rac).taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS)) { //pre-generate preview + generatePreview(submission) + } + submission to result } - logger.info("submission ${submission.uid} received status $result") + logger.info("Submission ${s.id} received status $r.") - return when (result) { + return when (r) { SubmissionStatus.CORRECT -> SuccessfulSubmissionsStatus(SubmissionStatus.CORRECT, "Submission correct!") SubmissionStatus.WRONG -> SuccessfulSubmissionsStatus(SubmissionStatus.WRONG, "Submission incorrect! Try again") SubmissionStatus.INDETERMINATE -> { @@ -122,12 +123,12 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co SuccessfulSubmissionsStatus(SubmissionStatus.INDETERMINATE, "Submission received. Waiting for verdict!") } SubmissionStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(SubmissionStatus.UNDECIDABLE,"Submission undecidable. Try again!") + else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } } - /** - * + * Returns the [InteractiveRunManager] that is eligible for the given [UserId] and [Context] */ private fun getEligibleRunManager(userId: UserId, ctx: Context): InteractiveRunManager { val managers = AccessManager.getRunManagerForUser(userId).filterIsInstance(InteractiveRunManager::class.java).filter { @@ -140,101 +141,121 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co } /** + * Converts the user request tu a [Submission]. * + * Creates the associated database entry. Requires an ongoing transaction. + * + * @param userId The [UserId] of the user who triggered the [Submission]. + * @param runManager The [InteractiveRunManager] + * @param submissionTime Time of the submission. + * @param ctx The HTTP [Context] */ private fun toSubmission(userId: UserId, runManager: InteractiveRunManager, submissionTime: Long, ctx: Context): Submission { val map = ctx.queryParamMap() /* Find team that the user belongs to. */ - val team = runManager.description.teams.find { - it.users.contains(userId) - }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) - + val user = User.query(User::id eq userId).firstOrNull() + ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) + val team = runManager.description.teams.filter { it.users.contains(user) }.firstOrNull() + ?: throw ErrorStatusException(404, "No team for user '$userId' could be found.", ctx) val rac = RunActionContext.runActionContext(ctx, runManager) + /* Create new submission. */ + Submission.new { + this.id = UUID.randomUUID().toString() + this.user = user + this.team = team + this.timestamp = submissionTime + this.text = map[PARAMETER_NAME_TEXT]?.first() + } + /* If text is supplied, it supersedes other parameters */ - val text = map[PARAMETER_NAME_TEXT]?.first() - if (text != null) { - return Submission.Text( - team, userId, submissionTime, text - ).also { - it.task = runManager.currentTask(rac) - } + val textParam = map[PARAMETER_NAME_TEXT]?.first() + val itemParam = map[PARAMETER_NAME_ITEM]?.first() + val submission = Submission.new { + this.id = submissionId + this.status = SubmissionStatus.INDETERMINATE + this.user = user + this.team = team + this.task = runManager.currentTask(rac) + this.timestamp = submissionTime } - /* Find collectionId the submission belongs to. */ - val collectionParam = map[PARAMETER_NAME_COLLECTION]?.first() - val collectionId: UID = when { - collectionParam != null -> this.collections.find { it.name == collectionParam }?.id - else -> runManager.currentTaskDescription(rac).mediaCollectionId - } ?: throw ErrorStatusException(404, "Media collection '$collectionParam' could not be found.", ctx) - - /* Find media item. */ - val itemParam = map[PARAMETER_NAME_ITEM]?.first() ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) - val item = this.itemIndex[collectionId to itemParam].firstOrNull() ?: - throw ErrorStatusException(404, "Media item '$itemParam (collection = $collectionId)' could not be found.", ctx) - - val mapToSegment = runManager.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.MAP_TO_SEGMENT } - return when { - map.containsKey(PARAMETER_NAME_SHOT) && item is MediaItem.VideoItem -> { - val segmentList = segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException(400, "Item '${item.name}' not found.", ctx) - val time = TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, segmentList) ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) - Submission.Temporal(team, userId, submissionTime, item, time.first, time.second) - } - map.containsKey(PARAMETER_NAME_FRAME) && (item is PlayableMediaItem) -> { - val time = TemporalPoint.Frame.toMilliseconds( - map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), - item.fps - ) - val range = if(mapToSegment && item is MediaItem.VideoItem) { - (TimeUtil.timeToSegment( - time, - segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException( - 400, - "Item '${item.name}' not found.", - ctx - ) - ) ?: throw ErrorStatusException(400, "No segments found for item '${item.name}'.", ctx)) - } else { - time to time + if (textParam != null) { + submission.type = SubmissionType.TEXT + submission.text = textParam + return submission + } else if (itemParam != null) { + val collection = runManager.currentTaskDescription(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ + val mapToSegment = runManager.currentTaskDescription(rac).taskGroup.type.options.contains(TaskOption.MAP_TO_SEGMENT) + val item = MediaItem.query((MediaItem::name eq itemParam) and (MediaItem::collection eq collection)).firstOrNull() + ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) + val range: Pair? = when { + map.containsKey(PARAMETER_NAME_SHOT) && item.type == MediaType.VIDEO -> { + val time = TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) + ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) + time.first to time.second } - Submission.Temporal(team, userId, submissionTime, item, range.first, range.second) - } - map.containsKey(PARAMETER_NAME_TIMECODE) && (item is PlayableMediaItem) -> { - val time = TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, item) ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) - val range = if(mapToSegment && item is MediaItem.VideoItem) { - (TimeUtil.timeToSegment( - time, - segmentIndex[item.id].firstOrNull() ?: throw ErrorStatusException( - 400, - "Item '${item.name}' not found.", - ctx - ) - ) ?: throw ErrorStatusException(400, "No segments found for item '${item.name}'.", ctx)) - } else { - time to time + map.containsKey(PARAMETER_NAME_FRAME) && item.type == MediaType.VIDEO -> { + val fps = item.fps + ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") + val time = TemporalPoint.Frame.toMilliseconds( + map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() + ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), + fps + ) + if (mapToSegment) { + TimeUtil.timeToSegment(time, item.segments.toList()) + ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + } else { + time to time + } + } + map.containsKey(PARAMETER_NAME_TIMECODE) -> { + val fps = item.fps + ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") + val time = + TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, fps) + ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) + if (mapToSegment) { + TimeUtil.timeToSegment(time, item.segments.toList()) + ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + } else { + time to time + } } - Submission.Temporal(team, userId, submissionTime, item, range.first, range.second) + else -> null + } + + /* Assign information to submission. */ + if (range != null) { + submission.item = item + submission.type = SubmissionType.TEMPORAL + submission.start = range.first + submission.end = range.second + } else { + submission.item = item + submission.type = SubmissionType.ITEM } - else -> Submission.Item(team, userId, submissionTime, item) - }.also { - it.task = runManager.currentTask(rac) + } else { + throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) } + + return submission } + /** + * Triggers generation of a preview image for the provided [Submission]. + * + * @param submission The [Submission] to generate preview for. + */ private fun generatePreview(submission: Submission) { - if (submission !is TemporalSubmissionAspect) { + if (submission.type != SubmissionType.TEMPORAL) return + if (submission.item == null) return + val destinationPath = Paths.get(this.config.cachePath, "previews", submission.item!!.collection.name, submission.item!!.name, "${submission.start}.jpg") + if (Files.exists(destinationPath)){ return } - val collection = collections[submission.item.collection] ?: return - val cacheLocation = Paths.get(config.cachePath + "/previews") - val cacheDir = cacheLocation.resolve("${submission.item.collection}/${submission.item.name}") - val imgPath = cacheDir.resolve("${submission.start}.jpg") - if (Files.exists(imgPath)){ - return - } - val mediaItemLocation = Path.of(collection.path, submission.item.location) - FFmpegUtil.extractFrame(mediaItemLocation, submission.start, imgPath) - + FFmpegUtil.extractFrame(submission.item!!.pathToOriginal(), submission.start, destinationPath) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt index ec0fbd1f7..7cc1f141b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt @@ -1,14 +1,16 @@ package dev.dres.api.rest.types.audit -import dev.dres.run.audit.AuditLogEntry /** - * A collection of [AuditLogEntry]s with pagination information + * A collection of [ApiAuditLogEntry]s with pagination information + * + * @author Loris Sauter + * @version 1.0.0 */ data class AuditLogPage( - val index: Int = 0, - val entries: List, - val timestamp: Long = System.currentTimeMillis(), - val oldest: Long +val index: Int = 0, +val entries: List, +val timestamp: Long = System.currentTimeMillis(), +val oldest: Long ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt index 7f7f931fa..2fa7008bc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt @@ -1,14 +1,17 @@ package dev.dres.api.rest.types.competition import dev.dres.api.rest.types.run.RunType -import dev.dres.data.model.UID import dev.dres.data.model.run.RunProperties -import dev.dres.utilities.extensions.UID /** * A data class that represents a RESTful request for creating a new [dres.data.model.run.CompetitionRun] * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ -data class CompetitionStartMessage(val competitionId: String, val name: String, val type: RunType, val properties: RunProperties = RunProperties()) \ No newline at end of file +data class CompetitionStartMessage( + val competitionId: String, + val name: String, + val type: RunType, + val properties: RunProperties = RunProperties() +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt deleted file mode 100644 index 054369acd..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetItem.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.dres.api.rest.types.competition.tasks - -/** - * The RESTful API representation of a target item. - * - * @see ApiTarget - * @author Ralph Gasser - * @version 1.0 - * - */ -data class ApiTargetItem(val target: String, val temporalRange: RestTemporalRange? = null) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt index 09afc3069..27dc0d02d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt @@ -19,7 +19,6 @@ import java.nio.file.Paths import java.nio.file.StandardOpenOption import java.util.* - /** * Represents the hint given by a [TaskDescription], e.g., a media item or text that is shown. * @@ -121,7 +120,7 @@ class Hint(entity: Entity) : XdEntity(entity) { HintType.IMAGE, HintType.VIDEO -> { val path = if (this.item != null) { - this.item!!.cachedItemName(config, this.temporalRangeStart, this.temporalRangeEnd) + Paths.get(config.cachePath, this.item!!.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)) } else if (this.path != null) { Paths.get(this.path!!) } else { diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt index a6a32e4bf..cb3ee5ece 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt @@ -15,6 +15,7 @@ import kotlinx.dnq.simple.requireIf import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files +import java.nio.file.Paths import java.nio.file.StandardOpenOption import java.util.* @@ -99,7 +100,7 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { MediaType.IMAGE -> ApiContentType.IMAGE else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") } - val path = this.item?.cachedItemName(config, this.temporalRangeStart, this.temporalRangeEnd) + val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)) ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> stream.readAllBytes() diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index cb7e87712..49c11b0ce 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -1,16 +1,20 @@ package dev.dres.data.model.submissions import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.UID +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.admin.User +import dev.dres.data.model.competition.team.Team import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.submissions.aspects.BaseSubmissionAspect -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.min +import kotlinx.dnq.simple.requireIf +typealias SubmissionId = String /** * A [Submission] as received by a competition participant. @@ -18,48 +22,46 @@ import dev.dres.data.model.submissions.aspects.TextAspect * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -sealed class Submission : BaseSubmissionAspect { +sealed class Submission(entity: Entity) : PersistentEntity(entity), BaseSubmissionAspect { + companion object : XdNaturalEntityType() - /** The [AbstractInteractiveTask] this [Submission] belongs to. */ - @JsonIgnore - override var task: AbstractInteractiveTask? = null + /** The [SubmissionId] of this [User]. */ + var submissionId: SubmissionId + get() = this.id + set(value) { this.id = value } + + /** The timestamp of this [Submission]. */ + override var timestamp by xdRequiredLongProp { min(0L) } /** The [SubmissionStatus] of this [Submission]. */ - override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE + override var status by xdLink1(SubmissionStatus) + + /** The [Team] that submitted this [Submission] */ + var team by xdLink1(Team) + + /** The [User] that submitted this [Submission] */ + var user by xdLink1(User) - /** - * - */ - data class Item( - override val teamId: UID, - override val memberId: UID, - override val timestamp: Long, - override val item: MediaItem, - override val uid: UID = UID() - ) : Submission(), ItemAspect + /** The [SubmissionType] of this [Submission]. */ + var type by xdLink1(SubmissionType) - /** - * - */ - data class Temporal( - override val teamId: UID, - override val memberId: UID, - override val timestamp: Long, - override val item: MediaItem, - override val start: Long, //in ms - override val end: Long, //in ms - override val uid: UID = UID() - ) : Submission(), ItemAspect, TemporalSubmissionAspect { + /** The [MediaItem] submitted. Only for [SubmissionType.ITEM] or [SubmissionType.TEMPORAL]. */ + var item by xdLink0_1(MediaItem) - override val temporalRange: TemporalRange - get() = TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) - } + /** The start frame number of this [Submission]. */ + var start by xdRequiredLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } + + /** The end frame number of this [Submission]. */ + var end by xdRequiredLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } + + /** The text submitted. Only for [SubmissionType.TEXT] . */ + var text by xdStringProp { requireIf { this.type == SubmissionType.TEXT } } + + /** The [AbstractInteractiveTask] this [Submission] belongs to. */ + @JsonIgnore + override var task: AbstractInteractiveTask? = null - data class Text( - override val teamId: UID, - override val memberId: UID, - override val timestamp: Long, - override val text: String, - override val uid: UID = UID() - ) : Submission(), TextAspect + /** */ + val temporalRange: TemporalRange + get() = TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt index 4bd6b622e..a3bc60f92 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt @@ -1,14 +1,44 @@ package dev.dres.data.model.submissions +import dev.dres.data.model.admin.Role +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + /** * Status of a [Submission] with respect to its validation. * * @author Luca Rossetto - * @version 1.0 + * @version 2.0.0 */ -enum class SubmissionStatus { - CORRECT, /** Submission has been deemed as correct. */ - WRONG, /** Submission has been deemed as wrong. */ - INDETERMINATE, /** Submission has not been validated yet. */ - UNDECIDABLE +class SubmissionStatus(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val CORRECT by enumField { description = "CORRECT" } /** Submission has been deemed as correct. */ + val WRONG by enumField { description = "WRONG" } + val INDETERMINATE by enumField { description = "INDETERMINATE" } /** Submission has been deemed as wrong. */ + val UNDECIDABLE by enumField { description = "UNDECIDABLE" } /** Submission has not been validated yet. */ + + /** + * Returns a list of all [Role] values. + * + * @return List of all [Role] values. + */ + fun values() = listOf(CORRECT, WRONG, INDETERMINATE, UNDECIDABLE) + + /** + * Parses a [Role] instance from a [String]. + */ + fun parse(string: String) = when (string.uppercase()) { + "CORRECT" -> CORRECT + "WRONG" -> WRONG + "INDETERMINATE" -> INDETERMINATE + "UNDECIDABLE" -> UNDECIDABLE + else -> throw IllegalArgumentException("Failed to parse submission status '$string'.") + } + } + + /** Name / description of the [SubmissionType]. */ + var description by xdRequiredStringProp(unique = true) + private set } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt new file mode 100644 index 000000000..d7ad9d42b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt @@ -0,0 +1,39 @@ +package dev.dres.data.model.submissions + +import dev.dres.data.model.admin.Role +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * + */ +class SubmissionType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val ITEM by enumField { description = "ITEM" } + val TEMPORAL by enumField { description = "TEMPORAL" } + val TEXT by enumField { description = "TEXT" } + + /** + * Returns a list of all [Role] values. + * + * @return List of all [Role] values. + */ + fun values() = listOf(ITEM, TEMPORAL, TEXT) + + /** + * Parses a [Role] instance from a [String]. + */ + fun parse(string: String) = when (string.uppercase()) { + "ITEM" -> ITEM + "TEMPORAL" -> TEMPORAL + "TEXT" -> TEXT + else -> throw IllegalArgumentException("Failed to parse submission type '$string'.") + } + } + + /** Name / description of the [SubmissionType]. */ + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt index 7f4d1579c..e041a5f83 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt @@ -1,5 +1,6 @@ package dev.dres.utilities +import dev.dres.data.model.media.MediaItemSegment import dev.dres.data.model.media.MediaItemSegmentList import dev.dres.data.model.media.time.TemporalRange import kotlin.math.abs @@ -41,25 +42,23 @@ object TimeUtil { } /** - * Converts a shot number to a timestamp in milliseconds. + * Converts a shot number to a timestamp in milliseconds given a list of [MediaItemSegment]s. */ - fun shotToTime(shot: String, segmentList: MediaItemSegmentList): Pair? { - val segment = segmentList.segments.find { it.name == shot } ?: return null + fun shotToTime(shot: String, segments: List): Pair? { + val segment = segments.find { it.name == shot } ?: return null return segment.range.toMilliseconds() } - - fun timeToSegment(time: Long, segmentList: MediaItemSegmentList): Pair? { - if (segmentList.segments.isEmpty()) { - return null - } - val segment = segmentList.segments.find { + /** + * Converts a shot number to a timestamp in milliseconds given a list of [MediaItemSegment]s. + */ + fun timeToSegment(time: Long, segments: List): Pair? { + if (segments.isEmpty()) return null + val segment = segments.find { val range = it.range.toMilliseconds() range.first <= time && range.second >= time - } ?: segmentList.segments.minByOrNull { abs(it.range.center - time) }!! + } ?: segments.minByOrNull { abs(it.range.center - time) }!! return segment.range.toMilliseconds() } - - } \ No newline at end of file From f215df9c969bca8dec1c9db137399995fafccbc7 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 31 Oct 2022 17:44:01 +0100 Subject: [PATCH 019/498] Minor adjustments to existing model. --- .../rest/types/competition/team/ApiTeam.kt | 1 - .../kotlin/dev/dres/data/model/admin/User.kt | 4 +- .../competition/CompetitionDescription.kt | 5 +- .../model/competition/task/TaskDescription.kt | 6 +- .../dres/data/model/competition/team/Team.kt | 75 ++----------------- 5 files changed, 18 insertions(+), 73 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt index 8a552ce68..dd9bc780f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt @@ -14,7 +14,6 @@ data class ApiTeam( val teamId: TeamId, val name: String, val color: String, - val logoId: String, val users: List = emptyList(), var logoData: String? = null ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index 3a8b7c733..3aea2d7e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -31,10 +31,10 @@ class User(entity: Entity): PersistentEntity(entity) { set(value) { this.id = value } /** The name held by this [User]. Must be unique!*/ - var username by xdRequiredStringProp(unique = true, trimmed = false) { length(MIN_LENGTH_USERNAME, 16, "Username must consist of between 4 and 16 characters")} + var username by xdRequiredStringProp(unique = true, trimmed = true) { length(MIN_LENGTH_USERNAME, 16, "Username must consist of between 4 and 16 characters")} /** The password held by this [User]. */ - var password by xdRequiredStringProp(unique = false, trimmed = false) + var password by xdRequiredStringProp(unique = false, trimmed = true) /** The [Role] of this [User]. */ var role by xdLink1(Role) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt index 2c95c6d2f..1566a98e5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt @@ -37,7 +37,10 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ set(value) { this.id = value } /** The name held by this [CompetitionDescription]. Must be unique!*/ - var name by xdRequiredStringProp(unique = true, trimmed = false) + var name by xdRequiredStringProp(unique = true, trimmed = true) + + /** If set, this [CompetitionDescription] is considered a template!*/ + var template by xdBooleanProp() /** An optional description of this [CompetitionDescription]. */ var description by xdStringProp(trimmed = false) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt index 8d4ae4cf4..096158176 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.interfaces.SubmissionFilterFactory import dev.dres.data.model.competition.interfaces.TaskScorerFactory @@ -40,7 +41,10 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact set(value) { this.id = value } /** The name held by this [Team]. Must be unique!*/ - var name by xdRequiredStringProp(unique = false, trimmed = false) + var name by xdRequiredStringProp(unique = false, trimmed = true) + + /** If set, this [CompetitionDescription] is considered a template!*/ + var template by xdBooleanProp() /** The [TaskGroup] this [TaskDescription] belongs to. */ var taskGroup by xdParent(TaskGroup::tasks) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt index f2dc16608..3c9558c12 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.competition.team import dev.dres.api.rest.types.competition.team.ApiTeam -import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User import dev.dres.data.model.competition.CompetitionDescription @@ -9,19 +8,11 @@ import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.query.asSequence -import java.awt.image.BufferedImage -import java.io.ByteArrayInputStream -import java.nio.file.Files -import java.nio.file.Paths -import java.util.* -import javax.imageio.ImageIO + /** The ID of a [Team]. */ typealias TeamId = String -/** The ID of a [Team]'s logo. */ -typealias LogoId = String - /** * Represents a [Team] that takes part in a competition managed by DRES. * @@ -34,58 +25,6 @@ class Team(entity: Entity) : PersistentEntity(entity) { override val compositeIndices = listOf( listOf(Team::name, Team::competition) ) - - /** - * Stores the given image data to disk. - * - * @param config The [Config] object with global configuration. - * @param data The Base64 encoded image data. - * @param logoId The [LogoId] of the logo to store. - * - * @return The UID of the image. - */ - fun storeLogo(config: Config, data: String, logoId: LogoId): LogoId { - /* Parse image data. */ - val base64Image: String = data.substringAfter(",") - val imageBytes = Base64.getDecoder().decode(base64Image) - val image = ByteArrayInputStream(imageBytes).use { - val original = ImageIO.read(it) - if (original.width <= config.logoMaxSize && original.height <= config.logoMaxSize) { - original - } else { - val target = if (original.width > original.height) { - Pair(config.logoMaxSize, (original.height * (config.logoMaxSize.toDouble() / original.width)).toInt()) - } else { - Pair((original.width * (config.logoMaxSize.toDouble() / original.height)).toInt(), config.logoMaxSize) - } - val resizedImage = BufferedImage(target.first, target.second, BufferedImage.TYPE_INT_ARGB) - val graphics2D = resizedImage.createGraphics() - graphics2D.drawImage(original, 0, 0, target.first, target.second, null) - graphics2D.dispose() - resizedImage - } - } - - /* Generate UID and prepare file path. */ - val path = Team.logoPath(config, logoId) - if (!Files.exists(path.parent)) { - Files.createDirectories(path.parent) - } - - /* Generate UID and write image to disk. */ - Files.newOutputStream(path).use { - ImageIO.write(image, "PNG", it) - } - return logoId - } - - /** - * Generates and returns the [Path] to the team logo with the given [logoId]. - * - * @param config The global [Config] used to construct the [Path]. - * @param logoId The ID of the desired logo. - */ - fun logoPath(config: Config, logoId: LogoId) = Paths.get(config.cachePath, "logos", "${logoId}.png") } /** The [TeamId] of this [Team]. */ @@ -94,13 +33,13 @@ class Team(entity: Entity) : PersistentEntity(entity) { set(value) { this.id = value } /** The name held by this [Team]. Must be unique!*/ - var name by xdRequiredStringProp(unique = false, trimmed = false) + var name by xdRequiredStringProp(unique = false, trimmed = true) - /** The password held by this [User]. */ - var color by xdRequiredStringProp(unique = false, trimmed = false) + /** The color used by this [Team]. HTML colour code. */ + var color by xdRequiredStringProp(unique = false, trimmed = true) - /** The password held by this [User]. */ - var logoId by xdRequiredStringProp(unique = false, trimmed = false) + /** Logo used by this [Team] as Blob. */ + var logo by xdRequiredBlobProp() /** The [CompetitionDescription] this [Team] belongs to. */ var competition by xdParent(CompetitionDescription::teams) @@ -118,5 +57,5 @@ class Team(entity: Entity) : PersistentEntity(entity) { * * @return [ApiTeam] */ - fun toApi() = ApiTeam(this.teamId, this.name, this.color, this.logoId, this.users.asSequence().map { it.toApi() }.toList()) + fun toApi() = ApiTeam(this.teamId, this.name, this.color, this.users.asSequence().map { it.toApi() }.toList()) } \ No newline at end of file From 7c83ba3ff0ba3a7aa6c0e1608102ddeb6f3faab0 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 2 Nov 2022 11:10:17 +0100 Subject: [PATCH 020/498] Continued work... started to attack the Task and Competition (Run) facilities. --- backend/src/main/kotlin/dev/dres/DRES.kt | 4 +- .../dres/api/cli/MediaCollectionCommand.kt | 6 +- .../description/UpdateCompetitionHandler.kt | 4 +- .../competition/ApiCompetitionDescription.kt | 2 + .../types/competition/tasks/ApiTaskGroup.kt | 2 +- .../types/competition/tasks/ApiTaskType.kt | 2 +- .../tasks/options/ApiTargetOption.kt | 4 +- .../competition/CompetitionDescription.kt | 9 +- .../model/competition/task/TaskDescription.kt | 5 +- .../competition/task/TaskDescriptionTarget.kt | 12 +-- .../data/model/competition/task/TaskGroup.kt | 5 +- .../data/model/competition/task/TaskType.kt | 4 +- .../competition/task/options/TargetOption.kt | 6 +- .../model/competition/team/TeamAggregator.kt | 16 +++- .../competition/team/TeamAggregatorImpl.kt | 8 +- .../data/model/competition/team/TeamGroup.kt | 10 +++ .../dev/dres/data/model/media/MediaItem.kt | 5 +- .../data/model/media/MediaItemSegmentList.kt | 2 +- .../{MediaItemSegment.kt => MediaSegment.kt} | 18 ++-- .../data/model/run/AbstractCompetitionRun.kt | 88 +++++++++++++++++++ .../data/model/run/AbstractInteractiveTask.kt | 86 ++++++++++-------- .../model/run/AbstractNonInteractiveTask.kt | 33 ++++--- .../dev/dres/data/model/run/AbstractRun.kt | 52 ----------- .../dres/data/model/run/AbstractTaskRun.kt | 24 ++--- .../dev/dres/data/model/run/Competition.kt | 53 +++++++++++ .../run/InteractiveAsynchronousCompetition.kt | 12 +-- .../run/InteractiveSynchronousCompetition.kt | 79 ++++++----------- .../model/run/NonInteractiveCompetition.kt | 13 +-- .../kotlin/dev/dres/data/model/run/RunType.kt | 23 +++++ .../kotlin/dev/dres/data/model/run/Task.kt | 39 ++++++++ .../data/model/run/interfaces/Competition.kt | 21 +++-- .../dev/dres/data/model/run/interfaces/Ids.kt | 6 -- .../dev/dres/data/model/run/interfaces/Run.kt | 3 - .../dres/data/model/run/interfaces/Task.kt | 3 +- .../run/InteractiveAsynchronousRunManager.kt | 3 +- .../run/InteractiveSynchronousRunManager.kt | 3 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 6 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 21 +++-- .../MediaItemsSubmissionValidator.kt | 30 ++++--- .../TemporalContainmentSubmissionValidator.kt | 4 +- ...TemporalOverlapSubmissionBatchValidator.kt | 11 ++- .../TemporalOverlapSubmissionValidator.kt | 50 ++++++----- .../kotlin/dev/dres/utilities/TimeUtil.kt | 11 ++- 43 files changed, 484 insertions(+), 314 deletions(-) rename backend/src/main/kotlin/dev/dres/data/model/media/{MediaItemSegment.kt => MediaSegment.kt} (59%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/Task.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index e3f8c2d81..4eddae180 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -15,7 +15,7 @@ import dev.dres.data.model.competition.team.TeamAggregator import dev.dres.data.model.competition.team.TeamGroup import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaItemSegment +import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.media.MediaType import dev.dres.mgmt.admin.UserManager import dev.dres.run.RunExecutor @@ -103,7 +103,7 @@ object DRES { MediaType, MediaCollection, MediaItem, - MediaItemSegment, + MediaSegment, ConfiguredOption, HintOption, TaskOption, diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index ab3f9fdff..8b22ab738 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -30,7 +30,7 @@ import kotlin.io.path.nameWithoutExtension import kotlin.io.path.relativeTo /** - * A collection of [CliktCommand]s for [MediaItem], [MediaCollection] and [MediaItemSegment] management. + * A collection of [CliktCommand]s for [MediaItem], [MediaCollection] and [MediaSegment] management. * * @author Luca Rossetto * @author Ralph Gasser @@ -552,7 +552,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /** - * [CliktCommand] to import a [MediaItemSegment]s. + * [CliktCommand] to import a [MediaSegment]s. * * Uses the VBS format. */ @@ -592,7 +592,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik if (videoItem != null) { inserted += 1 videoItem.segments.add( - MediaItemSegment.new { + MediaSegment.new { this.id = UUID.randomUUID().toString() this.name = videoName this.start = start diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt index 94fbf22d9..c83da2019 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt @@ -119,8 +119,8 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) t.targets.add(TaskDescriptionTarget.new { this.item = item this.type = target.type.type - this.temporalRangeStart = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() - this.temporalRangeEnd = target.range?.end?.toTemporalPoint(item.fps?: 0.0f)?.toMilliseconds() + this.start = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() + this.end = target.range?.end?.toTemporalPoint(item.fps?: 0.0f)?.toMilliseconds() }) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt index 5f6ec598a..07547c4ff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.competition +import dev.dres.api.rest.types.competition.tasks.ApiTaskDescription import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup import dev.dres.api.rest.types.competition.tasks.ApiTaskType import dev.dres.api.rest.types.competition.team.ApiTeam @@ -18,6 +19,7 @@ data class ApiCompetitionDescription( val description: String?, val taskTypes: List, val taskGroups: List, + val tasks: List, val teams: List, val teamGroups: List, val judges: List diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt index 69ca4be97..8030d1088 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt @@ -8,4 +8,4 @@ import dev.dres.data.model.competition.task.TaskGroup * @author Ralph Gasser * @version 1.0.0 */ -data class ApiTaskGroup(val name: String, val type: String, val tasks: List) \ No newline at end of file +data class ApiTaskGroup(val name: String, val type: String) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt index 25da79d9e..dea27d194 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.competition.task.TaskType data class ApiTaskType( val name: String, val duration: Long, - val targetOptions: List, + val targetOption: ApiTargetOption, val hintOptions: List, val submissionOptions: List, val taskOptions: List, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt index 3e1d22494..853f7ec73 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt @@ -9,8 +9,8 @@ import dev.dres.data.model.competition.task.options.TargetOption * @version 1.0.0 */ enum class ApiTargetOption(val option: TargetOption){ - SINGLE_MEDIA_ITEM(TargetOption.SINGLE_MEDIA_ITEM), - SINGLE_MEDIA_SEGMENT(TargetOption.SINGLE_MEDIA_SEGMENT), + SINGLE_MEDIA_ITEM(TargetOption.MEDIA_ITEM), + SINGLE_MEDIA_SEGMENT(TargetOption.MEDIA_SEGMENT), MULTIPLE_MEDIA_ITEMS(TargetOption.MULTIPLE_MEDIA_ITEMS), JUDGEMENT(TargetOption.JUDGEMENT), VOTE(TargetOption.VOTE), diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt index 1566a98e5..22300bce9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt @@ -3,6 +3,7 @@ package dev.dres.data.model.competition import dev.dres.api.rest.types.competition.ApiCompetitionDescription import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaType import dev.dres.data.model.competition.task.TaskGroup @@ -51,6 +52,9 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ /** The [TaskGroup]s that are part of this [CompetitionDescription]. */ val taskGroups by xdChildren0_N(TaskGroup::competition) + /** The [TaskDescription]s contained in this [CompetitionDescription]*/ + val tasks by xdChildren0_N(TaskDescription::competition) + /** The [Team]s that are part of this [CompetitionDescription]. */ val teams by xdChildren0_N(Team::competition) @@ -73,6 +77,7 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ description = this.description, taskTypes = this.taskTypes.asSequence().map { it.toApi() }.toList(), taskGroups = this.taskGroups.asSequence().map { it.toApi() }.toList(), + tasks = this.tasks.asSequence().map { it.toApi() }.toList(), teams = this.teams.asSequence().map { it.toApi() }.toList(), teamGroups = this.teamsGroups.asSequence().map { it.toApi() }.toList(), judges = this.judges.asSequence().map { it.id }.toList() @@ -102,13 +107,13 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ * @return [List] of [MediaItem]s */ fun getAllVideos(): List> { - val hints = this.taskGroups.flatMapDistinct { it.tasks } + val hints = this.tasks .flatMapDistinct { it.hints } .filter { (it.item ne null) and (it.item!!.type eq MediaType.VIDEO) }.asSequence().map { it.item!!to it.range!! } - val targets = this.taskGroups.flatMapDistinct { it.tasks } + val targets = this.tasks .flatMapDistinct { it.targets } .filter { (it.item ne null) and (it.item!!.type eq MediaType.VIDEO) }.asSequence().map { it.item!! to it.range!! diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt index 096158176..460a23689 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt @@ -47,7 +47,10 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact var template by xdBooleanProp() /** The [TaskGroup] this [TaskDescription] belongs to. */ - var taskGroup by xdParent(TaskGroup::tasks) + var taskGroup by xdLink1(TaskGroup) + + /** The [CompetitionDescription] this [TaskDescription] belongs to. */ + var competition by xdParent(CompetitionDescription::tasks) /** The [MediaCollection] this [TaskDescription] operates upon. */ var collection by xdLink1(MediaCollection) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt index cb3ee5ece..fd5232bb3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt @@ -41,15 +41,15 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { var text by xdStringProp() { requireIf { item == null }} /** The start of a (potential) range. */ - var temporalRangeStart by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } + var start by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } /** The start of a (potential) range. */ - var temporalRangeEnd by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } + var end by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ val range: TemporalRange? - get() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { - TemporalRange(TemporalPoint.Millisecond(this.temporalRangeStart!!), TemporalPoint.Millisecond(this.temporalRangeEnd!!)) + get() = if (this.start != null && this.end != null) { + TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) } else { null } @@ -64,7 +64,7 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { TargetType.JUDGEMENT -> "Judgement" TargetType.JUDGEMENT_WITH_VOTE -> "Judgement with vote" TargetType.MEDIA_ITEM -> "Media item ${this.item?.name}" - TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.temporalRangeStart} - ${this.temporalRangeEnd}" + TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.start} - ${this.end}" TargetType.TEXT -> "Text: ${this.text}" else -> throw IllegalStateException("The task description type ${this.type.description} is currently not supported.") } @@ -100,7 +100,7 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { MediaType.IMAGE -> ApiContentType.IMAGE else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") } - val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)) + val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.start, this.end)) ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> stream.readAllBytes() diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt index 9ed576fa8..70131165c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt @@ -30,9 +30,6 @@ class TaskGroup(entity: Entity) : XdEntity(entity) { /** The [CompetitionDescription] this [TaskGroup] belongs to. */ var competition by xdParent(CompetitionDescription::taskGroups) - /** The [TaskDescription]s contained in this [TaskGroup]*/ - val tasks by xdChildren0_N(TaskDescription::taskGroup) - /** * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. * @@ -41,5 +38,5 @@ class TaskGroup(entity: Entity) : XdEntity(entity) { * @return [ApiTargetType] */ fun toApi(): ApiTaskGroup - = ApiTaskGroup(this.name,this.type.name,this.tasks.asSequence().map { it.toApi() }.toList()) + = ApiTaskGroup(this.name,this.type.name) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt index 54bd31539..405713b16 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt @@ -38,7 +38,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { var duration by xdRequiredLongProp() { min(0L) } /** The [TargetOption] for this [TaskType]. Specifies the type of target. */ - val targets by xdLink1_N(TargetOption) + val target by xdLink1(TargetOption) /** The [HintOption]s that make-up this [TaskType]. */ val hints by xdLink0_N(HintOption) @@ -63,7 +63,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { fun toApi(): ApiTaskType = ApiTaskType( name = this.name, duration = this.duration, - targetOptions = this.targets.asSequence().map { it.toApi() }.toList(), + targetOption = this.target.toApi(), hintOptions = this.hints.asSequence().map { it.toApi() }.toList(), submissionOptions = this.submission.asSequence().map { it.toApi() }.toList(), taskOptions = this.options.asSequence().map { it.toApi() }.toList(), diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt index 93effe4fa..82e45ee55 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt @@ -5,6 +5,7 @@ import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp + /** * An enumeration of potential options for [TaskDescription] targets. * @@ -13,9 +14,8 @@ import kotlinx.dnq.xdRequiredStringProp */ class TargetOption(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { - val SINGLE_MEDIA_ITEM by enumField { description = "SINGLE_MEDIA_ITEM" } - val SINGLE_MEDIA_SEGMENT by enumField { description = "SINGLE_MEDIA_SEGMENT" } - val MULTIPLE_MEDIA_ITEMS by enumField { description = "MULTIPLE_MEDIA_ITEMS" } + val MEDIA_ITEM by enumField { description = "MEDIA_ITEM" } + val MEDIA_SEGMENT by enumField { description = "MEDIA_SEGMENT" } val JUDGEMENT by enumField { description = "JUDGEMENT" } val VOTE by enumField { description = "VOTE" } val TEXT by enumField { description = "TEXT" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt index ca30630b6..136b0f467 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt @@ -6,7 +6,7 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * + * Enumeration of available [TeamAggregator]s. * * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 @@ -22,4 +22,18 @@ class TeamAggregator(entity: Entity) : XdEnumEntity(entity) { /** Name / description of the [TeamAggregator]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Creates and returns a new [TeamAggregatorImpl] for this [TeamAggregator]. + * + * @param teams The list of [Team]s to create the [TeamAggregatorImpl] for. + * @return [TeamAggregatorImpl] + */ + fun newInstance(teams: List) = when(this) { + MAX -> TeamAggregatorImpl.Max(teams) + MIN -> TeamAggregatorImpl.Min(teams) + MEAN -> TeamAggregatorImpl.Mean(teams) + LAST -> TeamAggregatorImpl.Last(teams) + else -> throw IllegalStateException("Failed to generated aggregator for unknown team group ${this.description}.") + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt index b542ed7d1..88533480c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt @@ -1,13 +1,15 @@ package dev.dres.data.model.competition.team /** + * Implementation of different [TeamAggregator]s. * - * @author Ralph Gasser - * @version 1.0 + * @author Luca Rossetto + * @version 1.0.0 */ sealed class TeamAggregatorImpl constructor(teams: List) { - private val teamIds = teams.map { it }.toSet() + /** Internal set of [TeamId]s. */ + private val teamIds = teams.map { it.teamId }.toSet() var lastValue: Double = 0.0 private set diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt index 8dcc9e935..2e5d440e3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt @@ -7,6 +7,7 @@ import dev.dres.data.model.competition.CompetitionDescription import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.toList typealias TeamGroupId = String @@ -48,4 +49,13 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { * @return [ApiTeamGroup] */ fun toApi() = ApiTeamGroup(this.teamGroupId, this.name, this.teams.asSequence().map { it.toApi() }.toList(), this.defaultAggregator.name) + + /** + * Returns a new [TeamAggregatorImpl] for this [TeamGroup]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [TeamAggregatorImpl] + */ + fun newAggregator() : TeamAggregatorImpl = this.defaultAggregator.newInstance(this.teams.toList()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index 174b5cd4f..9ec2ac9ff 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.media import dev.dres.api.rest.types.collection.ApiMediaItem -import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -43,8 +42,8 @@ class MediaItem(entity: Entity) : PersistentEntity(entity) { /** The [MediaCollection] this [MediaItem] belongs to. */ var collection by xdParent(MediaCollection::items) - /** List of [MediaItemSegment] that this [MediaItem] contains. */ - val segments by xdChildren0_N(MediaItemSegment::item) + /** List of [MediaSegment] that this [MediaItem] contains. */ + val segments by xdChildren0_N(MediaSegment::item) /** * Generates a [ApiMediaItem] this [MediaItem] and returns it. diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt index dc3ed0037..e3aa4178a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt @@ -8,7 +8,7 @@ import dev.dres.data.model.UID * @author Ralph Gasser * @version 1.0 */ -data class MediaItemSegmentList(override var id: UID, val mediaItemId: UID, val segments: MutableList) : PersistentEntity { +data class MediaItemSegmentList(override var id: UID, val mediaItemId: UID, val segments: MutableList) : PersistentEntity { init { require(segments.all { it.mediaItemId == mediaItemId } ){"All segments need to belong to the same media item"} } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt similarity index 59% rename from backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt index e01927606..b991e8e47 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt @@ -13,27 +13,27 @@ import kotlinx.dnq.simple.min * @author Ralph Gasser * @version 2.0.0 */ -class MediaItemSegment(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() { - /** Combination of [MediaItemSegment] name / item must be unique. */ +class MediaSegment(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() { + /** Combination of [MediaSegment] name / item must be unique. */ override val compositeIndices = listOf( - listOf(MediaItemSegment::name, MediaItemSegment::item) + listOf(MediaSegment::name, MediaSegment::item) ) } - /** The name of this [MediaItemSegment]. */ + /** The name of this [MediaSegment]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [MediaType] of this [MediaItem]. */ - var item by xdParent(MediaItem::segments) + var item by xdParent(MediaItem::segments) - /** The start frame number of this [MediaItemSegment]. */ + /** The start frame number of this [MediaSegment]. */ var start by xdRequiredIntProp { min(0L) } - /** The end frame number of this [MediaItemSegment]. */ + /** The end frame number of this [MediaSegment]. */ var end by xdRequiredIntProp { min(0L) } - /** Returns the [range] of this [MediaItemSegment] as [TemporalRange]. */ + /** Returns the [range] of this [MediaSegment] as [TemporalRange]. */ val range: TemporalRange get() = TemporalRange(TemporalPoint.Frame(this.start, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.end, this.item.fps ?: 1.0f)) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt new file mode 100644 index 000000000..9e4565957 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt @@ -0,0 +1,88 @@ +package dev.dres.data.model.run + + +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.run.interfaces.Run + +/** + * An abstract [Run] implementation that can be used by different subtypes. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractCompetitionRun(protected val competition: Competition): dev.dres.data.model.run.interfaces.Competition { + + /** The name of this [AbstractCompetitionRun]. */ + override val id: CompetitionId + get() = this.competition.id + + /** The name of this [AbstractCompetitionRun]. */ + override val name: String + get() = this.competition.name + + /** The [CompetitionDescription] of this [AbstractCompetitionRun]. */ + override val description: CompetitionDescription + get() = this.competition.description + + /** Timestamp of when this [AbstractCompetitionRun] was started. */ + override var started: Long + get() = this.competition.started + protected set(value) { + this.competition.started = value + } + + /** Timestamp of when this [AbstractCompetitionRun] was ended. */ + override var ended: Long? + get() = this.competition.ended + protected set(value) { + this.competition.ended = value + } + + /** Flag indicating that participants can also use the viewer for this [Competition]. */ + override var participantCanView: Boolean + get() = this.competition.participantCanView + set(value) { + this.competition.participantCanView = value + } + + /** Flag indicating that tasks can be repeated.*/ + override var allowRepeatedTasks: Boolean + get() = this.competition.allowRepeatedTasks + set(value) { + this.competition.allowRepeatedTasks = value + } + + /** A fixed limit on submission previews. */ + override var limitSubmissionPreviews: Int + get() = this.competition.limitSubmissionPreviews + set(value) { + this.competition.limitSubmissionPreviews = value + } + + /** + * Starts this [AbstractCompetitionRun]. + */ + override fun start() { + if (this.hasStarted) { + throw IllegalStateException("Run has already been started.") + } + this.started = System.currentTimeMillis() + } + + /** + * Ends this [AbstractCompetitionRun]. + */ + override fun end() { + if (!this.isRunning) { + this.started = System.currentTimeMillis() + } + this.ended = System.currentTimeMillis() + } + + override fun reactivate() { + if (this.ended == null){ + throw IllegalStateException("Run has not yet ended.") + } + this.ended = null + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index a854eb942..6f113383a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -1,18 +1,21 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.* -import dev.dres.data.model.competition.options.TargetOption -import dev.dres.data.model.competition.task.TaskDescriptionTarget -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.options.TargetOption +import dev.dres.data.model.competition.team.TeamAggregatorImpl +import dev.dres.data.model.competition.team.TeamGroupId +import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.MediaItemsSubmissionValidator import dev.dres.run.validation.TemporalOverlapSubmissionValidator import dev.dres.run.validation.TextValidator +import dev.dres.run.validation.TransientMediaSegment import dev.dres.run.validation.interfaces.SubmissionValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.BasicVoteValidator import dev.dres.run.validation.judged.ItemRange +import kotlinx.dnq.query.* import java.util.concurrent.ConcurrentLinkedQueue /** @@ -21,7 +24,7 @@ import java.util.concurrent.ConcurrentLinkedQueue * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -abstract class AbstractInteractiveTask: AbstractTaskRun(), Task { +abstract class AbstractInteractiveTask(task: Task): AbstractTaskRun(task) { /** List of [Submission]s* registered for this [Task]. */ val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() @@ -34,6 +37,18 @@ abstract class AbstractInteractiveTask: AbstractTaskRun(), Task { /** The [SubmissionValidator] used to validate [Submission]s. */ abstract val validator: SubmissionValidator + /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ + val teamGroupAggregators: Map by lazy { + // TODO: Check if transaction context is available when this is called. + this.competition.description.teamsGroups.asSequence().associate { it.id to it.newAggregator() } + } + + /** + * Adds a new [Submission] to this [AbstractInteractiveTask]. + * + * @param submission The [Submission] to append. + */ + abstract fun postSubmission(submission: Submission) /** * Generates and returns a new [SubmissionValidator] for this [TaskDescription]. Depending @@ -41,42 +56,35 @@ abstract class AbstractInteractiveTask: AbstractTaskRun(), Task { * * @return [SubmissionValidator]. */ - internal fun newValidator(): SubmissionValidator = when(description.taskType.targetType.option){ - TargetOption.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) - TargetOption.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) - TargetOption.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) - TargetOption.JUDGEMENT -> BasicJudgementValidator(knownCorrectRanges = - (description.target as TaskDescriptionTarget.JudgementTaskDescriptionTarget).targets.map { - if (it.second == null) { - ItemRange(it.first) - } else { - val item = it.first - val range = it.second!!.toMilliseconds() - ItemRange(item, range.first, range.second) - } }) - TargetOption.VOTE -> BasicVoteValidator( - knownCorrectRanges = - (description.target as TaskDescriptionTarget.VoteTaskDescriptionTarget).targets.map { - if (it.second == null) { - ItemRange(it.first) - } else { - val item = it.first - val range = it.second!!.toMilliseconds() - ItemRange(item, range.first, range.second) - } }, - parameters = description.taskType.targetType.parameters - - ) - TargetOption.TEXT -> TextValidator((description.target as TaskDescriptionTarget.TextTaskDescriptionTarget).targets) + internal fun newValidator(): SubmissionValidator = when (val targetOption = this.description.taskGroup.type.target) { + TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.description.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + TargetOption.MEDIA_SEGMENT -> { + val target = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() + TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) + } + TargetOption.TEXT -> TextValidator(this.description.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + TargetOption.JUDGEMENT -> { + val knownRanges = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + BasicJudgementValidator(knownCorrectRanges = knownRanges) + } + TargetOption.VOTE -> { + val knownRanges = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + val parameters = this.description.taskGroup.type.configurations.filter { it.key eq targetOption.description }.asSequence().associate { it.key to it.value } + BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) + } + else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } - abstract fun addSubmission(submission: Submission) - - val teamGroupAggregators: Map by lazy { - this.competition.description.teamGroups.associate { it.uid to it.newAggregator() } - } - - fun updateTeamAggregation(teamScores: Map) { + /** + * Updates the per-team aggregation for this [AbstractInteractiveTask]. + * + * @param teamScores Map of team scores. + */ + internal fun updateTeamAggregation(teamScores: Map) { this.teamGroupAggregators.values.forEach { it.aggregate(teamScores) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index f03de0abf..3e9c2a85e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,33 +1,46 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.task.TaskDescriptionTarget -import dev.dres.data.model.competition.options.TargetOption -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.competition.task.options.TargetOption import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch import dev.dres.run.validation.MediaItemsSubmissionBatchValidator import dev.dres.run.validation.TemporalOverlapSubmissionBatchValidator +import dev.dres.run.validation.TransientMediaSegment import dev.dres.run.validation.interfaces.SubmissionBatchValidator +import kotlinx.dnq.query.* /** * An abstract [Task] implementation for non-interactive [Task], i.e., [Task]s that do not rely on human interaction and simply process input data in batches * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.0 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 */ -abstract class AbstractNonInteractiveTask: AbstractTaskRun(), Task { +abstract class AbstractNonInteractiveTask(task: Task): AbstractTaskRun(task) { + /** The [SubmissionBatchValidator] used by this [AbstractNonInteractiveTask]. */ @Transient val validator: SubmissionBatchValidator = newValidator() - internal fun newValidator(): SubmissionBatchValidator = when(this.description.taskType.targetType.option){ - TargetOption.SINGLE_MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(setOf((description.target as TaskDescriptionTarget.MediaItemTarget).item)) - TargetOption.SINGLE_MEDIA_SEGMENT -> TemporalOverlapSubmissionBatchValidator(description.target as TaskDescriptionTarget.VideoSegmentTarget) - TargetOption.MULTIPLE_MEDIA_ITEMS -> MediaItemsSubmissionBatchValidator((description.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.toSet()) + /** + * Generates a new [SubmissionBatchValidator]. + * + * @return [SubmissionBatchValidator] + */ + fun newValidator(): SubmissionBatchValidator = when(this.description.taskGroup.type.target){ + TargetOption.MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(this.description.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + TargetOption.MEDIA_SEGMENT -> { + val target = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() + TemporalOverlapSubmissionBatchValidator(TransientMediaSegment(target.item!!, target.range!!)) + } TargetOption.JUDGEMENT -> TODO() TargetOption.VOTE -> TODO() TargetOption.TEXT -> TODO() + else -> throw IllegalStateException("The provided target option ${this.description.taskGroup.type.target.description} is not supported by non-interactive tasks.") } + /** + * + */ abstract fun addSubmissionBatch(origin: OriginAspect, batches: List>) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt deleted file mode 100644 index e814aa34e..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractRun.kt +++ /dev/null @@ -1,52 +0,0 @@ -package dev.dres.data.model.run - - -import dev.dres.data.model.run.interfaces.Run - -/** - * An abstract [Run] implementation that can be used by different subtypes. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -abstract class AbstractRun: Run { - - /** Timestamp of when this [AbstractRun] was started. */ - @Volatile - override var started: Long? = null - protected set - - /** Timestamp of when this [AbstractRun] was ended. */ - @Volatile - override var ended: Long? = null - protected set - - /** - * Starts this [AbstractRun]. - */ - override fun start() { - if (this.hasStarted) { - throw IllegalStateException("Run has already been started.") - } - this.started = System.currentTimeMillis() - } - - /** - * Ends this [AbstractRun]. - */ - override fun end() { - if (!this.isRunning) { - this.started = System.currentTimeMillis() - } - this.ended = System.currentTimeMillis() - } - - override fun reactivate() { - - if (this.ended == null){ - throw IllegalStateException("Run has not yet ended.") - } - this.ended = null - - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt index e0faf8f77..6f5cc3a3f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt @@ -1,8 +1,6 @@ package dev.dres.data.model.run - import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.run.interfaces.Task import dev.dres.run.TaskRunStatus /** @@ -11,17 +9,24 @@ import dev.dres.run.TaskRunStatus * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractTaskRun: Task { +abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.run.interfaces.Task { + /** The Id of this [AbstractTaskRun]. */ + override val id: TaskId + get() = this.task.id /** Timestamp of when this [AbstractTaskRun] was started. */ - @Volatile - override var started: Long? = null - protected set + override var started: Long + get() = this.task.started + protected set(value) { + this.task.started = value + } /** Timestamp of when this [AbstractTaskRun] was ended. */ - @Volatile - override var ended: Long? = null - protected set + override var ended: Long? + get() = this.task.ended + protected set(value) { + this.task.ended = value + } @Volatile override var status: TaskRunStatus = TaskRunStatus.CREATED @@ -65,6 +70,5 @@ abstract class AbstractTaskRun: Task { } this.ended = null this.status = TaskRunStatus.RUNNING - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt new file mode 100644 index 000000000..a9db48dac --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt @@ -0,0 +1,53 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.competition.CompetitionDescription +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* + +typealias CompetitionId = String + +/** + * Represents a [Competition], i.e., a concrete instance of a [CompetitionDescription], as executed by DRES. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class Competition(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The [CompetitionId] of this [Competition]. */ + var competitionId: CompetitionId + get() = this.id + set(value) { this.id = value } + + /** The name held by this [Competition]. Must be unique!*/ + var name by xdRequiredStringProp(unique = true, trimmed = true) + + /** The [RunType] of this [Competition]. */ + var type by xdLink1(RunType) + + /** The [CompetitionDescription] backing this [Competition]. */ + var description by xdLink1(CompetitionDescription) + + /** Timestamp of when this [Competition] started. */ + var started by xdRequiredLongProp() + + /** Timestamp of when this [Competition] ended. */ + var ended by xdNullableLongProp() + + /** The [Task]s that belong to this [Competition]. */ + val tasks by xdChildren0_N(Task::competition) + + /** Flag indicating that participants can also use the viewer for this [Competition]. */ + var participantCanView by xdBooleanProp() + + /** Flag indicating that tasks should be shuffled. is only used for asynchronous runs */ + var shuffleTasks by xdBooleanProp() + + /** Flag indicating that tasks can be repeated. is only used for asynchronous runs */ + var allowRepeatedTasks by xdBooleanProp() + + /** A fixed limit on submission previews. */ + var limitSubmissionPreviews by xdIntProp() +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt index d38c566fc..544c786e7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt @@ -7,8 +7,6 @@ import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission @@ -26,13 +24,7 @@ import java.util.concurrent.ConcurrentHashMap * [InteractiveAsynchronousCompetition]s can be started and ended, and they can be used to create new [Task]s and access the current [Task]. * */ -class InteractiveAsynchronousCompetition( - override var id: CompetitionId, - override val name: String, - override val description: CompetitionDescription, - override var properties: RunProperties, - val permutation: Map> -) : AbstractRun(), Competition { +class InteractiveAsynchronousCompetition(competition: Competition, val permutation: Map>) : AbstractCompetitionRun(competition) { companion object { fun generatePermutation( @@ -292,7 +284,7 @@ class InteractiveAsynchronousCompetition( * @throws IllegalArgumentException If [Submission] could not be added for any reason. */ @Synchronized - override fun addSubmission(submission: Submission) { + override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveAsynchronousCompetition.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt index bc4fc87d2..80398829b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt @@ -1,14 +1,9 @@ package dev.dres.data.model.run -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId +import dev.dres.data.model.competition.task.TaskDescriptionId import dev.dres.data.model.run.InteractiveSynchronousCompetition.Task -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission @@ -16,9 +11,9 @@ import dev.dres.run.audit.AuditLogger import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.* import java.util.* - /** * Represents a concrete, interactive and synchronous [Run] of a [CompetitionDescription]. * @@ -27,56 +22,36 @@ import java.util.* * @author Ralph Gasser * @param 1.3.0 */ -class InteractiveSynchronousCompetition( - override var id: CompetitionId, - override val name: String, - override val description: CompetitionDescription, - override var properties: RunProperties -) : AbstractRun(), Competition { - - internal constructor( - id: CompetitionId, - name: String, - competitionDescription: CompetitionDescription, - runProperties: RunProperties, - started: Long, - ended: Long - ) : this(id, name, competitionDescription, runProperties) { - this.started = if (started == -1L) { - null - } else { - started - } - this.ended = if (ended == -1L) { - null - } else { - ended - } - } +class InteractiveSynchronousCompetition(competition: Competition) : AbstractCompetitionRun(competition) { init { - require(this.description.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. " } - require(this.description.teams.size > 0) { "Cannot create a run from a competition that doesn't have any teams. " } + require(this.competition.type == RunType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.competition.type}. This is a programmer's error!" } + require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } + require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [Task]s registered for this [InteractiveSynchronousCompetition]. */ override val tasks: List = LinkedList() /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ - var currentTaskDescription = this.description.tasks[0] + var currentTaskDescription = this.description.tasks.first() private set - fun goTo(index: Int) { - currentTaskDescription = this.description.tasks[index] - } - /** Returns the last [Task]. */ - @get:JsonIgnore val currentTask: Task? - get() = this.tasks.findLast { it.description.id == currentTaskDescription.id } + get() = this.tasks.findLast { it.description.id == this.currentTaskDescription.id } override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" + /** + * Moves this [InteractiveSynchronousCompetition] to the given task index. + * + * @param index The new task index to move to. + */ + fun goTo(index: Int) { + this.currentTaskDescription = this.description.tasks.drop(index).first() + } + /** * Represents a concrete [Run] of a [TaskDescription]. [Task]s always exist within a [InteractiveSynchronousCompetition]. * As a [InteractiveSynchronousCompetition], [Task]s can be started and ended and they can be used to register [Submission]s. @@ -84,9 +59,7 @@ class InteractiveSynchronousCompetition( * @version 1.2.0 * @author Ralph Gasser */ - @JsonIgnoreProperties(value = ["competition"]) - inner class Task(override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId) : Run, - AbstractInteractiveTask() { + inner class TaskRun(task: Task): AbstractInteractiveTask(task) { internal constructor(uid: TaskId, taskId: TaskDescriptionId, started: Long, ended: Long) : this(uid, taskId) { this.started = if (started == -1L) { @@ -107,20 +80,18 @@ class InteractiveSynchronousCompetition( /** The position of this [Task] within the [InteractiveSynchronousCompetition]. */ override val position: Int - get() = this@InteractiveSynchronousCompetition.tasks.indexOf(this) + get() = this@InteractiveSynchronousCompetition.tasks.indexOf(this.task) /** Reference to the [TaskDescription] describing this [Task]. */ - @Transient - override val description: TaskDescription = - this@InteractiveSynchronousCompetition.description.tasks.find { it.id == this.taskDescriptionId } - ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") + override val description: TaskDescription + get() = this.task.description @Transient - override val filter: SubmissionFilter = description.newFilter() + override val filter: SubmissionFilter = this.description.newFilter() @Transient override val scorer: TeamTaskScorer = this.description.newScorer() as? TeamTaskScorer - ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") + ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") @Transient override val validator: SubmissionValidator = newValidator() @@ -141,9 +112,9 @@ class InteractiveSynchronousCompetition( * @param submission The [Submission] to add. */ @Synchronized - override fun addSubmission(submission: Submission) { + override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveSynchronousCompetition.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this@InteractiveSynchronousCompetition.description.teams.any { it.uid == submission.teamId }) { + check(this@InteractiveSynchronousCompetition.description.teams.filter { it eq submission.team }.any()) { "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetition.name}. This is a programmer's error!" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt index 9c9bb94a7..617fd9441 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt @@ -1,29 +1,18 @@ package dev.dres.data.model.run import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch import dev.dres.run.score.interfaces.ResultBatchTaskScorer -class NonInteractiveCompetition( - override var id: CompetitionId, - override val name: String, - override val description: CompetitionDescription, - override var properties: RunProperties -) : AbstractRun(), Competition { +class NonInteractiveCompetition(competition: Competition) : AbstractCompetitionRun(competition) { /** */ override val tasks: List = this.description.tasks.map { TaskContainer(taskDescriptionId = it.id) } - inner class TaskContainer(override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId) : AbstractNonInteractiveTask() { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt new file mode 100644 index 000000000..de9e8e8fa --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt @@ -0,0 +1,23 @@ +package dev.dres.data.model.run + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * Enumeration of the type of [Competition]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class RunType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val INTERACTIVE_SYNCHRONOUS by enumField { description = "INTERACTIVE_SYNCHRONOUS" } + val INTERACTIVE_ASYNCHRONOUS by enumField { description = "INTERACTIVE_ASYNCHRONOUS" } + val NON_INTERACTIVE by enumField { description = "NON_INTERACTIVE" } + } + + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt new file mode 100644 index 000000000..274451416 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -0,0 +1,39 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.competition.task.TaskDescription +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* + +typealias TaskId = String + +/** + * Represents a [Task], i.e., a concrete instance of a [TaskDescription], as executed by DRES. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class Task(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The [TaskId] of this [Task]. */ + var taskId: TaskId + get() = this.id + set(value) { this.id = value } + + /** The [RunType] of this [Competition]. */ + var type by xdLink1(RunType) + + /** Timestamp of when this [Competition] started. */ + var started by xdRequiredLongProp() + + /** Timestamp of when this [Competition] ended. */ + var ended by xdNullableLongProp() + + /** The [TaskDescription] this [Task] is an instance of. */ + var description by xdLink1(TaskDescription) + + /** The [Competition] this [Task] belongs to. */ + var competition by xdParent(Competition::tasks) + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt index edabe329c..54a36e351 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt @@ -1,18 +1,17 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.PersistentEntity import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.RunProperties - +import dev.dres.data.model.run.Competition +import dev.dres.data.model.run.CompetitionId /** * Represents a [Competition] that a DRES user or client takes place in and that groups several [Task]s * * @author Ralph Gasser * @version 1.0.0 */ -interface Competition: Run, PersistentEntity { - /** The unique [CompetitionId] that identifies this [Competition]. Used by the persistence layer. */ - override var id: CompetitionId +interface Competition: Run { + /** The unique [CompetitionId] that identifies this [Competition]. */ + val id: CompetitionId /** The name human readable of this [Competition]. */ val name: String @@ -23,6 +22,12 @@ interface Competition: Run, PersistentEntity { /** Collection of [Task]s that make up this [Competition]. */ val tasks: List - /** Various run-specific settings */ - var properties: RunProperties + /** Flag indicating that participants can also use the viewer for this [Competition]. */ + var participantCanView: Boolean + + /** Flag indicating that tasks can be repeated.*/ + var allowRepeatedTasks: Boolean + + /** A fixed limit on submission previews. */ + var limitSubmissionPreviews: Int } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt deleted file mode 100644 index 3c19ad285..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Ids.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.dres.data.model.run.interfaces - -import dev.dres.data.model.UID - -/** The ID of a [Competition]. */ -typealias CompetitionId = UID \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt index 000973916..9bc9d448c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Run.kt @@ -1,7 +1,5 @@ package dev.dres.data.model.run.interfaces -import java.rmi.server.UID - /** * A [Run] that can be started and ended and keeps track of the points in time, these events took place. * @@ -41,7 +39,6 @@ interface Run { */ fun end() - /** * Reactivates an ended [Run]. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt index 284ab8337..89098302a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.competition.task.TaskDescriptionId import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task import dev.dres.run.TaskRunStatus import dev.dres.run.score.interfaces.TaskScorer +typealias TaskId = String /** * Represents a [Task] solved by a DRES user or client. @@ -14,7 +15,7 @@ import dev.dres.run.score.interfaces.TaskScorer */ interface Task: Run { /** The unique [TaskId] that identifies this [Task]. Used by the persistence layer. */ - val uid: TaskDescriptionId + val id: TaskId /** Reference to the [Competition] this [Task] belongs to. */ val competition: Competition diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index d4fe651ec..9010d0d2c 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -23,7 +23,6 @@ import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator -import dev.dres.utilities.extensions.UID import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -540,7 +539,7 @@ class InteractiveAsynchronousRunManager( /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") - task.addSubmission(sub) + task.postSubmission(sub) /* Mark dao for update. */ this.daoUpdatable.dirty = true diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 1e18a2fd7..c5d9a6111 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -27,7 +27,6 @@ import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.ReadyLatch -import dev.dres.utilities.extensions.UID import org.slf4j.LoggerFactory import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -493,7 +492,7 @@ class InteractiveSynchronousRunManager( /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") - task.addSubmission(sub) + task.postSubmission(sub) /** Checks for the presence of the [SimpleOption.PROLONG_ON_SUBMISSION] and applies it. */ val option = task.description.taskType.options.find { it.option == SimpleOption.PROLONG_ON_SUBMISSION } diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 2bbde7d1b..f6ceb26a9 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -1,6 +1,6 @@ package dev.dres.run.audit -import dev.dres.api.rest.handler.SessionId +import dev.dres.api.rest.handler.users.SessionId import dev.dres.data.model.UID import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogEntry @@ -8,8 +8,8 @@ import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.competition.task.TaskDescriptionId import dev.dres.data.model.run.interfaces.CompetitionId -import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.eventstream.* @@ -105,7 +105,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskModified(competitionId: CompetitionId, taskId: TaskId, modification: String, api: AuditLogSource, session: String?) { + fun taskModified(competitionId: CompetitionId, taskId: TaskDescriptionId, modification: String, api: AuditLogSource, session: String?) { this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.TASK_MODIFIED diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index b9e8d2310..c532ea7ac 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -4,7 +4,6 @@ import dev.dres.data.model.competition.team.TeamId import dev.dres.data.model.media.MediaType import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.ScoreEntry @@ -14,6 +13,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write +/** + * A [TeamTaskScorer] used for AVS tasks. + * + * @author Luca Rossetto + * @version 1.0.1 + */ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { private var lastScores: Map = emptyMap() @@ -45,21 +50,19 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { return teamScoreMap() } - private fun countQuantized(submissions: Collection): Int { - - return submissions.filterIsInstance().groupBy { it.item }.map { - when(it.key.type) { + private fun countQuantized(submissions: Collection): Int = submissions + .filter { it.item != null } + .groupBy { it.item }.map { + when(it.key!!.type) { MediaType.IMAGE -> 1 MediaType.VIDEO -> { - val ranges = it.value.map { s -> (s as Submission.Temporal).temporalRange } + val ranges = it.value.map { s -> s.temporalRange } TimeUtil.merge(ranges, overlap = 1).size } - else -> throw IllegalStateException("Unsupported media type ${it.key.type} for AVS task scorer.") + else -> throw IllegalStateException("Unsupported media type ${it.key!!.type} for AVS task scorer.") } }.sum() - } - override fun teamScoreMap(): Map = this.lastScoresLock.read { this.lastScores } override fun scores(): List = this.lastScoresLock.read { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index bce4b8baf..2fe80583b 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -3,25 +3,29 @@ package dev.dres.run.validation import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.run.validation.interfaces.SubmissionValidator +/** + * A [SubmissionValidator] that checks if the items specified by a [Submission] match the items in the provided set. + * + * @author Luca Rossetto + * @version 1.0.1 + */ class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { - override fun validate(submission: Submission) { + /** This type of [SubmissionValidator] can be executed directly.*/ + override val deferring = false - if (submission !is ItemAspect) { + /** + * Performs the validation. + * + * @param submission The [Submission] to validate. + */ + override fun validate(submission: Submission) { + if (submission.item == null || submission.item !in this.items) { submission.status = SubmissionStatus.WRONG - return - } - - submission.status = when (submission.item) { - in items -> SubmissionStatus.CORRECT - else -> SubmissionStatus.WRONG + } else { + submission.status = SubmissionStatus.CORRECT } } - - override val deferring = false - - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index c98eb54ee..09941de11 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.media.MediaItemSegment +import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect @@ -14,7 +14,7 @@ import dev.dres.run.validation.interfaces.SubmissionValidator * @author Luca Rossetto & Ralph Gasser * @version 1.0.1 */ -class TemporalContainmentSubmissionValidator(private val task: MediaItemSegment) : SubmissionValidator { +class TemporalContainmentSubmissionValidator(private val task: MediaSegment) : SubmissionValidator { /** * Validates a [Submission] based on the target segment and the temporal overlap of the diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt index 2e35a3073..6ce6b7374 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt @@ -1,6 +1,5 @@ package dev.dres.run.validation -import dev.dres.data.model.media.MediaItemSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.TemporalAspect @@ -9,22 +8,22 @@ import dev.dres.run.validation.interfaces.SubmissionBatchValidator /** * A [SubmissionBatchValidator] class that checks, if a submission is correct based on the target segment and the - * temporal overlap of the [Submission] with the provided [MediaSegmentTaskDescription]. + * temporal overlap of the [Submission] with the provided [TransientMediaSegment]. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0.1 + * @version 1.1.0 */ -class TemporalOverlapSubmissionBatchValidator(private val targetSegment: MediaItemSegment) : SubmissionBatchValidator { +class TemporalOverlapSubmissionBatchValidator(private val targetSegment: TransientMediaSegment) : SubmissionBatchValidator { override fun validate(batch: ResultBatch<*>) { batch.results.forEach { if (it is TemporalAspect){ it.status = when { it.start > it.end -> SubmissionStatus.WRONG - it.item != targetSegment.item -> SubmissionStatus.WRONG + it.item != targetSegment.first -> SubmissionStatus.WRONG else -> { val outer = - this.targetSegment.range.toMilliseconds() + this.targetSegment.second.toMilliseconds() if ((outer.first <= it.start && outer.second >= it.start) || (outer.first <= it.end && outer.second >= it.end)) { SubmissionStatus.CORRECT } else { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index cff2cb3b5..6d0c2fd83 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -1,20 +1,26 @@ package dev.dres.run.validation import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.media.MediaItemSegment +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect +import dev.dres.data.model.submissions.SubmissionType import dev.dres.run.validation.interfaces.SubmissionValidator +/** */ +typealias TransientMediaSegment = Pair + /** * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the - * temporal overlap of the [Submission] with the provided [MediaSegmentTaskDescription]. + * temporal overlap of the [Submission] with the provided [TransientMediaSegment]. * * @author Luca Rossetto & Ralph Gasser * @version 1.0.1 */ -class TemporalOverlapSubmissionValidator(private val targetSegment: MediaItemSegment) : SubmissionValidator { +class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMediaSegment) : SubmissionValidator { + + override val deferring: Boolean = false /** * Validates a [Submission] based on the target segment and the temporal overlap of the @@ -22,25 +28,29 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: MediaItemSeg * * @param submission The [Submission] to validate. */ - override fun validate(submission: Submission){ - if (submission !is TemporalSubmissionAspect){ + override fun validate(submission: Submission) { + if (submission.type != SubmissionType.TEMPORAL) { submission.status = SubmissionStatus.WRONG return } - submission.status = when { - submission.start > submission.end -> SubmissionStatus.WRONG - submission.item != targetSegment.item -> SubmissionStatus.WRONG - else -> { - val outer = this.targetSegment.range.toMilliseconds() - if ((outer.first <= submission.start && outer.second >= submission.start) || (outer.first <= submission.end && outer.second >= submission.end)) { - SubmissionStatus.CORRECT - } else { - SubmissionStatus.WRONG - } - } + + if (submission.start > submission.end) { + submission.status = SubmissionStatus.WRONG + return } - } - override val deferring: Boolean - get() = false + /* Perform item validation. */ + if (submission.item != this.targetSegment.first) { + submission.status = SubmissionStatus.WRONG + return + } + + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if ((outer.first <= submission.start && outer.second >= submission.start) || (outer.first <= submission.end && outer.second >= submission.end)) { + submission.status = SubmissionStatus.CORRECT + } else { + submission.status = SubmissionStatus.WRONG + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt index e041a5f83..aa11d09bb 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt @@ -1,7 +1,6 @@ package dev.dres.utilities -import dev.dres.data.model.media.MediaItemSegment -import dev.dres.data.model.media.MediaItemSegmentList +import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.media.time.TemporalRange import kotlin.math.abs @@ -42,17 +41,17 @@ object TimeUtil { } /** - * Converts a shot number to a timestamp in milliseconds given a list of [MediaItemSegment]s. + * Converts a shot number to a timestamp in milliseconds given a list of [MediaSegment]s. */ - fun shotToTime(shot: String, segments: List): Pair? { + fun shotToTime(shot: String, segments: List): Pair? { val segment = segments.find { it.name == shot } ?: return null return segment.range.toMilliseconds() } /** - * Converts a shot number to a timestamp in milliseconds given a list of [MediaItemSegment]s. + * Converts a shot number to a timestamp in milliseconds given a list of [MediaSegment]s. */ - fun timeToSegment(time: Long, segments: List): Pair? { + fun timeToSegment(time: Long, segments: List): Pair? { if (segments.isEmpty()) return null val segment = segments.find { val range = it.range.toMilliseconds() From 4f4007b46cd277b0400aa6d6e67be2be073f8a22 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 2 Nov 2022 16:49:47 +0100 Subject: [PATCH 021/498] Continued migration. --- .../dev/dres/api/cli/CompetitionRunCommand.kt | 18 ++-- .../handler/CompetitionRunAdminHandler.kt | 22 ++--- .../handler/CompetitionRunScoreHandler.kt | 8 +- .../dres/api/rest/handler/DownloadHandler.kt | 9 +- .../collection/AddCollectionHandler.kt | 2 - .../handler/users/ListActiveUsersHandler.kt | 1 - .../dres/api/rest/types/AbstractRestEntity.kt | 3 - .../api/rest/types/collection/ApiMediaItem.kt | 1 - .../api/rest/types/run/AdminRunOverview.kt | 4 +- .../dres/api/rest/types/run/PastTaskInfo.kt | 2 +- .../dev/dres/api/rest/types/run/RunInfo.kt | 4 +- .../dev/dres/api/rest/types/run/RunState.kt | 4 +- .../dev/dres/api/rest/types/run/TaskInfo.kt | 4 +- .../dev/dres/api/rest/types/run/TeamInfo.kt | 4 +- .../model/competition/task/TaskDescription.kt | 4 +- ...ompetitionRun.kt => AbstractEvaluation.kt} | 46 +++++----- .../dres/data/model/run/AbstractTaskRun.kt | 7 +- .../run/{Competition.kt => Evaluation.kt} | 28 +++--- ...t => InteractiveAsynchronousEvaluation.kt} | 39 ++++----- ...kt => InteractiveSynchronousEvaluation.kt} | 85 ++++++++++--------- .../model/run/NonInteractiveCompetition.kt | 44 ---------- .../model/run/NonInteractiveEvaluation.kt | 62 ++++++++++++++ .../dres/data/model/run/RunActionContext.kt | 18 ++-- .../kotlin/dev/dres/data/model/run/RunType.kt | 2 +- .../kotlin/dev/dres/data/model/run/Task.kt | 15 ++-- .../{Competition.kt => EvaluationRun.kt} | 22 ++--- .../run/interfaces/{Task.kt => TaskRun.kt} | 11 ++- .../dres/data/model/submissions/Submission.kt | 17 ++-- .../aspects/BaseSubmissionAspect.kt | 13 --- .../model/submissions/aspects/ItemAspect.kt | 12 --- .../model/submissions/aspects/OriginAspect.kt | 15 ---- .../aspects/SpatialSubmissionAspect.kt | 8 -- .../model/submissions/aspects/StatusAspect.kt | 12 --- .../submissions/aspects/TemporalAspect.kt | 19 ----- .../aspects/TemporalSubmissionAspect.kt | 8 -- .../model/submissions/aspects/TextAspect.kt | 7 -- .../run/InteractiveAsynchronousRunManager.kt | 24 +++--- .../dev/dres/run/InteractiveRunManager.kt | 10 +-- .../run/InteractiveSynchronousRunManager.kt | 48 +++++------ .../dev/dres/run/NonInteractiveRunManager.kt | 4 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 78 ++++++++--------- .../main/kotlin/dev/dres/run/RunManager.kt | 24 +++--- .../RecalculatingSubmissionTaskScorer.kt | 11 ++- .../dres/run/score/interfaces/TaskScorer.kt | 4 +- .../run/updatables/ScoreboardsUpdatable.kt | 4 +- .../dres/run/updatables/ScoresUpdatable.kt | 4 +- 46 files changed, 358 insertions(+), 433 deletions(-) rename backend/src/main/kotlin/dev/dres/data/model/run/{AbstractCompetitionRun.kt => AbstractEvaluation.kt} (54%) rename backend/src/main/kotlin/dev/dres/data/model/run/{Competition.kt => Evaluation.kt} (56%) rename backend/src/main/kotlin/dev/dres/data/model/run/{InteractiveAsynchronousCompetition.kt => InteractiveAsynchronousEvaluation.kt} (87%) rename backend/src/main/kotlin/dev/dres/data/model/run/{InteractiveSynchronousCompetition.kt => InteractiveSynchronousEvaluation.kt} (53%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt rename backend/src/main/kotlin/dev/dres/data/model/run/interfaces/{Competition.kt => EvaluationRun.kt} (51%) rename backend/src/main/kotlin/dev/dres/data/model/run/interfaces/{Task.kt => TaskRun.kt} (69%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TextAspect.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt index d30f8ffdf..9d2ab1c25 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt @@ -11,9 +11,9 @@ import com.jakewharton.picnic.table import dev.dres.data.model.UID import dev.dres.data.model.competition.task.TaskDescriptionTarget import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.ItemAspect import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect @@ -140,7 +140,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik it.id.string, it.name, it.description.description, - if (it is InteractiveSynchronousCompetition) it.currentTask?.description?.name + if (it is InteractiveSynchronousEvaluation) it.currentTask?.description?.name ?: "N/A" else "N/A" ) }" @@ -174,7 +174,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik it.id.string, it.name, it.description.description, - if (it is InteractiveSynchronousCompetition) it.currentTask?.description?.name + if (it is InteractiveSynchronousEvaluation) it.currentTask?.description?.name ?: "N/A" else "N/A", status, it.started?.toDateString() ?: "-", @@ -214,7 +214,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik */ inner class ExportRunCommand : CliktCommand(name = "export", help = "Exports the selected competition run to a JSON file.", printHelpOnEmptyArgs = true) { - /** [UID] of the [Competition] that should be exported. .*/ + /** [UID] of the [EvaluationRun] that should be exported. .*/ private val id: UID by option("-i", "--id").convert { it.UID() }.required() /** Path to the file that should be created .*/ @@ -329,7 +329,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik it.tasks.forEach { t -> println(t.description) - if (t is InteractiveSynchronousCompetition.Task) { + if (t is InteractiveSynchronousEvaluation.Task) { println("Submissions") t.submissions.forEach { s -> println(s) } } @@ -378,7 +378,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik return } - if (run is InteractiveSynchronousCompetition) { + if (run is InteractiveSynchronousEvaluation) { /* Fetch submissions and reset them. */ val submissions = run.tasks.flatMap { @@ -416,7 +416,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik return } - if (run is InteractiveSynchronousCompetition) { + if (run is InteractiveSynchronousEvaluation) { /* Fetch submissions and reset them. */ val submissions = run.tasks.filter { it.uid.string in ids @@ -455,7 +455,7 @@ class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpClik return } - if (run is InteractiveSynchronousCompetition) { + if (run is InteractiveSynchronousEvaluation) { val submissions = run.tasks.filter { it.description.taskGroup.name == taskGroup }.flatMap { it.submissions } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index e7f2467b2..a0e191c77 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.run.RunProperties import dev.dres.data.model.submissions.SubmissionStatus @@ -84,7 +84,7 @@ abstract class AbstractCompetitionRunAdminRestHandler( } /** - * REST handler to create a [InteractiveSynchronousCompetition]. + * REST handler to create a [InteractiveSynchronousEvaluation]. */ class CreateCompetitionRunAdminHandler( private val competitions: DAO, @@ -193,7 +193,7 @@ class CreateCompetitionRunAdminHandler( } /** - * REST handler to start a [InteractiveSynchronousCompetition]. + * REST handler to start a [InteractiveSynchronousEvaluation]. */ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { @@ -235,7 +235,7 @@ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(s } /** - * REST handler to move to the next task in a [InteractiveSynchronousCompetition]. + * REST handler to move to the next task in a [InteractiveSynchronousEvaluation]. */ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { @@ -296,7 +296,7 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle } /** - * REST handler to move to the next task in a [InteractiveSynchronousCompetition]. + * REST handler to move to the next task in a [InteractiveSynchronousEvaluation]. */ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { @@ -354,7 +354,7 @@ class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHand } /** - * REST handler to move to the previous task in a [InteractiveSynchronousCompetition]. + * REST handler to move to the previous task in a [InteractiveSynchronousEvaluation]. */ class PreviousTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), @@ -406,7 +406,7 @@ class PreviousTaskCompetitionRunAdminHandler : } /** - * REST handler to start the current task in a [InteractiveSynchronousCompetition]. + * REST handler to start the current task in a [InteractiveSynchronousEvaluation]. */ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), PostRestHandler { @@ -448,7 +448,7 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl } /** - * REST handler to abort the current task in a [InteractiveSynchronousCompetition]. + * REST handler to abort the current task in a [InteractiveSynchronousEvaluation]. */ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PostRestHandler { @@ -488,7 +488,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl } /** - * REST handler to terminate a [InteractiveSynchronousCompetition]. + * REST handler to terminate a [InteractiveSynchronousEvaluation]. */ class TerminateCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), @@ -528,7 +528,7 @@ class TerminateCompetitionRunAdminHandler : } /** - * REST handler to adjust a [InteractiveSynchronousCompetition.Task]'s duration. + * REST handler to adjust a [InteractiveSynchronousEvaluation.Task]'s duration. */ class AdjustDurationRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), @@ -756,7 +756,7 @@ class OverwriteSubmissionStatusRunAdminHandler : } /** - * REST handler to list all viewers for a [InteractiveSynchronousCompetition]. + * REST handler to list all viewers for a [InteractiveSynchronousEvaluation]. */ class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), GetRestHandler> { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index fef109358..bb552a0e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -19,7 +19,7 @@ import io.javalin.openapi.* /** * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing - * [dev.dres.data.model.run.InteractiveSynchronousCompetition]s. + * [dev.dres.data.model.run.InteractiveSynchronousEvaluation]s. * * @author Ralph Gasser * @version 1.0.0 @@ -57,7 +57,7 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler } /** - * Generates and lists all [ScoreOverview]s for the provided [dev.dres.data.model.run.InteractiveSynchronousCompetition]. + * Generates and lists all [ScoreOverview]s for the provided [dev.dres.data.model.run.InteractiveSynchronousEvaluation]. * * @author Ralph Gasser * @version 1.0.0 @@ -86,7 +86,7 @@ class ListCompetitionScoreHandler : AbstractScoreRestHandler(), GetRestHandler) : DownloadHandler(), GetRestHandler { + class CompetitionRun(private val runs: DAO) : DownloadHandler(), GetRestHandler { /** The route of this [DownloadHandler.CompetitionRun]. */ override val route = "download/run/{runId}" @@ -67,7 +66,7 @@ sealed class DownloadHandler : AccessManagedRestHandler { /** * REST handler to download the competition run scores description information. */ - class CompetitionRunScoreHandler(private val runs: DAO) : AbstractScoreRestHandler(), GetRestHandler { + class CompetitionRunScoreHandler(private val runs: DAO) : AbstractScoreRestHandler(), GetRestHandler { override val route = "download/run/{runId}/scores" @@ -95,7 +94,7 @@ sealed class DownloadHandler : AccessManagedRestHandler { ctx.contentType("text/csv") ctx.header("Content-Disposition", "attachment; filename=\"scores-${runId.string}.csv\"") - if (run is InteractiveAsynchronousCompetition) { + if (run is InteractiveAsynchronousEvaluation) { return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } .flatMap { task -> diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index c7b990ef9..e5150c88c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -5,9 +5,7 @@ import dev.dres.api.rest.types.collection.RestMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.MediaItem import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index 9d8710c1e..84929860e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -7,7 +7,6 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.UID -import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/AbstractRestEntity.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/AbstractRestEntity.kt index 2bbc59cb3..6271e4040 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/AbstractRestEntity.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/AbstractRestEntity.kt @@ -1,6 +1,3 @@ package dev.dres.api.rest.types -import dev.dres.data.model.UID -import org.checkerframework.checker.guieffect.qual.UI - abstract class AbstractRestEntity(val id:String) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 4e55b2682..751a5fadb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -2,7 +2,6 @@ package dev.dres.api.rest.types.collection import dev.dres.data.model.UID import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType /** * The RESTful API equivalent for [MediaItem]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt index e7f5f2816..781614f17 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.run -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.run.* data class AdminRunOverview(val state: RunManagerStatus, val teamOverviews: List) { @@ -40,7 +40,7 @@ data class TaskRunOverview( val status: TaskRunStatus, val started: Long?, val ended: Long?) { - constructor(task: Task) : this( + constructor(task: TaskRun) : this( task.description.id.string, task.description.name, task.description.taskGroup.name, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt index a60d3c500..6ff7052a9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.run /** - * Information of past task in a [dev.dres.data.model.run.InteractiveSynchronousCompetition]. + * Information of past task in a [dev.dres.data.model.run.InteractiveSynchronousEvaluation]. * The information includes the [dev.dres.data.model.competition.TaskDescription], * the actual task, its name, [dev.dres.data.model.competition.TaskGroup], * [dev.dres.data.model.competition.TaskType] and the number of submissions. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt index 6524b7c3b..c1d0457c9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt @@ -1,12 +1,12 @@ package dev.dres.api.rest.types.run -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunProperties import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.RunManager /** - * Contains the basic and most importantly static information about a [InteractiveSynchronousCompetition] and the + * Contains the basic and most importantly static information about a [InteractiveSynchronousEvaluation] and the * associated [RunManager]. Since this information usually doesn't change in the course of a run, * it allows for local caching and other optimizations. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt index 200481099..974c7577e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.run -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.* /** - * Contains the information about the state of a [InteractiveSynchronousCompetition] and the associated [RunManager]. + * Contains the information about the state of a [InteractiveSynchronousEvaluation] and the associated [RunManager]. * * This is information that changes in the course of a run and therefore must be updated frequently. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt index 7cd2b21d0..07c9a714d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt @@ -2,11 +2,11 @@ package dev.dres.api.rest.types.run import dev.dres.data.model.UID import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation /** * Basic and most importantly static information about the [TaskDescription] - * of a [InteractiveSynchronousCompetition]. Since this information usually doesn't change in the course of a run, it + * of a [InteractiveSynchronousEvaluation]. Since this information usually doesn't change in the course of a run, it * allows for local caching and other optimizations. * * @author Ralph Gasser and Loris Sauter diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt index 77f718586..7c0e4e478 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.run import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation /** - * Basic and most importantly static information about the [Team] partaking in a [InteractiveSynchronousCompetition]. + * Basic and most importantly static information about the [Team] partaking in a [InteractiveSynchronousEvaluation]. * Since this information usually doesn't change in the course of a run,t allows for local caching * and other optimizations. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt index 460a23689..109a95ecb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt @@ -11,7 +11,7 @@ import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.competition.interfaces.SubmissionFilterFactory import dev.dres.data.model.competition.interfaces.TaskScorerFactory import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator @@ -26,7 +26,7 @@ import java.lang.Long.max typealias TaskDescriptionId = String /** - * Basic description of a [Task] as executed in DRES. Defines basic attributes such as its name, its duration, + * Basic description of a [TaskRun] as executed in DRES. Defines basic attributes such as its name, its duration, * the [TaskDescriptionTarget] and the [Hint]s, that should be presented to the user. * * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt similarity index 54% rename from backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 9e4565957..cd660ce30 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractCompetitionRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -10,57 +10,57 @@ import dev.dres.data.model.run.interfaces.Run * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractCompetitionRun(protected val competition: Competition): dev.dres.data.model.run.interfaces.Competition { +abstract class AbstractEvaluation(protected val evaluation: Evaluation): dev.dres.data.model.run.interfaces.EvaluationRun { - /** The name of this [AbstractCompetitionRun]. */ - override val id: CompetitionId - get() = this.competition.id + /** The name of this [AbstractEvaluation]. */ + override val id: EvaluationId + get() = this.evaluation.id - /** The name of this [AbstractCompetitionRun]. */ + /** The name of this [AbstractEvaluation]. */ override val name: String - get() = this.competition.name + get() = this.evaluation.name - /** The [CompetitionDescription] of this [AbstractCompetitionRun]. */ + /** The [CompetitionDescription] of this [AbstractEvaluation]. */ override val description: CompetitionDescription - get() = this.competition.description + get() = this.evaluation.description - /** Timestamp of when this [AbstractCompetitionRun] was started. */ + /** Timestamp of when this [AbstractEvaluation] was started. */ override var started: Long - get() = this.competition.started + get() = this.evaluation.started protected set(value) { - this.competition.started = value + this.evaluation.started = value } - /** Timestamp of when this [AbstractCompetitionRun] was ended. */ + /** Timestamp of when this [AbstractEvaluation] was ended. */ override var ended: Long? - get() = this.competition.ended + get() = this.evaluation.ended protected set(value) { - this.competition.ended = value + this.evaluation.ended = value } - /** Flag indicating that participants can also use the viewer for this [Competition]. */ + /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ override var participantCanView: Boolean - get() = this.competition.participantCanView + get() = this.evaluation.participantCanView set(value) { - this.competition.participantCanView = value + this.evaluation.participantCanView = value } /** Flag indicating that tasks can be repeated.*/ override var allowRepeatedTasks: Boolean - get() = this.competition.allowRepeatedTasks + get() = this.evaluation.allowRepeatedTasks set(value) { - this.competition.allowRepeatedTasks = value + this.evaluation.allowRepeatedTasks = value } /** A fixed limit on submission previews. */ override var limitSubmissionPreviews: Int - get() = this.competition.limitSubmissionPreviews + get() = this.evaluation.limitSubmissionPreviews set(value) { - this.competition.limitSubmissionPreviews = value + this.evaluation.limitSubmissionPreviews = value } /** - * Starts this [AbstractCompetitionRun]. + * Starts this [AbstractEvaluation]. */ override fun start() { if (this.hasStarted) { @@ -70,7 +70,7 @@ abstract class AbstractCompetitionRun(protected val competition: Competition): d } /** - * Ends this [AbstractCompetitionRun]. + * Ends this [AbstractEvaluation]. */ override fun end() { if (!this.isRunning) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt index 6f5cc3a3f..aaf26e644 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.run +import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.run.interfaces.Run import dev.dres.run.TaskRunStatus @@ -9,7 +10,7 @@ import dev.dres.run.TaskRunStatus * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.run.interfaces.Task { +abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.run.interfaces.TaskRun { /** The Id of this [AbstractTaskRun]. */ override val id: TaskId get() = this.task.id @@ -28,6 +29,10 @@ abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.ru this.task.ended = value } + /** Reference to the [TaskDescription] describing this [AbstractTaskRun]. */ + override val description: TaskDescription + get() = this.task.description + @Volatile override var status: TaskRunStatus = TaskRunStatus.CREATED protected set diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt similarity index 56% rename from backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt index a9db48dac..9c30a6874 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Competition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt @@ -5,41 +5,41 @@ import dev.dres.data.model.competition.CompetitionDescription import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -typealias CompetitionId = String +typealias EvaluationId = String /** - * Represents a [Competition], i.e., a concrete instance of a [CompetitionDescription], as executed by DRES. + * Represents a [Evaluation], i.e., a concrete instance of a [CompetitionDescription], as executed by DRES. * * @author Ralph Gasser * @version 1.0.0 */ -class Competition(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() +class Evaluation(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() - /** The [CompetitionId] of this [Competition]. */ - var competitionId: CompetitionId + /** The [EvaluationId] of this [Evaluation]. */ + var evaluationId: EvaluationId get() = this.id set(value) { this.id = value } - /** The name held by this [Competition]. Must be unique!*/ + /** The name held by this [Evaluation]. Must be unique!*/ var name by xdRequiredStringProp(unique = true, trimmed = true) - /** The [RunType] of this [Competition]. */ + /** The [RunType] of this [Evaluation]. */ var type by xdLink1(RunType) - /** The [CompetitionDescription] backing this [Competition]. */ + /** The [CompetitionDescription] backing this [Evaluation]. */ var description by xdLink1(CompetitionDescription) - /** Timestamp of when this [Competition] started. */ + /** Timestamp of when this [Evaluation] started. */ var started by xdRequiredLongProp() - /** Timestamp of when this [Competition] ended. */ + /** Timestamp of when this [Evaluation] ended. */ var ended by xdNullableLongProp() - /** The [Task]s that belong to this [Competition]. */ - val tasks by xdChildren0_N(Task::competition) + /** The [Task]s that belong to this [Evaluation]. */ + val tasks by xdChildren0_N(Task::evaluation) - /** Flag indicating that participants can also use the viewer for this [Competition]. */ + /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ var participantCanView by xdBooleanProp() /** Flag indicating that tasks should be shuffled. is only used for asynchronous runs */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt similarity index 87% rename from backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 544c786e7..a4ccfa118 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -5,8 +5,8 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task +import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission @@ -21,10 +21,10 @@ import java.util.concurrent.ConcurrentHashMap /** * Represents a concrete, interactive and asynchronous [Run] of a [CompetitionDescription]. * - * [InteractiveAsynchronousCompetition]s can be started and ended, and they can be used to create new [Task]s and access the current [Task]. + * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [Task]s and access the current [Task]. * */ -class InteractiveAsynchronousCompetition(competition: Competition, val permutation: Map>) : AbstractCompetitionRun(competition) { +class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: Map>) : AbstractEvaluation(evaluation) { companion object { fun generatePermutation( @@ -41,11 +41,9 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati * generates a sequence of tasks that loops through all tasks exactly once */ private fun makeLoop(length: Int): List { - if (length <= 0) { return emptyList() } - val positions = (0 until length).shuffled() val array = IntArray(length) { -1 } @@ -86,12 +84,11 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati } return array.toList() - } } constructor( - id: CompetitionId, + id: EvaluationId, name: String, competitionDescription: CompetitionDescription, properties: RunProperties @@ -104,7 +101,7 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati ) internal constructor( - id: CompetitionId, + id: EvaluationId, name: String, competitionDescription: CompetitionDescription, runProperties: RunProperties, @@ -198,12 +195,12 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati ?: throw IllegalArgumentException("Given $teamId is unknown to this competition $id.") /** - * Generates and returns a [String] representation for this [InteractiveAsynchronousCompetition]. + * Generates and returns a [String] representation for this [InteractiveAsynchronousEvaluation]. */ override fun toString(): String = "InteractiveAsynchronousCompetition(id=$id, name=${name})" /** - * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousCompetition]. + * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousEvaluation]. * * @author Ralph Gasser * @version 1.0.0 @@ -237,18 +234,18 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati } } - /** The [InteractiveAsynchronousCompetition] this [Task] belongs to.*/ - override val competition: InteractiveAsynchronousCompetition - @JsonIgnore get() = this@InteractiveAsynchronousCompetition + /** The [InteractiveAsynchronousEvaluation] this [Task] belongs to.*/ + override val competition: InteractiveAsynchronousEvaluation + @JsonIgnore get() = this@InteractiveAsynchronousEvaluation - /** The position of this [Task] within the [InteractiveAsynchronousCompetition]. */ + /** The position of this [Task] within the [InteractiveAsynchronousEvaluation]. */ override val position: Int - get() = this@InteractiveAsynchronousCompetition.tasksMap[this.teamId]?.indexOf(this) + get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.teamId]?.indexOf(this) ?: -1 @Transient override val description: TaskDescription = - this@InteractiveAsynchronousCompetition.description.tasks.find { it.id == this.descriptionId } + this@InteractiveAsynchronousEvaluation.description.tasks.find { it.id == this.descriptionId } ?: throw IllegalArgumentException("Task with taskId ${this.descriptionId} not found.") @Transient @@ -266,10 +263,10 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati init { - check(this@InteractiveAsynchronousCompetition.description.teams.any { it.uid == this.teamId }) { + check(this@InteractiveAsynchronousEvaluation.description.teams.any { it.uid == this.teamId }) { "Cannot start a new task run for team with ID ${this.teamId}. Team is not registered for competition." } - this@InteractiveAsynchronousCompetition.tasksMap.compute(this.teamId) { _, v -> + this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.teamId) { _, v -> val list = v ?: LinkedList() check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } list.add(this) @@ -278,14 +275,14 @@ class InteractiveAsynchronousCompetition(competition: Competition, val permutati } /** - * Adds a [Submission] to this [InteractiveAsynchronousCompetition.Task]. + * Adds a [Submission] to this [InteractiveAsynchronousEvaluation.Task]. * * @param submission The [Submission] to add. * @throws IllegalArgumentException If [Submission] could not be added for any reason. */ @Synchronized override fun postSubmission(submission: Submission) { - check(this.isRunning) { "Task run '${this@InteractiveAsynchronousCompetition.name}.${this.position}' is currently not running. This is a programmer's error!" } + check(this.isRunning) { "Task run '${this@InteractiveAsynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } /* Execute submission filters. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt similarity index 53% rename from backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 80398829b..c8f56532e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -2,10 +2,8 @@ package dev.dres.data.model.run import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskDescriptionId -import dev.dres.data.model.run.InteractiveSynchronousCompetition.Task import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission import dev.dres.run.audit.AuditLogger import dev.dres.run.filter.SubmissionFilter @@ -17,34 +15,36 @@ import java.util.* /** * Represents a concrete, interactive and synchronous [Run] of a [CompetitionDescription]. * - * [InteractiveSynchronousCompetition]s can be started and ended and they can be used to create new [Task]s and access the current [Task]. + * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. * * @author Ralph Gasser * @param 1.3.0 */ -class InteractiveSynchronousCompetition(competition: Competition) : AbstractCompetitionRun(competition) { +class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { init { - require(this.competition.type == RunType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.competition.type}. This is a programmer's error!" } + require(this.evaluation.type == RunType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } - /** List of [Task]s registered for this [InteractiveSynchronousCompetition]. */ - override val tasks: List = LinkedList() + /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ + override val tasks: List = this.evaluation.tasks.asSequence().map { + ISTaskRun(it) + }.toList() /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ var currentTaskDescription = this.description.tasks.first() private set - /** Returns the last [Task]. */ - val currentTask: Task? + /** Returns the last [TaskRun]. */ + val currentTask: TaskRun? get() = this.tasks.findLast { it.description.id == this.currentTaskDescription.id } override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" /** - * Moves this [InteractiveSynchronousCompetition] to the given task index. + * Moves this [InteractiveSynchronousEvaluation] to the given task index. * * @param index The new task index to move to. */ @@ -53,57 +53,60 @@ class InteractiveSynchronousCompetition(competition: Competition) : AbstractComp } /** - * Represents a concrete [Run] of a [TaskDescription]. [Task]s always exist within a [InteractiveSynchronousCompetition]. - * As a [InteractiveSynchronousCompetition], [Task]s can be started and ended and they can be used to register [Submission]s. + * Represents a concrete [Run] of a [TaskDescription]. [Task]s always exist within a [InteractiveSynchronousEvaluation]. + * As a [InteractiveSynchronousEvaluation], [Task]s can be started and ended and they can be used to register [Submission]s. * * @version 1.2.0 * @author Ralph Gasser */ - inner class TaskRun(task: Task): AbstractInteractiveTask(task) { + inner class ISTaskRun(task: Task): AbstractInteractiveTask(task) { - internal constructor(uid: TaskId, taskId: TaskDescriptionId, started: Long, ended: Long) : this(uid, taskId) { - this.started = if (started == -1L) { - null - } else { - started - } - this.ended = if (ended == -1L) { - null - } else { - ended - } - } - - /** The [InteractiveSynchronousCompetition] this [Task] belongs to.*/ - override val competition: InteractiveSynchronousCompetition - get() = this@InteractiveSynchronousCompetition - - /** The position of this [Task] within the [InteractiveSynchronousCompetition]. */ + /** + * Constructor used to generate an [ISTaskRun] from a [TaskDescription]. + * + * @param description [TaskDescription] to generate [ISTaskRun] from. + */ + constructor(description: TaskDescription) : this(Task.new { + this.id = UUID.randomUUID().toString() + this.type = RunType.INTERACTIVE_SYNCHRONOUS + this.evaluation = this@InteractiveSynchronousEvaluation.evaluation + this.started = System.currentTimeMillis() + this.description = description + }) + + /** The [InteractiveSynchronousEvaluation] this [Task] belongs to.*/ + override val competition: InteractiveSynchronousEvaluation + get() = this@InteractiveSynchronousEvaluation + + /** The position of this [Task] within the [InteractiveSynchronousEvaluation]. */ override val position: Int - get() = this@InteractiveSynchronousCompetition.tasks.indexOf(this.task) + get() = this@InteractiveSynchronousEvaluation.tasks.indexOf(this) /** Reference to the [TaskDescription] describing this [Task]. */ override val description: TaskDescription get() = this.task.description - @Transient + /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ override val filter: SubmissionFilter = this.description.newFilter() - @Transient + /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ override val scorer: TeamTaskScorer = this.description.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") - @Transient + /** The [SubmissionValidator] used by this [ISTaskRun]. */ override val validator: SubmissionValidator = newValidator() /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ override var duration: Long = this.description.duration init { - check(this@InteractiveSynchronousCompetition.tasks.isEmpty() || this@InteractiveSynchronousCompetition.tasks.last().hasEnded) { + check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { "Cannot create a new task. Another task is currently running." } - (this@InteractiveSynchronousCompetition.tasks as MutableList).add(this) + check(this.task.type == RunType.INTERACTIVE_SYNCHRONOUS) { + "Incompatible competition type ${this.task.type}. This is a programmer's error!" + } + (this@InteractiveSynchronousEvaluation.tasks as MutableList).add(this) } /** @@ -113,9 +116,9 @@ class InteractiveSynchronousCompetition(competition: Competition) : AbstractComp */ @Synchronized override fun postSubmission(submission: Submission) { - check(this.isRunning) { "Task run '${this@InteractiveSynchronousCompetition.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this@InteractiveSynchronousCompetition.description.teams.filter { it eq submission.team }.any()) { - "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousCompetition.name}. This is a programmer's error!" + check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } + check(this@InteractiveSynchronousEvaluation.description.teams.filter { it eq submission.team }.any()) { + "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt deleted file mode 100644 index 617fd9441..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveCompetition.kt +++ /dev/null @@ -1,44 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.data.model.submissions.aspects.OriginAspect -import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.score.interfaces.ResultBatchTaskScorer - - -class NonInteractiveCompetition(competition: Competition) : AbstractCompetitionRun(competition) { - - /** */ - override val tasks: List = this.description.tasks.map { TaskContainer(taskDescriptionId = it.id) } - - inner class TaskContainer(override val uid: TaskId = UID(), val taskDescriptionId: TaskDescriptionId) : - AbstractNonInteractiveTask() { - - internal val submissions: MutableMap, ResultBatch<*>> = mutableMapOf() - - @Synchronized - override fun addSubmissionBatch(origin: OriginAspect, batches: List>) { - - batches.forEach { resultBatch -> - submissions[origin.teamId to resultBatch.name] = resultBatch - } - } - - - override val competition: Competition - get() = this@NonInteractiveCompetition - - override val position: Int - get() = this@NonInteractiveCompetition.tasks.indexOf(this) - - override val description: TaskDescription = this@NonInteractiveCompetition.description.tasks - .find { it.id == this.taskDescriptionId } - ?: throw IllegalArgumentException("There is no task with ID ${this.taskDescriptionId}.") - - @Transient - override val scorer: ResultBatchTaskScorer = description.newScorer() as? ResultBatchTaskScorer - ?: throw IllegalArgumentException("specified scorer is not of type ResultBatchTaskScorer") - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt new file mode 100644 index 000000000..4f7cd50dd --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -0,0 +1,62 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.run.interfaces.EvaluationRun +import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.submissions.aspects.OriginAspect +import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.score.interfaces.ResultBatchTaskScorer +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.size + + +/** + * Represents a concrete, interactive and synchronous [Run] of a [CompetitionDescription]. + * + * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. + * + * @author Luca Rossetto + * @param 1.0.0 + */ +class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { + + init { + require(this.evaluation.type == RunType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } + require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } + require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } + } + + /** List of [TaskRun]s registered for this [NonInteractiveEvaluation]. */ + override val tasks: List = this.evaluation.tasks.asSequence().map { + NITaskRun(it) + }.toList() + + /** + * The [TaskRun] used by a [NonInteractiveEvaluation]. + */ + inner class NITaskRun(task: Task): AbstractNonInteractiveTask(task) { + + internal val submissions: MutableMap, ResultBatch<*>> = mutableMapOf() + + /** Reference to the [EvaluationRun] hosting this [NITaskRun]. */ + override val competition: EvaluationRun + get() = this@NonInteractiveEvaluation + + /** The position of this [NITaskRun] within the [NonInteractiveEvaluation]. */ + override val position: Int + get() = this@NonInteractiveEvaluation.tasks.indexOf(this) + + @Transient + override val scorer: ResultBatchTaskScorer = description.newScorer() as? ResultBatchTaskScorer + ?: throw IllegalArgumentException("specified scorer is not of type ResultBatchTaskScorer") + + @Synchronized + override fun addSubmissionBatch(origin: OriginAspect, batches: List>) { + batches.forEach { resultBatch -> + submissions[origin.teamId to resultBatch.name] = resultBatch + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 4283b1ba7..39f5251fc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -2,22 +2,26 @@ package dev.dres.data.model.run import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.UID import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserId -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.competition.team.TeamId import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first +import kotlinx.dnq.query.query /** * The [RunActionContext] captures and encapsulates information usually required during the interaction with a [RunManager]. * It exposes information available to the OpenAPI facility (e.g., through session management) to the [RunManager]. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ -data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: Set) { +data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: Set) { /** True if the user associated with this [RunActionContext] acts as [Role.ADMIN]*/ val isAdmin: Boolean @@ -25,7 +29,7 @@ data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: companion object { /** A static [RunActionContext] used for internal invocations by DRES. Always acts as an implicit [Role.ADMIN]. */ - val INTERNAL = RunActionContext(UID.EMPTY, UID.EMPTY, setOf(Role.ADMIN)) + val INTERNAL = RunActionContext(null, null, setOf(Role.ADMIN)) /** * Constructs a [RunActionContext] from a [Context] and a [RunManager]. @@ -35,8 +39,8 @@ data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.convertApiRole(it) }.toSet() - val teamId = runManager.description.teams.find { it.users.contains(userId) }?.uid + val roles = AccessManager.rolesOfSession(ctx.sessionId()).mapNotNull { it.role }.toSet() + val teamId = runManager.description.teams.filter { it.users.contains(User.query(User::id eq userId).first()) }.first().teamId return RunActionContext(userId, teamId, roles) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt index de9e8e8fa..0809e3932 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt @@ -6,7 +6,7 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * Enumeration of the type of [Competition]. + * Enumeration of the type of [Evaluation]. * * @author Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index 274451416..fe63ee621 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -2,6 +2,7 @@ package dev.dres.data.model.run import dev.dres.data.model.PersistentEntity import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.submissions.Submission import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -14,26 +15,28 @@ typealias TaskId = String * @version 1.0.0 */ class Task(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() + companion object : XdNaturalEntityType() /** The [TaskId] of this [Task]. */ var taskId: TaskId get() = this.id set(value) { this.id = value } - /** The [RunType] of this [Competition]. */ + /** The [RunType] of this [Evaluation]. */ var type by xdLink1(RunType) - /** Timestamp of when this [Competition] started. */ + /** Timestamp of when this [Evaluation] started. */ var started by xdRequiredLongProp() - /** Timestamp of when this [Competition] ended. */ + /** Timestamp of when this [Evaluation] ended. */ var ended by xdNullableLongProp() /** The [TaskDescription] this [Task] is an instance of. */ var description by xdLink1(TaskDescription) - /** The [Competition] this [Task] belongs to. */ - var competition by xdParent(Competition::tasks) + /** The [Evaluation] this [Task] belongs to. */ + var evaluation by xdParent(Evaluation::tasks) + /** List of [Submission]s received by this [Task]. */ + val submissions by xdChildren0_N(Submission::task) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt similarity index 51% rename from backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index 54a36e351..b2506d44b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Competition.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -1,28 +1,28 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.Competition -import dev.dres.data.model.run.CompetitionId +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.EvaluationId /** - * Represents a [Competition] that a DRES user or client takes place in and that groups several [Task]s + * Represents a [Evaluation] that a DRES user or client takes place in and that groups several [TaskRun]s * * @author Ralph Gasser * @version 1.0.0 */ -interface Competition: Run { - /** The unique [CompetitionId] that identifies this [Competition]. */ - val id: CompetitionId +interface EvaluationRun: Run { + /** The unique [EvaluationId] that identifies this [Evaluation]. */ + val id: EvaluationId - /** The name human readable of this [Competition]. */ + /** The name human readable of this [Evaluation]. */ val name: String - /** Reference to the [CompetitionDescription] that describes the content of this [Competition]. */ + /** Reference to the [CompetitionDescription] that describes the content of this [Evaluation]. */ val description: CompetitionDescription - /** Collection of [Task]s that make up this [Competition]. */ - val tasks: List + /** Collection of [TaskRun]s that make up this [Evaluation]. */ + val tasks: List - /** Flag indicating that participants can also use the viewer for this [Competition]. */ + /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ var participantCanView: Boolean /** Flag indicating that tasks can be repeated.*/ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt similarity index 69% rename from backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 89098302a..c12924a86 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,8 +1,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskDescriptionId -import dev.dres.data.model.run.InteractiveAsynchronousCompetition.Task +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task import dev.dres.run.TaskRunStatus import dev.dres.run.score.interfaces.TaskScorer typealias TaskId = String @@ -13,14 +12,14 @@ typealias TaskId = String * @author Ralph Gasser * @version 1.0.0 */ -interface Task: Run { +interface TaskRun: Run { /** The unique [TaskId] that identifies this [Task]. Used by the persistence layer. */ val id: TaskId - /** Reference to the [Competition] this [Task] belongs to. */ - val competition: Competition + /** Reference to the [EvaluationRun] this [Task] belongs to. */ + val competition: EvaluationRun - /** The position of this [Task] within the enclosing [Competition]. */ + /** The position of this [Task] within the enclosing [EvaluationRun]. */ val position: Int /** Reference to the [TaskDescription] describing this [Task]. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 49c11b0ce..f8f02004f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -1,14 +1,12 @@ package dev.dres.data.model.submissions -import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User import dev.dres.data.model.competition.team.Team import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.submissions.aspects.BaseSubmissionAspect +import dev.dres.data.model.run.Task import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.simple.min @@ -22,7 +20,7 @@ typealias SubmissionId = String * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -sealed class Submission(entity: Entity) : PersistentEntity(entity), BaseSubmissionAspect { +sealed class Submission(entity: Entity) : PersistentEntity(entity) { companion object : XdNaturalEntityType() /** The [SubmissionId] of this [User]. */ @@ -31,10 +29,13 @@ sealed class Submission(entity: Entity) : PersistentEntity(entity), BaseSubmissi set(value) { this.id = value } /** The timestamp of this [Submission]. */ - override var timestamp by xdRequiredLongProp { min(0L) } + var timestamp by xdRequiredLongProp { min(0L) } /** The [SubmissionStatus] of this [Submission]. */ - override var status by xdLink1(SubmissionStatus) + var status by xdLink1(SubmissionStatus) + + /** The [Task] this [Submission] belongs to. */ + var task by xdParent(Task::submissions) /** The [Team] that submitted this [Submission] */ var team by xdLink1(Team) @@ -57,10 +58,6 @@ sealed class Submission(entity: Entity) : PersistentEntity(entity), BaseSubmissi /** The text submitted. Only for [SubmissionType.TEXT] . */ var text by xdStringProp { requireIf { this.type == SubmissionType.TEXT } } - /** The [AbstractInteractiveTask] this [Submission] belongs to. */ - @JsonIgnore - override var task: AbstractInteractiveTask? = null - /** */ val temporalRange: TemporalRange get() = TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt deleted file mode 100644 index 2fab02a68..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/BaseSubmissionAspect.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -import dev.dres.data.model.run.AbstractInteractiveTask - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface BaseSubmissionAspect : StatusAspect, OriginAspect { - var task: AbstractInteractiveTask? - val timestamp: Long -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt deleted file mode 100644 index 50762f418..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/ItemAspect.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -import dev.dres.data.model.media.MediaItem - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface ItemAspect { - val item: MediaItem -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt deleted file mode 100644 index cf950dbf6..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/OriginAspect.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -import dev.dres.data.model.UID -import dev.dres.data.model.competition.team.TeamId - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface OriginAspect { - val uid: UID - val teamId: TeamId - val memberId: UID -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt deleted file mode 100644 index 699fe78a3..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/SpatialSubmissionAspect.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface SpatialSubmissionAspect : BaseSubmissionAspect \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt deleted file mode 100644 index c29b50572..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/StatusAspect.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -import dev.dres.data.model.submissions.SubmissionStatus - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface StatusAspect { - var status: SubmissionStatus -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt deleted file mode 100644 index 25e231bec..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalAspect.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -import dev.dres.data.model.media.time.TemporalRange - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface TemporalAspect { - - /** Start time in milliseconds */ - val start: Long - - /** End time in milliseconds */ - val end: Long - - val temporalRange: TemporalRange -} diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt deleted file mode 100644 index 91ea8705f..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TemporalSubmissionAspect.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface TemporalSubmissionAspect : BaseSubmissionAspect, TemporalAspect, ItemAspect \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TextAspect.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TextAspect.kt deleted file mode 100644 index 35e095960..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/aspects/TextAspect.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.dres.data.model.submissions.aspects - -interface TextAspect { - - val text: String - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 9010d0d2c..779a95892 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.* -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.audit.AuditLogger @@ -43,7 +43,7 @@ import kotlin.math.max * @author Ralph Gasser */ class InteractiveAsynchronousRunManager( - val run: InteractiveAsynchronousCompetition + val run: InteractiveAsynchronousEvaluation ) : InteractiveRunManager { @@ -54,7 +54,7 @@ class InteractiveAsynchronousRunManager( } constructor(description: CompetitionDescription, name: String, runProperties: RunProperties) : this( - InteractiveAsynchronousCompetition( + InteractiveAsynchronousEvaluation( UID.EMPTY, name, description, @@ -91,11 +91,11 @@ class InteractiveAsynchronousRunManager( /** List of [Updatable] held by this [InteractiveAsynchronousRunManager]. */ private val updatables = mutableListOf() - /** Run ID of this [InteractiveAsynchronousCompetition]. */ + /** Run ID of this [InteractiveAsynchronousEvaluation]. */ override val id: UID get() = this.run.id - /** Name of this [InteractiveAsynchronousCompetition]. */ + /** Name of this [InteractiveAsynchronousEvaluation]. */ override val name: String get() = this.run.name @@ -172,7 +172,7 @@ class InteractiveAsynchronousRunManager( } /** - * Starts this [InteractiveAsynchronousCompetition] moving [RunManager.status] from [RunManagerStatus.CREATED] to + * Starts this [InteractiveAsynchronousEvaluation] moving [RunManager.status] from [RunManagerStatus.CREATED] to * [RunManagerStatus.ACTIVE] for all teams. This can only be executed by an administrator. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. @@ -201,7 +201,7 @@ class InteractiveAsynchronousRunManager( } /** - * Ends this [InteractiveAsynchronousCompetition] moving [RunManager.status] from [RunManagerStatus.ACTIVE] to + * Ends this [InteractiveAsynchronousEvaluation] moving [RunManager.status] from [RunManagerStatus.ACTIVE] to * [RunManagerStatus.TERMINATED] for all teams. This can only be executed by an administrator. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation @@ -243,7 +243,7 @@ class InteractiveAsynchronousRunManager( } /** - * Prepares this [InteractiveAsynchronousCompetition] for the execution of previous [TaskDescription] + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of previous [TaskDescription] * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting team * to be [RunManagerStatus.ACTIVE]. * @@ -265,7 +265,7 @@ class InteractiveAsynchronousRunManager( } /** - * Prepares this [InteractiveAsynchronousCompetition] for the execution of next [TaskDescription] + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of next [TaskDescription] * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting * team to be [RunManagerStatus.ACTIVE]. * @@ -286,7 +286,7 @@ class InteractiveAsynchronousRunManager( } /** - * Prepares this [InteractiveAsynchronousCompetition] for the execution of [TaskDescription] with the given [index] + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of [TaskDescription] with the given [index] * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting * team to be [RunManagerStatus.ACTIVE]. * @@ -418,7 +418,7 @@ class InteractiveAsynchronousRunManager( } /** - * Returns the time in milliseconds that has elapsed since the start of the current [InteractiveSynchronousCompetition.Task]. + * Returns the time in milliseconds that has elapsed since the start of the current [InteractiveSynchronousEvaluation.Task]. * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. @@ -516,7 +516,7 @@ class InteractiveAsynchronousRunManager( } /** - * Invoked by an external caller to post a new [Submission] for the [Task] that is currently being + * Invoked by an external caller to post a new [Submission] for the [TaskRun] that is currently being * executed by this [InteractiveAsynchronousRunManager]. [Submission]s usually cause updates to the * internal state and/or the [Scoreboard] of this [InteractiveRunManager]. * diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index c8e94d859..2e51e0b55 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -4,7 +4,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.task.TaskDescription import dev.dres.data.model.run.* -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.ScoreTimePoint @@ -19,7 +19,7 @@ interface InteractiveRunManager : RunManager { /** List of [ScoreTimePoint]s tracking the states of the different [Scoreboard]s over time*/ val scoreHistory: List - /** List of all [Submission]s for this [InteractiveRunManager], irrespective of the [InteractiveSynchronousCompetition.Task] it belongs to. */ + /** List of all [Submission]s for this [InteractiveRunManager], irrespective of the [InteractiveSynchronousEvaluation.Task] it belongs to. */ val allSubmissions: List /** @@ -30,7 +30,7 @@ interface InteractiveRunManager : RunManager { * an [IllegalStateException] if invocation does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [Task] was moved, false otherwise. Usually happens if last [Task] has been reached. + * @return True if [TaskRun] was moved, false otherwise. Usually happens if last [TaskRun] has been reached. * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] */ fun previous(context: RunActionContext): Boolean @@ -43,7 +43,7 @@ interface InteractiveRunManager : RunManager { * an [IllegalStateException] if invocation oes not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [Task] was moved, false otherwise. Usually happens if last [Task] has been reached. + * @return True if [TaskRun] was moved, false otherwise. Usually happens if last [TaskRun] has been reached. * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] */ fun next(context: RunActionContext): Boolean @@ -168,7 +168,7 @@ interface InteractiveRunManager : RunManager { fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean /** - * Invoked by an external caller to post a new [Submission] for the [Task] that is currently being + * Invoked by an external caller to post a new [Submission] for the [TaskRun] that is currently being * executed by this [InteractiveRunManager]. [Submission]s usually cause updates to the internal state and/or * the [Scoreboard] of this [InteractiveRunManager]. * diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index c5d9a6111..78722b94b 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.competition.options.ConfiguredOption import dev.dres.data.model.competition.options.Option import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.competition.options.SimpleOptionParameters -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunProperties import dev.dres.data.model.submissions.Submission @@ -35,13 +35,13 @@ import kotlin.math.max /** * An implementation of [RunManager] aimed at distributed execution having a single DRES Server instance and multiple - * viewers connected via WebSocket. Before starting a [InteractiveSynchronousCompetition.Task], all viewer instances are synchronized. + * viewers connected via WebSocket. Before starting a [InteractiveSynchronousEvaluation.Task], all viewer instances are synchronized. * * @version 2.1.0 * @author Ralph Gasser */ class InteractiveSynchronousRunManager( - val run: InteractiveSynchronousCompetition + val run: InteractiveSynchronousEvaluation ) : InteractiveRunManager { private val VIEWER_TIME_OUT = 30L //TODO make configurable @@ -54,10 +54,10 @@ class InteractiveSynchronousRunManager( private val maxErrorCount = 5 /** - * Alternative constructor from existing [InteractiveSynchronousCompetition]. + * Alternative constructor from existing [InteractiveSynchronousEvaluation]. */ constructor(description: CompetitionDescription, name: String, runProperties: RunProperties) : this( - InteractiveSynchronousCompetition(UID.EMPTY, name, description, runProperties).apply { + InteractiveSynchronousEvaluation(UID.EMPTY, name, description, runProperties).apply { RunExecutor.runs.append(this) } ) @@ -315,14 +315,14 @@ class InteractiveSynchronousRunManager( LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.run.currentTaskDescription}") } - /** List of [InteractiveSynchronousCompetition.Task] for this [InteractiveSynchronousRunManager]. */ - override fun tasks(context: RunActionContext): List = this.run.tasks + /** List of [InteractiveSynchronousEvaluation.Task] for this [InteractiveSynchronousRunManager]. */ + override fun tasks(context: RunActionContext): List = this.run.tasks /** - * Returns the currently active [InteractiveSynchronousCompetition.Task]s or null, if no such task is active. + * Returns the currently active [InteractiveSynchronousEvaluation.Task]s or null, if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return [InteractiveSynchronousCompetition.Task] or null + * @return [InteractiveSynchronousEvaluation.Task] or null */ override fun currentTask(context: RunActionContext) = this.stateLock.read { // when (this.status) { @@ -342,31 +342,31 @@ class InteractiveSynchronousRunManager( } /** - * Returns [InteractiveSynchronousCompetition.Task]s for a specific task [UID]. May be empty. + * Returns [InteractiveSynchronousEvaluation.Task]s for a specific task [UID]. May be empty. * - * @param taskId The [UID] of the [InteractiveSynchronousCompetition.Task]. + * @param taskId The [UID] of the [InteractiveSynchronousEvaluation.Task]. */ - override fun taskForId(context: RunActionContext, taskId: UID): InteractiveSynchronousCompetition.Task? = + override fun taskForId(context: RunActionContext, taskId: UID): InteractiveSynchronousEvaluation.Task? = this.run.tasks.find { it.uid == taskId } /** - * Returns the [Submission]s for all currently active [InteractiveSynchronousCompetition.Task]s or an empty [List], if no such task is active. + * Returns the [Submission]s for all currently active [InteractiveSynchronousEvaluation.Task]s or an empty [List], if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s for the currently active [InteractiveSynchronousCompetition.Task] + * @return List of [Submission]s for the currently active [InteractiveSynchronousEvaluation.Task] */ override fun submissions(context: RunActionContext): List = this.currentTask(context)?.submissions?.toList() ?: emptyList() /** - * Returns the number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager]. + * Returns the number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager]. * - * @return The number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager] + * @return The number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager] */ override fun taskCount(context: RunActionContext): Int = this.run.tasks.size /** - * Adjusts the duration of the current [InteractiveSynchronousCompetition.Task] by the specified amount. Amount can be either positive or negative. + * Adjusts the duration of the current [InteractiveSynchronousEvaluation.Task] by the specified amount. Amount can be either positive or negative. * * @param s The number of seconds to adjust the duration by. * @return Time remaining until the task will end in milliseconds @@ -387,7 +387,7 @@ class InteractiveSynchronousRunManager( } /** - * Returns the time in milliseconds that is left until the end of the current [InteractiveSynchronousCompetition.Task]. + * Returns the time in milliseconds that is left until the end of the current [InteractiveSynchronousEvaluation.Task]. * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, * this method returns -1L. * @@ -407,7 +407,7 @@ class InteractiveSynchronousRunManager( } /** - * Returns the time in milliseconds that has elapsed since the start of the current [InteractiveSynchronousCompetition.Task]. + * Returns the time in milliseconds that has elapsed since the start of the current [InteractiveSynchronousEvaluation.Task]. * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. @@ -476,8 +476,8 @@ class InteractiveSynchronousRunManager( } /** - * Processes incoming [Submission]s. If a [InteractiveSynchronousCompetition.Task] is running then that [Submission] will usually - * be associated with that [InteractiveSynchronousCompetition.Task]. + * Processes incoming [Submission]s. If a [InteractiveSynchronousEvaluation.Task] is running then that [Submission] will usually + * be associated with that [InteractiveSynchronousEvaluation.Task]. * * This method will not throw an exception and instead return false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke @@ -513,8 +513,8 @@ class InteractiveSynchronousRunManager( } /** - * Processes incoming [Submission]s. If a [InteractiveSynchronousCompetition.Task] is running then that [Submission] will usually - * be associated with that [InteractiveSynchronousCompetition.Task]. + * Processes incoming [Submission]s. If a [InteractiveSynchronousEvaluation.Task] is running then that [Submission] will usually + * be associated with that [InteractiveSynchronousEvaluation.Task]. * * This method will not throw an exception and instead return false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke @@ -553,7 +553,7 @@ class InteractiveSynchronousRunManager( } /** - * Internal method that orchestrates the internal progression of the [InteractiveSynchronousCompetition]. + * Internal method that orchestrates the internal progression of the [InteractiveSynchronousEvaluation]. */ override fun run() { /** Sort list of by [Phase] in ascending order. */ diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 0e1b5b595..74fd69bb8 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -7,7 +7,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription import dev.dres.data.model.competition.TeamId import dev.dres.data.model.run.AbstractNonInteractiveTask -import dev.dres.data.model.run.NonInteractiveCompetition +import dev.dres.data.model.run.NonInteractiveEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunProperties import dev.dres.data.model.run.interfaces.TaskId @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read -class NonInteractiveRunManager(val run: NonInteractiveCompetition) : RunManager { +class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 1e744ef15..1ca0dfb66 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -7,15 +7,14 @@ import dev.dres.api.rest.types.run.websocket.ClientMessage import dev.dres.api.rest.types.run.websocket.ClientMessageType import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType -import dev.dres.data.model.UID import dev.dres.data.model.competition.team.TeamId -import dev.dres.data.model.run.InteractiveAsynchronousCompetition -import dev.dres.data.model.run.InteractiveSynchronousCompetition -import dev.dres.data.model.run.NonInteractiveCompetition -import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation +import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.run.NonInteractiveEvaluation +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.run.audit.AuditLogger import dev.dres.run.validation.interfaces.JudgementValidator -import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write import io.javalin.websocket.WsConfig @@ -34,7 +33,7 @@ import java.util.function.Consumer * The execution environment for [RunManager]s * * @author Ralph Gasser - * @version 1.1 + * @version 1.2.0 */ object RunExecutor : Consumer { @@ -44,7 +43,7 @@ object RunExecutor : Consumer { private val executor = Executors.newCachedThreadPool() /** List of [RunManager] executed by this [RunExecutor]. */ - private val runManagers = HashMap() + private val runManagers = HashMap() /** List of [JudgementValidator]s registered with this [RunExecutor]. */ private val judgementValidators = LinkedList() @@ -53,7 +52,7 @@ object RunExecutor : Consumer { private val connectedClients = HashSet() /** List of session IDs that are currently observing a competition. */ - private val observingClients = HashMap>() + private val observingClients = HashMap>() /** Lock for accessing and changing all data structures related to WebSocket clients. */ private val clientLock = StampedLock() @@ -62,7 +61,7 @@ object RunExecutor : Consumer { private val runManagerLock = StampedLock() /** Internal array of [Future]s for cleaning after [RunManager]s. See [RunExecutor.cleanerThread]*/ - private val results = HashMap, UID>() + private val results = HashMap, EvaluationId>() /** The [TransientEntityStore] instance used by this [AuditLogger]. */ private lateinit var store: TransientEntityStore @@ -81,18 +80,21 @@ object RunExecutor : Consumer { */ } - fun schedule(competition: Competition) { + /** + * + */ + fun schedule(competition: EvaluationRun) { val run = when(competition) { - is InteractiveSynchronousCompetition -> { + is InteractiveSynchronousEvaluation -> { competition.tasks.forEach { t -> t.submissions.forEach { s -> s.task = t } } InteractiveSynchronousRunManager(competition) } - is NonInteractiveCompetition -> { + is NonInteractiveEvaluation -> { NonInteractiveRunManager(competition) } - is InteractiveAsynchronousCompetition -> { + is InteractiveAsynchronousEvaluation -> { competition.tasks.forEach { t -> t.submissions.forEach { s -> s.task = t } if (!t.hasEnded) { @@ -107,8 +109,8 @@ object RunExecutor : Consumer { } /** A thread that cleans after [RunManager] have finished. */ - private val cleanerThread = Thread(Runnable { - while(!this@RunExecutor.executor.isShutdown) { + private val cleanerThread = Thread { + while (!this@RunExecutor.executor.isShutdown) { var stamp = this@RunExecutor.runManagerLock.readLock() try { this@RunExecutor.results.entries.removeIf { entry -> @@ -135,7 +137,7 @@ object RunExecutor : Consumer { } Thread.sleep(500) } - }) + } init { this.cleanerThread.priority = Thread.MIN_PRIORITY @@ -147,7 +149,7 @@ object RunExecutor : Consumer { /** * Callback for when registering this [RunExecutor] as handler for Javalin's WebSocket. * - * @param t The [WsHandler] of the WebSocket endpoint. + * @param t The [WsConfig] of the WebSocket endpoint. */ override fun accept(t: WsConfig) { t.onConnect { @@ -162,10 +164,10 @@ object RunExecutor : Consumer { val connection = WebSocketConnection(it) this.connectedClients.remove(connection) this.runManagerLock.read { - for (m in runManagers) { + for (m in this.runManagers) { if (this.observingClients[m.key]?.contains(connection) == true) { this.observingClients[m.key]?.remove(connection) - m.value.wsMessageReceived(session, ClientMessage(m.key.string, ClientMessageType.UNREGISTER)) /* Send implicit unregister message associated with a disconnect. */ + m.value.wsMessageReceived(session, ClientMessage(m.key, ClientMessageType.UNREGISTER)) /* Send implicit unregister message associated with a disconnect. */ } } } @@ -181,14 +183,14 @@ object RunExecutor : Consumer { val session = WebSocketConnection(it) logger.debug("Received WebSocket message: $message from ${it.session.policy}") this.runManagerLock.read { - if (this.runManagers.containsKey(message.runId.UID())) { + if (this.runManagers.containsKey(message.runId)) { when (message.type) { ClientMessageType.ACK -> {} - ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.runId.UID()]?.add(WebSocketConnection(it)) } - ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.runId.UID()]?.remove(WebSocketConnection(it)) } + ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.runId]?.add(WebSocketConnection(it)) } + ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.runId]?.remove(WebSocketConnection(it)) } ClientMessageType.PING -> it.send(ServerMessage(message.runId, ServerMessageType.PING)) } - this.runManagers[message.runId.UID()]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ + this.runManagers[message.runId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ } } } @@ -206,11 +208,11 @@ object RunExecutor : Consumer { /** * Returns the [RunManager] for the given ID if such a [RunManager] exists. * - * @param runId The ID for which to return the [RunManager]. + * @param evaluationId The ID for which to return the [RunManager]. * @return Optional [RunManager]. */ - fun managerForId(runId: UID): RunManager? = this.runManagerLock.read { - return this.runManagers[runId] + fun managerForId(evaluationId: EvaluationId): RunManager? = this.runManagerLock.read { + return this.runManagers[evaluationId] } /** @@ -248,13 +250,13 @@ object RunExecutor : Consumer { /** * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager]. * - * @param runId The run ID identifying the [RunManager] for which clients should received the message. + * @param evaluationId The run ID identifying the [RunManager] for which clients should received the message. * @param message The [ServerMessage] that should be broadcast. */ - fun broadcastWsMessage(runId: UID, message: ServerMessage) = this.clientLock.read { + fun broadcastWsMessage(evaluationId: EvaluationId, message: ServerMessage) = this.clientLock.read { this.runManagerLock.read { this.connectedClients.filter { - this.observingClients[runId]?.contains(it) ?: false + this.observingClients[evaluationId]?.contains(it) ?: false }.forEach { it.send(message) } @@ -264,19 +266,19 @@ object RunExecutor : Consumer { /** * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager] and are member of the specified team. * - * @param runId The run ID identifying the [RunManager] for which clients should received the message. + * @param evaluationId The run ID identifying the [RunManager] for which clients should receive the message. * @param teamId The [TeamId] of the relevant team * @param message The [ServerMessage] that should be broadcast. */ - fun broadcastWsMessage(runId: UID, teamId: TeamId, message: ServerMessage) = this.clientLock.read { - val manager = managerForId(runId) + fun broadcastWsMessage(evaluationId: EvaluationId, teamId: TeamId, message: ServerMessage) = this.clientLock.read { + val manager = managerForId(evaluationId) if (manager != null) { val teamMembers = this.store.transactional(true) { manager.description.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() } this.runManagerLock.read { this.connectedClients.filter { - this.observingClients[runId]?.contains(it) ?: false && AccessManager.userIdForSession(it.httpSessionId) in teamMembers + this.observingClients[evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.httpSessionId) in teamMembers }.forEach { it.send(message) } @@ -293,13 +295,13 @@ object RunExecutor : Consumer { /** - * Dumps the given [Competition] to a file. + * Dumps the given [EvaluationRun] to a file. * - * @param competition [Competition] that should be dumped. + * @param competition [EvaluationRun] that should be dumped. */ - fun dump(competition: Competition) { + fun dump(competition: EvaluationRun) { try { - val file = File("run_dump_${competition.id.string}.json") + val file = File("run_dump_${competition.id}.json") jacksonObjectMapper().writeValue(file, competition) this.logger.info("Wrote current run state to ${file.absolutePath}") } catch (e: Exception){ diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index feb0f0ef0..ae457e643 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -2,26 +2,26 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.run.websocket.ClientMessage -import dev.dres.data.model.UID import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunProperties -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator /** - * A managing class for concrete executions of [CompetitionDescription], i.e. [InteractiveSynchronousCompetition]s. + * A managing class for concrete executions of [CompetitionDescription], i.e. [InteractiveSynchronousEvaluation]s. * - * @see InteractiveSynchronousCompetition + * @see InteractiveSynchronousEvaluation * * @author Ralph Gasser * @version 1.5.0 */ interface RunManager : Runnable { - /** Unique, public, numeric ID for this [RunManager]. */ - val id: UID + /** Unique, public [EvaluationId] for this [RunManager]. */ + val id: EvaluationId /** A name for identifying this [RunManager]. */ val name: String @@ -66,20 +66,20 @@ interface RunManager : Runnable { /** - * Returns the number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager]. + * Returns the number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager]. * * @param context The [RunActionContext] for this invocation. - * @return The number of [InteractiveSynchronousCompetition.Task]s held by this [RunManager] + * @return The number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager] */ fun taskCount(context: RunActionContext): Int /** - * Returns a list of all [Task]s that took or are taking place in the scope of this [RunManager]. + * Returns a list of all [TaskRun]s that took or are taking place in the scope of this [RunManager]. * * @param context The [RunActionContext] for this invocation. - * @return List of [Task] that took place (are taking place). + * @return List of [TaskRun] that took place (are taking place). */ - fun tasks(context: RunActionContext): List + fun tasks(context: RunActionContext): List /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt index 9121019af..81258c60f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt @@ -1,13 +1,12 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.UID import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission import dev.dres.run.score.TaskContext /** - * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [Task] based on the + * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [TaskRun] based on the * entire [Submission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. * * @author Luca Rossetto & Ralph Gasser @@ -18,9 +17,9 @@ interface RecalculatingSubmissionTaskScorer: TaskScorer { * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [Submission] history. * * @param submissions The [Submission]s used to update this [RecalculatingSubmissionTaskScorer] with. - * @param taskStartTime Time the [Task] started. - * @param taskDuration Duration of the [Task]. - * @param taskEndTime Time the [Task] ended. + * @param taskStartTime Time the [TaskRun] started. + * @param taskDuration Duration of the [TaskRun]. + * @param taskEndTime Time the [TaskRun] ended. * */ fun computeScores(submissions: Collection, context: TaskContext): Map diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt index ae18d3480..c309b5174 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt @@ -3,13 +3,13 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.UID import dev.dres.data.model.competition.team.Team import dev.dres.data.model.competition.team.TeamId -import dev.dres.data.model.run.interfaces.Task +import dev.dres.data.model.run.interfaces.TaskRun /** Type alias for a */ typealias ScoreEntry = Triple /** - * A scorer for a [Task]. A score is a [Double] value that captures a [Team] performance. + * A scorer for a [TaskRun]. A score is a [Double] value that captures a [Team] performance. * The [TaskScorer] calculates and tracks these scores per [TeamId]. * * @version 1.1.0 diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index 01205a270..8d3c9021b 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -1,7 +1,7 @@ package dev.dres.run.updatables import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.Competition +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus import dev.dres.run.score.ScoreTimePoint @@ -15,7 +15,7 @@ import java.util.* * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class ScoreboardsUpdatable(val scoreboards: List, private val updateIntervalMs: Long, private val competition: Competition): StatefulUpdatable { +class ScoreboardsUpdatable(val scoreboards: List, private val updateIntervalMs: Long, private val competition: EvaluationRun): StatefulUpdatable { companion object { val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE/*, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED, RunManagerStatus.PREPARING_TASK*/) diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index e404c3959..bbd8cd254 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.types.run.websocket.ServerMessage import dev.dres.api.rest.types.run.websocket.ServerMessageType import dev.dres.data.model.UID import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.InteractiveSynchronousCompetition +import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.RunManagerStatus @@ -15,7 +15,7 @@ import java.util.* /** * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated and - * updates the scores for the respective [InteractiveSynchronousCompetition.Task]. + * updates the scores for the respective [InteractiveSynchronousEvaluation.Task]. * * @author Ralph Gasser * @version 1.2.0 From bbab3c7a710b01a6674a6b71baedb20340bf906f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 4 Nov 2022 17:05:06 +0100 Subject: [PATCH 022/498] And yet another enormous commit. --- backend/src/main/kotlin/dev/dres/DRES.kt | 16 +- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 4 +- .../dev/dres/api/cli/CompetitionRunCommand.kt | 554 ----------- ...etitionCommand.kt => EvaluationCommand.kt} | 58 +- .../dev/dres/api/cli/EvaluationRunCommand.kt | 401 ++++++++ .../kotlin/dev/dres/api/rest/AccessManager.kt | 2 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 52 +- .../handler/CompetitionRunAdminHandler.kt | 908 ------------------ .../CompetitionRunClientInfoHandler.kt | 140 --- .../api/rest/handler/ContextExtensions.kt | 46 + .../dres/api/rest/handler/DownloadHandler.kt | 4 +- .../dres/api/rest/handler/JudgementHandler.kt | 2 +- .../dev/dres/api/rest/handler/LogHandler.kt | 7 +- .../handler/description/GetTeamLogoHandler.kt | 71 -- .../admin/AbstractEvaluationAdminHandler.kt | 53 + .../evaluation/admin/AdjustDurationHandler.kt | 63 ++ .../admin/AdjustPropertiesHandler.kt | 57 ++ .../admin/CreateEvaluationHandler.kt | 125 +++ .../admin/EvaluationOverviewHandler.kt | 41 + .../evaluation/admin/ForceViewerHandler.kt | 58 ++ .../evaluation/admin/ListPastTaskHandler.kt | 54 ++ .../admin/ListSubmissionsHandler.kt | 47 + .../evaluation/admin/ListViewersHandler.kt | 38 + .../evaluation/admin/NextTaskHandler.kt | 75 ++ .../admin/OverrideSubmissionHandler.kt | 77 ++ .../evaluation/admin/PreviousTaskHandler.kt | 55 ++ .../admin/StartEvaluationHandler.kt | 56 ++ .../evaluation/admin/StartTaskHandler.kt | 66 ++ .../evaluation/admin/StopEvaluationHandler.kt | 57 ++ .../evaluation/admin/StopTaskHandler.kt | 57 ++ .../evaluation/admin/SwitchTaskHandler.kt | 57 ++ .../client/AbstractEvaluationClientHandler.kt | 55 ++ .../client/ClientListEvaluationsHandler.kt | 55 ++ .../client/ClientTaskInfoHandler.kt | 68 ++ .../viewer/AbstractEvaluationViewerHandler.kt | 58 ++ .../viewer}/CompetitionRunHandler.kt | 105 +- .../viewer}/CompetitionRunScoreHandler.kt | 13 +- .../handler/preview/AbstractPreviewHandler.kt | 8 +- .../handler/preview/MediaPreviewHandler.kt | 1 - .../preview/SubmissionPreviewHandler.kt | 18 +- .../submission/BatchSubmissionHandler.kt | 21 +- .../handler/submission/SubmissionHandler.kt | 15 +- .../AbstractCompetitionDescriptionHandler.kt | 22 +- .../CreateEvaluationTemplateHandler.kt} | 18 +- .../DeleteEvaluationTemplateHandler.kt} | 20 +- .../handler/template/GetTeamLogoHandler.kt | 58 ++ .../ListEvaluationTemplatesHandler.kt} | 19 +- .../ListTasksHandler.kt | 19 +- .../ListTeamHandler.kt | 10 +- .../ShowEvaluationTemplateHandler.kt} | 19 +- .../UpdateCompetitionHandler.kt | 112 +-- .../handler/users/ListActiveUsersHandler.kt | 3 +- .../api/rest/types/collection/ApiMediaItem.kt | 3 +- .../types/collection/RestMediaCollection.kt | 6 +- .../competition/ApiCompetitionDescription.kt | 6 +- .../competition/ApiCompetitionOverview.kt | 4 +- .../competition/ApiCompetitionStartMessage.kt | 13 + .../competition/CompetitionStartMessage.kt | 17 - .../rest/types/competition/tasks/ApiHint.kt | 2 +- .../types/competition/tasks/ApiHintType.kt | 2 +- .../rest/types/competition/tasks/ApiTarget.kt | 2 +- .../types/competition/tasks/ApiTargetType.kt | 2 +- .../competition/tasks/ApiTaskDescription.kt | 8 +- .../types/competition/tasks/ApiTaskGroup.kt | 2 +- .../types/competition/tasks/ApiTaskType.kt | 2 +- .../tasks/options/ApiComponentOption.kt | 2 +- .../tasks/options/ApiScoreOption.kt | 2 +- .../tasks/options/ApiSubmissionOption.kt | 2 +- .../tasks/options/ApiTargetOption.kt | 2 +- .../tasks/options/ApiTaskOption.kt | 2 +- .../rest/types/competition/team/ApiTeam.kt | 4 +- .../types/competition/team/ApiTeamGroup.kt | 4 +- .../{run => evaluation}/AdminRunOverview.kt | 14 +- .../types/evaluation/ApiEvaluationInfo.kt | 24 + .../api/rest/types/evaluation/ApiRunType.kt | 11 + .../rest/types/evaluation/ApiSubmission.kt | 27 + .../types/evaluation/ApiSubmissionInfo.kt | 13 + .../api/rest/types/evaluation/ApiTaskInfo.kt | 21 + .../rest/types/evaluation/ApiViewerInfo.kt | 9 + .../types/{run => evaluation}/RunState.kt | 2 +- .../types/{run => evaluation}/TaskInfo.kt | 11 +- .../types/{run => evaluation}/TeamInfo.kt | 4 +- .../websocket/ClientMessage.kt | 2 +- .../websocket/ServerMessage.kt | 2 +- .../dres/api/rest/types/run/PastTaskInfo.kt | 19 - .../dev/dres/api/rest/types/run/RunInfo.kt | 40 - .../dev/dres/api/rest/types/run/RunType.kt | 6 - .../dres/api/rest/types/run/SubmissionInfo.kt | 62 -- .../rest/types/run/TaskRunSubmissionInfo.kt | 6 - .../dev/dres/api/rest/types/run/ViewerInfo.kt | 9 - .../main/kotlin/dev/dres/data/model/UID.kt | 14 - .../dres/data/model/media/MediaCollection.kt | 7 + .../data/model/media/MediaItemSegmentList.kt | 3 +- .../dres/data/model/run/AbstractEvaluation.kt | 45 +- .../data/model/run/AbstractInteractiveTask.kt | 28 +- .../model/run/AbstractNonInteractiveTask.kt | 17 +- .../dres/data/model/run/AbstractTaskRun.kt | 35 +- .../dev/dres/data/model/run/Evaluation.kt | 22 +- .../run/InteractiveAsynchronousEvaluation.kt | 43 +- .../run/InteractiveSynchronousEvaluation.kt | 38 +- .../model/run/NonInteractiveEvaluation.kt | 19 +- .../dres/data/model/run/RunActionContext.kt | 4 +- .../kotlin/dev/dres/data/model/run/Task.kt | 12 +- .../model/run/interfaces/EvaluationRun.kt | 6 +- .../dres/data/model/run/interfaces/TaskRun.kt | 6 +- .../model/submissions/BatchedSubmission.kt | 34 + .../dres/data/model/submissions/Submission.kt | 46 +- .../data/model/submissions/SubmissionType.kt | 3 + .../submissions/batch/BaseResultBatch.kt | 2 +- .../batch/BaseResultBatchElement.kt | 11 - .../submissions/batch/BaseSubmissionBatch.kt | 8 +- .../model/submissions/batch/ResultBatch.kt | 2 +- .../batch/TemporalSubmissionBatch.kt | 8 +- .../EvaluationTemplate.kt} | 56 +- .../interfaces/SubmissionFilterFactory.kt | 2 +- .../interfaces/TaskScorerFactory.kt | 2 +- .../{competition => template}/task/Hint.kt | 8 +- .../task/HintType.kt | 4 +- .../task/TargetType.kt | 4 +- .../task/TaskDescriptionTarget.kt | 8 +- .../task/TaskGroup.kt | 9 +- .../task/TaskTemplate.kt} | 50 +- .../task/TaskType.kt | 22 +- .../task/options/ConfiguredOption.kt | 8 +- .../task/options/HintOption.kt | 6 +- .../task/options/ScoreOption.kt | 2 +- .../task/options/SubmissionOption.kt | 2 +- .../task/options/TargetOption.kt | 2 +- .../task/options/TaskOption.kt | 6 +- .../{competition => template}/team/Team.kt | 10 +- .../team/TeamAggregator.kt | 2 +- .../team/TeamAggregatorImpl.kt | 2 +- .../team/TeamGroup.kt | 8 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 9 +- .../run/InteractiveAsynchronousRunManager.kt | 89 +- .../dev/dres/run/InteractiveRunManager.kt | 31 +- .../run/InteractiveSynchronousRunManager.kt | 71 +- .../dev/dres/run/NonInteractiveRunManager.kt | 31 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 81 +- .../main/kotlin/dev/dres/run/RunManager.kt | 17 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 37 +- .../dev/dres/run/eventstream/StreamEvent.kt | 21 +- .../handlers/ResultLogStatisticsHandler.kt | 21 +- .../handlers/SubmissionStatisticsHandler.kt | 9 +- .../handlers/TeamCombinationScoreHandler.kt | 13 +- .../run/exceptions/IllegalTeamIdException.kt | 2 +- .../kotlin/dev/dres/run/score/TaskContext.kt | 2 +- .../RecalculatingSubmissionTaskScorer.kt | 2 +- .../dres/run/score/interfaces/TaskScorer.kt | 7 +- .../run/score/interfaces/TeamTaskScorer.kt | 2 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 12 +- .../dres/run/score/scoreboard/Scoreboard.kt | 9 +- .../scoreboard/SumAggregateScoreBoard.kt | 4 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 2 +- .../scorer/InferredAveragePrecisionScorer.kt | 2 +- .../dres/run/score/scorer/KisTaskScorer.kt | 2 +- .../dres/run/updatables/EndTaskUpdatable.kt | 6 +- .../run/updatables/MessageQueueUpdatable.kt | 4 +- .../dres/run/updatables/ScoresUpdatable.kt | 9 +- .../dev/dres/run/updatables/Updatable.kt | 2 +- .../TemporalContainmentSubmissionValidator.kt | 4 +- .../TemporalOverlapSubmissionValidator.kt | 4 +- .../utilities/extensions/StringExtensions.kt | 9 +- .../run/score/scorer/AvsTaskScorerTest.kt | 163 ++-- .../run/score/scorer/KisTaskScorerTest.kt | 35 +- 165 files changed, 2904 insertions(+), 2821 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt rename backend/src/main/kotlin/dev/dres/api/cli/{CompetitionCommand.kt => EvaluationCommand.kt} (81%) create mode 100644 backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/handler/{ => evaluation/viewer}/CompetitionRunHandler.kt (83%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{ => evaluation/viewer}/CompetitionRunScoreHandler.kt (96%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description => template}/AbstractCompetitionDescriptionHandler.kt (54%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description/CreateCompetitionHandler.kt => template/CreateEvaluationTemplateHandler.kt} (77%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description/DeleteCompetitionHandler.kt => template/DeleteEvaluationTemplateHandler.kt} (58%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description/ListCompetitionHandler.kt => template/ListEvaluationTemplatesHandler.kt} (63%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description => template}/ListTasksHandler.kt (64%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description => template}/ListTeamHandler.kt (82%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description/ShowCompetitionHandler.kt => template/ShowEvaluationTemplateHandler.kt} (61%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/{description => template}/UpdateCompetitionHandler.kt (64%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/{run => evaluation}/AdminRunOverview.kt (85%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiViewerInfo.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/{run => evaluation}/RunState.kt (97%) rename backend/src/main/kotlin/dev/dres/api/rest/types/{run => evaluation}/TaskInfo.kt (72%) rename backend/src/main/kotlin/dev/dres/api/rest/types/{run => evaluation}/TeamInfo.kt (86%) rename backend/src/main/kotlin/dev/dres/api/rest/types/{run => evaluation}/websocket/ClientMessage.kt (86%) rename backend/src/main/kotlin/dev/dres/api/rest/types/{run => evaluation}/websocket/ServerMessage.kt (94%) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/run/RunType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskRunSubmissionInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/run/ViewerInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/UID.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt rename backend/src/main/kotlin/dev/dres/data/model/{competition/CompetitionDescription.kt => template/EvaluationTemplate.kt} (63%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/interfaces/SubmissionFilterFactory.kt (89%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/interfaces/TaskScorerFactory.kt (88%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/Hint.kt (95%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/HintType.kt (93%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/TargetType.kt (91%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/TaskDescriptionTarget.kt (93%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/TaskGroup.kt (78%) rename backend/src/main/kotlin/dev/dres/data/model/{competition/task/TaskDescription.kt => template/task/TaskTemplate.kt} (77%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/TaskType.kt (83%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/options/ConfiguredOption.kt (89%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/options/HintOption.kt (88%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/options/ScoreOption.kt (96%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/options/SubmissionOption.kt (98%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/options/TargetOption.kt (95%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/task/options/TaskOption.kt (87%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/team/Team.kt (85%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/team/TeamAggregator.kt (96%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/team/TeamAggregatorImpl.kt (97%) rename backend/src/main/kotlin/dev/dres/data/model/{competition => template}/team/TeamGroup.kt (86%) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 4eddae180..8ef2ecb05 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -7,12 +7,12 @@ import dev.dres.data.model.Config import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.* -import dev.dres.data.model.competition.task.options.* -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.team.TeamAggregator -import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.* +import dev.dres.data.model.template.task.options.* +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.TeamAggregator +import dev.dres.data.model.template.team.TeamGroup import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaSegment @@ -110,8 +110,8 @@ object DRES { ScoreOption, SubmissionOption, TargetOption, - CompetitionDescription, - TaskDescription, + EvaluationTemplate, + TaskTemplate, Team, TeamGroup, TeamAggregator, diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 43fe44413..4fe6a7cf6 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -41,10 +41,10 @@ object Cli { fun loop(store: TransientEntityStore, config: Config) { clikt = DRESBaseCommand().subcommands( - CompetitionCommand(store, config), + EvaluationCommand(store, config), UserCommand(), MediaCollectionCommand(store), - CompetitionRunCommand(store), + EvaluationRunCommand(store), OpenApiCommand(), ExecutionCommand() ) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt deleted file mode 100644 index 9d2ab1c25..000000000 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionRunCommand.kt +++ /dev/null @@ -1,554 +0,0 @@ -package dev.dres.api.cli - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.core.NoOpCliktCommand -import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.parameters.options.* -import com.github.ajalt.clikt.parameters.types.path -import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter -import com.jakewharton.picnic.table -import dev.dres.data.model.UID -import dev.dres.data.model.competition.task.TaskDescriptionTarget -import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.InteractiveSynchronousEvaluation -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect -import dev.dres.run.* -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.toDateString -import jetbrains.exodus.database.TransientEntityStore -import java.io.FileOutputStream -import java.io.OutputStream -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardOpenOption - -/** - * - */ -class CompetitionRunCommand(internal val store: TransientEntityStore) : NoOpCliktCommand(name = "run") { - - init { - subcommands( - OngoingCompetitionRunsCommand(), - ListCompetitionRunsCommand(), - DeleteRunCommand(), - ExportRunCommand(), - ReactivateRunCommand(), - CompetitionRunsHistoryCommand(), - ResetSubmissionStatusCommand(), - ExportRunJudgementsCommand() - ) - } - - override fun aliases(): Map> { - return mapOf( - "ls" to listOf("ongoing"), - "la" to listOf("list"), - "remove" to listOf("delete"), - "drop" to listOf("delete") - ) - } - - /** - * Helper class that contains all information regarding a [RunManager]. - */ - data class RunSummary(val id: String, val name: String, val description: String?, val task: String?) - - /** - * Lists all ongoing competitions runs for the current DRES instance. - */ - inner class OngoingCompetitionRunsCommand : - CliktCommand(name = "ongoing", help = "Lists all ongoing competition runs.") { - private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) - override fun run() { - if (RunExecutor.managers().isEmpty()) { - println("No runs are currently ongoing!") - return - } - if (plain) { - RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - println( - "${ - RunSummary( - it.id.string, - it.name, - it.description.description, - it.currentTaskDescription(RunActionContext.INTERNAL).name - ) - } (${it.status})" - ) - } - } else { - println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "type", "name", "description", "currentTask", "status") - } - body { - RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - when(it) { - is InteractiveSynchronousRunManager -> row( - it.id.string, - "Synchronous", - it.name, - it.description.description, - it.currentTaskDescription(RunActionContext.INTERNAL).name, - it.status - ) - is InteractiveAsynchronousRunManager -> row( - it.id.string, - "Asynchronous", - it.name, - it.description.description, - "N/A", - it.status - ) - else -> row("??", "??", "??", "??", "??", "??") - } - - } - } - } - ) - } - } - } - - /** - * Lists all competition runs (ongoing and past) for the current DRES instance. - */ - inner class ListCompetitionRunsCommand : - CliktCommand(name = "list", help = "Lists all (ongoing and past) competition runs.") { - private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) - override fun run() { - if (plain) { - this@CompetitionRunCommand.runs.sortedByDescending { it.started }.forEach { - println( - "${ - RunSummary( - it.id.string, - it.name, - it.description.description, - if (it is InteractiveSynchronousEvaluation) it.currentTask?.description?.name - ?: "N/A" else "N/A" - ) - }" - ) - } - } else { - println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "name", "description", "lastTask", "status", "start", "end") - } - body { - this@CompetitionRunCommand.runs.sortedByDescending { it.started }.forEach { - val status = if (it.hasStarted && !it.hasEnded && !it.isRunning) { - "started" - } else if (it.hasStarted && !it.hasEnded && it.isRunning) { - "running" - } else if (it.hasEnded) { - "ended" - } else if (!it.hasStarted) { - "idle" - } else { - "unknown" - } - row( - it.id.string, - it.name, - it.description.description, - if (it is InteractiveSynchronousEvaluation) it.currentTask?.description?.name - ?: "N/A" else "N/A", - status, - it.started?.toDateString() ?: "-", - it.ended?.toDateString() ?: "-", - ) - } - } - } - ) - } - } - } - - /** - * Deletes a selected competition run for the current DRES instance. - */ - inner class DeleteRunCommand : - CliktCommand(name = "delete", help = "Deletes an existing competition run.", printHelpOnEmptyArgs = true) { - private val id: UID by option("-r", "--run").convert { it.UID() }.required() - override fun run() { - if (RunExecutor.managers().any { it.id == id }) { - println("Run with ID $id could not be deleted because it is still running! Terminate it and try again.") - return - } - - val deleted = this@CompetitionRunCommand.runs.delete(this.id) - if (deleted != null) { - println("Run $deleted deleted successfully!") - } else { - println("Run with ID ${id.string} could not be deleted because it doesn't exist!") - } - } - } - - /** - * Exports a specific competition run as JSON. - */ - inner class ExportRunCommand : CliktCommand(name = "export", help = "Exports the selected competition run to a JSON file.", printHelpOnEmptyArgs = true) { - - /** [UID] of the [EvaluationRun] that should be exported. .*/ - private val id: UID by option("-i", "--id").convert { it.UID() }.required() - - /** Path to the file that should be created .*/ - private val path: Path by option("-o", "--output").path().required() - - /** Flag indicating whether export should be pretty printed.*/ - private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) - - override fun run() { - val run = this@CompetitionRunCommand.runs[this.id] - if (run == null) { - println("Run does not seem to exist.") - return - } - - val mapper = jacksonObjectMapper() - Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { - val writer = if (this.pretty) { - mapper.writerWithDefaultPrettyPrinter() - } else { - mapper.writer() - } - writer.writeValue(it, run) - } - println("Successfully wrote run ${run.id} to $path.") - } - } - - inner class ReactivateRunCommand : CliktCommand(name = "reactivate", help = "Reactivates a previously ended competition run", printHelpOnEmptyArgs = true) { - - private val id: UID by option("-r", "--run").convert { it.UID() }.required() - - override fun run() { - val run = this@CompetitionRunCommand.runs[this.id] - if (run == null) { - println("Run does not seem to exist.") - return - } - - if(!run.hasEnded || run.isRunning) { - println("Run has not ended.") - return - } - - if (RunExecutor.managers().any { it.id == run.id }) { - println("Run already active.") - return - } - - run.reactivate() - RunExecutor.schedule(run) - - println("Run reactivated") - - - } - - } - -// /** -// * Exports a specific competition run as JSON. -// */ -// inner class ExportLogsCommand: CliktCommand(name = "exportLogs", help = "Exports just the interaction logs of the competition run as JSON.") { -// private val id: Long by option("-r", "--run").long().required() -// private val path: String by option("-o", "--output").required() -// override fun run() { -// val run = this@CompetitionRunCommand.runs[this.id] -// if (run == null) { -// println("Run does not seem to exist.") -// return -// } -// -// val path = Paths.get(this.path) -// val mapper = ObjectMapper() -// mapper.registerModule(KotlinModule()) -// -// val resultLogs = run.runs.flatMap { it.data.sessionQueryResultLogs.values }.flatten() -// val interactionLogs = run.runs.flatMap { it.data.sessionQueryEventLogs.values }.flatten() -// -// Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { -// it.appendln(mapper.writeValueAsString(resultLogs)) -// it.newLine() -// it.appendln(mapper.writeValueAsString(interactionLogs)) -// } -// println("Successfully wrote run ${run.id} to $path.") -// } -// } - - - inner class CompetitionRunsHistoryCommand : CliktCommand(name = "history", help = "Lists past Competition Runs") { - // TODO fancification with table - - override fun run() { - - this@CompetitionRunCommand.runs.forEach { - - println(it.name) - - println("Teams:") - it.description.teams.forEach { team -> - println(team) - } - - println() - println("All Tasks:") - it.description.tasks.forEach { task -> - println(task) - } - - println() - println("Evaluated Tasks:") - it.tasks.forEach { t -> - println(t.description) - - if (t is InteractiveSynchronousEvaluation.Task) { - println("Submissions") - t.submissions.forEach { s -> println(s) } - } - - - } - println() - } - - } - - } - - - inner class ResetSubmissionStatusCommand : - NoOpCliktCommand( - name = "resetSubmission", - help = "Resets Submission Status to INDETERMINATE", - printHelpOnEmptyArgs = true - ) { - - init { - subcommands( - ResetSingleSubmissionStatusCommand(), - ResetTaskSubmissionStatusCommand(), - ResetTaskGroupSubmissionStatusCommand() - ) - } - - - inner class ResetSingleSubmissionStatusCommand : - CliktCommand( - name = "submission", - help = "Resets the status of individual submissions", - printHelpOnEmptyArgs = true - ) { - - private val runId: UID by option("-r", "--run", help = "Id of the run").convert { it.UID() }.required() - private val ids: List by option("-i", "--ids", help = "UIDs of the submissions to reset").multiple() - - override fun run() { - /* Fetch competition run. */ - val run = this@CompetitionRunCommand.runs[this.runId] - if (run == null) { - println("Run does not seem to exist.") - return - } - - if (run is InteractiveSynchronousEvaluation) { - - /* Fetch submissions and reset them. */ - val submissions = run.tasks.flatMap { - it.submissions - }.filter { - it.uid.string in ids - } - submissions.forEach { it.status = SubmissionStatus.INDETERMINATE } - - /* Update competition run through dao. */ - this@CompetitionRunCommand.runs.update(run) - println("Successfully reset ${submissions.size} submissions.") - } else { - println("Operation not supported for run type") - } - } - } - - inner class ResetTaskSubmissionStatusCommand : - CliktCommand( - name = "task", - help = "Resets the status of all submissions of specified tasks.", - printHelpOnEmptyArgs = true - ) { - - private val runId: UID by option("-r", "--run", help = "UID of the runs").convert { it.UID() }.required() - private val ids: List by option("-i", "--ids", help = "UIDs of the task runs to resets").multiple() - - override fun run() { - - /* Fetch competition run. */ - val run = this@CompetitionRunCommand.runs[this.runId] - if (run == null) { - println("Run does not seem to exist.") - return - } - - if (run is InteractiveSynchronousEvaluation) { - /* Fetch submissions and reset them. */ - val submissions = run.tasks.filter { - it.uid.string in ids - }.flatMap { - it.submissions - } - submissions.forEach { it.status = SubmissionStatus.INDETERMINATE } - - this@CompetitionRunCommand.runs.update(run) - println("Successfully reset ${submissions.size} submissions.") - } else { - println("Operation not supported for run type") - } - } - } - - inner class ResetTaskGroupSubmissionStatusCommand : - CliktCommand( - name = "taskGroup", - help = "Resets the status all submissions for tasks within a task group", - printHelpOnEmptyArgs = true - ) { - - private val runId: UID by option("-r", "--run", help = "Id of the run").convert { it.UID() }.required() - private val taskGroup: String by option( - "-g", - "--taskGroup", - help = "Name of the Task Group to reset" - ).required() - - override fun run() { - - val run = this@CompetitionRunCommand.runs[this.runId] - if (run == null) { - println("Run does not seem to exist.") - return - } - - if (run is InteractiveSynchronousEvaluation) { - - val submissions = - run.tasks.filter { it.description.taskGroup.name == taskGroup }.flatMap { it.submissions } - submissions.forEach { it.status = SubmissionStatus.INDETERMINATE } - - this@CompetitionRunCommand.runs.update(run) - - println("reset ${submissions.size} submissions") - } else { - println("Operation not supported for run type") - } - - } - } - - } - - inner class ExportRunJudgementsCommand : CliktCommand( - name = "exportJudgements", - help = "Exports all judgements made for all relevant tasks of a run as CSV", - printHelpOnEmptyArgs = true - ) { - - private val runId: UID by option("-r", "--run", help = "Id of the run").convert { it.UID() }.required() - - private fun fileOutputStream(file: String): OutputStream = FileOutputStream(file) - - private val outputStream: OutputStream by option( - "-f", - "--file", - help = "Path of the file the judgements are to be exported to" - ) - .convert { fileOutputStream(it) } - .default(System.out) - - - private val header = listOf("TaskId", "TaskName", "ItemId", "StartTime", "EndTime", "Status") - - override fun run() { - - val run = this@CompetitionRunCommand.runs[this.runId] - if (run == null) { - println("Run does not seem to exist.") - return - } - - - val relevantTasks = run.tasks - .filter { it.description.target is TaskDescriptionTarget.JudgementTaskDescriptionTarget || it.description.target is TaskDescriptionTarget.VoteTaskDescriptionTarget } - .filterIsInstance(AbstractInteractiveTask::class.java) - - if (relevantTasks.isEmpty()) { - println("No judged tasks in run.") - return - } - - csvWriter().open(outputStream) { - writeRow(header) - - relevantTasks.forEach { task -> - - val submittedItems = task.submissions.groupBy { - when { - it is ItemAspect && it is TemporalSubmissionAspect -> Triple(it.item.name, it.start, it.end) - it is ItemAspect -> Triple(it.item.name, 0L, 0L) - it is TextAspect -> Triple(it.text, 0L, 0L) - else -> Triple("unknown", 0L, 0L) - } - } - - submittedItems.entries.forEach { - val status = it.value.map { s -> s.status }.toSet() //should only contain one element - - writeRow( - listOf( - task.uid.string, - task.description.name, - it.key.first, - it.key.second, - it.key.third, - status - ) - ) - - } - - } - - } - - } - - } - - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt similarity index 81% rename from backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt rename to backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index d82efef6f..a58771445 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/CompetitionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -11,7 +11,7 @@ import com.github.ajalt.clikt.parameters.options.validate import com.github.ajalt.clikt.parameters.types.path import com.jakewharton.picnic.table import dev.dres.data.model.Config -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import dev.dres.utilities.FFmpegUtil import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* @@ -22,13 +22,13 @@ import java.nio.file.StandardOpenOption import java.util.* /** - * A collection of [CliktCommand]s for [CompetitionDescription] management. + * A collection of [CliktCommand]s for [EvaluationTemplate] management. * * @author Luca Rossetto * @author Ralph Gasser * @version 2.0.0 */ -class CompetitionCommand(private val store: TransientEntityStore, config: Config) : NoOpCliktCommand(name = "competition") { +class EvaluationCommand(private val store: TransientEntityStore, config: Config) : NoOpCliktCommand(name = "competition") { init { this.subcommands( @@ -61,7 +61,7 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } /** - * [CliktCommand] to create a new [CompetitionDescription]. + * [CliktCommand] to create a new [EvaluationTemplate]. */ inner class Create : CliktCommand(name = "create", help = "Creates a new Competition") { @@ -74,8 +74,8 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config .validate { require(it.isNotEmpty()) { "Competition description must be non empty." } } override fun run() { - val newCompetition = this@CompetitionCommand.store.transactional { - CompetitionDescription.new { + val newCompetition = this@EvaluationCommand.store.transactional { + EvaluationTemplate.new { this.id = UUID.randomUUID().toString() this.name = this@Create.name this.description = this@Create.description @@ -86,12 +86,12 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } /** - * [CliktCommand] to delete a [CompetitionDescription]. + * [CliktCommand] to delete a [EvaluationTemplate]. */ inner class DeleteCompetitionCommand : AbstractCompetitionCommand(name = "delete", help = "Deletes a competition") { override fun run() { - this@CompetitionCommand.store.transactional { - val competition = CompetitionDescription.query((CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name)).firstOrNull() + this@EvaluationCommand.store.transactional { + val competition = EvaluationTemplate.query((EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name)).firstOrNull() if (competition == null) { println("Could not find competition to delete.") return@transactional @@ -103,12 +103,12 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } /** - * [CliktCommand] to copy a [CompetitionDescription]. + * [CliktCommand] to copy a [EvaluationTemplate]. */ inner class CopyCompetitionCommand : AbstractCompetitionCommand(name = "copy", help = "Copies a Competition") { override fun run() { - this@CompetitionCommand.store.transactional { - val competition = CompetitionDescription.query((CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name)).firstOrNull() + this@EvaluationCommand.store.transactional { + val competition = EvaluationTemplate.query((EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name)).firstOrNull() if (competition == null) { println("Could not find competition to copy.") return@transactional @@ -121,10 +121,10 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } /** - * [CliktCommand] to list all [CompetitionDescription]s. + * [CliktCommand] to list all [EvaluationTemplate]s. */ inner class ListCompetitionCommand : CliktCommand(name = "list", help = "Lists an overview of all Competitions") { - override fun run() = this@CompetitionCommand.store.transactional(true) { + override fun run() = this@EvaluationCommand.store.transactional(true) { var no = 0 println(table { cellStyle { @@ -136,8 +136,8 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config row("name", "id", "# teams", "# tasks", "description", ) } body { - CompetitionDescription.all().asSequence().forEach { c -> - row(c.name, c.id, c.teams.size(), c.taskGroups.flatMapDistinct { it.tasks }.size(), c.description).also { no++ } + EvaluationTemplate.all().asSequence().forEach { c -> + row(c.name, c.id, c.teams.size(), c.tasks.size(), c.description).also { no++ } } } }) @@ -146,12 +146,12 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } /** - * [CliktCommand] to show a specific [CompetitionDescription]. + * [CliktCommand] to show a specific [EvaluationTemplate]. */ inner class ShowCompetitionCommand : AbstractCompetitionCommand(name = "show", help = "Shows details of a Competition") { - override fun run() = this@CompetitionCommand.store.transactional(true) { - val competition = CompetitionDescription.query( - (CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name) + override fun run() = this@EvaluationCommand.store.transactional(true) { + val competition = EvaluationTemplate.query( + (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) ).firstOrNull() if (competition == null) { @@ -167,7 +167,7 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config println() println("Tasks:") - competition.taskGroups.flatMapDistinct { it.tasks }.asSequence().forEach { _ -> + competition.tasks.asSequence().forEach { _ -> /* TODO: it.printOverview(System.out) */ println() } @@ -177,13 +177,13 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } /** - * [CliktCommand] to prepare a specific [CompetitionDescription]. + * [CliktCommand] to prepare a specific [EvaluationTemplate]. */ inner class PrepareCompetitionCommand : AbstractCompetitionCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { - override fun run() = this@CompetitionCommand.store.transactional(true) { - val competition = CompetitionDescription.query( - (CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name) + override fun run() = this@EvaluationCommand.store.transactional(true) { + val competition = EvaluationTemplate.query( + (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) ).firstOrNull() if (competition == null) { @@ -201,7 +201,7 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config } println("Rendering ${item.first.name}$ at ${item.second}") - FFmpegUtil.extractSegment(item.first, item.second, this@CompetitionCommand.cacheLocation) + FFmpegUtil.extractSegment(item.first, item.second, this@EvaluationCommand.cacheLocation) } } } @@ -218,9 +218,9 @@ class CompetitionCommand(private val store: TransientEntityStore, config: Config /** Flag indicating whether export should be pretty printed.*/ private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) - override fun run() = this@CompetitionCommand.store.transactional(true) { - val competition = CompetitionDescription.query( - (CompetitionDescription::id eq this.id).or(CompetitionDescription::name eq this.name) + override fun run() = this@EvaluationCommand.store.transactional(true) { + val competition = EvaluationTemplate.query( + (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) ).firstOrNull() if (competition == null) { diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt new file mode 100644 index 000000000..e5daa6e59 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt @@ -0,0 +1,401 @@ +package dev.dres.api.cli + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.NoOpCliktCommand +import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.types.path +import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter +import com.jakewharton.picnic.table +import dev.dres.data.model.template.task.TargetType +import dev.dres.data.model.run.* +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.run.* +import dev.dres.utilities.extensions.toDateString +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardOpenOption + +/** + * A collection of [CliktCommand]s for [Run] management. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpCliktCommand(name = "run") { + + init { + subcommands( + Ongoing(), + List(), + Delete(), + Export(), + Reactivate(), + History(), + ResetSubmission(), + ExportJudgements() + ) + } + + override fun aliases(): Map> { + return mapOf( + "ls" to listOf("ongoing"), + "la" to listOf("list"), + "remove" to listOf("delete"), + "drop" to listOf("delete") + ) + } + + /** + * Helper class that contains all information regarding a [RunManager]. + */ + data class RunSummary(val id: String, val name: String, val description: String?, val task: String?) + + /** + * Lists all ongoing competitions runs for the current DRES instance. + */ + inner class Ongoing : + CliktCommand(name = "ongoing", help = "Lists all ongoing evaluation runs.") { + private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) + override fun run() { + if (RunExecutor.managers().isEmpty()) { + println("No runs are currently ongoing!") + return + } + if (this.plain) { + RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { + println("${RunSummary(it.id, it.name, it.template.description, it.currentTaskDescription(RunActionContext.INTERNAL).name)} (${it.status})") + } + } else { + println( + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "type", "name", "description", "currentTask", "status") + } + body { + RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { + when(it) { + is InteractiveSynchronousRunManager -> row( + it.id, + "Synchronous", + it.name, + it.template.description, + it.currentTaskDescription(RunActionContext.INTERNAL).name, + it.status + ) + is InteractiveAsynchronousRunManager -> row( + it.id, + "Asynchronous", + it.name, + it.template.description, + "N/A", + it.status + ) + else -> row("??", "??", "??", "??", "??", "??") + } + } + } + } + ) + } + } + } + + /** + * [CliktCommand] to list all evaluation runs (ongoing and past) for the current DRES instance. + */ + inner class List : CliktCommand(name = "list", help = "Lists all (ongoing and past) competition runs.") { + private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) + override fun run() = this@EvaluationRunCommand.store.transactional(true) { + val query = Evaluation.all().sortedBy(Evaluation::started) + if (this.plain) { + query.asSequence().forEach { + println( + "${ + RunSummary( + it.id, + it.name, + it.template.description, + if (it.type == RunType.INTERACTIVE_SYNCHRONOUS) { + it.tasks.firstOrNull()?.description?.name ?: "N/A" + } else { + "N/A" + } + ) + }" + ) + } + } else { + println( + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "name", "description", "lastTask", "status", "start", "end") + } + body { + query.asSequence().forEach { + row( + it.id, + it.name, + it.template.description, + if (it.type == RunType.INTERACTIVE_SYNCHRONOUS) { + it.tasks.firstOrNull()?.description?.name ?: "N/A" + } else { + "N/A" + }, + it.started?.toDateString() ?: "-", + it.ended?.toDateString() ?: "-", + ) + } + } + } + ) + } + } + } + + /** + * [CliktCommand] to list past evaluation runs for the current DRES instance. + */ + inner class History : CliktCommand(name = "history", help = "Lists past evaluation runs.") { + // TODO fancification with table + + override fun run() = this@EvaluationRunCommand.store.transactional(true) { + val query = Evaluation.query(Evaluation::ended ne null).sortedBy(Evaluation::started) + query.asSequence().forEach { + println(it.name) + + println("Teams:") + it.template.teams.asSequence().forEach { team -> + println(team) + } + + println() + println("All Tasks:") + it.template.tasks.asSequence().forEach { task -> + println(task) + } + + println() + println("Evaluated Tasks:") + it.tasks.asSequence().forEach { t -> + println(t.description) + if (t.type == RunType.INTERACTIVE_SYNCHRONOUS) { + println("Submissions") + t.submissions.asSequence().forEach { s -> println(s) } + } + } + println() + } + } + } + + /** + * Deletes a selected [Evaluation] for the current DRES instance. + */ + inner class Delete : CliktCommand(name = "delete", help = "Deletes an existing competition run.", printHelpOnEmptyArgs = true) { + private val id: EvaluationId by option("-r", "--run").required() + + override fun run() { + if (RunExecutor.managers().any { it.id == id }) { + println("Evaluation with ID $id could not be deleted because it is still running! Terminate it and try again.") + return + } + + this@EvaluationRunCommand.store.transactional { + val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation with ID ${this.id} could not be deleted because it doesn't exist!") + return@transactional + } + evaluation.delete() + } + } + } + + /** + * [CliktCommand] to export a specific competition run as JSON. + */ + inner class Export : CliktCommand(name = "export", help = "Exports the selected competition run to a JSON file.", printHelpOnEmptyArgs = true) { + + /** [EvaluationId] of the [Evaluation] that should be exported. .*/ + private val id: EvaluationId by option("-i", "--id").required() + + /** Path to the file that should be created .*/ + private val path: Path by option("-o", "--output").path().required() + + /** Flag indicating whether export should be pretty printed.*/ + private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) + + override fun run() = this@EvaluationRunCommand.store.transactional(true) { + val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional + } + + val mapper = jacksonObjectMapper() + Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { + /*val writer = if (this.pretty) { + mapper.writerWithDefaultPrettyPrinter() + } else { + mapper.writer() + } + writer.writeValue(it, run)*/ + // TODO: Export must be re-conceived based on API classes. + } + println("Successfully exported evaluation ${this.id} to $path.") + } + } + + /** + * [CliktCommand] to reactivate an [Evaluation] that has previously ended. + */ + inner class Reactivate : CliktCommand(name = "reactivate", help = "Reactivates a previously ended competition run", printHelpOnEmptyArgs = true) { + + /** [EvaluationId] of the [Evaluation] that should be reactivated. .*/ + private val id: EvaluationId by option("-i", "--id").required() + + override fun run() = this@EvaluationRunCommand.store.transactional(true) { + val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional + } + + if (evaluation.ended == null) { + println("Evaluation has not ended yet.") + return@transactional + } + + if (RunExecutor.managers().any { it.id == evaluation.id }) { + println("Evaluation is already active.") + return@transactional + } + + /* Create run and reactivate. */ + val run = evaluation.toRun() + run.reactivate() + RunExecutor.schedule(run) + println("Evaluation ${this.id} was reactivated.") + } + } + + /** + * [CliktCommand] to reset the status of [Submission]s. + */ + inner class ResetSubmission : CliktCommand(name = "resetSubmission", help = "Resets submission status to INDETERMINATE for selected submissions.", printHelpOnEmptyArgs = true) { + + /** [EvaluationId] of the [Evaluation] that should be reactivated. .*/ + private val id: EvaluationId by option("-i", "--id").required() + + /** The [SubmissionId]s to reset. */ + private val submissionIds: kotlin.collections.List by option("-s", "--submissions", help = "IDs of the submissions to reset.").multiple() + + /** The [TaskId]s to reset [Submission]s for. */ + private val taskIds: kotlin.collections.List by option("-t", "--tasks", help = "IDs of the tasks to resetsubmissions for.").multiple() + + /** The names of the task groups to reset [Submission]s for. */ + private val taskGroups: kotlin.collections.List by option("-g", "--groups", help = "Names of the task groups to reset submissions for.").multiple() + + override fun run() = this@EvaluationRunCommand.store.transactional { + /* Fetch competition run. */ + val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional + } + + if (evaluation.type == RunType.INTERACTIVE_SYNCHRONOUS) { + /* Prepare query. */ + var query = if (this.taskIds.isNotEmpty()) { + evaluation.tasks.filter { it.id.isIn(this@ResetSubmission.taskIds) }.flatMapDistinct { it.submissions } + } else if (this.taskGroups.isNotEmpty()) { + evaluation.tasks.filter { it.description.taskGroup.name.isIn(this@ResetSubmission.taskGroups) }.flatMapDistinct { it.submissions } + } else { + evaluation.tasks.flatMapDistinct { it.submissions } + } + + if (this.submissionIds.isNotEmpty()) { + query = query.filter { it.id.isIn(this@ResetSubmission.submissionIds) } + } + + var affected = 0 + query.asSequence().forEach { + affected += 1 + it.status = SubmissionStatus.INDETERMINATE + } + + println("Successfully reset $affected} submissions.") + } else { + println("Operation not supported for run type") + } + } + } + + /** + * [CliktCommand] to export judgements made for relevant tasks as CSVs. + */ + inner class ExportJudgements : CliktCommand(name = "exportJudgements", help = "Exports all judgements made for all relevant tasks of an evaluation run as CSV", printHelpOnEmptyArgs = true) { + /** [EvaluationId] of the [Evaluation] for which judgements should be exported.*/ + private val id: EvaluationId by option("-r", "--run", help = "Id of the run").required() + + /** The [Path] to the output file. */ + private val path: Path by option("-o", "--output", help = "Path to the file the judgements are to be exported to.").convert { Paths.get(it) }.required() + + private val header = listOf("TaskId", "TaskName", "ItemId", "StartTime", "EndTime", "Status") + + override fun run() = this@EvaluationRunCommand.store.transactional(true) { + /* Fetch competition run. */ + val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional + } + + val tasks = evaluation.tasks.filter { + it.description.targets.filter { it.type.isIn(listOf(TargetType.JUDGEMENT,TargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() + } + + if (tasks.isEmpty) { + println("No judged tasks in run.") + return@transactional + } + + Files.newOutputStream(this.path, StandardOpenOption.WRITE).use { os -> + csvWriter().open(os) { + writeRow(header) + tasks.asSequence().forEach { task -> + val submittedItems = task.submissions.asSequence().groupBy {s -> + Triple(s.item?.name?: "unknown", s.start, s.end) + } + submittedItems.entries.forEach { items -> + val status = items.value.map { s -> s.status }.toSet() //should only contain one element + writeRow( + listOf(task.id, task.description.name, items.key.first, items.key.second, items.key.third, status) + ) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 3854244a6..c5e724e19 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -116,7 +116,7 @@ object AccessManager { * @param runManager The [RunManager] to register. */ fun registerRunManager(runManager: RunManager) = this.locks.write { - runManager.description.teams.flatMapDistinct { it.users }.asSequence().forEach { + runManager.template.teams.flatMapDistinct { it.users }.asSequence().forEach { if (this.usersToRunMap.containsKey(it.id)) { this.usersToRunMap[it.id]?.add(runManager) } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 7fb7f3e0f..9dea826ca 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,12 +1,14 @@ package dev.dres.api.rest import GetAuditLogInfoHandler -import dev.dres.api.cli.Cli import dev.dres.api.rest.handler.* import dev.dres.api.rest.handler.audit.ListAuditLogsHandler import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler import dev.dres.api.rest.handler.collection.* -import dev.dres.api.rest.handler.description.* +import dev.dres.api.rest.handler.evaluation.admin.* +import dev.dres.api.rest.handler.evaluation.client.ClientListEvaluationsHandler +import dev.dres.api.rest.handler.evaluation.client.ClientTaskInfoHandler +import dev.dres.api.rest.handler.template.* import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.MediaPreviewHandler import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler @@ -98,11 +100,11 @@ object RestApi { ListExternalItemHandler(config), // Competition - ListCompetitionHandler(store), - CreateCompetitionHandler(store), + ListEvaluationTemplatesHandler(store), + CreateEvaluationTemplateHandler(store), UpdateCompetitionHandler(store, config), - ShowCompetitionHandler(store), - DeleteCompetitionHandler(store), + ShowEvaluationTemplateHandler(store), + DeleteEvaluationTemplateHandler(store), ListTeamHandler(store), ListTasksHandler(store), GetTeamLogoHandler(store), @@ -135,23 +137,23 @@ object RestApi { ListScoreboardsHandler(), TeamGroupScoreHandler(), - // Competition run admin - CreateCompetitionRunAdminHandler(store, config), - StartCompetitionRunAdminHandler(), - NextTaskCompetitionRunAdminHandler(), - PreviousTaskCompetitionRunAdminHandler(), - SwitchTaskCompetitionRunAdminHandler(), - StartTaskCompetitionRunAdminHandler(), - AbortTaskCompetitionRunAdminHandler(), - TerminateCompetitionRunAdminHandler(), - AdjustDurationRunAdminHandler(), - ListViewersRunAdminHandler(), - ForceViewerRunAdminHandler(), - ListSubmissionsPerTaskRunAdminHandler(), - OverwriteSubmissionStatusRunAdminHandler(), - ListPastTasksPerTaskRunAdminHandler(), - OverviewRunAdminHandler(), - UpdateRunPropertiesAdminHandler(), + // Evaluation administration + CreateEvaluationHandler(store, config), + StartEvaluationHandler(store), + StopEvaluationHandler(store), + NextTaskHandler(store), + PreviousTaskHandler(store), + SwitchTaskHandler(store), + StartTaskHandler(store), + StopTaskHandler(store), + AdjustDurationHandler(store), + AdjustPropertiesHandler(store), + OverrideSubmissionHandler(store), + ForceViewerHandler(store), + ListViewersHandler(store), + ListSubmissionsHandler(store), + ListPastTaskHandler(store), + EvaluationOverviewHandler(store), // Judgement NextOpenJudgementHandler(store), @@ -170,8 +172,8 @@ object RestApi { InfoHandler(), //API Client - ListCompetitionRunClientInfoHandler(), - CompetitionRunClientCurrentTaskInfoHandler(), + ClientListEvaluationsHandler(store), + ClientTaskInfoHandler(store), // Downloads DownloadHandler.CompetitionRun(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt deleted file mode 100644 index a0e191c77..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ /dev/null @@ -1,908 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.competition.CompetitionStartMessage -import dev.dres.api.rest.types.run.* -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.Config -import dev.dres.data.model.UID -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.run.InteractiveSynchronousEvaluation -import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.data.model.run.RunProperties -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect -import dev.dres.mgmt.admin.UserManager -import dev.dres.run.* -import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.FFmpegUtil -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId -import io.javalin.http.BadRequestResponse -import io.javalin.http.Context -import io.javalin.http.bodyAsClass -import io.javalin.openapi.* -import io.javalin.security.RouteRole -import org.slf4j.LoggerFactory -import java.io.File - - -abstract class AbstractCompetitionRunAdminRestHandler( - override val permittedRoles: Set = setOf( - ApiRole.ADMIN, - ApiRole.PARTICIPANT - ) -) : AccessManagedRestHandler { - - override val apiVersion = "v1" - - /** - * Parses the run ID out of the [Context] and throws a 404 [ErrorStatusException] if the parameter is missing. - * - * @param ctx The [Context] to parse the runId from. - * @return [UID] representation of the runId. - */ - fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) - }.UID() - - /** - * Obtains the [InteractiveRunManager] for the given [UID]. - * - * @param runId The [UID] identifying the [InteractiveRunManager]. - * @return [InteractiveRunManager] or null. - */ - fun getRun(runId: UID): InteractiveRunManager? { - val run = RunExecutor.managerForId(runId) - if (run != null && run is InteractiveRunManager) { - return run - } - return null - } - - /** - * ensures that only admins are able to modify the state of synchronous runs - */ - fun synchronousAdminCheck(runId: UID, ctx: Context) { - - if (getRun(runId) is InteractiveAsynchronousRunManager) { - return - } - - if (!AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { - throw ErrorStatusException(403, "Access Denied.", ctx); - } - - } -} - -/** - * REST handler to create a [InteractiveSynchronousEvaluation]. - */ -class CreateCompetitionRunAdminHandler( - private val competitions: DAO, - private val collections: DAO, - config: Config -) : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - - private val cacheLocation = File(config.cachePath + "/tasks") - private val logger = LoggerFactory.getLogger(this.javaClass) - - private fun competitionById(id: UID, ctx: Context): CompetitionDescription = - competitions[id] ?: throw ErrorStatusException( - 404, - "Competition with ID $id not found.'", - ctx - ) - - override val route = "run/admin/create" - - @OpenApi( - summary = "Creates a new competition run from an existing competition", - path = "/api/v1/run/admin/create", - methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(CompetitionStartMessage::class)]), - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - - val competitionStartMessage = try { - ctx.bodyAsClass(CompetitionStartMessage::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val competitionToStart = - this.competitionById(competitionStartMessage.competitionId.UID(), ctx) - - /* ensure that only one synchronous run of a competition is happening at any given time */ - if (competitionStartMessage.type == RunType.SYNCHRONOUS && RunExecutor.managers().any { - it is InteractiveSynchronousRunManager && it.description == competitionToStart && it.status != RunManagerStatus.TERMINATED - } - ) { - throw ErrorStatusException( - 400, - "Synchronous run of competition ${competitionToStart.name} already exists", - ctx - ) - } - - val segmentTasks = competitionToStart.getAllVideos() - - /* check videos */ - segmentTasks.forEach { - val item = it.item - val collection = this.collections[item.collection] - ?: throw ErrorStatusException(400, "collection ${item.collection} not found", ctx) - - val videoFile = File(File(collection.path), item.location) - - if (!videoFile.exists()) { - logger.error("file ${videoFile.absolutePath} not found for item ${item.name}") - return@forEach - } - - val outputFile = File(cacheLocation, it.cacheItemName()) - if (!outputFile.exists()) { - logger.warn("Query video file for item ${it.item} not found, rendering to ${outputFile.absolutePath}") - FFmpegUtil.extractSegment(it, collection.path, cacheLocation) - } - - } - - /* Prepare... */ - try { - val manager = when (competitionStartMessage.type) { - RunType.ASYNCHRONOUS -> InteractiveAsynchronousRunManager( - competitionToStart, - competitionStartMessage.name, - competitionStartMessage.properties - ) - RunType.SYNCHRONOUS -> InteractiveSynchronousRunManager( - competitionToStart, - competitionStartMessage.name, - competitionStartMessage.properties - ) - } - - /**... and schedule RunManager. */ - RunExecutor.schedule(manager) - - return SuccessStatus("Competition '${competitionStartMessage.name}' was started and is running with ID ${manager.id}.") - } catch (e: IllegalArgumentException) { - throw ErrorStatusException( - 400, - e.message ?: "Invalid parameters. This is a programmers error!", - ctx - ) - } - } -} - -/** - * REST handler to start a [InteractiveSynchronousEvaluation]. - */ -class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/start" - - @OpenApi( - summary = "Starts a competition run.", - path = "/api/v1/run/admin/{runId}/start", - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - val rac = runActionContext(ctx, run) - - try { - run.start(rac) - AuditLogger.competitionStart(run.id, run.description, AuditLogSource.REST, ctx.sessionId()) - return SuccessStatus("Run $runId was successfully started.") - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Run $runId could not be started because it is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to move to the next task in a [InteractiveSynchronousEvaluation]. - */ -class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), - PostRestHandler { - override val route: String = "run/admin/{runId}/task/next" - - @OpenApi( - summary = "Moves to and selects the next task. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/task/next", - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - synchronousAdminCheck(runId, ctx) - - val rac = runActionContext(ctx, run) - - if (run is InteractiveAsynchronousRunManager - && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) - && run.currentTask(rac)?.status != TaskRunStatus.ENDED) { - throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) - } - - try { - if (run.next(rac)) { - return SuccessStatus( - "Task for run $runId was successfully moved to '${ - run.currentTaskDescription( - rac - ).name - }'." - ) - } else { - throw ErrorStatusException( - 400, - "Task for run $runId could not be changed because there are no tasks left.", - ctx - ) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to move to the next task in a [InteractiveSynchronousEvaluation]. - */ -class SwitchTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/task/switch/{idx}" - - @OpenApi( - summary = "Moves to and selects the specified task. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/task/switch/{idx}", - methods = [HttpMethod.POST], - pathParams = [ - OpenApiParam("runId", String::class, "Competition run ID"), - OpenApiParam("idx", Int::class, "Index of the task to switch to.") - ], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val idx = ctx.pathParamMap().getOrElse("idx") { - throw ErrorStatusException(404, "Parameter 'idx' is missing!'", ctx) - }.toInt() - - val rac = runActionContext(ctx, run) - - try { - run.goTo(rac, idx) - return SuccessStatus( - "Task for run $runId was successfully moved to '${ - run.currentTaskDescription( - rac - ).name - }'." - ) - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IndexOutOfBoundsException) { - throw ErrorStatusException( - 404, - "Task for run $runId could not be changed because index $idx is out of bounds for number of available tasks.", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to move to the previous task in a [InteractiveSynchronousEvaluation]. - */ -class PreviousTaskCompetitionRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/task/previous" - - @OpenApi( - summary = "Moves to and selects the previous task. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/task/previous", - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val rac = runActionContext(ctx, run) - try { - if (run.previous(rac)) { - return SuccessStatus( - "Task for run $runId was successfully moved to '${ - run.currentTaskDescription( - rac - ).name - }'." - ) - } else { - throw ErrorStatusException( - 400, - "Task for run $runId could not be changed because there are no tasks left.", - ctx - ) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Task for run $runId could not be changed because run is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to start the current task in a [InteractiveSynchronousEvaluation]. - */ -class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(), - PostRestHandler { - override val route: String = "run/admin/{runId}/task/start" - - @OpenApi( - summary = "Starts the currently active task as a new task run. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/task/start", - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - synchronousAdminCheck(runId, ctx) - - val rac = runActionContext(ctx, run) - try { - run.startTask(rac) - AuditLogger.taskStart(run.id, run.currentTask(rac)!!.uid, run.currentTaskDescription(rac), AuditLogSource.REST, ctx.sessionId()) - return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully started.") - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - e.message ?: "", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to abort the current task in a [InteractiveSynchronousEvaluation]. - */ -class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/task/abort" - - @OpenApi( - summary = "Aborts the currently running task run. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/task/abort", - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val rac = runActionContext(ctx, run) - try { - val task = run.currentTaskDescription(rac) - run.abortTask(rac) - AuditLogger.taskEnd(run.id, task.id, AuditLogSource.REST, ctx.sessionId()) - return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully aborted.") - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Task '${run.currentTaskDescription(rac).name}' for run $runId could not be aborted because run is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to terminate a [InteractiveSynchronousEvaluation]. - */ -class TerminateCompetitionRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/terminate" - - @OpenApi( - summary = "Terminates a competition run. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/terminate", - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val rac = runActionContext(ctx, run) - try { - run.end(rac) - AuditLogger.competitionEnd(run.id, AuditLogSource.REST, ctx.sessionId()) - return SuccessStatus("Run $runId was successfully terminated.") - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Run $runId could not be terminated because it is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * REST handler to adjust a [InteractiveSynchronousEvaluation.Task]'s duration. - */ -class AdjustDurationRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/adjust/{duration}" - - @OpenApi( - summary = "Adjusts the duration of a running task run. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/adjust/{duration}", - methods = [HttpMethod.POST], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("duration", Int::class, "Duration to add.") - ], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val duration = ctx.pathParamMap().getOrElse("duration") { - throw ErrorStatusException(404, "Parameter 'duration' is missing!'", ctx) - }.toInt() - val rac = runActionContext(ctx, run) - try { - run.adjustDuration(rac, duration) - AuditLogger.taskModified( - run.id, - run.currentTaskDescription(rac).id, - "Task duration adjusted by ${duration}s.", - AuditLogSource.REST, - ctx.sessionId() - ) - return SuccessStatus("Duration for run $runId was successfully adjusted.") - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Duration for run $runId could not be adjusted because it is in the wrong state (state = ${run.status}).", - ctx - ) - } catch (e: IllegalArgumentException) { - throw ErrorStatusException( - 400, - "Duration for run $runId could not be adjusted because new duration would drop bellow zero (state = ${run.status}).", - ctx - ) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } -} - -/** - * - */ -class ListPastTasksPerTaskRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - GetRestHandler> { - override val route: String = "run/admin/{runId}/task/past/list" - - @OpenApi( - summary = "Lists all past tasks for a given run", - path = "/api/v1/run/admin/{runId}/task/past/list", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID") - ], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): List { - val runId = runId(ctx) - val run = - getRun(runId) ?: throw ErrorStatusException(404, "No such run was found: $runId", ctx) - - val rac = runActionContext(ctx, run) - - return run.tasks(rac).filter { it.hasEnded }.map { - PastTaskInfo( - taskId = it.uid.string, - descriptionId = it.description.id.string, - name = it.description.name, - taskGroup = it.description.taskGroup.name, - taskType = it.description.taskType.name, - numberOfSubmissions = it.submissions.size - ) - } - } -} - -/** - * - */ -class ListSubmissionsPerTaskRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - GetRestHandler> { - override val route: String = "run/admin/{runId}/submission/list/{taskId}" - - @OpenApi( - summary = "Lists all submissions for a given task and run.", - path = "/api/v1/run/admin/{runId}/submission/list/{taskId}", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("taskId", String::class, "Task ID") - ], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): List { - val runId = runId(ctx) - val run = - getRun(runId) ?: throw ErrorStatusException(404, "No such run was found: $runId", ctx) - - val taskId = ctx.pathParamMap().getOrElse("taskId") { - throw ErrorStatusException( - 404, - "Parameter 'taskId' is missing!'", - ctx - ) - }.UID() - val teams = run.description.teams.associate { it.uid to it } - return run.tasks(runActionContext(ctx, run)).filter { it.description.id == taskId }.map { - TaskRunSubmissionInfo( - it.uid.string, - it.submissions.map { sub -> - SubmissionInfo( - id = sub.uid.string, - teamId = sub.teamId.string, - teamName = teams[sub.teamId]?.name, - memberId = sub.memberId.string, - memberName = UserManager.get(sub.memberId)?.username, - status = sub.status, - timestamp = sub.timestamp, - item = if (sub is ItemAspect) sub.item.toApi() else null, - text = if (sub is TextAspect) sub.text else null, - start = if (sub is TemporalSubmissionAspect) sub.start else null, - end = if (sub is TemporalSubmissionAspect) sub.end else null - ) - } - ) - } - } -} - -/** - * - */ -class OverwriteSubmissionStatusRunAdminHandler : - AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PatchRestHandler { - override val route: String = "run/admin/{runId}/submission/override" - - @OpenApi( - summary = "Lists all submissions for a given task and run", - path = "/api/v1/run/admin/{runId}/submission/override", - methods = [HttpMethod.PATCH], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID") - ], - requestBody = OpenApiRequestBody([OpenApiContent(SubmissionInfo::class)]), - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SubmissionInfo::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPatch(ctx: Context): SubmissionInfo { - val runId = runId(ctx) - val run = - getRun(runId) ?: throw ErrorStatusException(404, "No such run was found: $runId", ctx) - val rac = runActionContext(ctx, run) - - /* Extract HTTP body. */ - val toPatchRest = ctx.bodyAsClass() - val submissionId = toPatchRest.id?.UID() ?: throw ErrorStatusException( - 400, - "No submission ID was specified for update.", - ctx - ) - - val status = toPatchRest.status - - if (status == SubmissionStatus.INDETERMINATE) { - throw ErrorStatusException( - 400, - "Submission Status can not be set to INDETERMINATE", - ctx - ) - } - - /* Sanity check to see, whether the submission exists */ - if (run.allSubmissions.none { it.uid == submissionId }) { - throw ErrorStatusException(404, "The given submission $toPatchRest was not found.", ctx) - } - if (run.updateSubmission(rac, submissionId, status)) { - val submission = run.allSubmissions.single { it.uid == submissionId } - AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionId()) - return SubmissionInfo(submission) - } else { - throw ErrorStatusException( - 500, - "Could not update the submission. Please see the backend's log.", - ctx - ) - } - } -} - -/** - * REST handler to list all viewers for a [InteractiveSynchronousEvaluation]. - */ -class ListViewersRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - GetRestHandler> { - override val route: String = "run/admin/{runId}/viewer/list" - - @OpenApi( - summary = "Lists all registered viewers for a competition run. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/viewer/list", - methods = [HttpMethod.GET], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): Array { - val runId = runId(ctx) - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - return run.viewers() - .map { ViewerInfo(it.key.sessionId, it.key.userName, it.key.host, it.value) } - .toTypedArray() - } -} - - -/** - * REST handler to force the viewer state of a viewer instance registered for a [RunManager]. - */ -class ForceViewerRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), - PostRestHandler { - override val route: String = "run/admin/{runId}/viewer/list/{viewerId}/force" - - @OpenApi( - summary = "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/viewer/list/{viewerId}/force", - methods = [HttpMethod.POST], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("viewerId", String::class, "Viewer ID") - ], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = runId(ctx) - val viewerId = ctx.pathParamMap().getOrElse("viewerId") { - throw ErrorStatusException(404, "Parameter 'viewerId' is missing!'", ctx) - } - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val rac = runActionContext(ctx, run) - try { - if (run.overrideReadyState(rac, viewerId)) { - return SuccessStatus("State for viewer $viewerId forced successfully.") - } else { - throw ErrorStatusException(404, "Viewer $viewerId does not exist!'", ctx) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Viewer state for viewer $viewerId (run $runId) could not be enforced because run is in the wrong state (state = ${run.status}).", - ctx - ) - } - } -} - -class OverviewRunAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), GetRestHandler { - - override val route = "run/admin/{runId}/overview" - @OpenApi( - summary = "Provides a complete overview of a run.", - path = "/api/v1/run/admin/{runId}/overview", - methods = [HttpMethod.GET], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - ], - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(AdminRunOverview::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doGet(ctx: Context): AdminRunOverview { - - val runId = runId(ctx) - - val run = getRun(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - return AdminRunOverview.of(run) - } - -} - -class UpdateRunPropertiesAdminHandler : AbstractCompetitionRunAdminRestHandler(setOf(ApiRole.ADMIN)), PatchRestHandler { - - override val route = "run/admin/{runId}/properties" - - @OpenApi( - summary = "Changes the properties of a run", - path = "/api/v1/run/admin/{runId}/properties", - methods = [HttpMethod.PATCH], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - ], - requestBody = OpenApiRequestBody([OpenApiContent(RunProperties::class)]), - tags = ["Competition Run Admin"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPatch(ctx: Context): SuccessStatus { - - val properties = try { - ctx.bodyAsClass() - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val runId = runId(ctx) - - val runManager = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - when(runManager) { - is InteractiveAsynchronousRunManager -> runManager.run.properties = properties - is InteractiveSynchronousRunManager -> runManager.run.properties = properties - is NonInteractiveRunManager -> runManager.run.properties = properties - else -> throw ErrorStatusException(400, "Cannot change properties for ${runManager.javaClass.simpleName}", ctx) - } - - //TODO trigger persistence of run - - return SuccessStatus("Properties updated") - - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt deleted file mode 100644 index fb03cf448..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ /dev/null @@ -1,140 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.UID -import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.run.* -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId -import io.javalin.security.RouteRole -import io.javalin.http.Context -import io.javalin.openapi.* - -abstract class AbstractCompetitionRunClientInfoHandler : RestHandler, AccessManagedRestHandler { - - override val permittedRoles: Set = setOf(ApiRole.VIEWER) - override val apiVersion = "v1" - - private fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId())!! - - fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains( - ApiRole.ADMIN) - - fun getRelevantManagers(ctx: Context): List { - val userId = userId(ctx) - return RunExecutor.managers().filter { m -> m.description.teams.any { it.users.contains(userId) } } - } - - fun getRun(ctx: Context, runId: UID): RunManager? { - val userId = userId(ctx) - val run = RunExecutor.managerForId(runId) ?: return null - if (run.description.teams.any { it.users.contains(userId) }) { - return run - } - return null - - } - - fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) - }.UID() -} - -data class ClientRunInfo( - val id: String, - val name: String, - val description: String?, - val status: RunManagerStatus -) { - constructor(runManager: RunManager) : this( - runManager.id.string, - runManager.name, - runManager.description.description, - runManager.status - ) -} - -data class ClientRunInfoList(val runs : List) - -class ListCompetitionRunClientInfoHandler : AbstractCompetitionRunClientInfoHandler(), GetRestHandler { - - override val route = "client/run/info/list" - - @OpenApi( - summary = "Lists an overview of all competition runs visible to the current client", - path = "/api/v1/client/run/info/list", - tags = ["Client Run Info"], - responses = [ - OpenApiResponse("200", [OpenApiContent(ClientRunInfoList::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ClientRunInfoList = - ClientRunInfoList(getRelevantManagers(ctx).map { ClientRunInfo(it) }) - -} - -data class ClientTaskInfo( - val id: String, - val name: String, - val taskGroup: String, - val remainingTime: Long, - val running: Boolean -) - -class CompetitionRunClientCurrentTaskInfoHandler : AbstractCompetitionRunClientInfoHandler(), GetRestHandler { - - override val route = "client/run/info/currentTask/{runId}" - - @OpenApi( - summary = "Returns an overview of the currently active task for a run", - path = "/api/v1/client/run/info/currentTask/{runId}", - tags = ["Client Run Info"], - queryParams = [ - OpenApiParam("runId", String::class, "The runId of the run th ecurent task is queried", required = true, allowEmptyValue = false) - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(ClientTaskInfo::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ClientTaskInfo { - - val run = getRun(ctx, runId(ctx)) ?: throw ErrorStatusException(404, "Specified run not found", ctx) - val rac = runActionContext(ctx, run) - - if (run !is InteractiveRunManager) { - throw ErrorStatusException(404, "Specified run is not interactive", ctx) - } - - val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified run has no active task", ctx) - - - return ClientTaskInfo( - task.uid.string, - task.description.name, - task.description.taskGroup.name, - when(run.status){ - RunManagerStatus.CREATED -> 0 - RunManagerStatus.ACTIVE -> { - when(task.status) { - TaskRunStatus.CREATED, - TaskRunStatus.PREPARING -> task.duration - TaskRunStatus.RUNNING ->run.timeLeft(rac) / 1000 - TaskRunStatus.ENDED -> 0 - } - } - RunManagerStatus.TERMINATED -> 0 - }, - task.isRunning - ) - - } - -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt new file mode 100644 index 000000000..3c626e655 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt @@ -0,0 +1,46 @@ +package dev.dres.api.rest.handler + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.EvaluationId +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context + + +/** + * Returns the [UserId] for the current [Context]. + * + * @return [UserId] + */ +fun Context.userId(): UserId = AccessManager.userIdForSession(this.sessionId()) + ?: throw ErrorStatusException(401, "No user registered for session ${this.sessionId()}.", this) + +/** + * Returns the [EvaluationId] associated with the current [Context] + * + * @return [EvaluationId] + */ +fun Context.evaluationId(): EvaluationId + = this.pathParamMap()["evaluationId"] ?: throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", this) + +/** + * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. + * + * @return True if current user has [ApiRole.PARTICIPANT] + */ +fun Context.isParticipant(): Boolean { + val roles = AccessManager.rolesOfSession(this.sessionId()) + return roles.contains(ApiRole.PARTICIPANT) && !roles.contains(ApiRole.ADMIN) +} + +/** + * Checks uf user associated with current [Context] has [ApiRole.JUDGE]. + * + * @return True if current user has [ApiRole.JUDGE] + */ +fun Context.isJudge(): Boolean { + val roles = AccessManager.rolesOfSession(this.sessionId()) + return roles.contains(ApiRole.JUDGE) && !roles.contains(ApiRole.ADMIN) +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt index 36bfe694e..a3c72bbfb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.dbo.DAO -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation import dev.dres.utilities.extensions.UID import io.javalin.security.RouteRole @@ -115,7 +115,7 @@ sealed class DownloadHandler : AccessManagedRestHandler { /** * REST handler to download the competition description information. */ - class CompetitionDesc(private val competitions: DAO) : DownloadHandler(), GetRestHandler { + class CompetitionDesc(private val competitions: DAO) : DownloadHandler(), GetRestHandler { /** The route of this [DownloadHandler.CompetitionRun]. */ override val route = "download/competition/{competitionId}" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt index 2e54cc5e1..ec6dad593 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt @@ -44,7 +44,7 @@ abstract class AbstractJudgementHandler : RestHandler, AccessManagedRestHandler if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { return //Admins require no further check } - if (userId !in runManager.description.judges) { + if (userId !in runManager.template.judges) { throw ErrorStatusException(403, "Access denied.", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt index dfaa4f5a9..96a84cea5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt @@ -5,7 +5,6 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.UID import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog import dev.dres.run.RunManager @@ -25,16 +24,16 @@ abstract class LogHandler : PostRestHandler, AccessManagedRestHan override val apiVersion = "v1" - private fun getRelevantManagers(userId: UID): Set = AccessManager.getRunManagerForUser(userId) + private fun getRelevantManagers(userId: EvaluationId): Set = AccessManager.getRunManagerForUser(userId) - protected fun getActiveRun(userId: UID, ctx: Context): RunManager { + protected fun getActiveRun(userId: EvaluationId, ctx: Context): RunManager { val managers = getRelevantManagers(userId).filter { it.status != RunManagerStatus.CREATED && it.status != RunManagerStatus.TERMINATED } if (managers.isEmpty()) { throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) } if (managers.size > 1) { - throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.description.name }}", ctx) + throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) } return managers.first() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt deleted file mode 100644 index 3b0f1846b..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/GetTeamLogoHandler.kt +++ /dev/null @@ -1,71 +0,0 @@ -package dev.dres.api.rest.handler.description - -import dev.dres.api.rest.handler.AbstractCompetitionRunRestHandler -import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.competition.team.Team -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.errorResponse -import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiParam -import io.javalin.openapi.OpenApiResponse -import jetbrains.exodus.database.TransientEntityStore -import java.io.IOException -import java.nio.file.Files - -/** - * A [AbstractCompetitionRunRestHandler] that can be used to list all [Team] logos. - * - * @author Ralph Gasser - * @author Luca Rossetto - * @author Loris Sauter - * @version 1.0.0 - */ -class GetTeamLogoHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { - - override val route = "competition/logo/{logoId}" - override val apiVersion = "v1" - - //not used - override fun doGet(ctx: Context): Any = "" - - @OpenApi( - summary = "Returns the logo for the given logo ID.", - path = "/api/v1/competition/logo/{logoId}", - tags = ["Competition Run", "Media"], - pathParams = [OpenApiParam("logoId", String::class, "The ID of the logo.")], - responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], - ignore = true, - methods = [HttpMethod.GET] - ) - override fun get(ctx: Context) { - - /* Extract logoId. */ - val logoId = try { - ctx.pathParamMap().getOrElse("logoId") { - ctx.errorResponse(ErrorStatusException(400, "Parameter 'logoId' is missing!'", ctx)) - return@get - }.UID() - }catch (ex: java.lang.IllegalArgumentException){ - ctx.errorResponse(ErrorStatusException(400, "Could not deserialise logoId '${ctx.pathParamMap()["logoId"]}'", ctx)) - return - } - - - /* Load image and return it. */ - try { - val image = Files.newInputStream(Team.logoPath(this.config, logoId)).use { - it.readAllBytes() - } - ctx.contentType("image/png") - ctx.result(image) - } catch (e: IOException) { - ctx.status(404) - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) - //ctx.errorResponse(ErrorStatusException(404, "Logo file for team $logoId could not be read!", ctx)) - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt new file mode 100644 index 000000000..98ee37235 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt @@ -0,0 +1,53 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.EvaluationId +import dev.dres.run.InteractiveAsynchronousRunManager +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunExecutor +import dev.dres.run.RunManager +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractEvaluationAdminHandler(protected val store: TransientEntityStore) : AccessManagedRestHandler { + + /** By default [AbstractEvaluationAdminHandler] can only be used by [ApiRole.ADMIN]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + /** All [AbstractEvaluationAdminHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** + * Obtains the [InteractiveRunManager] for the given [EvaluationId]. + * + * @param evaluationId The [EvaluationId] identifying the [InteractiveRunManager]. + * @return [InteractiveRunManager] or null. + */ + protected fun getManager(evaluationId: EvaluationId): InteractiveRunManager? { + val run = RunExecutor.managerForId(evaluationId) + if (run != null && run is InteractiveRunManager) { + run + } + return null + } + + /** + * Ensures that only [ApiRole.ADMIN] are able to modify the state of [InteractiveRunManager] + */ + fun synchronousAdminCheck(manager: RunManager, ctx: Context) { + if (manager is InteractiveAsynchronousRunManager) return + if (!AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { + throw ErrorStatusException(403, "Access Denied.", ctx); + } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt new file mode 100644 index 000000000..57d8eca53 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -0,0 +1,63 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.Task +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [PatchRestHandler] handler to adjust an ongoing [Task]'s duration. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/adjust/{duration}" + + @OpenApi( + summary = "Adjusts the duration of a running task. This is a method for admins.", + path = "/api/v1/run/admin/{runId}/adjust/{duration}", + methods = [HttpMethod.POST], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("duration", Int::class, "Duration to add. Can be negative.", required = true, allowEmptyValue = false) + ], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val duration = ctx.pathParamMap()["duration"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'duration' is missing!'", ctx) + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + evaluationManager.adjustDuration(rac, duration) + AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskDescription(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionId()) + SuccessStatus("Duration for run $evaluationId was successfully adjusted.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalArgumentException) { + throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because new duration would drop bellow zero (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt new file mode 100644 index 000000000..f2bc0cc5c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -0,0 +1,57 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.RunProperties +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [PatchRestHandler] handler to adjust an ongoing [Evaluation]'s [RunProperties]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class AdjustPropertiesHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { + override val route = "evaluation/admin/{evaluationId}/properties" + + @OpenApi( + summary = "Changes the properties of an evaluation.", + path = "/api/v1/evaluation/admin/{evaluationId}/properties", + methods = [HttpMethod.PATCH], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), + ], + requestBody = OpenApiRequestBody([OpenApiContent(RunProperties::class)]), + tags = ["Competition Run Admin"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): SuccessStatus { + val properties = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional { + evaluationManager.updateProperties(properties) + SuccessStatus("Properties updated") + } + } +} + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt new file mode 100644 index 000000000..4cadac1c5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -0,0 +1,125 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.competition.ApiCompetitionStartMessage +import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.Config +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.run.InteractiveSynchronousRunManager +import dev.dres.run.RunExecutor +import dev.dres.run.RunManagerStatus +import dev.dres.utilities.FFmpegUtil +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query +import org.slf4j.LoggerFactory +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.* + +/** + * [PostRestHandler] to create an [Evaluation]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : AbstractEvaluationAdminHandler(store), PostRestHandler { + + /** The [Path] to preview cache location. */ + private val cacheLocation: Path = Paths.get(config.cachePath, "tasks") + + private val logger = LoggerFactory.getLogger(this.javaClass) + + override val route = "evaluation/admin/create" + + @OpenApi( + summary = "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + path = "/api/v1/evaluation/admin/create", + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(ApiCompetitionStartMessage::class)]), + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val message = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + /* Prepare run manager. */ + val evaluation = this.store.transactional { tx -> + val template = EvaluationTemplate.query(EvaluationTemplate::id eq message.templateId).firstOrNull() + ?: throw ErrorStatusException(404, "Competition with ID ${message.templateId} not found.'", ctx) + /* ensure that only one synchronous run of a competition is happening at any given time */ + + if (message.type == ApiRunType.SYNCHRONOUS && RunExecutor.managers().any { + it is InteractiveSynchronousRunManager && it.template == template && it.status != RunManagerStatus.TERMINATED + } + ) { + throw ErrorStatusException(400, "Synchronous run of evaluation template ${template.name} already exists.", ctx) + } + + /* Check and prepare videos */ + val segmentTasks = template.getAllVideos() + segmentTasks.forEach { + val item = it.first + val path = item.pathToOriginal() + if (!Files.exists(path)) { + logger.error("Required media file $path not found for item ${item.name}.") + return@forEach + } + + val cacheName = item.cachedItemName(it.second.start.toMilliseconds(), it.second.end.toMilliseconds()) + val cachePath = this.cacheLocation.resolve(cacheName) + if (!Files.exists(cachePath)) { + logger.warn("Query video file for item ${item.name} not found; rendering to $cachePath") + FFmpegUtil.extractSegment(item, it.second, cachePath) + } + } + + /* Prepare evaluation. */ + val evaluation = Evaluation.new { + this.id = UUID.randomUUID().toString() + this.name = message.name + this.template = template /* TODO: Create copy. */ + this.type = message.type.type + this.allowRepeatedTasks = message.properties.allowRepeatedTasks + this.participantCanView = message.properties.participantCanView + this.shuffleTasks = message.properties.shuffleTasks + this.limitSubmissionPreviews = message.properties.limitSubmissionPreviews + } + + /* Try to flush change prior to scheduling it. */ + if (!tx.flush()) { + throw ErrorStatusException(500, "Failed to store new evaluation.", ctx) + } + RunExecutor.schedule(when (message.type) { + ApiRunType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ + ApiRunType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) + }) + evaluation + } + + /* Schedule newly created run manager. */ + return SuccessStatus("Evaluation '${message.name}' was started and is running with ID ${evaluation.id}.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt new file mode 100644 index 000000000..3e648e778 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -0,0 +1,41 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.evaluation.AdminRunOverview +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler { + override val route = "run/admin/{runId}/overview" + @OpenApi( + summary = "Provides a complete overview of a run.", + path = "/api/v1/run/admin/{runId}/overview", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("runId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), + ], + tags = ["Competition Run Admin"], + responses = [ + OpenApiResponse("200", [OpenApiContent(AdminRunOverview::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): AdminRunOverview { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional(true) { + AdminRunOverview.of(evaluationManager) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt new file mode 100644 index 000000000..76ffbb7d3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [PostRestHandler] to enforce the state of a viewer for a particular evaluation. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" + + @OpenApi( + summary = "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + path = "/api/v1/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force", + methods = [HttpMethod.POST], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("viewerId", String::class, "The viewer ID.", required = true, allowEmptyValue = false) + ], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val viewerId = ctx.pathParamMap()["viewerId"] ?: throw ErrorStatusException(404, "Parameter 'viewerId' is missing!'", ctx) + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + + return this.store.transactional(true) { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + if (evaluationManager.overrideReadyState(rac, viewerId)) { + SuccessStatus("State for viewer $viewerId forced successfully.") + } else { + throw ErrorStatusException(404, "Viewer $viewerId does not exist!'", ctx) + } + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Viewer state for viewer $viewerId (evaluation $evaluationId) could not be enforced because evaluationId is in the wrong state (state = ${evaluationManager.status}).", ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt new file mode 100644 index 000000000..ae7f2f49d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -0,0 +1,54 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.evaluation.ApiTaskInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] to list all past [Task]s. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { + override val route: String = "evaluation/admin/{evaluationId}/task/past/list" + + @OpenApi( + summary = "Lists all past tasks for a given evaluation.", + path = "/api/v1/evaluation/admin/{evaluationId}/task/past/list", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) + ], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): List { + val evaluationId = ctx.evaluationId() + val runManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional (true) { + val rac = RunActionContext.runActionContext(ctx, runManager) + runManager.tasks(rac).filter { it.hasEnded }.map { + ApiTaskInfo( + taskId = it.id, + templateId = it.template.id, + name = it.template.name, + taskGroup = it.template.taskGroup.name, + taskType = it.template.taskGroup.type.name, + numberOfSubmissions = it.submissions.size + ) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt new file mode 100644 index 000000000..fe47e580d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -0,0 +1,47 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.evaluation.ApiSubmissionInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { + override val route: String = "evaluation/admin/{evaluationId}/submission/list/{templateId}" + + @OpenApi( + summary = "Lists all submissions for a given evaluation and task.", + path = "/api/v1/evaluation/admin/{evaluationId}/submission/list/{templateId}", + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("runId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("templateId", String::class, "The task template ID.", required = true, allowEmptyValue = false) + ], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): List { + val evaluationId = ctx.evaluationId() + val templateId = ctx.pathParamMap()["templateId"] ?: throw ErrorStatusException(404, "Parameter 'templateId' is missing!'", ctx) + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional(true) { + evaluationManager.tasks(RunActionContext.runActionContext(ctx, evaluationManager)).filter { it.template.templateId == templateId }.map { + ApiSubmissionInfo(evaluationId, it.id, it.submissions.map { sub -> sub.toApi() }) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt new file mode 100644 index 000000000..838ab0c1d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -0,0 +1,38 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.evaluation.ApiViewerInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { + override val route: String = "evaluation/admin/{evaluationId}/viewer/list" + + @OpenApi( + summary = "Lists all registered viewers for a evaluation. This is a method for admins.", + path = "/api/v1/evaluation/admin/{evaluationId}/viewer/list", + methods = [HttpMethod.GET], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): List { + val evaluationId = ctx.evaluationId() + val evaluation = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + return evaluation.viewers().map { ApiViewerInfo(it.key.sessionId, it.key.userName, it.key.host, it.value) } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt new file mode 100644 index 000000000..ae9765cf6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -0,0 +1,75 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.InteractiveAsynchronousRunManager +import dev.dres.run.TaskRunStatus +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore + +/** + * REST handler to move to the next task in a [InteractiveSynchronousEvaluation]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/task/next" + + /** The [NextTaskHandler] can be used by [ApiRole.ADMIN] and [ApiRole.PARTICIPANT] (in case of asynchronous evaluations). */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) + + @OpenApi( + summary = "Moves to and selects the next task within the evaluation. This is a method for admins.", + path = "/api/v1/evaluation/admin/{runId}/task/next", + methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus{ + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + /* Important: Check that user can actually change this manager. */ + synchronousAdminCheck(evaluationManager, ctx) + + + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + if (evaluationManager is InteractiveAsynchronousRunManager + && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) + && evaluationManager.currentTask(rac)?.status != TaskRunStatus.ENDED) { + throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) + } + + try { + if (evaluationManager.next(rac)) { + SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskDescription(rac).name}'.") + } else { + throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because there are no tasks left.", ctx) + } + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because evaluation is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt new file mode 100644 index 000000000..1ff25d595 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -0,0 +1,77 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [PatchRestHandler] used to overwrite [SubmissionStatus] information. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/submission/override" + + @OpenApi( + summary = "Lists all submissions for a given task and run", + path = "/api/v1/evaluation/admin/{evaluationId}/submission/override", + methods = [HttpMethod.PATCH], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) + ], + requestBody = OpenApiRequestBody([OpenApiContent(ApiSubmission::class)]), + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiSubmission::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): ApiSubmission { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + /* Extract HTTP body. */ + val submissionInfo = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + /* Perform sanity check. */ + if (submissionInfo.status == SubmissionStatus.INDETERMINATE) { + throw ErrorStatusException(400, "Submission status can not be set to INDETERMINATE.", ctx) + } + + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + + /* Sanity check to see, whether the submission exists */ + if (evaluationManager.allSubmissions.none { it.id == submissionInfo.id }) { + throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) + } + if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.status)) { + val submission = evaluationManager.allSubmissions.single { it.id == submissionInfo.id } + AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionId()) + submission.toApi() + } else { + throw ErrorStatusException(500, "Could not update the submission. Please see the backend's log.", ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt new file mode 100644 index 000000000..49541fc76 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -0,0 +1,55 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * REST handler to move to the previous task in a [InteractiveSynchronousEvaluation]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + override val route: String = "evaluation/admin/{runId}/task/previous" + + @OpenApi( + summary = "Moves to and selects the previous task. This is a method for admins.", + path = "/api/v1/evaluation/admin/{evaluationId}/task/previous", + methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + if (evaluationManager.previous(rac)) { + SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskDescription(rac).name}'.") + } else { + throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because there are no tasks left.", ctx) + } + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because evaluation is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt new file mode 100644 index 000000000..e6784448b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -0,0 +1,56 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * [PostRestHandler] to start an [Evaluation]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAdminHandler(store), PostRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/start" + + @OpenApi( + summary = "Starts a evaluation. This is a method for administrators.", + path = "/api/v1/evaluation/admin/{evaluationId}/start", + methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + evaluationManager.start(rac) + AuditLogger.competitionStart(evaluationManager.id, evaluationManager.template, AuditLogSource.REST, ctx.sessionId()) + SuccessStatus("Evaluation $evaluationId was successfully started.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt new file mode 100644 index 000000000..b2e0ee4d0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -0,0 +1,66 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore + +/** + * [PostRestHandler] to start the current task within an [Evaluation]. + + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + + override val route: String = "evaluation/admin/{evaluationId}/task/next" + + /** The [StartTaskHandler] can be used by [ApiRole.ADMIN] and [ApiRole.PARTICIPANT] (in case of asynchronous evaluations). */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) + + @OpenApi( + summary = "Starts the currently active task as a new task run. This is a method for admins.", + path = "/api/v1/evaluation/admin/{evaluationId}/task/start", + methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evalation ID.", required = true, allowEmptyValue = false)], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + /* Important: Check that user can actually change this manager. */ + synchronousAdminCheck(evaluationManager, ctx) + + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + evaluationManager.startTask(rac) + AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskDescription(rac), AuditLogSource.REST, ctx.sessionId()) + SuccessStatus("Task '${evaluationManager.currentTaskDescription(rac).name}' for evaluation $evaluationId was successfully started.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, e.message ?: "", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt new file mode 100644 index 000000000..5d7128613 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -0,0 +1,57 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * [PostRestHandler] to stop an ongoing [Evaluation]. + + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + + override val route: String = "evaluation/admin/{evaluationId}/terminate" + + @OpenApi( + summary = "Terminates an evaluation. This is a method for administrators.", + path = "/api/v1/evaluation/admin/{runId}/terminate", + methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + tags = ["Competition Run Admin"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + evaluationManager.end(rac) + AuditLogger.competitionEnd(evaluationManager.id, AuditLogSource.REST, ctx.sessionId()) + SuccessStatus("Evaluation $evaluationId was successfully stopped.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt new file mode 100644 index 000000000..2f738debb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -0,0 +1,57 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.audit.AuditLogger +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * [PostRestHandler] to abort the current task within an [Evaluation]. + + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/task/abort" + + @OpenApi( + summary = "Aborts the currently running task run. This is a method for admins.", + path = "/api/v1/evaluation/admin/{evaluationId}/task/abort", + methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + tags = ["Evaluation Administration"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + val task = evaluationManager.currentTaskDescription(rac) + evaluationManager.abortTask(rac) + AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionId()) + SuccessStatus("Task '${evaluationManager.currentTaskDescription(rac).name}' for evaluation $evaluationId was successfully aborted.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskDescription(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt new file mode 100644 index 000000000..a554ae05b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -0,0 +1,57 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * REST handler to move to the specific task within an [Evaluation]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/task/switch/{idx}" + + @OpenApi( + summary = "Moves to and selects the specified task. This is a method for admins.", + path = "/api/v1/evaluation/admin/{evaluationId}/task/switch/{idx}", + methods = [HttpMethod.POST], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("idx", Int::class, "Index of the task to switch to.", required = true, allowEmptyValue = false) + ], + tags = ["Evaluation Administration"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationId = ctx.evaluationId() + val idx = ctx.pathParamMap()["idx"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'idx' is missing!'", ctx) + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, evaluationManager) + try { + evaluationManager.goTo(rac, idx) + SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskDescription(rac).name}'.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because run is in the wrong state (state = ${evaluationManager.status}).", ctx) + } catch (e: IndexOutOfBoundsException) { + throw ErrorStatusException(404, "Task for evaluation $evaluationId could not be changed because index $idx is out of bounds for number of available tasks.", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt new file mode 100644 index 000000000..cd0fc9fc8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt @@ -0,0 +1,55 @@ +package dev.dres.api.rest.handler.evaluation.client + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.userId +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.EvaluationId +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunExecutor +import dev.dres.run.RunManager +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractEvaluationClientHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { + /** By default [AbstractEvaluationClientHandler] can only be used by [ApiRole.VIEWER]. */ + override val permittedRoles: Set = setOf(ApiRole.VIEWER) + + /** All [AbstractEvaluationClientHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** + * Returns the [RunManager] associated with the current [Context]. + * + * @param ctx The request [Context] + * @return [RunManager] or null + */ + protected fun getEvaluationManager(ctx: Context): RunManager? { + val run = RunExecutor.managerForId(ctx.evaluationId()) ?: return null + if (run.template.teams.filter { t -> t.users.filter { u -> u.id eq ctx.userId() }.isNotEmpty() }.isNotEmpty) { + return run + } + return null + } + + /** + * Returns the [RunManager] associated with the current [Context]. + * + * @param ctx The request [Context] + * @return [RunManager] or null + */ + fun getRelevantManagers(ctx: Context): List = + RunExecutor.managers().filter { m -> m.template.teams.filter { t -> t.users.filter { u -> u.id eq ctx.userId() }.isNotEmpty() }.isNotEmpty } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt new file mode 100644 index 000000000..192ced091 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -0,0 +1,55 @@ +package dev.dres.api.rest.handler.evaluation.client + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.run.Evaluation +import dev.dres.run.InteractiveAsynchronousRunManager +import dev.dres.run.InteractiveSynchronousRunManager +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] used to list all ongoing [Evaluation]s available to the current user. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ClientListEvaluationsHandler(store: TransientEntityStore): AbstractEvaluationClientHandler(store), GetRestHandler> { + + override val route = "client/evaluation/list" + + @OpenApi( + summary = "Lists an overview of all evaluation runs visible to the current client.", + path = "/api/v1/client/evaluation/list", + tags = ["Evaluation Client"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List = this.store.transactional(true) { tx -> + getRelevantManagers(ctx).map { ApiEvaluationInfo( + id = it.id, + name = it.name, + templateId = it.template.id, + templateDescription = it.template.description, + when (it) { + is InteractiveAsynchronousRunManager -> ApiRunType.ASYNCHRONOUS + is InteractiveSynchronousRunManager -> ApiRunType.SYNCHRONOUS + else -> TODO() + }, + properties = it.runProperties, + teams = emptyList(), + tasks = emptyList() + ) } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt new file mode 100644 index 000000000..060e89202 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -0,0 +1,68 @@ +package dev.dres.api.rest.handler.evaluation.client + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiTaskInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.* +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] used to list get information about ongoing [Task]s. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClientHandler(store), GetRestHandler { + override val route = "client/evaluation/currentTask/{runId}" + + @OpenApi( + summary = "Returns an overview of the currently active task for a run.", + path = "/api/v1/client/evaluation/currentTask/{evaluationId}", + tags = ["Client Run Info"], + queryParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiTaskInfo::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiTaskInfo = this.store.transactional(true) { tx -> + + val run = getEvaluationManager(ctx) ?: throw ErrorStatusException(404, "Evaluation ${this.evaluationId(ctx)} not found", ctx) + val rac = RunActionContext.runActionContext(ctx, run) + + if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) + val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) + + ApiTaskInfo( + taskId = task.id, + templateId = task.template.id, + name = task.template.name, + taskGroup = task.template.taskGroup.name, + taskType = task.template.taskGroup.type.name, + running = task.isRunning, + remainingTime = when(run.status){ + RunManagerStatus.CREATED -> 0 + RunManagerStatus.ACTIVE -> { + when(task.status) { + TaskRunStatus.CREATED, + TaskRunStatus.PREPARING -> task.duration + TaskRunStatus.RUNNING ->run.timeLeft(rac) / 1000 + TaskRunStatus.ENDED -> 0 + } + } + RunManagerStatus.TERMINATED -> 0 + }, + numberOfSubmissions = -1 + ) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt new file mode 100644 index 000000000..273066a4b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.* +import dev.dres.api.rest.handler.evaluation.client.AbstractEvaluationClientHandler +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.EvaluationId +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunExecutor +import dev.dres.run.RunManager +import io.javalin.http.Context +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct +import kotlinx.dnq.query.isEmpty +import kotlinx.dnq.query.isNotEmpty + +/** + * A [RestHandler] that provides basic functionality to query the state of [Evaluation]s. This is part of the internal DRES API. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractEvaluationViewerHandler(protected val store: TransientEntityStore): RestHandler, AccessManagedRestHandler { + /** By default [AbstractEvaluationViewerHandler] can only be used by [ApiRole.VIEWER]. */ + override val permittedRoles: Set = setOf(ApiRole.VIEWER) + + /** All [AbstractEvaluationClientHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** + * Returns the [RunManager] associated with the current [Context]. + * + * @param ctx The request [Context] + * @return [RunManager] or null + */ + fun getEvaluationManager(ctx: Context, evaluationId: EvaluationId): InteractiveRunManager? { + val run = RunExecutor.managerForId(evaluationId) ?: return null + if (run !is InteractiveRunManager) return null + if (ctx.isParticipant() && run.template.teams.flatMapDistinct { it.users }.filter { it.id eq ctx.userId() }.isEmpty) return null + return run + } + + /** + * Returns the [RunManager] associated with the current [Context]. + * + * @param ctx The request [Context] + * @return [RunManager] or null + */ + fun getRelevantManagers(ctx: Context): List { + val managers = RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java) + return when { + ctx.isParticipant() -> managers.filter { m -> m.template.teams.flatMapDistinct { it.users }.filter { it.id eq ctx.userId() }.isNotEmpty } + ctx.isJudge() -> managers.filter { m -> m.template.judges.filter { u -> u.id eq ctx.userId() }.isNotEmpty } + else -> managers + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt similarity index 83% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt index ed8208100..c4674540c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt @@ -2,19 +2,18 @@ package dev.dres.api.rest.handler import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.run.RunInfo -import dev.dres.api.rest.types.run.RunState -import dev.dres.api.rest.types.run.SubmissionInfo -import dev.dres.api.rest.types.run.TaskInfo +import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo +import dev.dres.api.rest.types.evaluation.RunState +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.TaskInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.competition.tasks.ApiHintContent import dev.dres.api.rest.types.competition.tasks.ApiTargetContent import dev.dres.data.dbo.DAO import dev.dres.data.model.Config -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.competition.options.SimpleOption +import dev.dres.data.model.template.options.SimpleOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.Submission import dev.dres.run.InteractiveRunManager @@ -34,7 +33,7 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes override val permittedRoles: Set = setOf(ApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): EvaluationId = AccessManager.userIdForSession(ctx.sessionId())!! private fun isJudge(ctx: Context): Boolean { val roles = AccessManager.rolesOfSession(ctx.sessionId()) @@ -46,37 +45,7 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes return roles.contains(ApiRole.PARTICIPANT) && !roles.contains(ApiRole.ADMIN) } - fun getRelevantManagers(ctx: Context): List { - val managers = RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java) - - if (isParticipant(ctx)) { - val userId = userId(ctx) - return managers.filter { m -> m.description.teams.any { it.users.contains(userId) } } - } - if (isJudge(ctx)) { - val userId = userId(ctx) - return managers.filter { m -> userId in m.description.judges } - } - return managers - } - - fun getRun(ctx: Context, runId: UID): InteractiveRunManager? { - if (isParticipant(ctx)) { - val userId = userId(ctx) - val run = RunExecutor.managerForId(runId) ?: return null - if (run is InteractiveRunManager && run.description.teams.any { it.users.contains(userId) }) { - return run - } - return null - } - val run = RunExecutor.managerForId(runId) - if (run != null && run is InteractiveRunManager) { - return run - } - return null - - } fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) @@ -84,7 +53,7 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes } -class ListCompetitionRunInfosHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { +class ListCompetitionRunInfosHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { override val route = "run/info/list" @@ -93,12 +62,12 @@ class ListCompetitionRunInfosHandler : AbstractCompetitionRunRestHandler(), GetR path = "/api/v1/run/info/list", tags = ["Competition Run"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = getRelevantManagers(ctx).map { RunInfo(it) } + override fun doGet(ctx: Context): List = getRelevantManagers(ctx).map { ApiEvaluationInfo(it) } } class ListCompetitionRunStatesHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { @@ -125,7 +94,7 @@ class ListCompetitionRunStatesHandler : AbstractCompetitionRunRestHandler(), Get } -class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler { +class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler { override val route = "run/{runId}/info" @@ -135,14 +104,14 @@ class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRes tags = ["Competition Run"], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], responses = [ - OpenApiResponse("200", [OpenApiContent(RunInfo::class)]), + OpenApiResponse("200", [OpenApiContent(ApiEvaluationInfo::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): RunInfo { + override fun doGet(ctx: Context): ApiEvaluationInfo { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) @@ -150,7 +119,7 @@ class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRes throw ErrorStatusException(403, "Access Denied", ctx) } - return RunInfo(run) + return ApiEvaluationInfo(run) } } @@ -255,7 +224,7 @@ class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRu val task = if (currentTaskDescription.id == taskId) { currentTaskDescription } else { - run.taskForId(rac, taskId)?.description + run.taskForId(rac, taskId)?.template } if (task == null) { //request to a task id that is either invalid or not yet available @@ -316,7 +285,7 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio val task = if (currentTaskDescription.id == taskId) { currentTaskDescription } else { - run.taskForId(rac, taskId)?.description + run.taskForId(rac, taskId)?.template } if (task == null) { //request to a task id that is either invalid or not yet available @@ -335,7 +304,7 @@ class CurrentTaskTargetHandler(private val config: Config, private val collectio } -class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { +class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { override val route = "run/{runId}/submission/list" @OpenApi( @@ -344,14 +313,14 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle tags = ["Competition Run"], pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { + override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) val rac = runActionContext(ctx, run) @@ -360,16 +329,16 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle throw ErrorStatusException(403, "Access denied.", ctx) } - fun limitSubmissions(submissions: List, limit: Int, blind: Boolean = false): List = + fun limitSubmissions(submissions: List, limit: Int, blind: Boolean = false): List = submissions.groupBy { it.teamId }.values .map { it.sortedBy { s -> s.timestamp }.take(limit) }.flatMap { it.map { s -> if (blind) { - SubmissionInfo.blind(s) + ApiSubmission.blind(s) } else { - SubmissionInfo(s) + ApiSubmission(s) } } } @@ -384,22 +353,22 @@ class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandle limitSubmissions(run.submissions(rac), limit, blind) } else { if (blind) { - run.submissions(rac).map { SubmissionInfo.blind(it) } + run.submissions(rac).map { ApiSubmission.blind(it) } } else { - run.submissions(rac).map { SubmissionInfo(it) } + run.submissions(rac).map { ApiSubmission(it) } } } } else { if (limit > 0) { limitSubmissions(run.submissions(rac), limit, blind) } else { - run.submissions(rac).map { SubmissionInfo(it) } + run.submissions(rac).map { ApiSubmission(it) } } } } } -class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { +class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { override val route = "run/{runId}/submission/list/after/{timestamp}" @OpenApi( @@ -411,14 +380,14 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest OpenApiParam("timestamp", Long::class, "Minimum Timestamp of returned submissions.") ], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { + override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) val rac = runActionContext(ctx, run) @@ -431,17 +400,17 @@ class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRest val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() return if (run.currentTask(rac)?.isRunning == true) { if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { - run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } + run.submissions(rac).filter { it.timestamp >= timestamp }.map { ApiSubmission.blind(it) } } else { - run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo(it) } + run.submissions(rac).filter { it.timestamp >= timestamp }.map { ApiSubmission(it) } } } else { - run.submissions(rac).filter { it.timestamp >= timestamp }.map { SubmissionInfo.blind(it) } + run.submissions(rac).filter { it.timestamp >= timestamp }.map { ApiSubmission.blind(it) } } } } -class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { +class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { override val route = "run/{runId}/task/{taskRunId}/submission/list" @@ -454,14 +423,14 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes OpenApiParam("taskRunId", String::class, "Task run ID") ], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { + override fun doGet(ctx: Context): List { val runId = runId(ctx) val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) val rac = runActionContext(ctx, run) @@ -476,14 +445,14 @@ class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRes val task = run.currentTask(rac) - return if (task?.description?.id == taskRunId && task.isRunning) { + return if (task?.template?.id == taskRunId && task.isRunning) { if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { - run.submissions(rac).map { SubmissionInfo.blind(it) } + run.submissions(rac).map { ApiSubmission.blind(it) } } else { - run.submissions(rac).map { SubmissionInfo(it) } + run.submissions(rac).map { ApiSubmission(it) } } } else { - run.taskForId(rac, taskRunId)?.submissions?.map { SubmissionInfo(it) } ?: emptyList() + run.taskForId(rac, taskRunId)?.submissions?.map { ApiSubmission(it) } ?: emptyList() } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt similarity index 96% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt index bb552a0e6..5a1937c0e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt @@ -4,7 +4,6 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.UID import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor @@ -29,7 +28,7 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler override val permittedRoles: Set = setOf(ApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): EvaluationId = AccessManager.userIdForSession(ctx.sessionId())!! /** * Checks if the current session has the [ApiRole.PARTICIPANT]. @@ -39,11 +38,11 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) - fun getRun(ctx: Context, runId: UID): InteractiveRunManager? { + fun getRun(ctx: Context, runId: EvaluationId): InteractiveRunManager? { if (isParticipant(ctx)) { val userId = userId(ctx) val run = RunExecutor.managerForId(runId) ?: return null - if (run is InteractiveRunManager && run.description.teams.any { it.users.contains(userId) }) { + if (run is InteractiveRunManager && run.template.teams.any { it.users.contains(userId) }) { return run } return null @@ -122,7 +121,7 @@ class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler + run.template.teams.map { team -> Score(team.uid.string, scores[team.uid] ?: 0.0) } ) @@ -176,7 +175,7 @@ class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler { - handlePreviewRequest(submission.item, if (submission is TemporalSubmissionAspect) submission.start else null, ctx) + when { + submission.item != null -> { + handlePreviewRequest(submission.item!!, if (submission.start != null) submission.start else null, ctx) } - is TextAspect -> { + submission.text != null -> { ctx.header("Cache-Control", "max-age=31622400") ctx.contentType("image/png") ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) @@ -69,7 +64,6 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab } catch (e: ErrorStatusException) { ctx.errorResponse(e) } - } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index c465d46fd..65de9b8b2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -8,7 +8,6 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.submission.RunResult import dev.dres.data.dbo.DAO import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaItemSegmentList @@ -25,25 +24,25 @@ import io.javalin.http.bodyAsClass import io.javalin.openapi.* import io.javalin.security.RouteRole -abstract class BatchSubmissionHandler(internal val collections: DAO, internal val itemIndex: DaoIndexer>, internal val segmentIndex: DaoIndexer) : PostRestHandler, AccessManagedRestHandler { +abstract class BatchSubmissionHandler(internal val collections: DAO, internal val itemIndex: DaoIndexer>, internal val segmentIndex: DaoIndexer) : PostRestHandler, AccessManagedRestHandler { override val apiVersion = "v1" override val permittedRoles: Set = setOf(ApiRole.PARTICIPANT) - internal fun userId(ctx: Context): UID = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + internal fun userId(ctx: Context): EvaluationId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(404, "Parameter 'runId' is missing!'", ctx) }.UID() - protected fun getInteractiveManager(userId: UID, runId: UID): InteractiveRunManager? + protected fun getInteractiveManager(userId: EvaluationId, runId: EvaluationId): InteractiveRunManager? = AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == runId } - protected fun getNonInteractiveManager(userId: UID, runId: UID): NonInteractiveRunManager? + protected fun getNonInteractiveManager(userId: EvaluationId, runId: EvaluationId): NonInteractiveRunManager? = AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == runId } } -class JsonBatchSubmissionHandler(collections: DAO, itemIndex: DaoIndexer>, segmentIndex: DaoIndexer) : BatchSubmissionHandler(collections, itemIndex, segmentIndex) { +class JsonBatchSubmissionHandler(collections: DAO, itemIndex: DaoIndexer>, segmentIndex: DaoIndexer) : BatchSubmissionHandler(collections, itemIndex, segmentIndex) { override val route: String = "submit/{runId}" @@ -75,13 +74,13 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D throw ErrorStatusException(400, "Error parsing json batch", ctx) } - val team = runManager.description.teams.find { + val team = runManager.template.teams.find { it.users.contains(userId) }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) val resultBatches = runResult.tasks.mapNotNull { taskResult -> - val task = runManager.tasks(rac).find { it.description.name == taskResult.task } ?: return@mapNotNull null - val mediaCollectionId = task.description.mediaCollectionId + val task = runManager.tasks(rac).find { it.template.name == taskResult.task } ?: return@mapNotNull null + val mediaCollectionId = task.template.mediaCollectionId val results = taskResult.results.map { result -> if (result.item != null) { val mediaItem = @@ -124,9 +123,9 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D val submissionBatch = if (resultBatches.all { it.results.first() is TemporalBatchElement }) { @Suppress("UNCHECKED_CAST") - (TemporalSubmissionBatch(team, userId, UID(), resultBatches as List>)) + (TemporalSubmissionBatch(team, userId, EvaluationId(), resultBatches as List>)) } else { - BaseSubmissionBatch(team, userId, UID(), resultBatches as List>) + BaseSubmissionBatch(team, userId, EvaluationId(), resultBatches as List>) } runManager.addSubmissionBatch(submissionBatch) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 84c6fba5b..8b39817c6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -12,10 +12,11 @@ import dev.dres.data.model.Config import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.competition.task.options.TaskOption +import dev.dres.data.model.template.task.options.TaskOption import dev.dres.data.model.media.* import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.Task import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.SubmissionType @@ -43,7 +44,7 @@ import java.util.* * @author Loris Sauter * @version 2.0.0 */ -class SubmissionHandler (private val store: TransientEntityStore, private val config: Config): GetRestHandler, AccessManagedRestHandler { +class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): GetRestHandler, AccessManagedRestHandler { /** [SubmissionHandler] requires [ApiRole.PARTICIPANT]. */ override val permittedRoles = setOf(ApiRole.PARTICIPANT) @@ -136,7 +137,7 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co it.currentTask(rac)?.isRunning == true } if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) - if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.description.name }}", ctx) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) return managers.first() } @@ -156,7 +157,7 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co /* Find team that the user belongs to. */ val user = User.query(User::id eq userId).firstOrNull() ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) - val team = runManager.description.teams.filter { it.users.contains(user) }.firstOrNull() + val team = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull() ?: throw ErrorStatusException(404, "No team for user '$userId' could be found.", ctx) val rac = RunActionContext.runActionContext(ctx, runManager) @@ -172,12 +173,14 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co /* If text is supplied, it supersedes other parameters */ val textParam = map[PARAMETER_NAME_TEXT]?.first() val itemParam = map[PARAMETER_NAME_ITEM]?.first() + val currentTaskId = runManager.currentTask(rac)?.id + val task = Task.query(Task::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException(404, "No active task for ID '$currentTaskId' could be found.", ctx) val submission = Submission.new { this.id = submissionId this.status = SubmissionStatus.INDETERMINATE this.user = user this.team = team - this.task = runManager.currentTask(rac) + this.task = task this.timestamp = submissionTime } @@ -256,6 +259,6 @@ class SubmissionHandler (private val store: TransientEntityStore, private val co if (Files.exists(destinationPath)){ return } - FFmpegUtil.extractFrame(submission.item!!.pathToOriginal(), submission.start, destinationPath) + FFmpegUtil.extractFrame(submission.item!!.pathToOriginal(), submission.start!!, destinationPath) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt similarity index 54% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt index f82fe1465..9dcccd373 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/AbstractCompetitionDescriptionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt @@ -1,12 +1,12 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.handler.collection.AbstractCollectionHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.CompetitionDescriptionId +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.TemplateId import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore @@ -15,7 +15,7 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * An abstract [RestHandler] used to access and manipulate [CompetitionDescription]s. + * An abstract [RestHandler] used to access and manipulate [EvaluationTemplate]s. * * @author Luca Rossetto * @author Ralph Gasser @@ -30,16 +30,16 @@ abstract class AbstractCompetitionDescriptionHandler(protected val store: Transi /** All [AbstractCollectionHandler]s are part of the v1 API. */ override val apiVersion = "v1" - /** Convenience method to extract [CompetitionDescription]'s ID from [Context]. */ - private fun competitionId(ctx: Context): CompetitionDescriptionId = + /** Convenience method to extract [EvaluationTemplate]'s ID from [Context]. */ + private fun competitionId(ctx: Context): TemplateId = ctx.pathParamMap().getOrElse("competitionId") { throw ErrorStatusException(404, "Parameter 'competitionId' is missing!'", ctx) } - /** Convenience method to extract [CompetitionDescription] from [Context]. */ - protected fun competitionFromContext(ctx: Context): CompetitionDescription = competitionById(competitionId(ctx), ctx) + /** Convenience method to extract [EvaluationTemplate] from [Context]. */ + protected fun competitionFromContext(ctx: Context): EvaluationTemplate = competitionById(competitionId(ctx), ctx) - /** Convenience method to extract [CompetitionDescription] by ID. */ - protected fun competitionById(id: CompetitionDescriptionId, ctx: Context): CompetitionDescription - = CompetitionDescription.query(CompetitionDescription::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) + /** Convenience method to extract [EvaluationTemplate] by ID. */ + protected fun competitionById(id: TemplateId, ctx: Context): EvaluationTemplate + = EvaluationTemplate.query(EvaluationTemplate::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt similarity index 77% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index 0cbc7cd68..9d8afc7ef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/CreateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -1,11 +1,11 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.competition.ApiCreateCompetition import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -14,23 +14,23 @@ import jetbrains.exodus.database.TransientEntityStore import java.util.* /** - * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [CompetitionDescription]. + * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class CreateCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), PostRestHandler { +class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), PostRestHandler { - override val route: String = "competition" + override val route: String = "template" @OpenApi( - summary = "Creates a new competition description.", - path = "/api/v1/competition", + summary = "Creates a new evaluation template.", + path = "/api/v1/template", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(ApiCreateCompetition::class)]), - tags = ["Competition"], + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -47,7 +47,7 @@ class CreateCompetitionHandler(store: TransientEntityStore) : AbstractCompetitio val newId = UUID.randomUUID().toString() this.store.transactional { - CompetitionDescription.new { + EvaluationTemplate.new { id = newId name = createRequest.name description = createRequest.description diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt similarity index 58% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index 121dff4c9..8dba0e951 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/DeleteCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -1,30 +1,30 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractCompetitionDescriptionHandler] that can be used to delete an existing [CompetitionDescription]. + * A [AbstractCompetitionDescriptionHandler] that can be used to delete an existing [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class DeleteCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), DeleteRestHandler { - override val route: String = "competition/{competitionId}" +class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), DeleteRestHandler { + override val route: String = "template/{templateId}" @OpenApi( - summary = "Deletes the competition description with the given competition ID.", - path = "/api/v1/competition/{competitionId}", + summary = "Deletes the evaluation template with the given template ID.", + path = "/api/v1/template/{templateId}", methods = [HttpMethod.DELETE], - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -35,7 +35,7 @@ class DeleteCompetitionHandler(store: TransientEntityStore) : AbstractCompetitio override fun doDelete(ctx: Context): SuccessStatus = this.store.transactional { val competitionToDelete = competitionFromContext(ctx) competitionToDelete.delete() - SuccessStatus("Competition with ID ${competitionToDelete.id} was deleted successfully.") + SuccessStatus("Evaluation template with ID ${competitionToDelete.id} was deleted successfully.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt new file mode 100644 index 000000000..b9d8b1ae5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.template + +import dev.dres.api.rest.handler.AbstractCompetitionRunRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.template.team.Team +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiParam +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * A [AbstractCompetitionRunRestHandler] that can be used to list all [Team] logos. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 1.0.0 + */ +class GetTeamLogoHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { + + override val route = "template/logo/{logoId}" + override val apiVersion = "v1" + + //not used + override fun doGet(ctx: Context): Any = "" + + @OpenApi( + summary = "Returns the logo for the given team ID.", + path = "/api/v1/template/logo/{teamId}", + tags = ["Competition Run", "Media"], + pathParams = [OpenApiParam("teamId", String::class, "The ID of the team to list load the logo for.")], + responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], + ignore = true, + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + /* Extract logoId. */ + val teamId = ctx.pathParamMap()["teamId"] ?: throw ErrorStatusException(400, "Parameter 'teamId' is missing!'", ctx) + + this.store.transactional(true) { + val logo = Team.query(Team::id eq teamId).firstOrNull()?.logo + if (logo != null) { + ctx.contentType("image/png") + ctx.result(logo) + } else { + ctx.status(404) + ctx.contentType("image/png") + ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt similarity index 63% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index d18aef501..df84af7d7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -1,9 +1,9 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.ApiCompetitionOverview import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi @@ -11,24 +11,23 @@ import io.javalin.openapi.OpenApiContent import io.javalin.openapi.OpenApiResponse import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.flatMapDistinct import kotlinx.dnq.query.size /** - * A [GetRestHandler] that can be used to list all [CompetitionDescription]s. + * A [GetRestHandler] that can be used to list all [EvaluationTemplate]s. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class ListCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { - override val route: String = "competition/list" +class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { + override val route: String = "template/list" @OpenApi( summary = "Lists an overview of all available competitions with basic information about their content.", - path = "/api/v1/competition/list", - tags = ["Competition"], + path = "/api/v1/template/list", + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) @@ -36,8 +35,8 @@ class ListCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionD methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = this.store.transactional(true) { - CompetitionDescription.all().asSequence().map { - ApiCompetitionOverview(it.id, it.name, it.description, it.taskGroups.flatMapDistinct { g -> g.tasks }.size(), it.teams.size()) + EvaluationTemplate.all().asSequence().map { + ApiCompetitionOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt similarity index 64% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 6a381f373..8fcacc540 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -1,17 +1,16 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.tasks.ApiTaskDescription import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.flatMapDistinct /** - * A [AbstractCompetitionDescriptionHandler] that can be used to list all [TaskDescription]s. + * A [AbstractCompetitionDescriptionHandler] that can be used to list all [TaskTemplate]s. * * @author Ralph Gasser * @author Luca Rossetto @@ -20,13 +19,13 @@ import kotlinx.dnq.query.flatMapDistinct */ class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { - override val route: String = "competition/{competitionId}/task/list" + override val route: String = "template/{templateId}/task/list" @OpenApi( - summary = "Lists the tasks of a specific competition.", - path = "/api/v1/competition/{competitionId}/task/list", - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], + summary = "Lists the task templates contained in a specific evaluation template.", + path = "/api/v1/competition/{templateId}/task/list", + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -37,6 +36,6 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescrip ) override fun doGet(ctx: Context) = this.store.transactional(true) { - competitionFromContext(ctx).taskGroups.flatMapDistinct { it.tasks }.asSequence().map { it.toApi() }.toList() + competitionFromContext(ctx).tasks.asSequence().map { it.toApi() }.toList() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt similarity index 82% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index 906506583..ed304f276 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -1,9 +1,9 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.template.team.Team import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -23,9 +23,9 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractCompetitionDescript @OpenApi( summary = "Lists all the teams of a specific competition.", - path = "/api/v1/competition/{competitionId}/team/list", - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], + path = "/api/v1/competition/{templateId}/team/list", + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt similarity index 61% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index 4adf697ea..fcb2fd33c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/ShowCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -1,27 +1,29 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.ApiCompetitionDescription import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractCompetitionDescriptionHandler] that can be used to show an existing [CompetitionDescription]. + * A [AbstractCompetitionDescriptionHandler] that can be used to show an existing [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class ShowCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { +class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { + override val route: String = "template/{templateId}" + @OpenApi( - summary = "Loads the detailed definition of a specific competition.", - path = "/api/v1/competition/{competitionId}", - pathParams = [OpenApiParam("competitionId", String::class, "Competition ID")], - tags = ["Competition"], + summary = "Loads the detailed definition of a specific evaluation template.", + path = "/api/v1/template/{templateId}", + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiCompetitionDescription::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -33,5 +35,4 @@ class ShowCompetitionHandler(store: TransientEntityStore) : AbstractCompetitionD override fun doGet(ctx: Context)= this.store.transactional(true) { competitionFromContext(ctx).toApi() } - override val route: String = "competition/{competitionId}" } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt similarity index 64% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt index c83da2019..413c54fa1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/description/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt @@ -1,5 +1,6 @@ -package dev.dres.api.rest.handler.description +package dev.dres.api.rest.handler.template +import com.github.kittinunf.fuel.util.decodeBase64 import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.competition.ApiCompetitionDescription import dev.dres.api.rest.types.status.ErrorStatus @@ -7,11 +8,11 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.Config import dev.dres.data.model.admin.User -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.* -import dev.dres.data.model.competition.task.options.ConfiguredOption -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.* +import dev.dres.data.model.template.task.options.ConfiguredOption +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.TeamGroup import dev.dres.data.model.media.MediaItem import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -19,10 +20,11 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.query.* +import java.io.ByteArrayInputStream import java.util.* /** - * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [CompetitionDescription]. + * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto @@ -31,14 +33,15 @@ import java.util.* */ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) : AbstractCompetitionDescriptionHandler(store), PatchRestHandler { - override val route: String = "competition" + override val route: String = "template" @OpenApi( - summary = "Updates an existing competition.", - path = "/api/v1/competition", + summary = "Updates an existing evaluation template.", + path = "/api/v1/template/{templateId}", + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(ApiCompetitionDescription::class)]), - tags = ["Competition"], + tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -71,8 +74,7 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) t.name = type.name t.duration = type.duration t.score = type.scoreOption.option - t.targets.clear() - t.targets.addAll(type.targetOptions.map { it.option }) + t.target = type.targetOption.option t.hints.clear() t.hints.addAll(type.hintOptions.map { it.option }) t.submission.clear() @@ -97,48 +99,48 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) } g.name = group.name g.type = TaskType.query((TaskType::name eq group.name) and (TaskGroup::competition eq existing)).first() + } - /* Update task information. */ - val taskIds = group.tasks.mapNotNull { it.id }.toTypedArray() - g.tasks.removeAll(TaskDescription.query(TaskDescription::taskGroup eq g and not(TaskDescription::id.containsIn(*taskIds)))) - for (task in group.tasks) { - val t = if (task.id != null) { - g.tasks.filter { it.id eq task.id }.first() - } else { - val desc = TaskDescription.new { this.id = UUID.randomUUID().toString() } - g.tasks.add(desc) - desc - } - t.name = task.name - t.duration = task.duration - - /* Update task targets. */ - t.targets.clear() - for (target in task.targets) { - val item = MediaItem.query(MediaItem::id eq target.target).first() - t.targets.add(TaskDescriptionTarget.new { - this.item = item - this.type = target.type.type - this.start = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() - this.end = target.range?.end?.toTemporalPoint(item.fps?: 0.0f)?.toMilliseconds() - }) - } + /* Update task information. */ + val taskIds = apiValue.tasks.mapNotNull { it.id }.toTypedArray() + existing.tasks.removeAll(TaskTemplate.query(TaskTemplate::competition eq existing and not(TaskTemplate::id.containsIn(*taskIds)))) + for (task in apiValue.tasks) { + val t = if (task.id != null) { + existing.tasks.filter { it.id eq task.id }.first() + } else { + val desc = TaskTemplate.new { this.id = UUID.randomUUID().toString() } + existing.tasks.add(desc) + desc + } + t.name = task.name + t.duration = task.duration - /* Update task hints. */ - t.hints.clear() - for (hint in task.hints) { - val item = MediaItem.query(MediaItem::id eq hint.mediaItem).firstOrNull() - t.hints.add(Hint.new { - this.type = hint.type.type - this.item = item - this.text = hint.description - this.path = hint.path - this.start = hint.start - this.end = hint.end - this.temporalRangeStart = hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - this.temporalRangeEnd = hint.range?.end?.toTemporalPoint(item?.fps?: 0.0f)?.toMilliseconds() - }) - } + /* Update task targets. */ + t.targets.clear() + for (target in task.targets) { + val item = MediaItem.query(MediaItem::id eq target.target).first() + t.targets.add(TaskDescriptionTarget.new { + this.item = item + this.type = target.type.type + this.start = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() + this.end = target.range?.end?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() + }) + } + + /* Update task hints. */ + t.hints.clear() + for (hint in task.hints) { + val item = MediaItem.query(MediaItem::id eq hint.mediaItem).firstOrNull() + t.hints.add(Hint.new { + this.type = hint.type.type + this.item = item + this.text = hint.description + this.path = hint.path + this.start = hint.start + this.end = hint.end + this.temporalRangeStart = hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + this.temporalRangeEnd = hint.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + }) } } @@ -151,7 +153,9 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) } t.name = team.name t.color = team.color - t.logoId = team.logoId + if (team.logoData != null) { + t.logo = ByteArrayInputStream(team.logoData!!.decodeBase64()) + } t.users.clear() t.users.addAll(User.query(User::id.containsIn(*team.users.map { it.id }.toTypedArray()))) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index 84929860e..72abe6334 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -6,7 +6,6 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.UID import dev.dres.data.model.admin.User import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context @@ -44,6 +43,6 @@ class ListActiveUsersHandler : GetRestHandler>, AccessManagedRestH UserManager.get(id = it) }?.let { it.toApi() - } ?: return@map ApiUser(UID.EMPTY.string, "??", ApiRole.VIEWER, session) + } ?: return@map ApiUser("??", "??", ApiRole.VIEWER, session) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 751a5fadb..3bc3112e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -1,6 +1,5 @@ package dev.dres.api.rest.types.collection -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaItem /** @@ -10,7 +9,7 @@ import dev.dres.data.model.media.MediaItem * @author Ralph Gasser * @version 1.1.0 */ -data class ApiMediaItem(val id: String= UID.EMPTY.string, val name: String, val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { +data class ApiMediaItem(val id: String= EvaluationId.EMPTY.string, val name: String, val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { init { if (this.type == ApiMediaType.VIDEO) { require(this.durationMs != null) { "Duration must be set for a video item." } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt index eca39ed8d..e30df632c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.types.collection import dev.dres.data.model.media.CollectionId import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate /** * The RESTful API equivalent for [MediaCollection]. @@ -14,9 +14,9 @@ import dev.dres.data.model.competition.task.TaskDescription data class RestMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { companion object { /** - * Generates a [ApiMediaItem] from a [TaskDescription] and returns it. + * Generates a [ApiMediaItem] from a [TaskTemplate] and returns it. * - * @param task The [TaskDescription] to convert. + * @param task The [TaskTemplate] to convert. */ fun fromMediaCollection(item: MediaCollection) = RestMediaCollection(item.id, item.name, item.description, item.path) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt index 07547c4ff..f777cbe8c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt @@ -5,11 +5,11 @@ import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup import dev.dres.api.rest.types.competition.tasks.ApiTaskType import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.competition.team.ApiTeamGroup -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate /** - * The RESTful API equivalent for [CompetitionDescription]. + * The RESTful API equivalent for [EvaluationTemplate]. * - * @see CompetitionDescription + * @see EvaluationTemplate * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt index f3528704f..5a73ce704 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate /** - * An overview over a [CompetitionDescription]. + * An overview over a [EvaluationTemplate]. * * @author Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt new file mode 100644 index 000000000..b979a9a8a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt @@ -0,0 +1,13 @@ +package dev.dres.api.rest.types.competition + +import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.data.model.run.RunProperties +import dev.dres.data.model.template.TemplateId + +/** + * A data class that represents a RESTful request for creating a new [dres.data.model.run.CompetitionRun] + * + * @author Ralph Gasser + * @version 1.1.0 + */ +data class ApiCompetitionStartMessage(val templateId: TemplateId, val name: String, val type: ApiRunType, val properties: RunProperties = RunProperties()) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt deleted file mode 100644 index 2fa7008bc..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/CompetitionStartMessage.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dev.dres.api.rest.types.competition - -import dev.dres.api.rest.types.run.RunType -import dev.dres.data.model.run.RunProperties - -/** - * A data class that represents a RESTful request for creating a new [dres.data.model.run.CompetitionRun] - * - * @author Ralph Gasser - * @version 1.0.0 - */ -data class CompetitionStartMessage( - val competitionId: String, - val name: String, - val type: RunType, - val properties: RunProperties = RunProperties() -) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt index 53246504b..f6297820e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHint.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.api.rest.types.collection.time.ApiTemporalRange -import dev.dres.data.model.competition.* +import dev.dres.data.model.template.* /** * The RESTful API equivalent for [TaskDescriptionHint]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt index a55ea2713..6d455600a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.competition.task.HintType +import dev.dres.data.model.template.task.HintType /** * The RESTful API equivalent for [HintType]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt index eaf021eff..749e26cae 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.api.rest.types.collection.time.ApiTemporalRange -import dev.dres.data.model.competition.task.TaskDescriptionTarget +import dev.dres.data.model.template.task.TaskDescriptionTarget /** * The RESTful API equivalent for [TaskDescriptionTarget]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt index e2886f619..6fac625e8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.competition.task.TargetType +import dev.dres.data.model.template.task.TargetType /** * The RESTful API equivalent for [TargetType]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt index 1c8c9470d..20307624f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt @@ -1,13 +1,13 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskDescriptionId +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.TaskDescriptionId import dev.dres.data.model.media.CollectionId /** - * The RESTful API equivalent for [TaskDescription]. + * The RESTful API equivalent for [TaskTemplate]. * - * @see TaskDescription + * @see TaskTemplate * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt index 8030d1088..b666f032f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.competition.task.TaskGroup +import dev.dres.data.model.template.task.TaskGroup /** * The RESTful API equivalent of a [TaskGroup]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt index dea27d194..e6a52ee29 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.api.rest.types.competition.tasks.options.* -import dev.dres.data.model.competition.task.TaskType +import dev.dres.data.model.template.task.TaskType /** * The RESTful API equivalent of a [TaskType]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt index 37610dceb..acec5525d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.competition.task.options.HintOption +import dev.dres.data.model.template.task.options.HintOption /** * A RESTful API representation of [HintOption]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt index 8de044608..8fa300fc6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.competition.task.options.ScoreOption +import dev.dres.data.model.template.task.options.ScoreOption /** * A RESTful API representation of [ScoreOption]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt index 9148dd9ae..54625a4f4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.competition.task.options.SubmissionOption +import dev.dres.data.model.template.task.options.SubmissionOption /** * A RESTful API representation of [SubmissionOption]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt index 853f7ec73..642639772 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.competition.task.options.TargetOption +import dev.dres.data.model.template.task.options.TargetOption /** * A RESTful API representation of [TargetOption]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt index 63b5d6322..f2f3692bd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.competition.task.options.TaskOption +import dev.dres.data.model.template.task.options.TaskOption /** * A RESTful API representation of [TaskOption]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt index dd9bc780f..a6de9417c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt @@ -1,8 +1,8 @@ package dev.dres.api.rest.types.competition.team import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.TeamId /** * A RESTful API representation of a [Team] diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt index 4b5a9d91b..4d06f14d1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.competition.team -import dev.dres.data.model.competition.team.TeamGroup -import dev.dres.data.model.competition.team.TeamGroupId +import dev.dres.data.model.template.team.TeamGroup +import dev.dres.data.model.template.team.TeamGroupId /** * A RESTful API representation of a [TeamGroup] diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt similarity index 85% rename from backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt index 781614f17..4792ee387 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/AdminRunOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt @@ -1,4 +1,4 @@ -package dev.dres.api.rest.types.run +package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.run.* @@ -11,7 +11,7 @@ data class AdminRunOverview(val state: RunManagerStatus, val teamOverviews: List val teamOverviews = when (run) { is InteractiveSynchronousRunManager -> { val overview = run.run.tasks.map { TaskRunOverview(it) } - run.description.teams.map { + run.template.teams.map { TeamTaskOverview(it.uid.string, overview) } } @@ -41,11 +41,11 @@ data class TaskRunOverview( val started: Long?, val ended: Long?) { constructor(task: TaskRun) : this( - task.description.id.string, - task.description.name, - task.description.taskGroup.name, - task.description.taskType.name, - task.description.duration, + task.template.id.string, + task.template.name, + task.template.taskGroup.name, + task.template.taskType.name, + task.template.duration, task.uid.string, task.status, task.started, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt new file mode 100644 index 000000000..8ac611050 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -0,0 +1,24 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.run.RunProperties +import dev.dres.run.RunManager + +/** + * Contains the basic and most importantly static information about a [InteractiveSynchronousEvaluation] and the + * associated [RunManager]. Since this information usually doesn't change in the course of a run, + * it allows for local caching and other optimizations. + * + * @author Ralph Gasser + * @version 1.0.2 + */ +data class ApiEvaluationInfo( + val id: String, + val name: String, + val templateId: String, + val templateDescription: String?, + val type: ApiRunType, + val properties: RunProperties, + val teams: List, + val tasks: List, +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt new file mode 100644 index 000000000..405173259 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.run.RunType + +/** + * + */ +enum class ApiRunType(val type: RunType) { + SYNCHRONOUS(RunType.INTERACTIVE_SYNCHRONOUS), + ASYNCHRONOUS(RunType.INTERACTIVE_ASYNCHRONOUS) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt new file mode 100644 index 000000000..0a9eed0fa --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -0,0 +1,27 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.submissions.SubmissionStatus + +/** + * Contains information about a [Submission]. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +data class ApiSubmission( + val id: SubmissionId, + val teamId: String? = null, + val teamName: String? = null, + val memberId: String? = null, + val memberName: String? = null, + val status: SubmissionStatus, + val timestamp: Long? = null, + val item: ApiMediaItem? = null, + val text: String? = null, + val start: Long? = null, + val end: Long? = null +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt new file mode 100644 index 000000000..ed88206ac --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt @@ -0,0 +1,13 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.TaskId +import dev.dres.data.model.template.TemplateId + +/** + * Encodes [ApiSubmission] data for a specific [EvaluationId] and (optionally) [TaskId]. + * + * @author Loris Sauter + * @version 1.1.0 + */ +data class ApiSubmissionInfo(val evaluationId: EvaluationId, val taskId: TemplateId, val submissions: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt new file mode 100644 index 000000000..b3118894d --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt @@ -0,0 +1,21 @@ +package dev.dres.api.rest.types.evaluation + +/** + * Information of past task in a [dev.dres.data.model.run.InteractiveSynchronousEvaluation]. + * The information includes the [dev.dres.data.model.template.TaskDescription], + * the actual task, its name, [dev.dres.data.model.template.TaskGroup], + * [dev.dres.data.model.template.TaskType] and the number of submissions. + * + * @author Loris Sauter + * @version 1.0.0 + */ +data class ApiTaskInfo( + val taskId: String, + val templateId: String, + val name: String, + val taskGroup: String, + val taskType: String, + val numberOfSubmissions: Int, + val remainingTime: Long, + val running: Boolean +) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiViewerInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiViewerInfo.kt new file mode 100644 index 000000000..aa2be6e61 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiViewerInfo.kt @@ -0,0 +1,9 @@ +package dev.dres.api.rest.types.evaluation + +/** + * Basic information regarding a viewer instance + * + * @author Ralph Gasser + * @version 1.1.0 + */ +data class ApiViewerInfo(val viewersId: String, val username: String, val host: String, val ready: Boolean) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt similarity index 97% rename from backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt index 974c7577e..9fe3acc24 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt @@ -1,4 +1,4 @@ -package dev.dres.api.rest.types.run +package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt similarity index 72% rename from backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt index 07c9a714d..049f9e7a5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt @@ -1,11 +1,10 @@ -package dev.dres.api.rest.types.run +package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.UID -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.InteractiveSynchronousEvaluation /** - * Basic and most importantly static information about the [TaskDescription] + * Basic and most importantly static information about the [TaskTemplate] * of a [InteractiveSynchronousEvaluation]. Since this information usually doesn't change in the course of a run, it * allows for local caching and other optimizations. * @@ -19,7 +18,7 @@ data class TaskInfo( val taskType: String, val duration: Long) { - constructor(task: TaskDescription) : this( + constructor(task: TaskTemplate) : this( task.id.string, task.name, task.taskGroup.name, @@ -28,6 +27,6 @@ data class TaskInfo( ) companion object { - val EMPTY_INFO = TaskInfo(UID.EMPTY.string, "N/A", "N/A", "N/A", 0) + val EMPTY_INFO = TaskInfo(EvaluationId.EMPTY.string, "N/A", "N/A", "N/A", 0) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TeamInfo.kt similarity index 86% rename from backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TeamInfo.kt index 7c0e4e478..ad9e6af76 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TeamInfo.kt @@ -1,6 +1,6 @@ -package dev.dres.api.rest.types.run +package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.template.team.Team import dev.dres.data.model.run.InteractiveSynchronousEvaluation /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/websocket/ClientMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt similarity index 86% rename from backend/src/main/kotlin/dev/dres/api/rest/types/run/websocket/ClientMessage.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt index 820d92a56..f5f4828d0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/websocket/ClientMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt @@ -1,4 +1,4 @@ -package dev.dres.api.rest.types.run.websocket +package dev.dres.api.rest.types.evaluation.websocket /** * Message send by the DRES client via WebSocket to communicate with the DRES server. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/websocket/ServerMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt similarity index 94% rename from backend/src/main/kotlin/dev/dres/api/rest/types/run/websocket/ServerMessage.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt index be3192dae..e77aeec79 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/websocket/ServerMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt @@ -1,4 +1,4 @@ -package dev.dres.api.rest.types.run.websocket +package dev.dres.api.rest.types.evaluation.websocket /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt deleted file mode 100644 index 6ff7052a9..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/PastTaskInfo.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.dres.api.rest.types.run - -/** - * Information of past task in a [dev.dres.data.model.run.InteractiveSynchronousEvaluation]. - * The information includes the [dev.dres.data.model.competition.TaskDescription], - * the actual task, its name, [dev.dres.data.model.competition.TaskGroup], - * [dev.dres.data.model.competition.TaskType] and the number of submissions. - * - * @author Loris Sauter - * @version 1.0.0 - */ -data class PastTaskInfo( - val taskId: String, - val descriptionId: String, - val name: String, - val taskGroup: String, - val taskType: String, - val numberOfSubmissions: Int -) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt deleted file mode 100644 index c1d0457c9..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunInfo.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.dres.api.rest.types.run - -import dev.dres.data.model.run.InteractiveSynchronousEvaluation -import dev.dres.data.model.run.RunProperties -import dev.dres.run.InteractiveAsynchronousRunManager -import dev.dres.run.RunManager - -/** - * Contains the basic and most importantly static information about a [InteractiveSynchronousEvaluation] and the - * associated [RunManager]. Since this information usually doesn't change in the course of a run, - * it allows for local caching and other optimizations. - * - * @author Ralph Gasser - * @version 1.0.2 - */ -data class RunInfo( - val id: String, - val name: String, - val description: String?, - val teams: List, - val tasks: List, - val competitionId: String, - val properties: RunProperties, - val type: RunType - ) { - constructor(run: RunManager) : this( - run.id.string, - run.name, - run.description.description, - run.description.teams.map { TeamInfo(it) }, - run.description.tasks.map { TaskInfo(it) }, - run.description.id.string, - run.runProperties, - if (run is InteractiveAsynchronousRunManager) { - RunType.ASYNCHRONOUS - } else { - RunType.SYNCHRONOUS - } - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunType.kt deleted file mode 100644 index d28440067..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/RunType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.dres.api.rest.types.run - -enum class RunType { - SYNCHRONOUS, - ASYNCHRONOUS -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt deleted file mode 100644 index f00428a74..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/SubmissionInfo.kt +++ /dev/null @@ -1,62 +0,0 @@ -package dev.dres.api.rest.types.run - -import dev.dres.api.rest.types.collection.ApiMediaItem -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect - -/** - * Contains information about a [Submission]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 - */ -data class SubmissionInfo( - val id: String? = null, - val teamId: String, - val teamName: String? = null, - val memberId: String, - val memberName: String? = null, - val status: SubmissionStatus, - val timestamp: Long, - val item: ApiMediaItem? = null, - val text: String? = null, - val start: Long? = null, - val end: Long? = null -) { - - /** - * Constructor that generates a [SubmissionInfo] from a [Submission]. Contains - * all information, that can be extracted from the [Submission] directly but does not - * resolve advanced information, such as [teamName] and [memberName]. - * - * @param submission The [Submission] to convert. - */ - constructor(submission: Submission) : this( - id = submission.uid.string, - teamId = submission.teamId.string, - memberId = submission.memberId.string, - status = submission.status, - timestamp = submission.timestamp, - item = if (submission is ItemAspect) submission.item.toApi() else null, - text = if (submission is TextAspect) submission.text else null, - start = if (submission is TemporalSubmissionAspect) submission.start else null, - end = if (submission is TemporalSubmissionAspect) submission.end else null - ) - - companion object { - /** - * Generates and returns a blind [SubmissionInfo] containing only [teamId], [memberId], [status] and [timestamp] - * - * @param submission The [Submission] to convert. - */ - fun blind(submission: Submission): SubmissionInfo = SubmissionInfo( - teamId = submission.teamId.string, - memberId = submission.memberId.string, - status = submission.status, - timestamp = submission.timestamp - ) - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskRunSubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskRunSubmissionInfo.kt deleted file mode 100644 index f2102b3cc..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/TaskRunSubmissionInfo.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.dres.api.rest.types.run - -data class TaskRunSubmissionInfo( - val taskRunId: String, - val submissions: List -) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/run/ViewerInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/run/ViewerInfo.kt deleted file mode 100644 index d88e9435d..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/run/ViewerInfo.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.dres.api.rest.types.run - -/** - * Basic information regarding a viewer instance - * - * @author Ralph Gasser - * @version 1.1 - */ -data class ViewerInfo(val viewersId: String, val username: String, val host: String, val ready: Boolean) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/UID.kt b/backend/src/main/kotlin/dev/dres/data/model/UID.kt deleted file mode 100644 index 5ff3f7cee..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/UID.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.data.model - -import java.util.* - - -data class UID (val string: String) { - companion object { - val EMPTY = UID(UUID(0L, 0L)) - } - constructor() : this(UUID.randomUUID().toString()) - constructor(uuid: UUID) : this(uuid.toString()) - override fun toString(): String = "UID($string)" -} - diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt index ba9efa8ae..f65a7c970 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt @@ -14,9 +14,16 @@ typealias CollectionId = String */ class MediaCollection(entity: Entity): PersistentEntity(entity) { companion object : XdNaturalEntityType() + /** The name of this [MediaItem]. */ var name: String by xdRequiredStringProp(unique = true, trimmed = false) + + /** The path to the folder containing [MediaItem]s in this [MediaCollection]. */ var path: String by xdRequiredStringProp(unique = true, trimmed = false) + + /** A textual description of this [MediaCollection]. */ var description: String? by xdStringProp(trimmed = false) + + /** A list of [MediaItem]s in this [MediaCollection]. */ val items by xdChildren0_N(MediaItem::collection) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt index e3aa4178a..a6800dfb5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt @@ -1,14 +1,13 @@ package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.UID /** * * @author Ralph Gasser * @version 1.0 */ -data class MediaItemSegmentList(override var id: UID, val mediaItemId: UID, val segments: MutableList) : PersistentEntity { +data class MediaItemSegmentList(override var id: EvaluationId, val mediaItemId: EvaluationId, val segments: MutableList) : PersistentEntity { init { require(segments.all { it.mediaItemId == mediaItemId } ){"All segments need to belong to the same media item"} } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index cd660ce30..1660aa963 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -1,8 +1,10 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.run.interfaces.EvaluationRun +import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.interfaces.Run +import kotlinx.dnq.util.findById /** * An abstract [Run] implementation that can be used by different subtypes. @@ -10,19 +12,37 @@ import dev.dres.data.model.run.interfaces.Run * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractEvaluation(protected val evaluation: Evaluation): dev.dres.data.model.run.interfaces.EvaluationRun { +abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { - /** The name of this [AbstractEvaluation]. */ - override val id: EvaluationId - get() = this.evaluation.id + /** The internal [xdId] of this [AbstractEvaluation]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + private val xdId = evaluation.xdId + + /** + * Accessor for the [Evaluation] underpinning this [AbstractEvaluation] + */ + protected val evaluation: Evaluation + get() = Evaluation.findById(this.xdId) + + /** The [EvaluationId] of this [AbstractEvaluation]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + override val id: EvaluationId = evaluation.id - /** The name of this [AbstractEvaluation]. */ - override val name: String - get() = this.evaluation.name + /** The name of this [AbstractEvaluation]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. * + */ + override val name: String = evaluation.name - /** The [CompetitionDescription] of this [AbstractEvaluation]. */ - override val description: CompetitionDescription - get() = this.evaluation.description + /** The [EvaluationTemplate] used by this [AbstractEvaluation]. + * + * Since this cannot change during the lifetime of an evaluation, it is stored in memory. + */ + override val description: EvaluationTemplate = evaluation.template /** Timestamp of when this [AbstractEvaluation] was started. */ override var started: Long @@ -79,6 +99,9 @@ abstract class AbstractEvaluation(protected val evaluation: Evaluation): dev.dre this.ended = System.currentTimeMillis() } + /** + * Re-activates this [AbstractEvaluation] + */ override fun reactivate() { if (this.ended == null){ throw IllegalStateException("Run has not yet ended.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index 6f113383a..824674d4a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -1,10 +1,10 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.options.TargetOption -import dev.dres.data.model.competition.team.TeamAggregatorImpl -import dev.dres.data.model.competition.team.TeamGroupId -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.options.TargetOption +import dev.dres.data.model.template.team.TeamAggregatorImpl +import dev.dres.data.model.template.team.TeamGroupId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.MediaItemsSubmissionValidator @@ -28,7 +28,7 @@ abstract class AbstractInteractiveTask(task: Task): AbstractTaskRun(task) { /** List of [Submission]s* registered for this [Task]. */ val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() - /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ + /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ abstract var duration: Long /** The [SubmissionFilter] used to filter [Submission]s. */ @@ -51,29 +51,29 @@ abstract class AbstractInteractiveTask(task: Task): AbstractTaskRun(task) { abstract fun postSubmission(submission: Submission) /** - * Generates and returns a new [SubmissionValidator] for this [TaskDescription]. Depending + * Generates and returns a new [SubmissionValidator] for this [TaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * @return [SubmissionValidator]. */ - internal fun newValidator(): SubmissionValidator = when (val targetOption = this.description.taskGroup.type.target) { - TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.description.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + internal fun newValidator(): SubmissionValidator = when (val targetOption = this.template.taskGroup.type.target) { + TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) TargetOption.MEDIA_SEGMENT -> { - val target = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() + val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) } - TargetOption.TEXT -> TextValidator(this.description.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + TargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) TargetOption.JUDGEMENT -> { - val knownRanges = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() BasicJudgementValidator(knownCorrectRanges = knownRanges) } TargetOption.VOTE -> { - val knownRanges = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() - val parameters = this.description.taskGroup.type.configurations.filter { it.key eq targetOption.description }.asSequence().associate { it.key to it.value } + val parameters = this.template.taskGroup.type.configurations.filter { it.key eq targetOption.description }.asSequence().associate { it.key to it.value } BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) } else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index 3e9c2a85e..de00da3f3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,7 +1,8 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.task.options.TargetOption -import dev.dres.data.model.submissions.aspects.OriginAspect +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.template.task.options.TargetOption +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.batch.ResultBatch import dev.dres.run.validation.MediaItemsSubmissionBatchValidator import dev.dres.run.validation.TemporalOverlapSubmissionBatchValidator @@ -27,20 +28,20 @@ abstract class AbstractNonInteractiveTask(task: Task): AbstractTaskRun(task) { * * @return [SubmissionBatchValidator] */ - fun newValidator(): SubmissionBatchValidator = when(this.description.taskGroup.type.target){ - TargetOption.MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(this.description.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + fun newValidator(): SubmissionBatchValidator = when(this.template.taskGroup.type.target){ + TargetOption.MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) TargetOption.MEDIA_SEGMENT -> { - val target = this.description.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() + val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() TemporalOverlapSubmissionBatchValidator(TransientMediaSegment(target.item!!, target.range!!)) } TargetOption.JUDGEMENT -> TODO() TargetOption.VOTE -> TODO() TargetOption.TEXT -> TODO() - else -> throw IllegalStateException("The provided target option ${this.description.taskGroup.type.target.description} is not supported by non-interactive tasks.") + else -> throw IllegalStateException("The provided target option ${this.template.taskGroup.type.target.description} is not supported by non-interactive tasks.") } /** - * + * Submits a batch of [Submissions]. */ - abstract fun addSubmissionBatch(origin: OriginAspect, batches: List>) + abstract fun addSubmissionBatch(teamId: TeamId, memberId: UserId, batches: List>) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt index aaf26e644..a99681f1e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt @@ -1,8 +1,10 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.run.TaskRunStatus +import kotlinx.dnq.util.findById /** * An abstract [Run] implementation that can be used by different subtypes. @@ -10,10 +12,25 @@ import dev.dres.run.TaskRunStatus * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.run.interfaces.TaskRun { - /** The Id of this [AbstractTaskRun]. */ - override val id: TaskId - get() = this.task.id +abstract class AbstractTaskRun(task: Task): TaskRun { + /** The internal [xdId] of this [AbstractEvaluation]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + private val xdId = task.xdId + + /** + * Accessor for the [Task] underpinning this [AbstractTaskRun] + */ + protected val task: Task + get() = Task.findById(this.xdId) + + /** + * The [TaskId] of this [AbstractTaskRun]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + override val id: TaskId = this.task.id /** Timestamp of when this [AbstractTaskRun] was started. */ override var started: Long @@ -29,8 +46,8 @@ abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.ru this.task.ended = value } - /** Reference to the [TaskDescription] describing this [AbstractTaskRun]. */ - override val description: TaskDescription + /** Reference to the [TaskTemplate] describing this [AbstractTaskRun]. */ + override val template: TaskTemplate get() = this.task.description @Volatile @@ -69,6 +86,10 @@ abstract class AbstractTaskRun(protected val task: Task): dev.dres.data.model.ru this.status = TaskRunStatus.ENDED } + + /** + * Reactivates this [AbstractTaskRun]. + */ override fun reactivate() { if (this.ended == null){ throw IllegalStateException("Run has not yet ended.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt index 9c30a6874..9fba9ce43 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt @@ -1,14 +1,15 @@ package dev.dres.data.model.run import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.run.interfaces.EvaluationRun import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* typealias EvaluationId = String /** - * Represents a [Evaluation], i.e., a concrete instance of a [CompetitionDescription], as executed by DRES. + * Represents a [Evaluation], i.e., a concrete instance of a [EvaluationTemplate], as executed by DRES. * * @author Ralph Gasser * @version 1.0.0 @@ -27,8 +28,8 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { /** The [RunType] of this [Evaluation]. */ var type by xdLink1(RunType) - /** The [CompetitionDescription] backing this [Evaluation]. */ - var description by xdLink1(CompetitionDescription) + /** The [EvaluationTemplate] backing this [Evaluation]. */ + var template by xdLink1(EvaluationTemplate) /** Timestamp of when this [Evaluation] started. */ var started by xdRequiredLongProp() @@ -50,4 +51,17 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { /** A fixed limit on submission previews. */ var limitSubmissionPreviews by xdIntProp() + + + /** + * Generates and returns an [EvaluationRun] instance for this [Evaluation]. + * + * @return [EvaluationRun] + */ + fun toRun(): EvaluationRun = when(this.type) { + RunType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) + RunType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this, emptyMap()) /* TODO: Not sure about semantics here. */ + RunType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) + else -> throw IllegalArgumentException("Unsupported run type ${this.type.description}. This is a programmer's error!") + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index a4ccfa118..54c7a1a09 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -1,11 +1,10 @@ package dev.dres.data.model.run import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.TaskDescriptionId -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.TaskDescriptionId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskId @@ -19,7 +18,7 @@ import java.util.* import java.util.concurrent.ConcurrentHashMap /** - * Represents a concrete, interactive and asynchronous [Run] of a [CompetitionDescription]. + * Represents a concrete, interactive and asynchronous [Run] of a [EvaluationTemplate]. * * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [Task]s and access the current [Task]. * @@ -28,7 +27,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: companion object { fun generatePermutation( - description: CompetitionDescription, + description: EvaluationTemplate, shuffle: Boolean ): Map> = if (shuffle) { @@ -90,25 +89,25 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: constructor( id: EvaluationId, name: String, - competitionDescription: CompetitionDescription, + evaluationTemplate: EvaluationTemplate, properties: RunProperties ) : this( id, name, - competitionDescription, + evaluationTemplate, properties, - generatePermutation(competitionDescription, properties.shuffleTasks) + generatePermutation(evaluationTemplate, properties.shuffleTasks) ) internal constructor( id: EvaluationId, name: String, - competitionDescription: CompetitionDescription, + evaluationTemplate: EvaluationTemplate, runProperties: RunProperties, started: Long, ended: Long, permutation: Map> - ) : this(id, name, competitionDescription, runProperties, permutation) { + ) : this(id, name, evaluationTemplate, runProperties, permutation) { this.started = if (started == -1L) { null } else { @@ -128,8 +127,8 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: override val tasks: List get() = this.tasksMap.values.flatten() - /** Tracks the current [TaskDescription] per [TeamId]. */ - private val navigationMap: MutableMap = HashMap() + /** Tracks the current [TaskTemplate] per [TeamId]. */ + private val navigationMap: MutableMap = HashMap() fun goTo(teamId: TeamId, index: Int) { navigationMap[teamId] = this.description.tasks[ @@ -137,7 +136,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: ] } - fun currentTaskDescription(teamId: TeamId): TaskDescription = + fun currentTaskDescription(teamId: TeamId): TaskTemplate = navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) init { @@ -156,7 +155,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: val tasks = this.tasksMap[it.uid] if (tasks != null && tasks.isNotEmpty()) { val lastTask = tasks.last() - val taskIndex = this.description.tasks.indexOf(lastTask.description) + val taskIndex = this.description.tasks.indexOf(lastTask.template) if (lastTask.ended != null) { this.navigationMap[it.uid] = @@ -206,7 +205,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: * @version 1.0.0 */ inner class Task internal constructor( - override val uid: TaskId = UID(), + override val uid: TaskId = EvaluationId(), val teamId: TeamId, val descriptionId: TaskDescriptionId ) : AbstractInteractiveTask() { @@ -244,22 +243,22 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: ?: -1 @Transient - override val description: TaskDescription = + override val template: TaskTemplate = this@InteractiveAsynchronousEvaluation.description.tasks.find { it.id == this.descriptionId } ?: throw IllegalArgumentException("Task with taskId ${this.descriptionId} not found.") @Transient - override val filter: SubmissionFilter = this.description.newFilter() + override val filter: SubmissionFilter = this.template.newFilter() @Transient - override val scorer: TeamTaskScorer = this.description.newScorer() as? TeamTaskScorer + override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") @Transient override val validator: SubmissionValidator = this.newValidator() - /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ - override var duration: Long = this.description.duration + /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ + override var duration: Long = this.template.duration init { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index c8f56532e..bbd8e22a7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission @@ -13,7 +13,7 @@ import kotlinx.dnq.query.* import java.util.* /** - * Represents a concrete, interactive and synchronous [Run] of a [CompetitionDescription]. + * Represents a concrete, interactive and synchronous [Run] of a [EvaluationTemplate]. * * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. * @@ -31,15 +31,15 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ override val tasks: List = this.evaluation.tasks.asSequence().map { ISTaskRun(it) - }.toList() + }.toMutableList() - /** Reference to the currently active [TaskDescription]. This is part of the task navigation. */ - var currentTaskDescription = this.description.tasks.first() + /** Reference to the currently active [TaskTemplate]. This is part of the task navigation. */ + var currentTaskTemplate = this.description.tasks.first() private set /** Returns the last [TaskRun]. */ val currentTask: TaskRun? - get() = this.tasks.findLast { it.description.id == this.currentTaskDescription.id } + get() = this.tasks.firstOrNull { it.template.id == this.currentTaskTemplate.id } override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" @@ -49,11 +49,11 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat * @param index The new task index to move to. */ fun goTo(index: Int) { - this.currentTaskDescription = this.description.tasks.drop(index).first() + this.currentTaskTemplate = this.description.tasks.drop(index).first() } /** - * Represents a concrete [Run] of a [TaskDescription]. [Task]s always exist within a [InteractiveSynchronousEvaluation]. + * Represents a concrete [Run] of a [TaskTemplate]. [Task]s always exist within a [InteractiveSynchronousEvaluation]. * As a [InteractiveSynchronousEvaluation], [Task]s can be started and ended and they can be used to register [Submission]s. * * @version 1.2.0 @@ -62,11 +62,11 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat inner class ISTaskRun(task: Task): AbstractInteractiveTask(task) { /** - * Constructor used to generate an [ISTaskRun] from a [TaskDescription]. + * Constructor used to generate an [ISTaskRun] from a [TaskTemplate]. * - * @param description [TaskDescription] to generate [ISTaskRun] from. + * @param description [TaskTemplate] to generate [ISTaskRun] from. */ - constructor(description: TaskDescription) : this(Task.new { + constructor(description: TaskTemplate) : this(Task.new { this.id = UUID.randomUUID().toString() this.type = RunType.INTERACTIVE_SYNCHRONOUS this.evaluation = this@InteractiveSynchronousEvaluation.evaluation @@ -82,22 +82,22 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat override val position: Int get() = this@InteractiveSynchronousEvaluation.tasks.indexOf(this) - /** Reference to the [TaskDescription] describing this [Task]. */ - override val description: TaskDescription + /** Reference to the [TaskTemplate] describing this [Task]. */ + override val template: TaskTemplate get() = this.task.description /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ - override val filter: SubmissionFilter = this.description.newFilter() + override val filter: SubmissionFilter = this.template.newFilter() /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ - override val scorer: TeamTaskScorer = this.description.newScorer() as? TeamTaskScorer + override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") /** The [SubmissionValidator] used by this [ISTaskRun]. */ override val validator: SubmissionValidator = newValidator() - /** The total duration in milliseconds of this task. Usually determined by the [TaskDescription] but can be adjusted! */ - override var duration: Long = this.description.duration + /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ + override var duration: Long = this.template.duration init { check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { @@ -118,7 +118,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this@InteractiveSynchronousEvaluation.description.teams.filter { it eq submission.team }.any()) { - "Team ${submission.teamId} does not exists for competition run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" + "Team ${submission.team.teamId} does not exists for competition run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 4f7cd50dd..2b023b055 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -1,11 +1,11 @@ package dev.dres.data.model.run -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.aspects.OriginAspect import dev.dres.data.model.submissions.batch.ResultBatch import dev.dres.run.score.interfaces.ResultBatchTaskScorer import kotlinx.dnq.query.asSequence @@ -13,7 +13,7 @@ import kotlinx.dnq.query.size /** - * Represents a concrete, interactive and synchronous [Run] of a [CompetitionDescription]. + * Represents a concrete, interactive and synchronous [Run] of a [EvaluationTemplate]. * * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. * @@ -38,8 +38,6 @@ class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(eval */ inner class NITaskRun(task: Task): AbstractNonInteractiveTask(task) { - internal val submissions: MutableMap, ResultBatch<*>> = mutableMapOf() - /** Reference to the [EvaluationRun] hosting this [NITaskRun]. */ override val competition: EvaluationRun get() = this@NonInteractiveEvaluation @@ -48,14 +46,17 @@ class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(eval override val position: Int get() = this@NonInteractiveEvaluation.tasks.indexOf(this) + + internal val submissions: MutableMap, ResultBatch<*>> = mutableMapOf() + @Transient - override val scorer: ResultBatchTaskScorer = description.newScorer() as? ResultBatchTaskScorer + override val scorer: ResultBatchTaskScorer = template.newScorer() as? ResultBatchTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type ResultBatchTaskScorer") @Synchronized - override fun addSubmissionBatch(origin: OriginAspect, batches: List>) { + override fun addSubmissionBatch(teamId: TeamId, memberId: UserId, batches: List>) { batches.forEach { resultBatch -> - submissions[origin.teamId to resultBatch.name] = resultBatch + submissions[teamId to resultBatch.name] = resultBatch } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 39f5251fc..cb7ecedc8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserId -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context @@ -40,7 +40,7 @@ data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) val roles = AccessManager.rolesOfSession(ctx.sessionId()).mapNotNull { it.role }.toSet() - val teamId = runManager.description.teams.filter { it.users.contains(User.query(User::id eq userId).first()) }.first().teamId + val teamId = runManager.template.teams.filter { it.users.contains(User.query(User::id eq userId).first()) }.first().teamId return RunActionContext(userId, teamId, roles) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index fe63ee621..67de3b23c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -1,7 +1,8 @@ package dev.dres.data.model.run import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.submissions.BatchedSubmission import dev.dres.data.model.submissions.Submission import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -9,7 +10,7 @@ import kotlinx.dnq.* typealias TaskId = String /** - * Represents a [Task], i.e., a concrete instance of a [TaskDescription], as executed by DRES. + * Represents a [Task], i.e., a concrete instance of a [TaskTemplate], as executed by DRES. * * @author Ralph Gasser * @version 1.0.0 @@ -31,12 +32,15 @@ class Task(entity: Entity) : PersistentEntity(entity) { /** Timestamp of when this [Evaluation] ended. */ var ended by xdNullableLongProp() - /** The [TaskDescription] this [Task] is an instance of. */ - var description by xdLink1(TaskDescription) + /** The [TaskTemplate] this [Task] is an instance of. */ + var description by xdLink1(TaskTemplate) /** The [Evaluation] this [Task] belongs to. */ var evaluation by xdParent(Evaluation::tasks) /** List of [Submission]s received by this [Task]. */ val submissions by xdChildren0_N(Submission::task) + + /** List of [BatchedSubmission]s received by this [Task]. */ + val batched by xdChildren0_N(BatchedSubmission::task) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index b2506d44b..6745f74ad 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.Evaluation import dev.dres.data.model.run.EvaluationId /** @@ -16,8 +16,8 @@ interface EvaluationRun: Run { /** The name human readable of this [Evaluation]. */ val name: String - /** Reference to the [CompetitionDescription] that describes the content of this [Evaluation]. */ - val description: CompetitionDescription + /** Reference to the [EvaluationTemplate] that describes the content of this [Evaluation]. */ + val description: EvaluationTemplate /** Collection of [TaskRun]s that make up this [Evaluation]. */ val tasks: List diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index c12924a86..4dcca77e1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task import dev.dres.run.TaskRunStatus import dev.dres.run.score.interfaces.TaskScorer @@ -22,8 +22,8 @@ interface TaskRun: Run { /** The position of this [Task] within the enclosing [EvaluationRun]. */ val position: Int - /** Reference to the [TaskDescription] describing this [Task]. */ - val description: TaskDescription + /** Reference to the [TaskTemplate] describing this [Task]. */ + val template: TaskTemplate /** The [TaskScorer] used to update score for this [Task]. */ val scorer: TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt new file mode 100644 index 000000000..baa76f057 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt @@ -0,0 +1,34 @@ +package dev.dres.data.model.submissions + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.admin.User +import dev.dres.data.model.template.team.Team +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.simple.min +import kotlinx.dnq.xdLink1 +import kotlinx.dnq.xdRequiredLongProp + +/** + * A [BatchedSubmission] as submitted by a competition participant and received by DRES. + * + * @author Luca Rossetto + * @version 2.0.0 + */ +class BatchedSubmission(entity: Entity) : PersistentEntity(entity) { + /** The [SubmissionId] of this [User]. */ + var submissionId: SubmissionId + get() = this.id + set(value) { this.id = value } + + /** The timestamp of this [Submission]. */ + var timestamp by xdRequiredLongProp { min(0L) } + + /** The [SubmissionStatus] of this [Submission]. */ + var status by xdLink1(SubmissionStatus) + + /** The [Team] that submitted this [Submission] */ + var team by xdLink1(Team) + + /** The [User] that submitted this [Submission] */ + var user by xdLink1(User) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index f8f02004f..963bbbd37 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -1,8 +1,9 @@ package dev.dres.data.model.submissions +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User -import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.template.team.Team import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange @@ -15,10 +16,11 @@ import kotlinx.dnq.simple.requireIf typealias SubmissionId = String /** - * A [Submission] as received by a competition participant. + * A [Submission] as submitted by a competition participant and received by DRES. * - * @author Ralph Gasser & Luca Rossetto - * @version 1.1.0 + * @author Ralph Gasser + * @author Luca Rossetto + * @version 2.0.0 */ sealed class Submission(entity: Entity) : PersistentEntity(entity) { companion object : XdNaturalEntityType() @@ -50,15 +52,41 @@ sealed class Submission(entity: Entity) : PersistentEntity(entity) { var item by xdLink0_1(MediaItem) /** The start frame number of this [Submission]. */ - var start by xdRequiredLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } + var start by xdNullableLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } /** The end frame number of this [Submission]. */ - var end by xdRequiredLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } + var end by xdNullableLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } /** The text submitted. Only for [SubmissionType.TEXT] . */ var text by xdStringProp { requireIf { this.type == SubmissionType.TEXT } } - /** */ - val temporalRange: TemporalRange - get() = TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) + /** Returns the [TemporalRange] for this [Submission]. */ + val temporalRange: TemporalRange? + get() = try { + TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) + } catch (e: NullPointerException) { + null + } + + /** + * Converts this [Submission] to a RESTful API representation [ApiSubmission]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiSubmission] + */ + fun toApi(blind: Boolean = false): ApiSubmission = ApiSubmission( + id = this.id, + teamId = this.team.id, + teamName = this.team.name, + memberId = this.user.id, + memberName = this.user.username, + status = this.status, + timestamp = this.timestamp, + item = if (!blind) { this.item?.toApi() } else { null }, + text = if (!blind) { this.text } else { null }, + start = if (!blind) { this.start } else { null }, + end = if (!blind) { this.end } else { null } + ) + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt index d7ad9d42b..c2c9773bf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt @@ -7,7 +7,10 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** + * The type of [Submission] with respect to its content * + * @author Ralph Gasser & Luca Rossetto + * @version 1.1.0 */ class SubmissionType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt index 64dcc66b0..530e4b95a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions.batch -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.TaskId /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt deleted file mode 100644 index 86538b0cb..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatchElement.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.dres.data.model.submissions.batch - -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.StatusAspect - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface BaseResultBatchElement : ItemAspect, StatusAspect diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt index abacb028f..3b94de055 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt @@ -1,15 +1,13 @@ package dev.dres.data.model.submissions.batch -import dev.dres.data.model.UID - /** * * @author Luca Rossetto * @version 1.0.0 */ data class BaseSubmissionBatch( - override val uid: UID, - override val teamId: UID, - override val memberId: UID, + override val uid: EvaluationId, + override val teamId: EvaluationId, + override val memberId: EvaluationId, override val results: Collection> ) : SubmissionBatch> diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt index 87b52c865..68705ffdc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions.batch -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.interfaces.TaskId /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt index 7d5420cc5..3c1f2a266 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt @@ -1,15 +1,13 @@ package dev.dres.data.model.submissions.batch -import dev.dres.data.model.UID - /** * * @author Luca Rossetto * @version 1.0.0 */ data class TemporalSubmissionBatch( - override val teamId: UID, - override val memberId: UID, - override val uid: UID, + override val teamId: EvaluationId, + override val memberId: EvaluationId, + override val uid: EvaluationId, override val results: List>, ) : SubmissionBatch> \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt similarity index 63% rename from backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt index 22300bce9..b8df25cb2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/CompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt @@ -1,15 +1,15 @@ -package dev.dres.data.model.competition +package dev.dres.data.model.template import dev.dres.api.rest.types.competition.ApiCompetitionDescription import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaType -import dev.dres.data.model.competition.task.TaskGroup -import dev.dres.data.model.competition.task.TaskType -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.team.TeamGroup +import dev.dres.data.model.template.task.TaskGroup +import dev.dres.data.model.template.task.TaskType +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.TeamGroup import dev.dres.data.model.media.time.TemporalRange import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard @@ -19,7 +19,7 @@ import kotlinx.dnq.* import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.query.* -typealias CompetitionDescriptionId = String +typealias TemplateId = String /** * Basic description of a competitions as executed in DRES. @@ -29,43 +29,43 @@ typealias CompetitionDescriptionId = String * @version 2.0.0 * @author Luca Rossetto & Ralph Gasser */ -class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ - companion object: XdNaturalEntityType() +class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ + companion object: XdNaturalEntityType() - /** The [CompetitionDescriptionId] of this [CompetitionDescription]. */ - var teamId: CompetitionDescriptionId + /** The [TemplateId] of this [EvaluationTemplate]. */ + var teamId: TemplateId get() = this.id set(value) { this.id = value } - /** The name held by this [CompetitionDescription]. Must be unique!*/ + /** The name held by this [EvaluationTemplate]. Must be unique!*/ var name by xdRequiredStringProp(unique = true, trimmed = true) - /** If set, this [CompetitionDescription] is considered a template!*/ + /** If set, this [EvaluationTemplate] is considered a template!*/ var template by xdBooleanProp() - /** An optional description of this [CompetitionDescription]. */ + /** An optional description of this [EvaluationTemplate]. */ var description by xdStringProp(trimmed = false) - /** The [TaskType]s defined within this [CompetitionDescription]. */ - val taskTypes by xdChildren0_N(TaskType::competition) + /** The [TaskType]s defined within this [EvaluationTemplate]. */ + val taskTypes by xdChildren0_N(TaskType::competition) - /** The [TaskGroup]s that are part of this [CompetitionDescription]. */ - val taskGroups by xdChildren0_N(TaskGroup::competition) + /** The [TaskGroup]s that are part of this [EvaluationTemplate]. */ + val taskGroups by xdChildren0_N(TaskGroup::competition) - /** The [TaskDescription]s contained in this [CompetitionDescription]*/ - val tasks by xdChildren0_N(TaskDescription::competition) + /** The [TaskTemplate]s contained in this [EvaluationTemplate]*/ + val tasks by xdChildren0_N(TaskTemplate::competition) - /** The [Team]s that are part of this [CompetitionDescription]. */ - val teams by xdChildren0_N(Team::competition) + /** The [Team]s that are part of this [EvaluationTemplate]. */ + val teams by xdChildren0_N(Team::competition) - /** The [Team]s that are part of this [CompetitionDescription]. */ - val teamsGroups by xdChildren0_N(TeamGroup::competition) + /** The [Team]s that are part of this [EvaluationTemplate]. */ + val teamsGroups by xdChildren0_N(TeamGroup::competition) - /** The [User]s that act as judge for this [CompetitionDescription] */ + /** The [User]s that act as judge for this [EvaluationTemplate] */ val judges by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) /** - * Converts this [CompetitionDescription] to a RESTful API representation [ApiCompetitionDescription]. + * Converts this [EvaluationTemplate] to a RESTful API representation [ApiCompetitionDescription]. * * This is a convenience method and it requires and active transaction context. * @@ -84,7 +84,7 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ ) /** - * Generates and returns the default [Scoreboard] implementations for this [CompetitionDescription]. + * Generates and returns the default [Scoreboard] implementations for this [EvaluationTemplate]. * * This is a convenience method and requires an active transaction context. * @@ -100,7 +100,7 @@ class CompetitionDescription(entity: Entity) : PersistentEntity(entity){ } /** - * Generates and returns a list of all [MediaItem] for this [CompetitionDescription]. + * Generates and returns a list of all [MediaItem] for this [EvaluationTemplate]. * * This is a convenience method and requires an active transaction context. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt similarity index 89% rename from backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt index 6ffdc64e3..6a70c88e0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/SubmissionFilterFactory.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.interfaces +package dev.dres.data.model.template.interfaces import dev.dres.run.filter.SubmissionFilter diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt similarity index 88% rename from backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt index 687050d3e..6e3c4d449 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/interfaces/TaskScorerFactory.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.interfaces +package dev.dres.data.model.template.interfaces import dev.dres.run.score.interfaces.TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt similarity index 95% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt index 27dc0d02d..fed703da0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/Hint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.api.rest.types.competition.tasks.ApiHint @@ -20,7 +20,7 @@ import java.nio.file.StandardOpenOption import java.util.* /** - * Represents the hint given by a [TaskDescription], e.g., a media item or text that is shown. + * Represents the hint given by a [TaskTemplate], e.g., a media item or text that is shown. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 @@ -37,8 +37,8 @@ class Hint(entity: Entity) : XdEntity(entity) { /** The start of a (potential) range. */ var end by xdNullableLongProp { min(this@Hint.start ?: 0L) } - /** The parent [TaskDescription] this [Hint] belongs to. */ - var task by xdParent(TaskDescription::hints) + /** The parent [TaskTemplate] this [Hint] belongs to. */ + var task by xdParent(TaskTemplate::hints) /** The[MediaItem] shown as part of the [Hint]. Can be null. */ var item by xdLink0_1(MediaItem) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt similarity index 93% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt index 6a0761173..a487dd9df 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/HintType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt @@ -1,8 +1,8 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiHintType import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.competition.task.options.ScoreOption +import dev.dres.data.model.template.task.options.ScoreOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt similarity index 91% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt index 607ba42ce..b11fd1673 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TargetType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt @@ -1,7 +1,7 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiTargetType -import dev.dres.data.model.competition.task.options.ScoreOption +import dev.dres.data.model.template.task.options.ScoreOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt similarity index 93% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt index fd5232bb3..9149193da 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescriptionTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.api.rest.types.competition.tasks.ApiTarget @@ -20,7 +20,7 @@ import java.nio.file.StandardOpenOption import java.util.* /** - * Represents the target of a [TaskDescription], i.e., the [MediaItem] or part thereof that is considered correct. + * Represents the target of a [TaskTemplate], i.e., the [MediaItem] or part thereof that is considered correct. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 @@ -31,8 +31,8 @@ class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { /** The [TargetType] of this [TaskDescriptionTarget]. */ var type by xdLink1(TargetType) - /** The parent [TaskDescription] this [TaskDescriptionTarget] belongs to. */ - var task by xdParent(TaskDescription::targets) + /** The parent [TaskTemplate] this [TaskDescriptionTarget] belongs to. */ + var task by xdParent(TaskTemplate::targets) /** The targeted [MediaItem]. Can be null. */ var item by xdLink0_1(MediaItem) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt similarity index 78% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt index 70131165c..0e7e5cad4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt @@ -1,11 +1,10 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiTargetType import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.query.asSequence /** * A [TaskGroup] allows the user to specify common traits among a group of [Task]s. @@ -27,8 +26,8 @@ class TaskGroup(entity: Entity) : XdEntity(entity) { /** The [TaskType] this [TaskGroup] belongs to.*/ var type by xdLink1(TaskType) - /** The [CompetitionDescription] this [TaskGroup] belongs to. */ - var competition by xdParent(CompetitionDescription::taskGroups) + /** The [EvaluationTemplate] this [TaskGroup] belongs to. */ + var competition by xdParent(EvaluationTemplate::taskGroups) /** * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt similarity index 77% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt index 109a95ecb..5f8df8332 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.* import dev.dres.api.rest.types.competition.team.ApiTeam @@ -6,12 +6,13 @@ import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.competition.interfaces.SubmissionFilterFactory -import dev.dres.data.model.competition.interfaces.TaskScorerFactory -import dev.dres.data.model.competition.team.Team +import dev.dres.data.model.template.interfaces.SubmissionFilterFactory +import dev.dres.data.model.template.interfaces.TaskScorerFactory +import dev.dres.data.model.template.team.Team import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.template.TemplateId import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator @@ -23,49 +24,48 @@ import java.io.FileNotFoundException import java.io.IOException import java.lang.Long.max -typealias TaskDescriptionId = String - /** * Basic description of a [TaskRun] as executed in DRES. Defines basic attributes such as its name, its duration, * the [TaskDescriptionTarget] and the [Hint]s, that should be presented to the user. * * @version 2.0.0 - * @author Luca Rossetto & Ralph Gasser + * @author Luca Rossetto + * @author Ralph Gasser */ -class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { - companion object: XdNaturalEntityType() +class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { + companion object: XdNaturalEntityType() - /** The [TaskDescriptionId] of this [TaskDescription]. */ - var taskId: TaskDescriptionId + /** The [TemplateId] of this [TaskTemplate]. */ + var templateId: TemplateId get() = this.id set(value) { this.id = value } /** The name held by this [Team]. Must be unique!*/ var name by xdRequiredStringProp(unique = false, trimmed = true) - /** If set, this [CompetitionDescription] is considered a template!*/ + /** If set, this [EvaluationTemplate] is considered a template!*/ var template by xdBooleanProp() - /** The [TaskGroup] this [TaskDescription] belongs to. */ + /** The [TaskGroup] this [TaskTemplate] belongs to. */ var taskGroup by xdLink1(TaskGroup) - /** The [CompetitionDescription] this [TaskDescription] belongs to. */ - var competition by xdParent(CompetitionDescription::tasks) + /** The [EvaluationTemplate] this [TaskTemplate] belongs to. */ + var competition by xdParent(EvaluationTemplate::tasks) - /** The [MediaCollection] this [TaskDescription] operates upon. */ + /** The [MediaCollection] this [TaskTemplate] operates upon. */ var collection by xdLink1(MediaCollection) - /** The duration of the [TaskDescription] in seconds. */ + /** The duration of the [TaskTemplate] in seconds. */ var duration by xdRequiredLongProp { min(0L) } /** The [TaskDescriptionTarget]s that identify the target. Multiple entries indicate the existence of multiple targets. */ - val targets by xdChildren1_N(TaskDescriptionTarget::task) + val targets by xdChildren1_N(TaskDescriptionTarget::task) /** The [Hint]s that act as clues to find the target media. */ - val hints by xdChildren0_N(Hint::task) + val hints by xdChildren0_N(Hint::task) /** - * Generates a new [TaskScorer] for this [TaskDescription]. Depending + * Generates a new [TaskScorer] for this [TaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * @return [TaskScorer]. @@ -74,7 +74,7 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact /** - * Generates and returns a [SubmissionValidator] instance for this [TaskDescription]. Depending + * Generates and returns a [SubmissionValidator] instance for this [TaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * @return [SubmissionFilter] @@ -136,14 +136,14 @@ class TaskDescription(entity: Entity) : PersistentEntity(entity), TaskScorerFact } /** - * Produces a Textual description of the content of the [TaskDescription] if possible + * Produces a Textual description of the content of the [TaskTemplate] if possible * - * @return Textual description of this [TaskDescription]'s content, + * @return Textual description of this [TaskTemplate]'s content, */ fun textualDescription(): String = this.hints.asSequence().filter { it.type == HintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name /** - * Converts this [TaskDescription] to a RESTful API representation [ApiTaskDescription]. + * Converts this [TaskTemplate] to a RESTful API representation [ApiTaskDescription]. * * This is a convenience method and requires an active transaction context. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt similarity index 83% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt index 405713b16..0c6b262b9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt @@ -1,9 +1,9 @@ -package dev.dres.data.model.competition.task +package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiTaskType -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.options.* -import dev.dres.data.model.competition.task.options.ConfiguredOption +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.options.* +import dev.dres.data.model.template.task.options.ConfiguredOption import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter import dev.dres.run.filter.SubmissionFilterAggregator @@ -15,7 +15,7 @@ import kotlinx.dnq.query.* import kotlinx.dnq.simple.min /** - * Specifies the type of a [TaskDescription] and allows for many aspects of its configuration. + * Specifies the type of a [TaskTemplate] and allows for many aspects of its configuration. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 @@ -31,14 +31,14 @@ class TaskType(entity: Entity) : XdEntity(entity) { /** The name of this [TaskType]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) - /** The [CompetitionDescription] this [TaskType] belongs to. */ - var competition by xdParent(CompetitionDescription::taskTypes) + /** The [EvaluationTemplate] this [TaskType] belongs to. */ + var competition by xdParent(EvaluationTemplate::taskTypes) /** The (default) duration of this [TaskType] in seconds. */ var duration by xdRequiredLongProp() { min(0L) } /** The [TargetOption] for this [TaskType]. Specifies the type of target. */ - val target by xdLink1(TargetOption) + var target by xdLink1(TargetOption) /** The [HintOption]s that make-up this [TaskType]. */ val hints by xdLink0_N(HintOption) @@ -52,7 +52,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { /** The [TaskOption]s for this [TaskType]. */ val options by xdLink0_N(TaskOption) - /** [ConfiguredOption]s registered for this [TaskDescription]. */ + /** [ConfiguredOption]s registered for this [TaskTemplate]. */ val configurations by xdChildren0_N(ConfiguredOption::task) /** @@ -72,7 +72,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { ) /** - * Generates a new [TaskScorer] for this [TaskDescription]. Depending + * Generates a new [TaskScorer] for this [TaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * Calling this method requires an ongoing transaction! @@ -86,7 +86,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { } /** - * Generates and returns a [SubmissionValidator] instance for this [TaskDescription]. Depending + * Generates and returns a [SubmissionValidator] instance for this [TaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * Calling this method requires an ongoing transaction! diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt similarity index 89% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt index 53af2140b..e6e9bca71 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ConfiguredOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt @@ -1,7 +1,7 @@ -package dev.dres.data.model.competition.task.options +package dev.dres.data.model.template.task.options -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskType +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.TaskType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -21,7 +21,7 @@ class ConfiguredOption(entity: Entity) : XdEntity(entity) { /** The conifgured value for this [ConfiguredOption]. */ var value by xdRequiredStringProp() - /** The [TaskDescription] this [ConfiguredOption] belongs to. */ + /** The [TaskTemplate] this [ConfiguredOption] belongs to. */ val task by xdParent(TaskType::configurations) /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/HintOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/HintOption.kt similarity index 88% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/HintOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/HintOption.kt index 0d1ddfb39..ce79fe4b0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/HintOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/HintOption.kt @@ -1,14 +1,14 @@ -package dev.dres.data.model.competition.task.options +package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiComponentOption -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * An enumeration of potential options for [TaskDescription] targets. + * An enumeration of potential options for [TaskTemplate] targets. * * @author Ralph Gasser * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt similarity index 96% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ScoreOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt index 65714a282..f2e42a677 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/ScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.task.options +package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiScoreOption import dev.dres.run.score.interfaces.TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/SubmissionOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt similarity index 98% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/SubmissionOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt index 4c71313a4..190f0b25f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/SubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.task.options +package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiSubmissionOption import dev.dres.run.filter.* diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt similarity index 95% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt index 82e45ee55..f64d7659a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.task.options +package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiTargetOption import jetbrains.exodus.entitystore.Entity diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt similarity index 87% rename from backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt index 3ed0f9a84..6eb596824 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/task/options/TaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt @@ -1,14 +1,14 @@ -package dev.dres.data.model.competition.task.options +package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiTaskOption -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * An enumeration of potential general options for [TaskDescription]. + * An enumeration of potential general options for [TaskTemplate]. * * @author Ralph Gasser * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt similarity index 85% rename from backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt index 3c9558c12..40539efef 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt @@ -1,9 +1,9 @@ -package dev.dres.data.model.competition.team +package dev.dres.data.model.template.team import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.link.OnDeletePolicy @@ -39,10 +39,10 @@ class Team(entity: Entity) : PersistentEntity(entity) { var color by xdRequiredStringProp(unique = false, trimmed = true) /** Logo used by this [Team] as Blob. */ - var logo by xdRequiredBlobProp() + var logo by xdBlobProp() - /** The [CompetitionDescription] this [Team] belongs to. */ - var competition by xdParent(CompetitionDescription::teams) + /** The [EvaluationTemplate] this [Team] belongs to. */ + var competition by xdParent(EvaluationTemplate::teams) /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ var group by xdLink0_1(TeamGroup::teams) diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregator.kt similarity index 96% rename from backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregator.kt index 136b0f467..78ba0cf65 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregator.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregator.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.team +package dev.dres.data.model.template.team import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt similarity index 97% rename from backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt index 88533480c..dbbbabf3b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamAggregatorImpl.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt @@ -1,4 +1,4 @@ -package dev.dres.data.model.competition.team +package dev.dres.data.model.template.team /** * Implementation of different [TeamAggregator]s. diff --git a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt similarity index 86% rename from backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt index 2e5d440e3..1dc1c722a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/competition/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt @@ -1,9 +1,9 @@ -package dev.dres.data.model.competition.team +package dev.dres.data.model.template.team import dev.dres.api.rest.types.competition.team.ApiTeamGroup import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.data.model.template.EvaluationTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -34,8 +34,8 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { /** The default [TeamAggregator] to use for this [TeamGroup]. */ var defaultAggregator by xdLink1(TeamAggregator) - /** The [CompetitionDescription] this [Team] belongs to. */ - var competition by xdParent(CompetitionDescription::teamsGroups) + /** The [EvaluationTemplate] this [Team] belongs to. */ + var competition by xdParent(EvaluationTemplate::teamsGroups) /** The [Team]s that belong to this [TeamGroup]. */ val teams by xdLink0_N(Team::group) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 371343f9f..3abeb2601 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -1,7 +1,6 @@ package dev.dres.mgmt.admin import dev.dres.api.rest.types.users.UserRequest -import dev.dres.data.model.UID import dev.dres.data.model.admin.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* @@ -132,9 +131,9 @@ object UserManager { } /** - * Checks for the existence of the [User] with the given [UID]. + * Checks for the existence of the [User] with the given [EvaluationId]. * - * @param id [UID] to check. + * @param id [EvaluationId] to check. * @return True if [User] exists, false otherwise. */ fun exists(id: UserId? = null, username: String? = null): Boolean = this.store.transactional(readonly = true) { @@ -148,9 +147,9 @@ object UserManager { } /** - * Returns the [User] for the given [UID] or null if [User] doesn't exist. + * Returns the [User] for the given [EvaluationId] or null if [User] doesn't exist. * - * @param id The [UID] of the [User] to fetch. + * @param id The [EvaluationId] of the [User] to fetch. * @return [User] or null */ fun get(id: UserId? = null, username: String? = null): User? = this.store.transactional(readonly = true) { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 779a95892..420e2e64c 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -1,16 +1,15 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.run.websocket.ClientMessage -import dev.dres.api.rest.types.run.websocket.ClientMessageType -import dev.dres.api.rest.types.run.websocket.ServerMessage -import dev.dres.api.rest.types.run.websocket.ServerMessageType -import dev.dres.data.model.UID +import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType +import dev.dres.api.rest.types.evaluation.websocket.ServerMessage +import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.admin.Role import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission @@ -42,10 +41,7 @@ import kotlin.math.max * @version 1.0.0 * @author Ralph Gasser */ -class InteractiveAsynchronousRunManager( - val run: InteractiveAsynchronousEvaluation -) : - InteractiveRunManager { +class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronousEvaluation): InteractiveRunManager { companion object { private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) @@ -53,19 +49,10 @@ class InteractiveAsynchronousRunManager( private const val MAXIMUM_ERROR_COUNT = 5 } - constructor(description: CompetitionDescription, name: String, runProperties: RunProperties) : this( - InteractiveAsynchronousEvaluation( - UID.EMPTY, - name, - description, - runProperties - ).apply { RunExecutor.runs.append(this) } - ) - override val runProperties: RunProperties get() = run.properties - /** Tracks the current [TaskDescription] per [TeamId]. */ + /** Tracks the current [TaskTemplate] per [TeamId]. */ private val statusMap: MutableMap = HashMap() /** A [Map] of all viewers, i.e., DRES cliets currently registered with this [InteractiveAsynchronousRunManager]. */ @@ -76,7 +63,7 @@ class InteractiveAsynchronousRunManager( /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = - ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) @@ -92,15 +79,15 @@ class InteractiveAsynchronousRunManager( private val updatables = mutableListOf() /** Run ID of this [InteractiveAsynchronousEvaluation]. */ - override val id: UID + override val id: EvaluationId get() = this.run.id /** Name of this [InteractiveAsynchronousEvaluation]. */ override val name: String get() = this.run.name - /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ - override val description: CompetitionDescription + /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template: EvaluationTemplate get() = this.run.description /** The global [RunManagerStatus] of this [InteractiveAsynchronousRunManager]. */ @@ -131,9 +118,9 @@ class InteractiveAsynchronousRunManager( this.updatables.add(this.messageQueueUpdatable) this.updatables.add(this.daoUpdatable) - this.description.teams.forEach { + this.template.teams.forEach { - val teamContext = RunActionContext(UID.EMPTY, it.uid, setOf(Role.ADMIN)) + val teamContext = RunActionContext(EvaluationId.EMPTY, it.uid, setOf(Role.ADMIN)) this.updatables.add( EndTaskUpdatable(this, teamContext) @@ -142,7 +129,7 @@ class InteractiveAsynchronousRunManager( } /* Initialize map and set all tasks pointers to the first task. */ - this.description.teams.forEach { + this.template.teams.forEach { this.statusMap[it.uid] = if (this.run.hasStarted) { RunManagerStatus.ACTIVE } else { @@ -231,31 +218,31 @@ class InteractiveAsynchronousRunManager( } /** - * Returns the currently active [TaskDescription] for the given team. Requires [RunManager.status] for the requesting team + * Returns the currently active [TaskTemplate] for the given team. Requires [RunManager.status] for the requesting team * to be [RunManagerStatus.ACTIVE]. * * @param context The [RunActionContext] used for the invocation. - * @return The [TaskDescription] for the given team. + * @return The [TaskTemplate] for the given team. */ - override fun currentTaskDescription(context: RunActionContext): TaskDescription { + override fun currentTaskDescription(context: RunActionContext): TaskTemplate { require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager." } return this.run.currentTaskDescription(context.teamId) } /** - * Prepares this [InteractiveAsynchronousEvaluation] for the execution of previous [TaskDescription] - * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting team + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of previous [TaskTemplate] + * as per order defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting team * to be [RunManagerStatus.ACTIVE]. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation * does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [TaskDescription] was moved, false otherwise. Usually happens if last [TaskDescription] has been reached. + * @return True if [TaskTemplate] was moved, false otherwise. Usually happens if last [TaskTemplate] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.description.tasks.indexOf(this.currentTaskDescription(context)) - 1 + val newIndex = this.template.tasks.indexOf(this.currentTaskDescription(context)) - 1 return try { this.goTo(context, newIndex) true @@ -265,18 +252,18 @@ class InteractiveAsynchronousRunManager( } /** - * Prepares this [InteractiveAsynchronousEvaluation] for the execution of next [TaskDescription] - * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of next [TaskTemplate] + * as per order defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting * team to be [RunManagerStatus.ACTIVE]. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [TaskDescription] was moved, false otherwise. Usually happens if last [TaskDescription] has been reached. + * @return True if [TaskTemplate] was moved, false otherwise. Usually happens if last [TaskTemplate] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.description.tasks.indexOf(this.currentTaskDescription(context)) + 1 + val newIndex = this.template.tasks.indexOf(this.currentTaskDescription(context)) + 1 return try { this.goTo(context, newIndex) true @@ -286,14 +273,14 @@ class InteractiveAsynchronousRunManager( } /** - * Prepares this [InteractiveAsynchronousEvaluation] for the execution of [TaskDescription] with the given [index] - * as per order defined in [CompetitionDescription.tasks]. Requires [RunManager.status] for the requesting + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of [TaskTemplate] with the given [index] + * as per order defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting * team to be [RunManagerStatus.ACTIVE]. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [TaskDescription] was moved, false otherwise. Usually happens if last [TaskDescription] has been reached. + * @return True if [TaskTemplate] was moved, false otherwise. Usually happens if last [TaskTemplate] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun goTo(context: RunActionContext, index: Int) = this.stateLock.write { @@ -301,7 +288,7 @@ class InteractiveAsynchronousRunManager( checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE)//, RunManagerStatus.TASK_ENDED) require(!teamHasRunningTask(context.teamId)) { "Cannot change task while task is active" } - val idx = (index + this.description.tasks.size) % this.description.tasks.size + val idx = (index + this.template.tasks.size) % this.template.tasks.size /* Update active task. */ @@ -461,12 +448,12 @@ class InteractiveAsynchronousRunManager( } /** - * Returns [AbstractInteractiveTask]s for a specific task [UID]. May be empty. + * Returns [AbstractInteractiveTask]s for a specific task [EvaluationId]. May be empty. * * @param context The [RunActionContext] used for the invocation. - * @param taskId The [UID] of the [AbstractInteractiveTask]. + * @param taskId The [EvaluationId] of the [AbstractInteractiveTask]. */ - override fun taskForId(context: RunActionContext, taskId: UID): AbstractInteractiveTask? = + override fun taskForId(context: RunActionContext, taskId: EvaluationId): AbstractInteractiveTask? = this.tasks(context).find { it.uid == taskId } /** @@ -565,14 +552,14 @@ class InteractiveAsynchronousRunManager( * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [UID] of the [Submission] to update. + * @param submissionId The [EvaluationId] of the [Submission] to update. * @param submissionStatus The new [SubmissionStatus] * * @return Whether the update was successful or not */ override fun updateSubmission( context: RunActionContext, - submissionId: UID, + submissionId: EvaluationId, submissionStatus: SubmissionStatus ): Boolean = this.stateLock.read { val found = this.allSubmissions.find { it.uid == submissionId } ?: return false @@ -703,7 +690,7 @@ class InteractiveAsynchronousRunManager( this.stateLock.write { task.end() //this.statusMap[teamId] = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, task.uid, task.description, LogEventSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, task.uid, task.template, LogEventSource.INTERNAL, null) } /* Mark DAO for update. */ @@ -722,7 +709,7 @@ class InteractiveAsynchronousRunManager( val task = this.run.currentTaskForTeam(teamId) ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") task.start() - AuditLogger.taskStart(this.id, task.uid, task.description, AuditLogSource.REST, null) + AuditLogger.taskStart(this.id, task.uid, task.template, AuditLogSource.REST, null) this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_START), teamId) } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 2e51e0b55..c8a89f48e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -1,8 +1,7 @@ package dev.dres.run -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission @@ -23,8 +22,8 @@ interface InteractiveRunManager : RunManager { val allSubmissions: List /** - * Prepares this [InteractiveRunManager] for the execution of previous [TaskDescription] as per order defined in - * [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of previous [TaskTemplate] as per order defined in + * [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws * an [IllegalStateException] if invocation does not match the current state. @@ -36,8 +35,8 @@ interface InteractiveRunManager : RunManager { fun previous(context: RunActionContext): Boolean /** - * Prepares this [InteractiveRunManager] for the execution of next [TaskDescription] as per order defined in - * [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of next [TaskTemplate] as per order defined in + * [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws * an [IllegalStateException] if invocation oes not match the current state. @@ -49,8 +48,8 @@ interface InteractiveRunManager : RunManager { fun next(context: RunActionContext): Boolean /** - * Prepares this [InteractiveRunManager] for the execution of the [TaskDescription] given by the index as per order - * defined in [CompetitionDescription.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of the [TaskTemplate] given by the index as per order + * defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws * an [IllegalStateException] if invocation does not match the current state. @@ -62,13 +61,13 @@ interface InteractiveRunManager : RunManager { fun goTo(context: RunActionContext, index: Int) /** - * Reference to the currently active [TaskDescription]. This is part of the [InteractiveRunManager]'s + * Reference to the currently active [TaskTemplate]. This is part of the [InteractiveRunManager]'s * navigational state. * * @param context The [RunActionContext] used for the invocation. - * @return [TaskDescription] + * @return [TaskTemplate] */ - fun currentTaskDescription(context: RunActionContext): TaskDescription + fun currentTaskDescription(context: RunActionContext): TaskTemplate /** * Starts the [currentTask] and thus moves the [RunManager.status] from @@ -146,9 +145,9 @@ interface InteractiveRunManager : RunManager { * first [AbstractInteractiveTask], index of 1 the second etc. * * @param context The [RunActionContext] used for the invocation. - * @param taskId The [UID] of the desired [AbstractInteractiveTask]. + * @param taskId The [EvaluationId] of the desired [AbstractInteractiveTask]. */ - fun taskForId(context: RunActionContext, taskId: UID): AbstractInteractiveTask? + fun taskForId(context: RunActionContext, taskId: EvaluationId): AbstractInteractiveTask? /** * List of [Submission]s for the current [AbstractInteractiveTask]. @@ -194,11 +193,11 @@ interface InteractiveRunManager : RunManager { * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [UID] of the [Submission] to update. + * @param submissionId The [EvaluationId] of the [Submission] to update. * @param submissionStatus The new [SubmissionStatus] * * @return Whether the update was successful or not * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun updateSubmission(context: RunActionContext, submissionId: UID, submissionStatus: SubmissionStatus): Boolean + fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: SubmissionStatus): Boolean } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 78722b94b..561f182ed 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -1,18 +1,14 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.run.websocket.ClientMessage -import dev.dres.api.rest.types.run.websocket.ClientMessageType -import dev.dres.api.rest.types.run.websocket.ServerMessage -import dev.dres.api.rest.types.run.websocket.ServerMessageType -import dev.dres.data.model.UID +import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType +import dev.dres.api.rest.types.evaluation.websocket.ServerMessage +import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.options.ConfiguredOption -import dev.dres.data.model.competition.options.Option -import dev.dres.data.model.competition.options.SimpleOption -import dev.dres.data.model.competition.options.SimpleOptionParameters +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunProperties @@ -37,12 +33,10 @@ import kotlin.math.max * An implementation of [RunManager] aimed at distributed execution having a single DRES Server instance and multiple * viewers connected via WebSocket. Before starting a [InteractiveSynchronousEvaluation.Task], all viewer instances are synchronized. * - * @version 2.1.0 + * @version 3.0.0 * @author Ralph Gasser */ -class InteractiveSynchronousRunManager( - val run: InteractiveSynchronousEvaluation -) : InteractiveRunManager { +class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEvaluation) : InteractiveRunManager { private val VIEWER_TIME_OUT = 30L //TODO make configurable @@ -53,28 +47,19 @@ class InteractiveSynchronousRunManager( /** Number of consecutive errors which have to occur within the main execution loop before it tries to gracefully terminate */ private val maxErrorCount = 5 - /** - * Alternative constructor from existing [InteractiveSynchronousEvaluation]. - */ - constructor(description: CompetitionDescription, name: String, runProperties: RunProperties) : this( - InteractiveSynchronousEvaluation(UID.EMPTY, name, description, runProperties).apply { - RunExecutor.runs.append(this) - } - ) - override val runProperties: RunProperties get() = run.properties /** Run ID of this [InteractiveSynchronousRunManager]. */ - override val id: UID + override val id: EvaluationId get() = this.run.id /** Name of this [InteractiveSynchronousRunManager]. */ override val name: String get() = this.run.name - /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ - override val description: CompetitionDescription + /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template: EvaluationTemplate get() = this.run.description /** The list of all [Submission]s tracked ever received by this [InteractiveSynchronousRunManager]. */ @@ -112,7 +97,7 @@ class InteractiveSynchronousRunManager( /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = - ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) @@ -207,17 +192,17 @@ class InteractiveSynchronousRunManager( LOGGER.info("SynchronousRunManager ${this.id} terminated") } - override fun currentTaskDescription(context: RunActionContext): TaskDescription = this.stateLock.write { + override fun currentTaskDescription(context: RunActionContext): TaskTemplate = this.stateLock.write { checkStatus( RunManagerStatus.CREATED, RunManagerStatus.ACTIVE/*, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED*/ ) - this.run.currentTaskDescription + this.run.currentTaskTemplate } override fun previous(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.description.tasks.indexOf(this.run.currentTaskDescription) - 1 + val newIndex = this.template.tasks.indexOf(this.run.currentTaskTemplate) - 1 return try { this.goTo(context, newIndex) true @@ -228,7 +213,7 @@ class InteractiveSynchronousRunManager( override fun next(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.description.tasks.indexOf(this.run.currentTaskDescription) + 1 + val newIndex = this.template.tasks.indexOf(this.run.currentTaskTemplate) + 1 return try { this.goTo(context, newIndex) true @@ -240,7 +225,7 @@ class InteractiveSynchronousRunManager( override fun goTo(context: RunActionContext, index: Int) { checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() - if (index >= 0 && index < this.description.tasks.size) { + if (index >= 0 && index < this.template.tasks.size) { /* Update active task. */ this.run.goTo(index) @@ -269,7 +254,7 @@ class InteractiveSynchronousRunManager( val currentTaskDescription = this.currentTaskDescription(context) /* Check for duplicate task runs */ - if (!runProperties.allowRepeatedTasks && this.run.tasks.any { it.description.id == currentTaskDescription.id }) { + if (!runProperties.allowRepeatedTasks && this.run.tasks.any { it.template.id == currentTaskDescription.id }) { throw IllegalStateException("Task '${currentTaskDescription.name}' has already been used") } @@ -290,7 +275,7 @@ class InteractiveSynchronousRunManager( /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_PREPARE)) - LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.run.currentTaskDescription}") + LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.run.currentTaskTemplate}") } override fun abortTask(context: RunActionContext) = this.stateLock.write { @@ -312,7 +297,7 @@ class InteractiveSynchronousRunManager( /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END)) - LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.run.currentTaskDescription}") + LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.run.currentTaskTemplate}") } /** List of [InteractiveSynchronousEvaluation.Task] for this [InteractiveSynchronousRunManager]. */ @@ -342,11 +327,11 @@ class InteractiveSynchronousRunManager( } /** - * Returns [InteractiveSynchronousEvaluation.Task]s for a specific task [UID]. May be empty. + * Returns [InteractiveSynchronousEvaluation.Task]s for a specific task [EvaluationId]. May be empty. * - * @param taskId The [UID] of the [InteractiveSynchronousEvaluation.Task]. + * @param taskId The [EvaluationId] of the [InteractiveSynchronousEvaluation.Task]. */ - override fun taskForId(context: RunActionContext, taskId: UID): InteractiveSynchronousEvaluation.Task? = + override fun taskForId(context: RunActionContext, taskId: EvaluationId): InteractiveSynchronousEvaluation.Task? = this.run.tasks.find { it.uid == taskId } /** @@ -495,7 +480,7 @@ class InteractiveSynchronousRunManager( task.postSubmission(sub) /** Checks for the presence of the [SimpleOption.PROLONG_ON_SUBMISSION] and applies it. */ - val option = task.description.taskType.options.find { it.option == SimpleOption.PROLONG_ON_SUBMISSION } + val option = task.template.taskType.options.find { it.option == SimpleOption.PROLONG_ON_SUBMISSION } if (option != null) { this.prolongOnSubmit(context, option, sub) } @@ -521,13 +506,13 @@ class InteractiveSynchronousRunManager( * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [UID] of the [Submission] to update. + * @param submissionId The [EvaluationId] of the [Submission] to update. * @param submissionStatus The new [SubmissionStatus] * @return True on success, false otherwise. */ override fun updateSubmission( context: RunActionContext, - submissionId: UID, + submissionId: EvaluationId, submissionStatus: SubmissionStatus ): Boolean = this.stateLock.read { /* Sanity check. */ @@ -633,7 +618,7 @@ class InteractiveSynchronousRunManager( this.stateLock.write { this.run.currentTask!!.start() //this.status = RunManagerStatus.RUNNING_TASK - AuditLogger.taskStart(this.id, this.run.currentTask!!.uid, this.run.currentTaskDescription, AuditLogSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.run.currentTask!!.uid, this.run.currentTaskTemplate, AuditLogSource.INTERNAL, null) } /* Mark DAO for update. */ diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 74fd69bb8..31504249c 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -1,15 +1,11 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.run.websocket.ClientMessage -import dev.dres.api.rest.types.run.websocket.ClientMessageType -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.TeamId -import dev.dres.data.model.run.AbstractNonInteractiveTask -import dev.dres.data.model.run.NonInteractiveEvaluation -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.RunProperties +import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType +import dev.dres.data.model.run.* +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.batch.SubmissionBatch import dev.dres.run.score.scoreboard.Scoreboard @@ -38,19 +34,19 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) /** Run ID of this [InteractiveSynchronousRunManager]. */ - override val id: UID + override val id: EvaluationId get() = this.run.id /** Name of this [InteractiveSynchronousRunManager]. */ override val name: String get() = this.run.name - /** The [CompetitionDescription] executed by this [InteractiveSynchronousRunManager]. */ - override val description: CompetitionDescription + /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template: EvaluationTemplate get() = this.run.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.description.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) //TODO requires some changes + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) //TODO requires some changes override val scoreboards: List get() = this.scoreboardsUpdatable.scoreboards @@ -61,11 +57,12 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { } else { RunManagerStatus.CREATED } - get() = this.stateLock.read { - return field - } - private set + get() = this.stateLock.read { + return field + } + private set + /** */ override val judgementValidators: List get() = this.run.tasks.map { it.validator }.filterIsInstance(JudgementValidator::class.java) diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 1ca0dfb66..6a33fa401 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -3,11 +3,11 @@ package dev.dres.run import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.run.websocket.ClientMessage -import dev.dres.api.rest.types.run.websocket.ClientMessageType -import dev.dres.api.rest.types.run.websocket.ServerMessage -import dev.dres.api.rest.types.run.websocket.ServerMessageType -import dev.dres.data.model.competition.team.TeamId +import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType +import dev.dres.api.rest.types.evaluation.websocket.ServerMessage +import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation import dev.dres.data.model.run.InteractiveSynchronousEvaluation @@ -81,33 +81,39 @@ object RunExecutor : Consumer { } /** + * Schedules a new [EvaluationRun] with this [RunExecutor]. * + * @param evaluation [EvaluationRun] to execute. */ - fun schedule(competition: EvaluationRun) { - val run = when(competition) { - is InteractiveSynchronousEvaluation -> { - competition.tasks.forEach { t -> - t.submissions.forEach { s -> s.task = t } - } - InteractiveSynchronousRunManager(competition) - } - is NonInteractiveEvaluation -> { - NonInteractiveRunManager(competition) - } - is InteractiveAsynchronousEvaluation -> { - competition.tasks.forEach { t -> - t.submissions.forEach { s -> s.task = t } - if (!t.hasEnded) { - t.end() //abort tasks that were active during last save - } - } - InteractiveAsynchronousRunManager(competition) - } - else -> throw NotImplementedError("No matching run manager found for $competition") + fun schedule(evaluation: EvaluationRun) { + val run = when(evaluation) { + is InteractiveSynchronousEvaluation -> InteractiveSynchronousRunManager(evaluation) + is InteractiveAsynchronousEvaluation -> InteractiveAsynchronousRunManager(evaluation) + is NonInteractiveEvaluation -> NonInteractiveRunManager(evaluation) + else -> throw NotImplementedError("No matching run manager found for $evaluation") } this.schedule(run) } + /** + * Schedules a new [RunManager] with this [RunExecutor]. + * + * @param manager [RunManager] to execute. + */ + fun schedule(manager: RunManager) = this.runManagerLock.write { + if (this.runManagers.containsKey(manager.id)) { + throw IllegalArgumentException("This RunExecutor already runs a RunManager with the given ID ${manager.id}. The same RunManager cannot be executed twice!") + } + + /* Register [RunManager] with AccessManager. */ + AccessManager.registerRunManager(manager) + + /* Setup all the required data structures. */ + this.runManagers[manager.id] = manager + this.observingClients[manager.id] = HashSet() + this.results[this.executor.submit(manager)] = manager.id /* Register future for cleanup thread. */ + } + /** A thread that cleans after [RunManager] have finished. */ private val cleanerThread = Thread { while (!this@RunExecutor.executor.isShutdown) { @@ -215,27 +221,6 @@ object RunExecutor : Consumer { return this.runManagers[evaluationId] } - /** - * Schedules a new [RunManager] with this [RunExecutor]. - * - * @param manager [RunManager] to execute. - */ - fun schedule(manager: RunManager) = this.runManagerLock.write { - if (this.runManagers.containsKey(manager.id)) { - throw IllegalArgumentException("This RunExecutor already runs a RunManager with the given ID ${manager.id}. The same RunManager cannot be executed twice!") - } - - /* Register [RunManager] with AccessManager. */ - this.store.transactional(true) { - AccessManager.registerRunManager(manager) - } - - /* Setup all the required data structures. */ - this.runManagers[manager.id] = manager - this.observingClients[manager.id] = HashSet() - this.results[this.executor.submit(manager)] = manager.id /* Register future for cleanup thread. */ - } - /** * Broadcasts a [ServerMessage] to all clients currently connected. * @@ -274,7 +259,7 @@ object RunExecutor : Consumer { val manager = managerForId(evaluationId) if (manager != null) { val teamMembers = this.store.transactional(true) { - manager.description.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() + manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() } this.runManagerLock.read { this.connectedClients.filter { diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index ae457e643..5b635d258 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -1,8 +1,8 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.run.websocket.ClientMessage -import dev.dres.data.model.competition.CompetitionDescription +import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext @@ -12,7 +12,7 @@ import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator /** - * A managing class for concrete executions of [CompetitionDescription], i.e. [InteractiveSynchronousEvaluation]s. + * A managing class for concrete executions of [EvaluationTemplate], i.e. [InteractiveSynchronousEvaluation]s. * * @see InteractiveSynchronousEvaluation * @@ -26,8 +26,8 @@ interface RunManager : Runnable { /** A name for identifying this [RunManager]. */ val name: String - /** The [CompetitionDescription] that is executed / run by this [RunManager]. */ - val description: CompetitionDescription + /** The [EvaluationTemplate] that is executed / run by this [RunManager]. */ + val template: EvaluationTemplate /** List of [Scoreboard]s for this [RunManager]. */ val scoreboards: List @@ -38,6 +38,7 @@ interface RunManager : Runnable { /** [JudgementValidator]s for all tasks that use them */ val judgementValidators: List + /** [JudgementValidator]s for all tasks that use them */ val runProperties: RunProperties /** @@ -64,6 +65,12 @@ interface RunManager : Runnable { */ fun end(context: RunActionContext) + /** + * Updates the [RunProperties] for this [RunManager]. + * + * @param properties The new [RunProperties] + */ + fun updateProperties(properties: RunProperties) /** * Returns the number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager]. diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index f6ceb26a9..ab66ab8a6 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -1,14 +1,13 @@ package dev.dres.run.audit import dev.dres.api.rest.handler.users.SessionId -import dev.dres.data.model.UID import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskDescriptionId +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.TaskDescriptionId import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus @@ -36,11 +35,11 @@ object AuditLogger { /** * Logs the start of a DRES competition. * - * @param description The [CompetitionDescription]. + * @param description The [EvaluationTemplate]. * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionStart(competitionId: CompetitionId, description: CompetitionDescription, api: AuditLogSource, session: SessionId?) { + fun competitionStart(competitionId: CompetitionId, description: EvaluationTemplate, api: AuditLogSource, session: SessionId?) { this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.COMPETITION_START @@ -56,7 +55,7 @@ object AuditLogger { /** * Logs the end of a DRES competition. * - * @param competitionId [UID] that identifies the competition + * @param competitionId [EvaluationId] that identifies the competition * @param api The [AuditLogSource] * @param session The identifier of the user session. */ @@ -76,13 +75,13 @@ object AuditLogger { /** * Logs the start of a DRES task. * - * @param competitionId [UID] that identifies the competition - * @param taskId [UID] that identifies the task - * @param description The [TaskDescription]. + * @param competitionId [EvaluationId] that identifies the competition + * @param taskId [EvaluationId] that identifies the task + * @param description The [TaskTemplate]. * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(competitionId: CompetitionId, taskId: TaskId, description: TaskDescription, api: AuditLogSource, session: SessionId?) { + fun taskStart(competitionId: CompetitionId, taskId: TaskId, description: TaskTemplate, api: AuditLogSource, session: SessionId?) { this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.TASK_START @@ -99,8 +98,8 @@ object AuditLogger { /** * Logs the start of a DRES task. * - * @param competitionId [UID] that identifies the competition - * @param taskId [UID] that identifies the task + * @param competitionId [EvaluationId] that identifies the competition + * @param taskId [EvaluationId] that identifies the task * @param modification Description of the modification. * @param api The [AuditLogSource] * @param session The identifier of the user session. @@ -122,8 +121,8 @@ object AuditLogger { /** * Logs the end of a DRES task. * - * @param competitionId [UID] that identifies the competition - * @param taskId [UID] that identifies the task + * @param competitionId [EvaluationId] that identifies the competition + * @param taskId [EvaluationId] that identifies the task * @param api The [AuditLogSource] * @param session The identifier of the user session. */ @@ -235,14 +234,14 @@ object AuditLogger { /** * Logs a submission override to DRES. * - * @param competitionId [UID] that identifies the competition + * @param competitionId [EvaluationId] that identifies the competition * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system * @param verdict The [SubmissionStatus] submitted by the judge. * @param api The [AuditLogSource] * @param sessionId The identifier of the user session. */ - fun judgement(competitionId: UID, validator: JudgementValidator, token: String, verdict: SubmissionStatus, api: AuditLogSource, sessionId: SessionId?) { + fun judgement(competitionId: EvaluationId, validator: JudgementValidator, token: String, verdict: SubmissionStatus, api: AuditLogSource, sessionId: SessionId?) { AuditLogEntry.new { this.type = AuditLogType.JUDGEMENT this.source = api @@ -258,7 +257,7 @@ object AuditLogger { /** * Logs a user user login event. * - * @param userId [UID] of the user who logged out. + * @param userId [EvaluationId] of the user who logged out. * @param api The [AuditLogSource] * @param sessionId The [SessionId] */ @@ -277,7 +276,7 @@ object AuditLogger { /** * Logs a user logout event. * - * @param userId [UID] of the user who logged out. + * @param userId [EvaluationId] of the user who logged out. * @param api The [AuditLogSource] * @param sessionId The [SessionId] */ diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index 9cacc5469..660c83848 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -1,9 +1,8 @@ package dev.dres.run.eventstream import com.fasterxml.jackson.annotation.JsonTypeInfo -import dev.dres.data.model.UID -import dev.dres.data.model.competition.CompetitionDescription -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog import dev.dres.data.model.submissions.Submission @@ -12,13 +11,13 @@ import dev.dres.data.model.submissions.Submission sealed class StreamEvent(var timeStamp : Long = System.currentTimeMillis(), var session: String? = null) -class TaskStartEvent(val runId: UID, val taskId: UID, val taskDescription: TaskDescription) : StreamEvent() -class TaskEndEvent(val runId: UID, val taskId: UID) : StreamEvent() -class RunStartEvent(val runId: UID, val description: CompetitionDescription) : StreamEvent() -class RunEndEvent(val runId: UID) : StreamEvent() +class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val taskTemplate: TaskTemplate) : StreamEvent() +class TaskEndEvent(val runId: EvaluationId, val taskId: EvaluationId) : StreamEvent() +class RunStartEvent(val runId: EvaluationId, val description: EvaluationTemplate) : StreamEvent() +class RunEndEvent(val runId: EvaluationId) : StreamEvent() -class SubmissionEvent(session: String, val runId: UID, val taskId: UID?, val submission : Submission) : StreamEvent(session = session) -class QueryEventLogEvent(session: String, val runId: UID, val queryEventLog: QueryEventLog) : StreamEvent(session = session) -class QueryResultLogEvent(session: String, val runId: UID, val queryResultLog: QueryResultLog) : StreamEvent(session = session) -class InvalidRequestEvent(session: String, val runId: UID, val requestData: String) : StreamEvent(session = session) \ No newline at end of file +class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : Submission) : StreamEvent(session = session) +class QueryEventLogEvent(session: String, val runId: EvaluationId, val queryEventLog: QueryEventLog) : StreamEvent(session = session) +class QueryResultLogEvent(session: String, val runId: EvaluationId, val queryResultLog: QueryResultLog) : StreamEvent(session = session) +class InvalidRequestEvent(session: String, val runId: EvaluationId, val requestData: String) : StreamEvent(session = session) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt index e66c0f4fc..5756d6422 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt @@ -1,13 +1,12 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaItemSegmentList import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskDescriptionTarget +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.TaskDescriptionTarget import dev.dres.run.eventstream.QueryResultLogEvent import dev.dres.run.eventstream.StreamEvent import dev.dres.run.eventstream.StreamEventHandler @@ -16,12 +15,12 @@ import dev.dres.utilities.TimeUtil import java.io.File import java.io.PrintWriter -class ResultLogStatisticsHandler(private val segmentIndex: DaoIndexer) : StreamEventHandler { +class ResultLogStatisticsHandler(private val segmentIndex: DaoIndexer) : StreamEventHandler { private val writer = PrintWriter(File("statistics/result_log_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - private val lastActiveTask = mutableMapOf() - private val lastActiveTargets = mutableMapOf>>() + private val lastActiveTask = mutableMapOf() + private val lastActiveTargets = mutableMapOf>>() init { @@ -32,12 +31,12 @@ class ResultLogStatisticsHandler(private val segmentIndex: DaoIndexer { - lastActiveTask[event.runId] = event.taskDescription - lastActiveTargets[event.runId] = when(event.taskDescription.target) { + lastActiveTask[event.runId] = event.taskTemplate + lastActiveTargets[event.runId] = when(event.taskTemplate.target) { is TaskDescriptionTarget.JudgementTaskDescriptionTarget, is TaskDescriptionTarget.VoteTaskDescriptionTarget, -> return //no analysis possible - is TaskDescriptionTarget.MediaItemTarget -> listOf(event.taskDescription.target.item to null) - is TaskDescriptionTarget.VideoSegmentTarget -> listOf(event.taskDescription.target.item to event.taskDescription.target.temporalRange) - is TaskDescriptionTarget.MultipleMediaItemTarget -> event.taskDescription.target.items.map { it to null } + is TaskDescriptionTarget.MediaItemTarget -> listOf(event.taskTemplate.target.item to null) + is TaskDescriptionTarget.VideoSegmentTarget -> listOf(event.taskTemplate.target.item to event.taskTemplate.target.temporalRange) + is TaskDescriptionTarget.MultipleMediaItemTarget -> event.taskTemplate.target.items.map { it to null } is TaskDescriptionTarget.TextTaskDescriptionTarget -> return //TODO maybe some analysis would be possible, needs restructuring } } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index dd7bedc68..922eb1c79 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -1,6 +1,5 @@ package dev.dres.run.eventstream.handlers -import dev.dres.data.model.UID import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.eventstream.* @@ -12,9 +11,9 @@ class SubmissionStatisticsHandler : StreamEventHandler { private val writer = PrintWriter(File("statistics/submission_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - private val submissionTaskMap = mutableMapOf>() - private val taskStartMap = mutableMapOf() - private val taskNameMap = mutableMapOf() + private val submissionTaskMap = mutableMapOf>() + private val taskStartMap = mutableMapOf() + private val taskNameMap = mutableMapOf() private val logger = LoggerFactory.getLogger(this.javaClass) @@ -27,7 +26,7 @@ class SubmissionStatisticsHandler : StreamEventHandler { is TaskStartEvent -> { submissionTaskMap[event.taskId] = mutableListOf() taskStartMap[event.taskId] = event.timeStamp - taskNameMap[event.taskId] = event.taskDescription.name + taskNameMap[event.taskId] = event.taskTemplate.name } is SubmissionEvent -> if (event.taskId != null && taskStartMap.containsKey(event.taskId)){ submissionTaskMap[event.taskId]!!.add(event.submission) diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 871cbdc8d..bf46bdecb 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -1,7 +1,6 @@ package dev.dres.run.eventstream.handlers -import dev.dres.data.model.UID -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* import dev.dres.run.score.TaskContext @@ -15,9 +14,9 @@ class TeamCombinationScoreHandler : StreamEventHandler { private val writer = PrintWriter(File("statistics/combined_team_scores_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - private val tasks = mutableMapOf() - private val taskStartMap = mutableMapOf() - private val submissionTaskMap = mutableMapOf>() + private val tasks = mutableMapOf() + private val taskStartMap = mutableMapOf() + private val submissionTaskMap = mutableMapOf>() init { writer.println("task,team1,team2,score") @@ -27,7 +26,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { when(event){ is TaskStartEvent -> { - tasks[event.taskId] = event.taskDescription + tasks[event.taskId] = event.taskTemplate taskStartMap[event.taskId] = event.timeStamp submissionTaskMap[event.taskId] = mutableListOf() } @@ -50,7 +49,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { val combinations = teams.mapIndexed { firstIndex, uidA -> teams.mapIndexed {secondIndex, uidB -> if (firstIndex > secondIndex) (uidA to uidB) else null} - }.flatten().filterNotNull().map { UID() to it }.toMap() + }.flatten().filterNotNull().map { EvaluationId() to it }.toMap() val combinedSubmissions = submissions.flatMap { submission -> combinations.map { diff --git a/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt index b1cdbf8f8..f5d87fb41 100644 --- a/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt +++ b/backend/src/main/kotlin/dev/dres/run/exceptions/IllegalTeamIdException.kt @@ -1,6 +1,6 @@ package dev.dres.run.exceptions -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager /** diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt index 30436c512..7547783fa 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt @@ -1,5 +1,5 @@ package dev.dres.run.score -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId data class TaskContext(val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt index 81258c60f..87c3cd5d9 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission import dev.dres.run.score.TaskContext diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt index c309b5174..7ba4d9697 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt @@ -1,8 +1,7 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.UID -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.TaskRun /** Type alias for a */ @@ -19,7 +18,7 @@ interface TaskScorer { /** * Generates and returns the current scores for all teams in the relevant Task. * - * @return Map of team [UID] to team score + * @return Map of team [EvaluationId] to team score */ fun scores(): List } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt index 2b38b9da9..9fe2929cf 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId /** * A [TaskScorer] that aggregates scores per [TeamId]. diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 4b17e75d6..9faa46f03 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,15 +1,15 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.competition.task.TaskDescription -import dev.dres.data.model.competition.task.TaskDescriptionId -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.TaskDescriptionId +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.run.score.interfaces.TaskScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max -class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (TaskDescription) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 100.0) : Scoreboard { +class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (TaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 100.0) : Scoreboard { private val scorePerTaskMap = ConcurrentHashMap>() @@ -48,7 +48,7 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri override fun update(runs: List) { update( runs - .filter { taskFilter(it.description) && (it.started != null) } + .filter { taskFilter(it.template) && (it.started != null) } .map { it.uid to it.scorer }.toMap() ) } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index e149db5ee..e03cbdbb4 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -1,8 +1,7 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.UID -import dev.dres.data.model.competition.team.Team -import dev.dres.data.model.competition.TeamId +import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.interfaces.TaskScorer @@ -33,9 +32,9 @@ interface Scoreboard { fun scores(): List /** - * Retrieves and returns the score of the given [Team] [UID] + * Retrieves and returns the score of the given [Team] [EvaluationId] * - * @param teamId The [Team]'s [UID]. + * @param teamId The [Team]'s [EvaluationId]. * @return The score for the given [Team]. */ fun score(teamId: TeamId): Double diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index 2b159bd4b..a16283704 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -1,7 +1,7 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.competition.task.TaskDescriptionId -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.task.TaskDescriptionId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.run.score.interfaces.TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index c532ea7ac..9364246aa 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.media.MediaType import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index c6be4f665..8aa007b06 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.data.model.submissions.aspects.StatusAspect diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 0382d01db..4b6f2a89e 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.competition.team.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.TaskContext diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 2570582f4..72afe836f 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -1,6 +1,6 @@ package dev.dres.run.updatables -import dev.dres.data.model.competition.options.SubmissionFilterOption +import dev.dres.data.model.template.options.SubmissionFilterOption import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.InteractiveAsynchronousRunManager @@ -21,7 +21,7 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte val taskRun = this.run.currentTask(this.context) if (taskRun != null) { val limitingFilter = - taskRun.description.taskType.filter.find { it.option == SubmissionFilterOption.LIMIT_CORRECT_PER_TEAM } + taskRun.template.taskType.filter.find { it.option == SubmissionFilterOption.LIMIT_CORRECT_PER_TEAM } ?: return val limit = limitingFilter.getAsInt("limit") ?: 1 if (this.run.timeLeft(context) > 0) { @@ -39,7 +39,7 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte } else { /* Determine of all teams have submitted . */ - val allDone = this.run.description.teams.all { team -> + val allDone = this.run.template.teams.all { team -> this.run.submissions(this.context) .count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt index bbe02ed4d..48526622a 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt @@ -1,7 +1,7 @@ package dev.dres.run.updatables -import dev.dres.api.rest.types.run.websocket.ServerMessage -import dev.dres.data.model.competition.TeamId +import dev.dres.api.rest.types.evaluation.websocket.ServerMessage +import dev.dres.data.model.template.TeamId import dev.dres.run.RunExecutor import dev.dres.run.RunManagerStatus import dev.dres.utilities.extensions.UID diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index bbd8cd254..175e6e10c 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -1,8 +1,7 @@ package dev.dres.run.updatables -import dev.dres.api.rest.types.run.websocket.ServerMessage -import dev.dres.api.rest.types.run.websocket.ServerMessageType -import dev.dres.data.model.UID +import dev.dres.api.rest.types.evaluation.websocket.ServerMessage +import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.submissions.Submission @@ -20,7 +19,7 @@ import java.util.* * @author Ralph Gasser * @version 1.2.0 */ -class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdatable, val messageQueueUpdatable: MessageQueueUpdatable, val daoUpdatable: DAOUpdatable<*>): Updatable { +class ScoresUpdatable(val runId: EvaluationId, val scoreboardsUpdatable: ScoreboardsUpdatable, val messageQueueUpdatable: MessageQueueUpdatable, val daoUpdatable: DAOUpdatable<*>): Updatable { companion object { val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE/*, RunManagerStatus.RUNNING_TASK, RunManagerStatus.PREPARING_TASK, RunManagerStatus.TASK_ENDED*/) @@ -56,7 +55,7 @@ class ScoresUpdatable(val runId: UID, val scoreboardsUpdatable: ScoreboardsUpdat scorersToUpdate.forEach { val task = it.first if (it.first.started != null) { - val scores = it.second.computeScores(task.submissions, TaskContext(task.competition.description.teams.map { t -> t.uid }, task.started, task.description.duration, task.ended)) + val scores = it.second.computeScores(task.submissions, TaskContext(task.competition.description.teams.map { t -> t.uid }, task.started, task.template.duration, task.ended)) it.first.updateTeamAggregation(scores) } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt index dad8cdb25..bf570f85c 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt @@ -1,6 +1,6 @@ package dev.dres.run.updatables -import dev.dres.api.rest.types.run.RunState +import dev.dres.api.rest.types.evaluation.RunState import dev.dres.run.RunManagerStatus /** diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 09941de11..878d81eb5 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus @@ -18,7 +18,7 @@ class TemporalContainmentSubmissionValidator(private val task: MediaSegment) : S /** * Validates a [Submission] based on the target segment and the temporal overlap of the - * [Submission] with the [TaskDescription]. + * [Submission] with the [TaskTemplate]. * * @param submission The [Submission] to validate. */ diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 6d0c2fd83..8d464a1be 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -1,6 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.competition.task.TaskDescription +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.Submission @@ -24,7 +24,7 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed /** * Validates a [Submission] based on the target segment and the temporal overlap of the - * [Submission] with the [TaskDescription]. + * [Submission] with the [TaskTemplate]. * * @param submission The [Submission] to validate. */ diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt index 03878621f..076b245ec 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/StringExtensions.kt @@ -1,18 +1,17 @@ package dev.dres.utilities.extensions -import dev.dres.data.model.UID import java.util.regex.Matcher /** - * Converts a [String] to a [UID]. + * Converts a [String] to a [EvaluationId]. * - * @return [UID] + * @return [EvaluationId] */ fun String.cleanPathString() = this.trim().replaceFirst("^~", Matcher.quoteReplacement(System.getProperty("user.home"))) /** - * Converts a [String] to a [UID]. + * Converts a [String] to a [EvaluationId]. * - * @return [UID] + * @return [EvaluationId] */ fun String.toPathParamKey() = "{$this}" \ No newline at end of file diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 7459abb80..6bac94782 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -1,6 +1,5 @@ package dres.run.score.scorer -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus @@ -14,29 +13,29 @@ import org.junit.jupiter.api.Test class AvsTaskScorerTest { private lateinit var scorer: AvsTaskScorer - private val teams = listOf(UID(), UID(), UID()) //3 random team ids + private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids private val defaultTaskDuration = 5 * 60L private val dummyImageItems: List private val dummyVideoItems: List init { - val collectionId = UID() + val collectionId = EvaluationId() dummyImageItems = listOf( - MediaItem.ImageItem(UID(), "Image 1", "images/1", collectionId), - MediaItem.ImageItem(UID(), "Image 2", "images/2", collectionId), - MediaItem.ImageItem(UID(), "Image 3", "images/3", collectionId), - MediaItem.ImageItem(UID(), "Image 4", "images/4", collectionId), - MediaItem.ImageItem(UID(), "Image 5", "images/5", collectionId), - MediaItem.ImageItem(UID(), "Image 6", "images/6", collectionId) + MediaItem.ImageItem(EvaluationId(), "Image 1", "images/1", collectionId), + MediaItem.ImageItem(EvaluationId(), "Image 2", "images/2", collectionId), + MediaItem.ImageItem(EvaluationId(), "Image 3", "images/3", collectionId), + MediaItem.ImageItem(EvaluationId(), "Image 4", "images/4", collectionId), + MediaItem.ImageItem(EvaluationId(), "Image 5", "images/5", collectionId), + MediaItem.ImageItem(EvaluationId(), "Image 6", "images/6", collectionId) ) dummyVideoItems = listOf( - MediaItem.VideoItem(UID(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), - MediaItem.VideoItem(UID(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), - MediaItem.VideoItem(UID(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), - MediaItem.VideoItem(UID(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), - MediaItem.VideoItem(UID(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) + MediaItem.VideoItem(EvaluationId(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), + MediaItem.VideoItem(EvaluationId(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), + MediaItem.VideoItem(EvaluationId(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), + MediaItem.VideoItem(EvaluationId(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), + MediaItem.VideoItem(EvaluationId(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) ) } @@ -58,9 +57,9 @@ class AvsTaskScorerTest { fun allWrongSameImage() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -72,9 +71,9 @@ class AvsTaskScorerTest { fun allWrongDifferentImages() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -86,9 +85,9 @@ class AvsTaskScorerTest { fun allSameImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -100,9 +99,9 @@ class AvsTaskScorerTest { fun allDifferentImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) @@ -121,20 +120,20 @@ class AvsTaskScorerTest { */ //3 out of 4 correct - Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[0], UID(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[0], UID(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[0], UID(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, //1 out of 3 correct - Submission.Item(teams[1], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], UID(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, //1 out of 2 correct - Submission.Item(teams[2], UID(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG } ) @@ -173,9 +172,9 @@ class AvsTaskScorerTest { fun allSameVideoSameSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -188,9 +187,9 @@ class AvsTaskScorerTest { fun allSameVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) @@ -203,9 +202,9 @@ class AvsTaskScorerTest { fun allDifferentVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) @@ -217,17 +216,17 @@ class AvsTaskScorerTest { fun allSameVideoOverlappingSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -239,21 +238,21 @@ class AvsTaskScorerTest { fun allSameVideoOverlappingSegmentSomeCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[1], UID(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG } ) @@ -271,40 +270,40 @@ class AvsTaskScorerTest { val submissions = listOf( //team 1 //gets merged to one - Submission.Temporal(teams[0], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, //plus 2 independent correct ones - Submission.Temporal(teams[0], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], UID(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = SubmissionStatus.CORRECT }, //and an incorrect one directly next to it - Submission.Temporal(teams[0], UID(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = SubmissionStatus.WRONG }, //c = 5, q(c) = 3, i = 1 //team 2 //the center part is missing, so it's counted as two in the quantization - Submission.Temporal(teams[1], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, //another correct one, same as team 1 - Submission.Temporal(teams[1], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, //and two wrong ones - Submission.Temporal(teams[1], UID(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[1], UID(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, //c = 3, q(c) = 3, i = 2 //team 3 //same as team 2, but with all 3 segments of the 1st video, same as team 1 - Submission.Temporal(teams[2], UID(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], UID(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, //another correct one, same as team 1 - Submission.Temporal(teams[2], UID(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, //and two wrong ones - Submission.Temporal(teams[2], UID(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[2], UID(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, //c = 4, q(c) = 2, i = 2 diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index fef671ae4..b5ab61dc2 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -1,6 +1,5 @@ package dres.run.score.scorer -import dev.dres.data.model.UID import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionStatus @@ -13,8 +12,8 @@ import org.junit.jupiter.api.Test class KisTaskScorerTest { private lateinit var scorer: KisTaskScorer - private val teams = listOf(UID(), UID(), UID()) //3 random team ids - private val dummyImageItems = listOf(MediaItem.ImageItem(UID(), "image 1", "images/1", UID())) + private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids + private val dummyImageItems = listOf(MediaItem.ImageItem(EvaluationId(), "image 1", "images/1", EvaluationId())) private val defaultTaskDuration = 5 * 60L private val maxPointsPerTask = 100.0 private val maxPointsAtTaskEnd = 50.0 @@ -37,9 +36,9 @@ class KisTaskScorerTest { fun allWrong() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], UID(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], UID(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -51,7 +50,7 @@ class KisTaskScorerTest { fun immediatelyRight() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -63,7 +62,7 @@ class KisTaskScorerTest { fun rightAtTheEnd() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) @@ -73,7 +72,7 @@ class KisTaskScorerTest { fun rightInTheMiddle() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) @@ -85,19 +84,19 @@ class KisTaskScorerTest { val submissions = listOf( //incorrect submissions - Submission.Item(teams[0], UID(), taskStartTime + 1, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], UID(), taskStartTime + 2, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], UID(), taskStartTime + 3, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], UID(), taskStartTime + 4, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], UID(), taskStartTime + 5, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], UID(), taskStartTime + 6, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, //correct submissions at 1/2 the task time - Submission.Item(teams[0], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], UID(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) From 61e015878d179b0cba7df485f13b89005334b474 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 7 Nov 2022 05:18:42 +0100 Subject: [PATCH 023/498] Continued refactoring... --- backend/src/main/kotlin/dev/dres/DRES.kt | 36 ++- .../dev/dres/api/cli/EvaluationRunCommand.kt | 18 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 25 +- .../api/rest/handler/ContextExtensions.kt | 44 +++ .../dres/api/rest/handler/DownloadHandler.kt | 151 --------- .../dres/api/rest/handler/JudgementHandler.kt | 297 ------------------ .../dev/dres/api/rest/handler/LogHandler.kt | 115 ------- .../download/AbstractDownloadHandler.kt | 21 ++ .../download/EvaluationDownloadHandler.kt | 56 ++++ .../EvaluationTemplateDownloadHandler.kt | 57 ++++ .../admin/AbstractEvaluationAdminHandler.kt | 2 +- .../admin/CreateEvaluationHandler.kt | 1 + .../evaluation/admin/ListPastTaskHandler.kt | 4 +- .../admin/OverrideSubmissionHandler.kt | 14 +- .../client/AbstractEvaluationClientHandler.kt | 20 -- .../client/ClientTaskInfoHandler.kt | 3 +- .../viewer/CompetitionRunHandler.kt | 2 - .../evaluation/viewer/ScoreDownloadHandler.kt | 61 ++++ .../judgement/AbstractJudgementHandler.kt | 45 +++ .../judgement/DequeueJudgementHandler.kt | 71 +++++ .../handler/judgement/DequeueVoteHandler.kt | 69 ++++ .../judgement/JudgementStatusHandler.kt | 46 +++ .../handler/judgement/PostJudgementHandler.kt | 66 ++++ .../rest/handler/judgement/PostVoteHandler.kt | 56 ++++ .../rest/handler/log/AbstractLogHandler.kt | 20 ++ .../api/rest/handler/log/QueryLogHandler.kt | 52 +++ .../api/rest/handler/log/ResultLogHandler.kt | 51 +++ .../preview/SubmissionPreviewHandler.kt | 19 +- .../submission/BatchSubmissionHandler.kt | 151 +++++---- .../handler/submission/SubmissionHandler.kt | 67 ++-- .../rest/handler/template/ListTasksHandler.kt | 6 +- .../template/ShowEvaluationTemplateHandler.kt | 6 +- .../template/UpdateCompetitionHandler.kt | 8 +- .../api/rest/types/collection/ApiMediaItem.kt | 2 +- .../api/rest/types/collection/ApiMediaType.kt | 3 +- ...escription.kt => ApiEvaluationTemplate.kt} | 10 +- .../rest/types/competition/tasks/ApiTarget.kt | 4 +- ...iTaskDescription.kt => ApiTaskTemplate.kt} | 6 +- .../tasks/options/ApiTargetOption.kt | 1 - .../rest/types/evaluation/ApiEvaluation.kt | 22 ++ .../api/rest/types/evaluation/ApiRunType.kt | 3 +- .../rest/types/evaluation/ApiSubmission.kt | 20 +- .../dres/api/rest/types/evaluation/ApiTask.kt | 20 ++ .../api/rest/types/evaluation/ApiVerdict.kt | 19 ++ .../rest/types/evaluation/ApiVerdictStatus.kt | 17 + .../rest/types/evaluation/ApiVerdictType.kt | 17 + .../api/rest/types/evaluation/RunState.kt | 12 +- .../evaluation/websocket/ServerMessage.kt | 6 +- .../api/rest/types/judgement/ApiJudgement.kt | 10 + .../types/judgement/ApiJudgementRequest.kt | 10 + .../judgement/ApiJudgementValidatorStatus.kt | 8 + .../dres/api/rest/types/judgement/ApiVote.kt | 10 + .../status/SuccessfulSubmissionsStatus.kt | 4 +- .../data/model/media/MediaItemSegmentList.kt | 14 - .../dev/dres/data/model/media/MediaType.kt | 1 + .../dres/data/model/run/AbstractEvaluation.kt | 1 - .../model/run/AbstractNonInteractiveTask.kt | 3 - .../dres/data/model/run/AbstractTaskRun.kt | 2 +- .../dev/dres/data/model/run/Evaluation.kt | 20 ++ .../run/InteractiveSynchronousEvaluation.kt | 4 +- .../model/run/NonInteractiveEvaluation.kt | 1 - .../kotlin/dev/dres/data/model/run/RunType.kt | 10 + .../kotlin/dev/dres/data/model/run/Task.kt | 31 +- .../model/submissions/BatchedSubmission.kt | 34 -- .../dres/data/model/submissions/Submission.kt | 44 +-- .../dres/data/model/submissions/Verdict.kt | 70 +++++ .../{SubmissionStatus.kt => VerdictStatus.kt} | 16 +- .../{SubmissionType.kt => VerdictType.kt} | 16 +- .../submissions/batch/BaseResultBatch.kt | 16 - .../submissions/batch/BaseSubmissionBatch.kt | 13 - .../submissions/batch/ItemBatchElement.kt | 13 - .../model/submissions/batch/ResultBatch.kt | 16 - .../submissions/batch/SubmissionBatch.kt | 12 - .../submissions/batch/TemporalBatchElement.kt | 18 -- .../batch/TemporalSubmissionBatch.kt | 13 - .../data/model/template/EvaluationTemplate.kt | 12 +- .../dev/dres/data/model/template/task/Hint.kt | 2 +- .../template/task/TaskDescriptionTarget.kt | 115 ------- .../data/model/template/task/TaskTemplate.kt | 10 +- .../dev/dres/data/model/template/team/Team.kt | 2 +- .../data/model/template/team/TeamGroup.kt | 2 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 17 +- .../dev/dres/run/InteractiveRunManager.kt | 25 +- .../run/InteractiveSynchronousRunManager.kt | 18 +- .../dev/dres/run/NonInteractiveRunManager.kt | 22 -- .../main/kotlin/dev/dres/run/RunManager.kt | 22 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 31 +- .../dev/dres/run/eventstream/StreamEvent.kt | 5 +- .../handlers/ResultLogStatisticsHandler.kt | 15 +- .../handlers/SubmissionStatisticsHandler.kt | 6 +- .../handlers/TeamCombinationScoreHandler.kt | 23 +- .../dres/run/filter/CorrectPerTeamFilter.kt | 24 +- .../run/filter/CorrectPerTeamMemberFilter.kt | 25 +- .../run/filter/DuplicateSubmissionFilter.kt | 33 +- .../dres/run/filter/ItemSubmissionFilter.kt | 13 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 4 +- .../kotlin/dev/dres/run/score/TaskContext.kt | 6 +- .../IncrementalSubmissionTaskScorer.kt | 3 +- .../RecalculatingSubmissionTaskScorer.kt | 12 +- .../score/interfaces/ResultBatchTaskScorer.kt | 9 - .../scoreboard/MaxNormalizingScoreBoard.kt | 27 +- .../dev/dres/run/score/scoreboard/Score.kt | 11 + .../run/score/scoreboard/ScoreOverview.kt | 9 + .../dres/run/score/scoreboard/Scoreboard.kt | 16 +- .../scoreboard/SumAggregateScoreBoard.kt | 16 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 6 +- .../scorer/InferredAveragePrecisionScorer.kt | 35 ++- .../dres/run/score/scorer/KisTaskScorer.kt | 21 +- .../dev/dres/run/updatables/DAOUpdatable.kt | 47 --- .../dres/run/updatables/EndTaskUpdatable.kt | 54 ++-- .../run/updatables/MessageQueueUpdatable.kt | 14 +- .../kotlin/dev/dres/run/updatables/Phase.kt | 8 +- .../run/updatables/ScoreboardsUpdatable.kt | 2 +- .../dres/run/updatables/ScoresUpdatable.kt | 39 ++- .../dev/dres/run/updatables/Updatable.kt | 2 +- .../validation/ChainedSubmissionValidator.kt | 33 +- .../MediaItemsSubmissionBatchValidator.kt | 17 - .../MediaItemsSubmissionValidator.kt | 13 +- .../TemporalContainmentSubmissionValidator.kt | 61 ++-- ...TemporalOverlapSubmissionBatchValidator.kt | 39 --- .../TemporalOverlapSubmissionValidator.kt | 54 ++-- .../dev/dres/run/validation/TextValidator.kt | 47 ++- .../interfaces/JudgementValidator.kt | 20 +- .../interfaces/SubmissionBatchValidator.kt | 7 - .../SubmissionJudgementValidator.kt | 18 -- .../interfaces/SubmissionValidator.kt | 4 +- .../validation/interfaces/VoteValidator.kt | 15 +- .../judged/BasicJudgementValidator.kt | 112 ++++--- .../validation/judged/BasicVoteValidator.kt | 50 ++- .../dres/run/validation/judged/ItemRange.kt | 39 +-- .../run/score/scorer/AvsTaskScorerTest.kt | 138 ++++---- .../run/score/scorer/KisTaskScorerTest.kt | 32 +- 133 files changed, 1898 insertions(+), 1798 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/{ApiCompetitionDescription.kt => ApiEvaluationTemplate.kt} (77%) rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/{ApiTaskDescription.kt => ApiTaskTemplate.kt} (79%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementValidatorStatus.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt rename backend/src/main/kotlin/dev/dres/data/model/submissions/{SubmissionStatus.kt => VerdictStatus.kt} (68%) rename backend/src/main/kotlin/dev/dres/data/model/submissions/{SubmissionType.kt => VerdictType.kt} (65%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 8ef2ecb05..3c84f60a2 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -4,6 +4,8 @@ import dev.dres.api.cli.Cli import dev.dres.api.cli.OpenApiCommand import dev.dres.api.rest.RestApi import dev.dres.data.model.Config +import dev.dres.data.model.admin.Role +import dev.dres.data.model.admin.User import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType @@ -17,6 +19,12 @@ import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.media.MediaType +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.Task +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.VerdictType import dev.dres.mgmt.admin.UserManager import dev.dres.run.RunExecutor import dev.dres.run.audit.AuditLogger @@ -97,28 +105,36 @@ object DRES { */ private fun prepareDatabase(config: Config): TransientEntityStore { XdModel.registerNodes( + AuditLogEntry, AuditLogSource, AuditLogType, - AuditLogEntry, + ConfiguredOption, + Evaluation, + EvaluationTemplate, + Hint, + HintOption, + HintType, MediaType, MediaCollection, MediaItem, MediaSegment, - ConfiguredOption, - HintOption, - TaskOption, + Role, ScoreOption, + Submission, SubmissionOption, + TargetType, TargetOption, - EvaluationTemplate, + Task, TaskTemplate, + TaskTemplateTarget, + TaskOption, Team, - TeamGroup, TeamAggregator, - HintType, - Hint, - TargetType, - TaskDescriptionTarget + TeamGroup, + User, + Verdict, + VerdictType, + VerdictStatus, ) val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") initMetaData(XdModel.hierarchy, store) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt index e5daa6e59..fb7e77ce2 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt @@ -14,7 +14,7 @@ import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionId -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.* import dev.dres.utilities.extensions.toDateString import jetbrains.exodus.database.TransientEntityStore @@ -131,7 +131,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt it.name, it.template.description, if (it.type == RunType.INTERACTIVE_SYNCHRONOUS) { - it.tasks.firstOrNull()?.description?.name ?: "N/A" + it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" } @@ -157,7 +157,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt it.name, it.template.description, if (it.type == RunType.INTERACTIVE_SYNCHRONOUS) { - it.tasks.firstOrNull()?.description?.name ?: "N/A" + it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" }, @@ -197,8 +197,8 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt println() println("Evaluated Tasks:") it.tasks.asSequence().forEach { t -> - println(t.description) - if (t.type == RunType.INTERACTIVE_SYNCHRONOUS) { + println(t.template) + if (t.evaluation.type == RunType.INTERACTIVE_SYNCHRONOUS) { println("Submissions") t.submissions.asSequence().forEach { s -> println(s) } } @@ -329,7 +329,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt var query = if (this.taskIds.isNotEmpty()) { evaluation.tasks.filter { it.id.isIn(this@ResetSubmission.taskIds) }.flatMapDistinct { it.submissions } } else if (this.taskGroups.isNotEmpty()) { - evaluation.tasks.filter { it.description.taskGroup.name.isIn(this@ResetSubmission.taskGroups) }.flatMapDistinct { it.submissions } + evaluation.tasks.filter { it.template.taskGroup.name.isIn(this@ResetSubmission.taskGroups) }.flatMapDistinct { it.submissions } } else { evaluation.tasks.flatMapDistinct { it.submissions } } @@ -341,7 +341,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt var affected = 0 query.asSequence().forEach { affected += 1 - it.status = SubmissionStatus.INDETERMINATE + it.status = VerdictStatus.INDETERMINATE } println("Successfully reset $affected} submissions.") @@ -372,7 +372,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt } val tasks = evaluation.tasks.filter { - it.description.targets.filter { it.type.isIn(listOf(TargetType.JUDGEMENT,TargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() + it.template.targets.filter { it.type.isIn(listOf(TargetType.JUDGEMENT,TargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() } if (tasks.isEmpty) { @@ -390,7 +390,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt submittedItems.entries.forEach { items -> val status = items.value.map { s -> s.status }.toSet() //should only contain one element writeRow( - listOf(task.id, task.description.name, items.key.first, items.key.second, items.key.third, status) + listOf(task.id, task.template.name, items.key.first, items.key.second, items.key.third, status) ) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 9dea826ca..74d54c301 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -5,13 +5,19 @@ import dev.dres.api.rest.handler.* import dev.dres.api.rest.handler.audit.ListAuditLogsHandler import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler import dev.dres.api.rest.handler.collection.* +import dev.dres.api.rest.handler.download.EvaluationDownloadHandler +import dev.dres.api.rest.handler.download.EvaluationTemplateDownloadHandler import dev.dres.api.rest.handler.evaluation.admin.* import dev.dres.api.rest.handler.evaluation.client.ClientListEvaluationsHandler import dev.dres.api.rest.handler.evaluation.client.ClientTaskInfoHandler +import dev.dres.api.rest.handler.judgement.* +import dev.dres.api.rest.handler.log.QueryLogHandler +import dev.dres.api.rest.handler.log.ResultLogHandler import dev.dres.api.rest.handler.template.* import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.MediaPreviewHandler import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler +import dev.dres.api.rest.handler.submission.BatchSubmissionHandler import dev.dres.api.rest.handler.submission.SubmissionHandler import dev.dres.api.rest.handler.system.CurrentTimeHandler import dev.dres.api.rest.handler.system.InfoHandler @@ -19,6 +25,7 @@ import dev.dres.api.rest.handler.system.LoginHandler import dev.dres.api.rest.handler.system.LogoutHandler import dev.dres.api.rest.handler.users.* import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.Config import dev.dres.run.RunExecutor import dev.dres.utilities.NamedThreadFactory @@ -111,7 +118,7 @@ object RestApi { // Submission SubmissionHandler(store, config), - JsonBatchSubmissionHandler(store), + BatchSubmissionHandler(store, config), // Log QueryLogHandler(), @@ -156,11 +163,11 @@ object RestApi { EvaluationOverviewHandler(store), // Judgement - NextOpenJudgementHandler(store), - NextOpenVoteJudgementHandler(store), - PostJudgementHandler(), - JudgementStatusHandler(), - JudgementVoteHandler(), + DequeueJudgementHandler(store), + DequeueVoteHandler(store), + PostJudgementHandler(store), + PostVoteHandler(store), + JudgementStatusHandler(store), // Audit Log GetAuditLogInfoHandler(store), @@ -176,9 +183,9 @@ object RestApi { ClientTaskInfoHandler(store), // Downloads - DownloadHandler.CompetitionRun(store), - DownloadHandler.CompetitionRunScoreHandler(store), - DownloadHandler.CompetitionDesc(store) + EvaluationDownloadHandler(store), + EvaluationTemplateDownloadHandler(store) + /* DownloadHandler.CompetitionRunScoreHandler(store), */ ) javalin = Javalin.create { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt index 3c626e655..1ff395ed2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt @@ -5,8 +5,14 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.EvaluationId +import dev.dres.run.RunExecutor +import dev.dres.run.RunManager +import dev.dres.run.RunManagerStatus import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct +import kotlinx.dnq.query.isEmpty /** @@ -25,6 +31,44 @@ fun Context.userId(): UserId = AccessManager.userIdForSession(this.sessionId()) fun Context.evaluationId(): EvaluationId = this.pathParamMap()["evaluationId"] ?: throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", this) + +/** + * Returns the active [RunManager] the used defined in the current [Context] can access. + * + * @return [RunManager] + */ +fun Context.activeManagerForUser(): RunManager { + val userId = this.userId() + val managers = AccessManager.getRunManagerForUser(userId).filter { + it.status != RunManagerStatus.CREATED && it.status != RunManagerStatus.TERMINATED + } + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", this) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", this) + return managers.first() +} + +/** + * Returns the active [RunManager] the [EvaluationId] defined in the current [Context]. + * + * @return [RunManager] + */ +fun Context.eligibleManagerForId(): RunManager { + val userId = this.userId() + val evaluationId = this.evaluationId() + val manager = RunExecutor.managerForId(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) + if (this.isJudge()) { + if (manager.template.judges.filter { it.userId eq userId }.isEmpty) { + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as judge.", this) + } + } + if (this.isParticipant()) { + if (manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isEmpty) { + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) + } + } + return manager +} + /** * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt deleted file mode 100644 index a3c72bbfb..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/DownloadHandler.kt +++ /dev/null @@ -1,151 +0,0 @@ -package dev.dres.api.rest.handler - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.dbo.DAO -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.run.InteractiveAsynchronousEvaluation -import dev.dres.utilities.extensions.UID -import io.javalin.security.RouteRole -import io.javalin.http.Context -import io.javalin.openapi.* - -/** - * A [AccessManagedRestHandler] implementation that provides certain data structures as downloadable files. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -sealed class DownloadHandler : AccessManagedRestHandler { - - /** The version of the API this [DownloadHandler] belongs to. */ - override val apiVersion = "v1" - - /** The roles permitted to access the [DownloadHandler]. */ - override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) - - /** - * REST handler to download the competition run information. - */ - class CompetitionRun(private val runs: DAO) : DownloadHandler(), GetRestHandler { - - /** The route of this [DownloadHandler.CompetitionRun]. */ - override val route = "download/run/{runId}" - - @OpenApi( - summary = "Provides a JSON download of the entire competition run structure.", - path = "/api/v1/download/run/{runId}", - tags = ["Download"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition run ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): String { - /* Obtain run id and run. */ - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = this.runs[runId] ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - /* Set header for download. */ - ctx.header("Content-Disposition", "attachment; filename=\"run-${runId.string}.json\"") - - /* Return value. */ - val mapper = jacksonObjectMapper() - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(run) - } - } - - /** - * REST handler to download the competition run scores description information. - */ - class CompetitionRunScoreHandler(private val runs: DAO) : AbstractScoreRestHandler(), GetRestHandler { - - override val route = "download/run/{runId}/scores" - - @OpenApi( - summary = "Provides a CSV download with the scores for a given competition run.", - path = "/api/v1/download/run/{runId}/scores", - tags = ["Download"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition run ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(String::class, type = "text/csv")]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): String { - - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = this.runs[runId] ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - /* Update response header. */ - ctx.contentType("text/csv") - ctx.header("Content-Disposition", "attachment; filename=\"scores-${runId.string}.csv\"") - - if (run is InteractiveAsynchronousEvaluation) { - - return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } - .flatMap { task -> - task.scorer.scores().filter { it.first == task.teamId } - .map { "${task.started},\"${task.description.name}\",\"${task.description.taskGroup.name}\",\"${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"}\",${it.third}" } - }.joinToString(separator = "\n") - - } - - return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } - .flatMap { task -> - task.scorer.scores().map { "${task.started},\"${task.description.name}\",\"${task.description.taskGroup.name}\",\"${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"}\",${it.third}" } - }.joinToString(separator = "\n") - } - - } - - /** - * REST handler to download the competition description information. - */ - class CompetitionDesc(private val competitions: DAO) : DownloadHandler(), GetRestHandler { - - /** The route of this [DownloadHandler.CompetitionRun]. */ - override val route = "download/competition/{competitionId}" - - @OpenApi( - summary = "Provides a JSON download of the entire competition description structure.", - path = "/api/v1/download/competition/{competitionId}", - tags = ["Download"], - pathParams = [ - OpenApiParam("competitionId", String::class, "Competition ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): String { - /* Obtain run id and run. */ - val competitionId = ctx.pathParamMap().getOrElse("competitionId") { throw ErrorStatusException(400, "Parameter 'competitionId' is missing!'", ctx) }.UID() - val competition = this.competitions[competitionId] ?: throw ErrorStatusException(404, "Competition $competitionId not found", ctx) - - /* Set header for download. */ - ctx.header("Content-Disposition", "attachment; filename=\"competition-${competitionId.string}.json") - - /* Return value. */ - val mapper = jacksonObjectMapper() - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(competition) - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt deleted file mode 100644 index ec6dad593..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ /dev/null @@ -1,297 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.dbo.DAO -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.PlayableMediaItem -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect -import dev.dres.run.RunExecutor -import dev.dres.run.RunManager -import dev.dres.run.audit.AuditLogger -import dev.dres.run.exceptions.JudgementTimeoutException -import dev.dres.run.validation.interfaces.VoteValidator -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId -import io.javalin.security.RouteRole -import io.javalin.http.BadRequestResponse -import io.javalin.http.Context -import io.javalin.openapi.* - -abstract class AbstractJudgementHandler : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(ApiRole.JUDGE) - override val apiVersion = "v1" - - protected fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) - }.UID() - - - companion object { - fun checkRunManagerAccess(ctx: Context, runManager: RunManager) { - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException( - 403, - "No valid user.", - ctx - ) - if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { - return //Admins require no further check - } - if (userId !in runManager.template.judges) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - } - } -} - -data class Judgement(val token: String, val validator: String, val verdict: SubmissionStatus) - -data class JudgementVote(val verdict: SubmissionStatus) - -data class JudgementRequest(val token: String, val mediaType: JudgementRequestMediaType, val validator: String, val collection: String, val item: String, val taskDescription: String, val startTime: String?, val endTime: String?) - -enum class JudgementRequestMediaType { - TEXT, - VIDEO, - IMAGE, -} - -class NextOpenJudgementHandler(val collections: DAO) : AbstractJudgementHandler(), GetRestHandler { - override val route = "run/{runId}/judge/next" - - @OpenApi( - summary = "Gets the next open Submission to be judged.", - path = "/api/v1/run/{runId}/judge/next", - pathParams = [OpenApiParam("runId", String::class, "Run ID")], - tags = ["Judgement"], - responses = [ - OpenApiResponse("200", [OpenApiContent(JudgementRequest::class)]), - OpenApiResponse("202", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): JudgementRequest { - val runId = this.runId(ctx) - val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - checkRunManagerAccess(ctx, run) - - val validator = run.judgementValidators.find { it.hasOpen } ?: throw ErrorStatusException(202, "There is currently no submission awaiting judgement", ctx, true) - val next = validator.next(ctx.sessionId()) ?: throw ErrorStatusException(202, "There is currently no submission awaiting judgement", ctx) - - val taskDescription = next.second.task?.description?.textualDescription() ?: next.second.task?.description?.name ?: "no task description available" - - if (next.second is TextAspect) { - return JudgementRequest(next.first, JudgementRequestMediaType.TEXT, validator.id, "text", (next.second as TextAspect).text, taskDescription, null, null) - } - - if (next.second !is ItemAspect) { - throw ErrorStatusException(400, "Submission has neither item nor text", ctx) - } - - val item = (next.second as ItemAspect).item - - val collection = this.collections[item.collection] ?: throw ErrorStatusException(404, "Could not find collection with id ${item.collection}", ctx) - - return if (next.second is TemporalSubmissionAspect){ - val tsa = next.second as TemporalSubmissionAspect - // Video is assumed, due to Temporal Submission Aspect - might want to change this later - JudgementRequest(next.first, JudgementRequestMediaType.VIDEO, validator.id, collection.id.string, tsa.item.id.string, taskDescription, tsa.start.toString(), tsa.end.toString()) - } else { - val type = if(item is PlayableMediaItem){ - JudgementRequestMediaType.VIDEO - } else { - JudgementRequestMediaType.IMAGE - } - JudgementRequest(next.first, type, validator.id, collection.id.string, item.id.string, taskDescription, null, null) - } - } -} - -class PostJudgementHandler : AbstractJudgementHandler(), PostRestHandler { - override val route = "run/{runId}/judge" - - @OpenApi( - summary = "Returns a Judgement.", - path = "/api/v1/run/{runId}/judge", methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Run ID")], - requestBody = OpenApiRequestBody([OpenApiContent(Judgement::class)]), - tags = ["Judgement"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("408", [OpenApiContent(ErrorStatus::class)], "On timeout: Judgement took too long"), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - val runId = this.runId(ctx) - val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - checkRunManagerAccess(ctx, run) - - val judgement = try { - ctx.bodyAsClass(Judgement::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - val validator = run.judgementValidators.find { it.id == judgement.validator } ?: throw ErrorStatusException(404, "no matching task found with validator ${judgement.validator}", ctx) - try { - validator.judge(judgement.token, judgement.verdict) - } catch(ex: JudgementTimeoutException) { - throw ErrorStatusException(408, ex.message!!, ctx) - } - AuditLogger.judgement(run.id, validator, judgement.token, judgement.verdict, AuditLogSource.REST, ctx.sessionId()) - - return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") - } -} - -class JudgementStatusHandler : GetRestHandler>, AccessManagedRestHandler { - override val permittedRoles = setOf(ApiRole.VIEWER) - override val route = "run/{runId}/judge/status" - override val apiVersion = "v1" - - - @OpenApi( - summary = "Gets the status of all judgement validators.", - path = "/api/v1/run/{runId}/judge/status", - pathParams = [OpenApiParam("runId", String::class, "Run ID")], - tags = ["Judgement"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - - val runId = ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) - }.UID() - - val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - AbstractJudgementHandler.checkRunManagerAccess(ctx, run) - - return run.judgementValidators.map { JudgementValidatorStatus(it.id, it.pending, it.open) } - } - -} - -data class JudgementValidatorStatus(val validator: String, val pending: Int, val open: Int) - -class JudgementVoteHandler : PostRestHandler { - override val route = "run/{runId}/judge/vote" - override val apiVersion = "v1" - - - @OpenApi( - summary = "Returns a Vote.", - path = "/api/v1/run/{runId}/judge/vote", methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Run ID")], - requestBody = OpenApiRequestBody([OpenApiContent(JudgementVote::class)]), - tags = ["Judgement"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) - override fun doPost(ctx: Context): SuccessStatus { - - val runId = try{ - ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) - }.UID() - } catch (e: IllegalArgumentException) { - throw ErrorStatusException(400, "Parameter 'runId' is invalid!'", ctx) - } - val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - val vote = try { - ctx.bodyAsClass(JudgementVote::class.java) - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) - } - - //gets the first active VoteValidator - val validator = run.judgementValidators.find { it is VoteValidator && it.isActive } ?: throw ErrorStatusException(404, "There is currently no voting going on in run $runId", ctx) - validator as VoteValidator - validator.vote(vote.verdict) - - return SuccessStatus("vote received") - } - -} - -class NextOpenVoteJudgementHandler(val collections: DAO) : AbstractJudgementHandler(), GetRestHandler { - override val route = "run/{runId}/vote/next" - - @OpenApi( - summary = "Gets the next open Submission to voted on.", - path = "/api/v1/run/{runId}/vote/next", - pathParams = [OpenApiParam("runId", String::class, "Run ID")], - tags = ["Judgement"], - responses = [ - OpenApiResponse("200", [OpenApiContent(JudgementRequest::class)]), - OpenApiResponse("202", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): JudgementRequest { - val runId = this.runId(ctx) - val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - val validator = run.judgementValidators.find { it is VoteValidator && it.isActive } ?: throw ErrorStatusException(202, "There is currently no voting going on in run", ctx, true) - - validator as VoteValidator - - val next = validator.nextSubmissionToVoteOn() ?: throw ErrorStatusException(202, "There is currently no voting going on in run", ctx) - - val taskDescription = next.task?.description?.textualDescription() ?: next.task?.description?.name ?: "no task description available" - - if (next is TextAspect) { - return JudgementRequest("vote", JudgementRequestMediaType.TEXT, validator.id, "text", (next as TextAspect).text, taskDescription, null, null) - } - - if (next !is ItemAspect) { - throw ErrorStatusException(400, "Submission has neither item nor text", ctx) - } - - val item = (next as ItemAspect).item - - val collection = this.collections[next.item.collection] ?: throw ErrorStatusException(404, "Could not find collection with id ${next.item.collection}", ctx) - - return if (next is TemporalSubmissionAspect){ - val tsa = next as TemporalSubmissionAspect - JudgementRequest("vote",JudgementRequestMediaType.VIDEO, validator.id, collection.id.string, tsa.item.id.string, taskDescription, tsa.start.toString(), tsa.end.toString()) - } else { - val type = if(item is PlayableMediaItem){ - JudgementRequestMediaType.VIDEO - } else { - JudgementRequestMediaType.IMAGE - } - JudgementRequest("vote",type, validator.id, collection.id.string, next.item.id.string, taskDescription, null, null) - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt deleted file mode 100644 index 96a84cea5..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogHandler.kt +++ /dev/null @@ -1,115 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.log.QueryEventLog -import dev.dres.data.model.log.QueryResultLog -import dev.dres.run.RunManager -import dev.dres.run.RunManagerStatus -import dev.dres.run.eventstream.EventStreamProcessor -import dev.dres.run.eventstream.InvalidRequestEvent -import dev.dres.run.eventstream.QueryEventLogEvent -import dev.dres.run.eventstream.QueryResultLogEvent -import dev.dres.utilities.extensions.sessionId -import io.javalin.security.RouteRole -import io.javalin.http.BadRequestResponse -import io.javalin.http.Context -import io.javalin.http.bodyAsClass -import io.javalin.openapi.* - -abstract class LogHandler : PostRestHandler, AccessManagedRestHandler { - - override val apiVersion = "v1" - - private fun getRelevantManagers(userId: EvaluationId): Set = AccessManager.getRunManagerForUser(userId) - - protected fun getActiveRun(userId: EvaluationId, ctx: Context): RunManager { - val managers = getRelevantManagers(userId).filter { it.status != RunManagerStatus.CREATED && it.status != RunManagerStatus.TERMINATED } - if (managers.isEmpty()) { - throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) - } - - if (managers.size > 1) { - throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) - } - - return managers.first() - } - -} - -class QueryLogHandler : LogHandler() { - override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) - override val route = "log/query" - - @OpenApi(summary = "Accepts query logs from participants", - path = "/api/v1/log/query", - methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(QueryEventLog::class)]), - tags = ["Log"], - queryParams = [ - OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)])] - ) - override fun doPost(ctx: Context): SuccessStatus { - - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) - val run = getActiveRun(userId, ctx) - - - val queryEventLog = try { - ctx.bodyAsClass() - } catch (e: BadRequestResponse){ - EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), run.id, ctx.body())) - throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) - }.copy(serverTimeStamp = System.currentTimeMillis()) - - EventStreamProcessor.event(QueryEventLogEvent(ctx.sessionId(), run.id, queryEventLog)) - - return SuccessStatus("Log received") - } - -} - -class ResultLogHandler : LogHandler() { - override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) - override val route = "log/result" - - @OpenApi(summary = "Accepts result logs from participants", - path = "/api/v1/log/result", - methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(QueryResultLog::class)]), - tags = ["Log"], - queryParams = [ - OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)])] - ) - override fun doPost(ctx: Context): SuccessStatus { - - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) - val run = getActiveRun(userId, ctx) - - val queryResultLog = try { - ctx.bodyAsClass(QueryResultLog::class.java) - } catch (e: BadRequestResponse){ - EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), run.id, ctx.body())) - throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) - }.copy(serverTimeStamp = System.currentTimeMillis()) - - EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionId(), run.id, queryResultLog)) - - return SuccessStatus("Log received") - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt new file mode 100644 index 000000000..a570cfa87 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt @@ -0,0 +1,21 @@ +package dev.dres.api.rest.handler.download + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.types.users.ApiRole +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [AccessManagedRestHandler] implementation that provides certain data structures as downloadable files. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractDownloadHandler(protected val store: TransientEntityStore) : AccessManagedRestHandler { + + /** The version of the API this [AbstractDownloadHandler] belongs to. */ + override val apiVersion = "v1" + + /** The roles permitted to access the [AbstractDownloadHandler]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt new file mode 100644 index 000000000..8a9da27ed --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -0,0 +1,56 @@ +package dev.dres.api.rest.handler.download + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.Evaluation +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * A [GetRestHandler] that allows for downloading the entire [Evaluation] structure as JSON file. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandler(store), GetRestHandler { + + /** The route of this [EvaluationDownloadHandler]. */ + override val route = "download/evaluation/{evaluationId}" + + @OpenApi( + summary = "Provides a JSON download of the entire evaluation structure.", + path = "/api/v1/download/evaluation/{evaluationId}", + tags = ["Download"], + pathParams = [ + OpenApiParam("runId", String::class, "The evaluation ID.") + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): String { + /* Obtain run id and run. */ + val evaluationId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", ctx) } + val evaluation = this.store.transactional(true) { + Evaluation.query(Evaluation::id eq evaluationId).firstOrNull()?.toApi() + ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + } + + /* Set header for download. */ + ctx.header("Content-Disposition", "attachment; filename=\"run-${evaluationId}.json\"") + + /* Return value. */ + val mapper = jacksonObjectMapper() + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(evaluation) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt new file mode 100644 index 000000000..d09a44a38 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -0,0 +1,57 @@ +package dev.dres.api.rest.handler.download + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation +import dev.dres.data.model.template.EvaluationTemplate +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * A [GetRestHandler] that allow for downloading the entire [EvaluationTemplate] structure as JSON file. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandler(store), GetRestHandler { + + /** The route of this [EvaluationTemplateDownloadHandler]. */ + override val route = "download/template/{templateId}" + + @OpenApi( + summary = "Provides a JSON download of the entire evaluation template structure.", + path = "/api/v1/download/template/{templateId}", + tags = ["Download"], + pathParams = [ + OpenApiParam("templateId", String::class, "The evaluation template ID") + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): String { + /* Obtain run id and run. */ + val templateId = ctx.pathParamMap()["competitionId"] ?: throw ErrorStatusException(400, "Parameter 'templateId' is missing!'", ctx) + val template = this.store.transactional(true) { + EvaluationTemplate.query(EvaluationTemplate::id eq templateId).firstOrNull()?.toApi() + ?: throw ErrorStatusException(404, "Competition $templateId not found", ctx) + } + + /* Set header for download. */ + ctx.header("Content-Disposition", "attachment; filename=\"competition-${templateId}.json") + + /* Return value. */ + val mapper = jacksonObjectMapper() + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt index 98ee37235..be5ec601d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt @@ -36,7 +36,7 @@ abstract class AbstractEvaluationAdminHandler(protected val store: TransientEnti protected fun getManager(evaluationId: EvaluationId): InteractiveRunManager? { val run = RunExecutor.managerForId(evaluationId) if (run != null && run is InteractiveRunManager) { - run + return run } return null } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 4cadac1c5..c89dceb33 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -115,6 +115,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs RunExecutor.schedule(when (message.type) { ApiRunType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ ApiRunType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) + ApiRunType.NON_INTERACTIVE -> TODO() }) evaluation } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index ae7f2f49d..883519ed0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -46,7 +46,9 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH name = it.template.name, taskGroup = it.template.taskGroup.name, taskType = it.template.taskGroup.type.name, - numberOfSubmissions = it.submissions.size + numberOfSubmissions = it.submissions.size, + remainingTime = 0L, + running = false ) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 1ff25d595..87fc1a5bf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -3,11 +3,12 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.sessionId import io.javalin.http.BadRequestResponse @@ -17,7 +18,7 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [PatchRestHandler] used to overwrite [SubmissionStatus] information. + * A [PatchRestHandler] used to overwrite [VerdictStatus] information. * * @author Ralph Gasser * @author Luca Rossetto @@ -28,7 +29,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation override val route: String = "evaluation/admin/{evaluationId}/submission/override" @OpenApi( - summary = "Lists all submissions for a given task and run", + summary = "Override the submission status for a given submission.", path = "/api/v1/evaluation/admin/{evaluationId}/submission/override", methods = [HttpMethod.PATCH], pathParams = [ @@ -47,6 +48,8 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation val evaluationId = ctx.evaluationId() val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + /* TODO: Make this work for batched submissions! */ + /* Extract HTTP body. */ val submissionInfo = try { ctx.bodyAsClass() @@ -54,7 +57,8 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } /* Perform sanity check. */ - if (submissionInfo.status == SubmissionStatus.INDETERMINATE) { + + if (submissionInfo.verdicts.first().status == ApiVerdictStatus.INDETERMINATE ) { throw ErrorStatusException(400, "Submission status can not be set to INDETERMINATE.", ctx) } @@ -65,7 +69,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation if (evaluationManager.allSubmissions.none { it.id == submissionInfo.id }) { throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) } - if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.status)) { + if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.status)) { val submission = evaluationManager.allSubmissions.single { it.id == submissionInfo.id } AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionId()) submission.toApi() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt index cd0fc9fc8..da685d513 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt @@ -1,18 +1,12 @@ package dev.dres.api.rest.handler.evaluation.client -import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.handler.userId -import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.admin.UserId -import dev.dres.data.model.run.EvaluationId -import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager -import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore @@ -30,20 +24,6 @@ abstract class AbstractEvaluationClientHandler(protected val store: TransientEnt /** All [AbstractEvaluationClientHandler]s are part of the v1 API. */ override val apiVersion = "v1" - /** - * Returns the [RunManager] associated with the current [Context]. - * - * @param ctx The request [Context] - * @return [RunManager] or null - */ - protected fun getEvaluationManager(ctx: Context): RunManager? { - val run = RunExecutor.managerForId(ctx.evaluationId()) ?: return null - if (run.template.teams.filter { t -> t.users.filter { u -> u.id eq ctx.userId() }.isNotEmpty() }.isNotEmpty) { - return run - } - return null - } - /** * Returns the [RunManager] associated with the current [Context]. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index 060e89202..88d7903b8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.handler.evaluation.client import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId import dev.dres.api.rest.types.evaluation.ApiTaskInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException @@ -37,7 +38,7 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie ) override fun doGet(ctx: Context): ApiTaskInfo = this.store.transactional(true) { tx -> - val run = getEvaluationManager(ctx) ?: throw ErrorStatusException(404, "Evaluation ${this.evaluationId(ctx)} not found", ctx) + val run = ctx.eligibleManagerForId() val rac = RunActionContext.runActionContext(ctx, run) if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt index c4674540c..29f0389d0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt @@ -16,8 +16,6 @@ import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.template.options.SimpleOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.Submission -import dev.dres.run.InteractiveRunManager -import dev.dres.run.RunExecutor import dev.dres.run.TaskRunStatus import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt new file mode 100644 index 000000000..a2a4fe258 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt @@ -0,0 +1,61 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.AbstractScoreRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ScoreDownloadHandler(store: TransientEntityStore) : AbstractScoreRestHandler(), GetRestHandler { + + override val route = "download/run/{runId}/scores" + + @OpenApi( + summary = "Provides a CSV download with the scores for a given competition run.", + path = "/api/v1/download/run/{runId}/scores", + tags = ["Download"], + pathParams = [ + OpenApiParam("runId", String::class, "Competition run ID") + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(String::class, type = "text/csv")]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): String { + + val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() + val run = this.runs[runId] ?: throw ErrorStatusException(404, "Run $runId not found", ctx) + + /* Update response header. */ + ctx.contentType("text/csv") + ctx.header("Content-Disposition", "attachment; filename=\"scores-${runId.string}.csv\"") + + if (run is InteractiveAsynchronousEvaluation) { + + return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } + .flatMap { task -> + task.scorer.scores().filter { it.first == task.teamId } + .map { "${task.started},\"${task.description.name}\",\"${task.description.taskGroup.name}\",\"${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"}\",${it.third}" } + }.joinToString(separator = "\n") + + } + + return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } + .flatMap { task -> + task.scorer.scores().map { "${task.started},\"${task.description.name}\",\"${task.description.taskGroup.name}\",\"${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"}\",${it.third}" } + }.joinToString(separator = "\n") + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt new file mode 100644 index 000000000..9c23b2749 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt @@ -0,0 +1,45 @@ +package dev.dres.api.rest.handler.judgement + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.handler.userId +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.run.RunManager +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.isEmpty + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractJudgementHandler(protected val store: TransientEntityStore): RestHandler, AccessManagedRestHandler { + /** */ + override val permittedRoles: Set = setOf(ApiRole.JUDGE) + + /** */ + override val apiVersion = "v1" + + /** + * Checks if [RunManager] can actually be judged by the user defined in the current [Context]. + * + * @param ctx The [Context] to check eligibility for. + * @param runManager The [RunManager] to check eligibility for. + * @throws [ErrorStatusException] if user is not eligible. + */ + protected fun checkEligibility(ctx: Context, runManager: RunManager) { + val userId = ctx.userId() + if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { + return //Admins require no further check + } + if (runManager.template.judges.filter { it.userId eq userId }.isEmpty) { + throw ErrorStatusException(403, "Access to specified run is denied.", ctx) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt new file mode 100644 index 000000000..2f26ae3ce --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -0,0 +1,71 @@ +package dev.dres.api.rest.handler.judgement + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.api.rest.types.judgement.ApiJudgementRequest +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.submissions.VerdictType +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] to dequeue the next [ApiJudgementRequest] that is ready for judgement. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHandler(store), GetRestHandler { + override val route = "evaluation/{evaluationId}/judge/next" + + @OpenApi( + summary = "Gets the next open submission that is waiting to be judged.", + path = "/api/v1/evaluation/{evaluationId}/judge/next", + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + tags = ["Judgement"], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiJudgementRequest::class)]), + OpenApiResponse("202", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiJudgementRequest { + /* Obtain manager and check if any submissions are waiting for judgement. */ + val evaluationManager = ctx.eligibleManagerForId() + + /* Start transaction. */ + this.store.transactional { + checkEligibility(ctx, evaluationManager) + do { + val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break + val next = validator.next(ctx.sessionId()) ?: break + val taskDescription = next.second.task.template.textualDescription() + when (next.second.type) { + VerdictType.TEXT -> { + val text = next.second.text ?: continue + return@transactional ApiJudgementRequest(next.first, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) + } + VerdictType.ITEM -> { + val item = next.second.item ?: continue + return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) + } + VerdictType.TEMPORAL -> { + val item = next.second.item ?: continue + val start = next.second.start ?: continue + val end = next.second.end ?: continue + return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, start, end) + } + else -> continue + } + } while (true) + } + throw ErrorStatusException(202, "There is currently no submission awaiting judgement.", ctx) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt new file mode 100644 index 000000000..8541ae75f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -0,0 +1,69 @@ +package dev.dres.api.rest.handler.judgement + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.api.rest.types.judgement.ApiJudgementRequest +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.submissions.VerdictType +import dev.dres.run.validation.interfaces.VoteValidator +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] to dequeue the next [ApiJudgementRequest] that is ready for public voting. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(store), GetRestHandler { + override val route = "evaluation/{evaluationId}/vote/next" + + @OpenApi( + summary = "Gets the next open submission that is waiting to be voted on.", + path = "/api/v1/evaluation/{evaluationId}/vote/next", + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + tags = ["Judgement"], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiJudgementRequest::class)]), + OpenApiResponse("202", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiJudgementRequest { + /* Obtain manager and check if any submissions are waiting for judgement. */ + val evaluationManager = ctx.eligibleManagerForId() + + /* Start transaction. */ + this.store.transactional { + do { + val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break + val next = validator.nextSubmissionToVoteOn() ?: break + val taskDescription = next.task.template.textualDescription() + when (next.type) { + VerdictType.TEXT -> { + val text = next.text ?: continue + return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) + } + VerdictType.ITEM -> { + val item = next.item ?: continue + return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) + } + VerdictType.TEMPORAL -> { + val item = next.item ?: continue + val start = next.start ?: continue + val end = next.end ?: continue + return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, start, end) + } + else -> continue + } + } while (true) + } + throw ErrorStatusException(202, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx, true) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt new file mode 100644 index 000000000..bc3960033 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -0,0 +1,46 @@ +package dev.dres.api.rest.handler.judgement + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.judgement.ApiJudgementValidatorStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.status.ErrorStatus +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + + +/** + * A [GetRestHandler] to list the status of all judgement validators for a given evaluation. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHandler(store), GetRestHandler> { + override val permittedRoles = setOf(ApiRole.VIEWER) + override val route = "evaluation/{evaluationId}/judge/status" + override val apiVersion = "v1" + + @OpenApi( + summary = "Retrieves the status of all judgement validators.", + path = "/api/v1/evaluation/{evaluationId}/judge/status", + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + tags = ["Judgement"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val evaluationManager = ctx.eligibleManagerForId() + return this.store.transactional(true) { + checkEligibility(ctx, evaluationManager) + evaluationManager.judgementValidators.map { ApiJudgementValidatorStatus(it.id, it.pending, it.open) } + } + } +} + + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt new file mode 100644 index 000000000..355358bda --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -0,0 +1,66 @@ +package dev.dres.api.rest.handler.judgement + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.judgement.ApiJudgement +import dev.dres.api.rest.types.judgement.ApiJudgementRequest +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger +import dev.dres.run.exceptions.JudgementTimeoutException +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [PostRestHandler] to post an [ApiJudgement] for a previously dequeued [ApiJudgementRequest]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandler(store), PostRestHandler { + override val route = "evaluation/{evaluationId}/judge" + + @OpenApi( + summary = "Endpoint to post a judgement for a previously detached judgement request.", + path = "/api/v1/run/{runId}/judge", methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + requestBody = OpenApiRequestBody([OpenApiContent(ApiJudgement::class)]), + tags = ["Judgement"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("408", [OpenApiContent(ErrorStatus::class)], "On timeout: Judgement took too long"), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + /* Obtain manager and check if any submissions are waiting for judgement. */ + val evaluationManager = ctx.eligibleManagerForId() + val judgement = try { + ctx.bodyAsClass(ApiJudgement::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + /* Start transaction. */ + this.store.transactional { + checkEligibility(ctx, evaluationManager) + val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } + ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) + try { + validator.judge(judgement.token, judgement.verdict.status) + } catch (ex: JudgementTimeoutException) { + throw ErrorStatusException(408, ex.message!!, ctx) + } + AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.status, AuditLogSource.REST, ctx.sessionId()) + } + return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt new file mode 100644 index 000000000..516d3620a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -0,0 +1,56 @@ +package dev.dres.api.rest.handler.judgement + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.judgement.ApiJudgementRequest +import dev.dres.api.rest.types.judgement.ApiVote +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.run.validation.interfaces.VoteValidator +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [PostRestHandler] to post an [ApiVote] on a previously dequeued [ApiJudgementRequest]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(store), PostRestHandler { + override val route = "evaluation/{evaluationId}/judge/vote" + override val apiVersion = "v1" + + + @OpenApi( + summary = "Returns a Vote.", + path = "/api/v1/evaluation/{evaluationId}/judge/vote", methods = [HttpMethod.POST], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + requestBody = OpenApiRequestBody([OpenApiContent(ApiVote::class)]), + tags = ["Judgement"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationManager = ctx.eligibleManagerForId() + val vote = try { + ctx.bodyAsClass(ApiVote::class.java) + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + this.store.transactional { + val validator = evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } // Get first active vote validator + ?: throw ErrorStatusException(404, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx) + validator as VoteValidator + validator.vote(vote.verdict) + } + return SuccessStatus("vote received") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt new file mode 100644 index 000000000..6a166bda9 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt @@ -0,0 +1,20 @@ +package dev.dres.api.rest.handler.log + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.api.rest.types.users.ApiRole +import io.javalin.security.RouteRole + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractLogHandler: PostRestHandler, AccessManagedRestHandler { + /** All [AbstractLogHandler]s are part of the v1 API. */ + override val apiVersion = "v1" + + /** All [AbstractLogHandler]s require [ApiRole.ADMIN] or [ApiRole.PARTICIPANT]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt new file mode 100644 index 000000000..92944b116 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt @@ -0,0 +1,52 @@ +package dev.dres.api.rest.handler.log + +import dev.dres.api.rest.handler.activeManagerForUser +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.log.QueryEventLog +import dev.dres.run.eventstream.EventStreamProcessor +import dev.dres.run.eventstream.InvalidRequestEvent +import dev.dres.run.eventstream.QueryEventLogEvent +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* + +/** + * A client facing [PostRestHandler] that can be used to sumit query logs. + * + * @author Loris Sauter + * @author Luca Rossetto + * @version 2.0.0 + */ +class QueryLogHandler : AbstractLogHandler() { + override val route = "log/query" + + @OpenApi(summary = "Accepts query logs from participants", + path = "/api/v1/log/query", + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(QueryEventLog::class)]), + tags = ["Log"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)])] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationManager = ctx.activeManagerForUser() + val queryEventLog = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse){ + EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), evaluationManager.id, ctx.body())) + throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) + }.copy(serverTimeStamp = System.currentTimeMillis()) + EventStreamProcessor.event(QueryEventLogEvent(ctx.sessionId(), evaluationManager.id, queryEventLog)) + return SuccessStatus("Log received") + } +} + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt new file mode 100644 index 000000000..a865c81f8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt @@ -0,0 +1,51 @@ +package dev.dres.api.rest.handler.log + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.activeManagerForUser +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.log.QueryResultLog +import dev.dres.run.eventstream.EventStreamProcessor +import dev.dres.run.eventstream.InvalidRequestEvent +import dev.dres.run.eventstream.QueryResultLogEvent +import dev.dres.utilities.extensions.sessionId +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.openapi.* + +/** + * A client facing [PostRestHandler] that can be used to submit results logs. + * + * @author Loris Sauter + * @author Luca Rossetto + * @version 2.0.0 + */ +class ResultLogHandler: AbstractLogHandler() { + override val route = "log/result" + + @OpenApi(summary = "Accepts result logs from participants.", + path = "/api/v1/log/result", + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(QueryResultLog::class)]), + tags = ["Log"], + queryParams = [ + OpenApiParam("session", String::class, "Session Token", required = true, allowEmptyValue = false) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)])] + ) + override fun doPost(ctx: Context): SuccessStatus { + val evaluationManager = ctx.activeManagerForUser() + val queryResultLog = try { + ctx.bodyAsClass(QueryResultLog::class.java) + } catch (e: BadRequestResponse){ + EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), evaluationManager.id, ctx.body())) + throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) + }.copy(serverTimeStamp = System.currentTimeMillis()) + EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionId(), evaluationManager.id, queryResultLog)) + return SuccessStatus("Log entry received.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index c3bfd674e..94d1ba8f7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -9,6 +9,7 @@ import dev.dres.utilities.extensions.errorResponse import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.firstOrNull /** * An [AbstractPreviewHandler] used to access previews of [MediaItem]s based on a specific [Submission]. @@ -18,14 +19,14 @@ import jetbrains.exodus.database.TransientEntityStore * @version 2.0.0 */ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : AbstractPreviewHandler(store, config) { - override val route: String = "preview/submission/{runId}/{submissionId}" + override val route: String = "preview/submission/{evaluationId}/{submissionId}" @OpenApi( summary = "Returns a preview image for a specific submission.", - path = "/api/v1/preview/submission/{runId}/{submissionId}", + path = "/api/v1/preview/submission/{evaluationId}/{submissionId}", pathParams = [ - OpenApiParam("runId", String::class, "Competition ID"), - OpenApiParam("submissionId", String::class, "Subission ID") + OpenApiParam("evaluationId", String::class, "The evaluation ID."), + OpenApiParam("submissionId", String::class, "The submission ID") ], tags = ["Media"], responses = [OpenApiResponse( @@ -43,14 +44,18 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Competition Run $runId not found", ctx) if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Competition Run $runId is not interactive", ctx) + /* TODO: Make this work for batched submissions ? */ + val submission = run.allSubmissions.find { it.id == submissionId } ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) + val verdict = submission.verdicts.firstOrNull() + ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) when { - submission.item != null -> { - handlePreviewRequest(submission.item!!, if (submission.start != null) submission.start else null, ctx) + verdict.item != null -> { + handlePreviewRequest(verdict.item!!, if (verdict.start != null) verdict.start else null, ctx) } - submission.text != null -> { + verdict.text != null -> { ctx.header("Cache-Control", "max-age=31622400") ctx.contentType("image/png") ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index 65de9b8b2..7ff09ef05 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -1,55 +1,54 @@ -package dev.dres.api.rest.handler +package dev.dres.api.rest.handler.submission import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.preview.AbstractPreviewHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.submission.RunResult -import dev.dres.data.dbo.DAO -import dev.dres.data.dbo.DaoIndexer -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaItemSegmentList -import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.Config +import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.batch.* +import dev.dres.data.model.submissions.Submission import dev.dres.run.InteractiveRunManager import dev.dres.run.NonInteractiveRunManager -import dev.dres.utilities.TimeUtil -import dev.dres.utilities.extensions.UID import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* -import io.javalin.security.RouteRole - -abstract class BatchSubmissionHandler(internal val collections: DAO, internal val itemIndex: DaoIndexer>, internal val segmentIndex: DaoIndexer) : PostRestHandler, AccessManagedRestHandler { - +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query +import java.util.* + +/** + * An [GetRestHandler] used to process batched [Submission]s. + * + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class BatchSubmissionHandler(private val store: TransientEntityStore, private val config: Config) : PostRestHandler, AccessManagedRestHandler { + /** [BatchSubmissionHandler] requires [ApiRole.PARTICIPANT]. */ + override val permittedRoles = setOf(ApiRole.PARTICIPANT) + + /** All [BatchSubmissionHandler]s are part of the v1 API. */ override val apiVersion = "v1" - override val permittedRoles: Set = setOf(ApiRole.PARTICIPANT) - internal fun userId(ctx: Context): EvaluationId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) - - fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(404, "Parameter 'runId' is missing!'", ctx) - }.UID() - - protected fun getInteractiveManager(userId: EvaluationId, runId: EvaluationId): InteractiveRunManager? - = AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == runId } - - protected fun getNonInteractiveManager(userId: EvaluationId, runId: EvaluationId): NonInteractiveRunManager? - = AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == runId } -} - -class JsonBatchSubmissionHandler(collections: DAO, itemIndex: DaoIndexer>, segmentIndex: DaoIndexer) : BatchSubmissionHandler(collections, itemIndex, segmentIndex) { - - override val route: String = "submit/{runId}" + override val route: String = "submit/{evaluationId}" @OpenApi(summary = "Endpoint to accept batch submissions in JSON format", - path = "/api/v1/submit/{runId}", + path = "/api/v1/submit/{evaluationId}", methods = [HttpMethod.POST], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(RunResult::class)]), tags = ["Batch Submission"], responses = [ @@ -57,29 +56,56 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ] - ) + ]) override fun doPost(ctx: Context): SuccessStatus { - - val userId = userId(ctx) - val runId = runId(ctx) - - val runManager = getNonInteractiveManager(userId, runId) ?: throw ErrorStatusException(404, "Run ${runId.string} not found", ctx) - - val rac = RunActionContext.runActionContext(ctx, runManager) - - val runResult = try{ + /* Try to parse message. */ + val runResult = try { ctx.bodyAsClass() } catch (e: Exception) { - throw ErrorStatusException(400, "Error parsing json batch", ctx) + throw ErrorStatusException(400, "Error parsing JSON body", ctx) + } + + /* Obtain basic information required for submission processing. */ + val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val runManager = this.getEligibleRunManager(userId, ctx) + val time = System.currentTimeMillis() + this.store.transactional { + val rac = RunActionContext.runActionContext(ctx, runManager) + val submission = toSubmission(userId, runManager, runResult, time, ctx) + runManager.postSubmission(rac, submission) } + return SuccessStatus("Submission received.") + } - val team = runManager.template.teams.find { - it.users.contains(userId) - }?.uid ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) + /** + * Converts the user request tu a [Submission]. + * + * Creates the associated database entry. Requires an ongoing transaction. + * + * @param userId The [UserId] of the user who triggered the [Submission]. + * @param runManager The [InteractiveRunManager] + * @param submission The submitted [RunResult]s. + * @param submissionTime Time of the submission. + * @param ctx The HTTP [Context] + */ + private fun toSubmission(userId: UserId, runManager: NonInteractiveRunManager, submission: RunResult, submissionTime: Long, ctx: Context): Submission { + /* Find team that the user belongs to. */ + val user = User.query(User::id eq userId).firstOrNull() + ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) + val team = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull() + ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) + + /* Create new submission. */ + val new = Submission.new { + this.id = UUID.randomUUID().toString() + this.user = user + this.team = team + this.timestamp = submissionTime + } - val resultBatches = runResult.tasks.mapNotNull { taskResult -> - val task = runManager.tasks(rac).find { it.template.name == taskResult.task } ?: return@mapNotNull null + /* Process submitted results. */ + val resultBatches = submission.tasks.mapNotNull { taskResult -> + /*val task = runManager.tasks(rac).find { it.template.name == taskResult.task } ?: return@mapNotNull null val mediaCollectionId = task.template.mediaCollectionId val results = taskResult.results.map { result -> if (result.item != null) { @@ -117,21 +143,18 @@ class JsonBatchSubmissionHandler(collections: DAO, itemIndex: D (BaseResultBatch(mediaCollectionId, taskResult.resultName, team, results as List)) } else { BaseResultBatch(mediaCollectionId, taskResult.resultName, team, results) - } - - } - - val submissionBatch = if (resultBatches.all { it.results.first() is TemporalBatchElement }) { - @Suppress("UNCHECKED_CAST") - (TemporalSubmissionBatch(team, userId, EvaluationId(), resultBatches as List>)) - } else { - BaseSubmissionBatch(team, userId, EvaluationId(), resultBatches as List>) + }*/ } - runManager.addSubmissionBatch(submissionBatch) - - return SuccessStatus("Submission batch received") - + return new } + /** + * Returns the [NonInteractiveRunManager] that is eligible for the given [Context] + */ + private fun getEligibleRunManager(userId: UserId, ctx: Context): NonInteractiveRunManager { + val evaluationId = ctx.evaluationId() + return AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == evaluationId } + ?: throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 8b39817c6..daf23be5c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -18,8 +18,9 @@ import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.Task import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.SubmissionType +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.VerdictType import dev.dres.run.InteractiveRunManager import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -38,7 +39,9 @@ import java.nio.file.Paths import java.util.* /** - * An [GetRestHandler] used to process [Submission]s + * An [GetRestHandler] used to process [Submission]s. + * + * This endpoint strictly considers [Submission]s to contain single [Verdict]s. * * @author Luca Rossetto * @author Loris Sauter @@ -49,7 +52,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con /** [SubmissionHandler] requires [ApiRole.PARTICIPANT]. */ override val permittedRoles = setOf(ApiRole.PARTICIPANT) - /** All [AbstractPreviewHandler]s are part of the v1 API. */ + /** All [SubmissionHandler]s are part of the v1 API. */ override val apiVersion = "v1" override val route = "submit" @@ -109,7 +112,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionId(), ctx.req().remoteAddr) if (run.currentTaskDescription(rac).taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS)) { //pre-generate preview - generatePreview(submission) + generatePreview(submission.verdicts.first()) } submission to result } @@ -117,13 +120,13 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con logger.info("Submission ${s.id} received status $r.") return when (r) { - SubmissionStatus.CORRECT -> SuccessfulSubmissionsStatus(SubmissionStatus.CORRECT, "Submission correct!") - SubmissionStatus.WRONG -> SuccessfulSubmissionsStatus(SubmissionStatus.WRONG, "Submission incorrect! Try again") - SubmissionStatus.INDETERMINATE -> { + VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(VerdictStatus.CORRECT, "Submission correct!") + VerdictStatus.WRONG -> SuccessfulSubmissionsStatus(VerdictStatus.WRONG, "Submission incorrect! Try again") + VerdictStatus.INDETERMINATE -> { ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(SubmissionStatus.INDETERMINATE, "Submission received. Waiting for verdict!") + SuccessfulSubmissionsStatus(VerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") } - SubmissionStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(SubmissionStatus.UNDECIDABLE,"Submission undecidable. Try again!") + VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(VerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } } @@ -136,7 +139,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con val rac = RunActionContext.runActionContext(ctx, it) it.currentTask(rac)?.isRunning == true } - if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) return managers.first() } @@ -162,12 +165,11 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con val rac = RunActionContext.runActionContext(ctx, runManager) /* Create new submission. */ - Submission.new { + val submission = Submission.new { this.id = UUID.randomUUID().toString() this.user = user this.team = team this.timestamp = submissionTime - this.text = map[PARAMETER_NAME_TEXT]?.first() } /* If text is supplied, it supersedes other parameters */ @@ -175,18 +177,17 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con val itemParam = map[PARAMETER_NAME_ITEM]?.first() val currentTaskId = runManager.currentTask(rac)?.id val task = Task.query(Task::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException(404, "No active task for ID '$currentTaskId' could be found.", ctx) - val submission = Submission.new { - this.id = submissionId - this.status = SubmissionStatus.INDETERMINATE - this.user = user - this.team = team + + /* Create Verdict. */ + val verdict = Verdict.new { + this.status = VerdictStatus.INDETERMINATE this.task = task - this.timestamp = submissionTime } + submission.verdicts.add(verdict) if (textParam != null) { - submission.type = SubmissionType.TEXT - submission.text = textParam + verdict.type = VerdictType.TEXT + verdict.text = textParam return submission } else if (itemParam != null) { val collection = runManager.currentTaskDescription(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ @@ -232,13 +233,13 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con /* Assign information to submission. */ if (range != null) { - submission.item = item - submission.type = SubmissionType.TEMPORAL - submission.start = range.first - submission.end = range.second + verdict.item = item + verdict.type = VerdictType.TEMPORAL + verdict.start = range.first + verdict.end = range.second } else { - submission.item = item - submission.type = SubmissionType.ITEM + verdict.item = item + verdict.type = VerdictType.ITEM } } else { throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) @@ -250,15 +251,15 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con /** * Triggers generation of a preview image for the provided [Submission]. * - * @param submission The [Submission] to generate preview for. + * @param verdict The [Verdict] to generate preview for. */ - private fun generatePreview(submission: Submission) { - if (submission.type != SubmissionType.TEMPORAL) return - if (submission.item == null) return - val destinationPath = Paths.get(this.config.cachePath, "previews", submission.item!!.collection.name, submission.item!!.name, "${submission.start}.jpg") + private fun generatePreview(verdict: Verdict) { + if (verdict.type != VerdictType.TEMPORAL) return + if (verdict.item == null) return + val destinationPath = Paths.get(this.config.cachePath, "previews", verdict.item!!.collection.name, verdict.item!!.name, "${verdict.start}.jpg") if (Files.exists(destinationPath)){ return } - FFmpegUtil.extractFrame(submission.item!!.pathToOriginal(), submission.start!!, destinationPath) + FFmpegUtil.extractFrame(verdict.item!!.pathToOriginal(), verdict.start!!, destinationPath) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 8fcacc540..628d2ee13 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.competition.tasks.ApiTaskDescription +import dev.dres.api.rest.types.competition.tasks.ApiTaskTemplate import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.template.task.TaskTemplate import io.javalin.http.Context @@ -17,7 +17,7 @@ import kotlinx.dnq.query.asSequence * @author Loris Sauter * @version 2.0.0 */ -class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { +class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { override val route: String = "template/{templateId}/task/list" @@ -27,7 +27,7 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescrip pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index fcb2fd33c..f31fa2d9c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.competition.ApiCompetitionDescription +import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.template.EvaluationTemplate import io.javalin.http.Context @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { +class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { override val route: String = "template/{templateId}" @OpenApi( @@ -25,7 +25,7 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompe pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ - OpenApiResponse("200", [OpenApiContent(ApiCompetitionDescription::class)]), + OpenApiResponse("200", [OpenApiContent(ApiEvaluationTemplate::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt index 413c54fa1..adb160f7a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler.template import com.github.kittinunf.fuel.util.decodeBase64 import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.types.competition.ApiCompetitionDescription +import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -40,7 +40,7 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) path = "/api/v1/template/{templateId}", pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], methods = [HttpMethod.PATCH], - requestBody = OpenApiRequestBody([OpenApiContent(ApiCompetitionDescription::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiEvaluationTemplate::class)]), tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -51,7 +51,7 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) ) override fun doPatch(ctx: Context): SuccessStatus { val apiValue = try { - ctx.bodyAsClass(ApiCompetitionDescription::class.java) + ctx.bodyAsClass(ApiEvaluationTemplate::class.java) } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } @@ -119,7 +119,7 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) t.targets.clear() for (target in task.targets) { val item = MediaItem.query(MediaItem::id eq target.target).first() - t.targets.add(TaskDescriptionTarget.new { + t.targets.add(TaskTemplateTarget.new { this.item = item this.type = target.type.type this.start = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 3bc3112e6..9e50df6a9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -9,7 +9,7 @@ import dev.dres.data.model.media.MediaItem * @author Ralph Gasser * @version 1.1.0 */ -data class ApiMediaItem(val id: String= EvaluationId.EMPTY.string, val name: String, val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { +data class ApiMediaItem(val id: String, val name: String, val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, val fps: Float? = null) { init { if (this.type == ApiMediaType.VIDEO) { require(this.durationMs != null) { "Duration must be set for a video item." } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt index f0d0b4252..9c91b2adb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -11,5 +11,6 @@ import dev.dres.data.model.media.MediaType */ enum class ApiMediaType(val type: MediaType) { IMAGE(MediaType.IMAGE), - VIDEO(MediaType.VIDEO) + VIDEO(MediaType.VIDEO), + TEXT(MediaType.TEXT) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt similarity index 77% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt index f777cbe8c..690556800 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt @@ -1,11 +1,13 @@ package dev.dres.api.rest.types.competition -import dev.dres.api.rest.types.competition.tasks.ApiTaskDescription +import dev.dres.api.rest.types.competition.tasks.ApiTaskTemplate import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup import dev.dres.api.rest.types.competition.tasks.ApiTaskType import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.competition.team.ApiTeamGroup import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.TemplateId + /** * The RESTful API equivalent for [EvaluationTemplate]. * @@ -13,13 +15,13 @@ import dev.dres.data.model.template.EvaluationTemplate * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -data class ApiCompetitionDescription( - val id: String, +data class ApiEvaluationTemplate( + val id: TemplateId, val name: String, val description: String?, val taskTypes: List, val taskGroups: List, - val tasks: List, + val tasks: List, val teams: List, val teamGroups: List, val judges: List diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt index 749e26cae..9dda47de1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.api.rest.types.collection.time.ApiTemporalRange -import dev.dres.data.model.template.task.TaskDescriptionTarget +import dev.dres.data.model.template.task.TaskTemplateTarget /** - * The RESTful API equivalent for [TaskDescriptionTarget]. + * The RESTful API equivalent for [TaskTemplateTarget]. * * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt similarity index 79% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt index 20307624f..59e8eac52 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskDescription.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt @@ -1,8 +1,8 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.TaskDescriptionId import dev.dres.data.model.media.CollectionId +import dev.dres.data.model.template.TemplateId /** * The RESTful API equivalent for [TaskTemplate]. @@ -11,8 +11,8 @@ import dev.dres.data.model.media.CollectionId * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class ApiTaskDescription( - val id: TaskDescriptionId? = null, +class ApiTaskTemplate( + val id: TemplateId? = null, val name: String, val taskGroup: String, val taskType: String, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt index 642639772..0ea405310 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt @@ -11,7 +11,6 @@ import dev.dres.data.model.template.task.options.TargetOption enum class ApiTargetOption(val option: TargetOption){ SINGLE_MEDIA_ITEM(TargetOption.MEDIA_ITEM), SINGLE_MEDIA_SEGMENT(TargetOption.MEDIA_SEGMENT), - MULTIPLE_MEDIA_ITEMS(TargetOption.MULTIPLE_MEDIA_ITEMS), JUDGEMENT(TargetOption.JUDGEMENT), VOTE(TargetOption.VOTE), TEXT(TargetOption.TEXT) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt new file mode 100644 index 000000000..665093d60 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt @@ -0,0 +1,22 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.api.rest.types.competition.ApiEvaluationTemplate +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.EvaluationId + +/** + * The RESTful API equivalent of a [Evaluation]. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +data class ApiEvaluation( + val evaluationId: EvaluationId, + val name: String, + val type: ApiRunType, + val template: ApiEvaluationTemplate, + val started: Long, + val ended: Long?, + val tasks: List +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt index 405173259..1f6b06a26 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt @@ -7,5 +7,6 @@ import dev.dres.data.model.run.RunType */ enum class ApiRunType(val type: RunType) { SYNCHRONOUS(RunType.INTERACTIVE_SYNCHRONOUS), - ASYNCHRONOUS(RunType.INTERACTIVE_ASYNCHRONOUS) + ASYNCHRONOUS(RunType.INTERACTIVE_ASYNCHRONOUS), + NON_INTERACTIVE(RunType.NON_INTERACTIVE) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index 0a9eed0fa..e78794391 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -1,12 +1,10 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionId -import dev.dres.data.model.submissions.SubmissionStatus /** - * Contains information about a [Submission]. + * The RESTful API equivalent of a [Submission]. * * @author Luca Rossetto * @author Ralph Gasser @@ -14,14 +12,10 @@ import dev.dres.data.model.submissions.SubmissionStatus */ data class ApiSubmission( val id: SubmissionId, - val teamId: String? = null, - val teamName: String? = null, - val memberId: String? = null, - val memberName: String? = null, - val status: SubmissionStatus, - val timestamp: Long? = null, - val item: ApiMediaItem? = null, - val text: String? = null, - val start: Long? = null, - val end: Long? = null + val teamId: String, + val teamName: String, + val memberId: String, + val memberName: String, + val timestamp: Long, + val verdicts: List, ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt new file mode 100644 index 000000000..66f2d2cc1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -0,0 +1,20 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.run.Task +import dev.dres.data.model.run.TaskId +import dev.dres.data.model.template.TemplateId + +/** + * The RESTful API equivalent of a [Task]. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +data class ApiTask( + val taskId: TaskId, + val templateId: TemplateId, + val started: Long?, + val ended: Long?, + val submissions: List +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt new file mode 100644 index 000000000..54c803d8b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt @@ -0,0 +1,19 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.api.rest.types.collection.ApiMediaItem + +/** + * The RESTful API equivalent for the type of a [ApiVerdict]. + * + * @see ApiVerdict + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiVerdict( + val type: ApiVerdictType, + val status: ApiVerdictStatus, + val item: ApiMediaItem?, + val text: String?, + val start: Long?, + val end: Long? +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt new file mode 100644 index 000000000..20f6212bb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt @@ -0,0 +1,17 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.submissions.VerdictStatus + +/** + * The RESTful API equivalent for the type of a [VerdictStatus] + * + * @see ApiVerdict + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiVerdictStatus(val status: VerdictStatus) { + CORRECT(VerdictStatus.CORRECT), + WRONG(VerdictStatus.WRONG), + INDETERMINATE(VerdictStatus.INDETERMINATE), + UNDECIDABLE(VerdictStatus.UNDECIDABLE) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt new file mode 100644 index 000000000..3e570bafa --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt @@ -0,0 +1,17 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.VerdictType + +/** + * The RESTful API equivalent for the type of a [VerdictType] + * + * @see ApiVerdict + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiVerdictType(val type: VerdictType) { + ITEM(VerdictType.ITEM), + TEMPORAL(VerdictType.TEMPORAL), + TEXT(VerdictType.TEXT) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt index 9fe3acc24..c2c219510 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt @@ -20,7 +20,7 @@ data class RunState( val timeElapsed: Long ) { constructor(run: InteractiveRunManager, context: RunActionContext) : this( - run.id.string, + run.id, //RestRunManagerStatus.getState(run, context), run.status, RestTaskRunStatus.fromTaskRunStatus(run.currentTask(context)?.status), @@ -32,16 +32,6 @@ data class RunState( run.timeLeft(context) / 1000, run.timeElapsed(context) / 1000 ) - -// companion object { -// /** -// * Checks if the given run is asynchronous and the current user (from the context) is an admin. -// */ -// fun checkAsyncAdmin(run: InteractiveRunManager, context: RunActionContext): Boolean { -// return run is InteractiveAsynchronousRunManager && context.isAdmin -// } -// } - } enum class RestTaskRunStatus { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt index e77aeec79..2d9692d67 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt @@ -1,13 +1,15 @@ package dev.dres.api.rest.types.evaluation.websocket +import dev.dres.data.model.run.EvaluationId + /** * Message send by the DRES server via WebSocket to inform clients about the state of the run. * * @author Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ -data class ServerMessage(val runId: String, val type: ServerMessageType, val timestamp: Long = System.currentTimeMillis()) +data class ServerMessage(val evaluationId: EvaluationId, val type: ServerMessageType, val timestamp: Long = System.currentTimeMillis()) enum class ServerMessageType { COMPETITION_START, /** Competition run started. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt new file mode 100644 index 000000000..3860e4eac --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt @@ -0,0 +1,10 @@ +package dev.dres.api.rest.types.judgement + +import dev.dres.api.rest.types.evaluation.ApiVerdictStatus + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +data class ApiJudgement(val token: String, val validator: String, val verdict: ApiVerdictStatus) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt new file mode 100644 index 000000000..05798b129 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt @@ -0,0 +1,10 @@ +package dev.dres.api.rest.types.judgement + +import dev.dres.api.rest.types.collection.ApiMediaType + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ApiJudgementRequest(val token: String?, val mediaType: ApiMediaType, val validator: String, val collection: String, val item: String, val taskDescription: String, val startTime: Long?, val endTime: Long?) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementValidatorStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementValidatorStatus.kt new file mode 100644 index 000000000..036eaf4d4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementValidatorStatus.kt @@ -0,0 +1,8 @@ +package dev.dres.api.rest.types.judgement + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +class ApiJudgementValidatorStatus(val validatorName: String, val pending: Int, val open: Int) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt new file mode 100644 index 000000000..da4351a7a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt @@ -0,0 +1,10 @@ +package dev.dres.api.rest.types.judgement + +import dev.dres.data.model.submissions.VerdictStatus + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +data class ApiVote(val verdict: VerdictStatus) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt index 266f424b0..70b13a5df 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt @@ -1,5 +1,5 @@ package dev.dres.api.rest.types.status -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus -data class SuccessfulSubmissionsStatus(val submission: SubmissionStatus, val description: String) : AbstractStatus(status = true) \ No newline at end of file +data class SuccessfulSubmissionsStatus(val submission: VerdictStatus, val description: String) : AbstractStatus(status = true) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt deleted file mode 100644 index a6800dfb5..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemSegmentList.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.dres.data.model.media - -import dev.dres.data.model.PersistentEntity - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -data class MediaItemSegmentList(override var id: EvaluationId, val mediaItemId: EvaluationId, val segments: MutableList) : PersistentEntity { - init { - require(segments.all { it.mediaItemId == mediaItemId } ){"All segments need to belong to the same media item"} - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt index ab4f03619..cb5feeead 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt @@ -16,6 +16,7 @@ class MediaType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { val IMAGE by enumField { description = "IMAGE"; suffix = "mp4"; } val VIDEO by enumField { description = "VIDEO"; suffix = "jpg"; } + val TEXT by enumField { description = "TEXT"; suffix = "txt"; } } var description by xdRequiredStringProp(unique = true) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 1660aa963..6beee06e7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -1,6 +1,5 @@ package dev.dres.data.model.run - import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.interfaces.Run diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index de00da3f3..21bf45e23 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -4,10 +4,7 @@ import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.task.options.TargetOption import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.validation.MediaItemsSubmissionBatchValidator -import dev.dres.run.validation.TemporalOverlapSubmissionBatchValidator import dev.dres.run.validation.TransientMediaSegment -import dev.dres.run.validation.interfaces.SubmissionBatchValidator import kotlinx.dnq.query.* /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt index a99681f1e..e2feddfdc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt @@ -48,7 +48,7 @@ abstract class AbstractTaskRun(task: Task): TaskRun { /** Reference to the [TaskTemplate] describing this [AbstractTaskRun]. */ override val template: TaskTemplate - get() = this.task.description + get() = this.task.template @Volatile override var status: TaskRunStatus = TaskRunStatus.CREATED diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt index 9fba9ce43..bbe00e5a5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt @@ -1,10 +1,14 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.evaluation.ApiEvaluation +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun +import dev.dres.data.model.submissions.Submission import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence typealias EvaluationId = String @@ -52,6 +56,22 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { /** A fixed limit on submission previews. */ var limitSubmissionPreviews by xdIntProp() + /** + * Converts this [Evaluation] to a RESTful API representation [ApiEvaluation]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiEvaluation] + */ + fun toApi(): ApiEvaluation = ApiEvaluation( + evaluationId = this.evaluationId, + name = this.name, + type = this.type.toApi(), + template = this.template.toApi(), + started = this.started, + ended = this.ended, + tasks = this.tasks.asSequence().map { it.toApi() }.toList() + ) /** * Generates and returns an [EvaluationRun] instance for this [Evaluation]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index bbd8e22a7..d46d93c17 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -71,7 +71,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat this.type = RunType.INTERACTIVE_SYNCHRONOUS this.evaluation = this@InteractiveSynchronousEvaluation.evaluation this.started = System.currentTimeMillis() - this.description = description + this.template = description }) /** The [InteractiveSynchronousEvaluation] this [Task] belongs to.*/ @@ -84,7 +84,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat /** Reference to the [TaskTemplate] describing this [Task]. */ override val template: TaskTemplate - get() = this.task.description + get() = this.task.template /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ override val filter: SubmissionFilter = this.template.newFilter() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 2b023b055..4f6e6a0de 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -7,7 +7,6 @@ import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.score.interfaces.ResultBatchTaskScorer import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.size diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt index 0809e3932..c78299834 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt @@ -1,5 +1,8 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.evaluation.ApiVerdictType +import dev.dres.data.model.submissions.VerdictType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -20,4 +23,11 @@ class RunType(entity: Entity) : XdEnumEntity(entity) { var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [RunType] to a RESTful API representation [ApiRunType]. + * + * @return [ApiRunType] + */ + fun toApi() = ApiRunType.values().find { it.type == this } ?: throw IllegalStateException("Run type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index 67de3b23c..7e5141eb5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -1,13 +1,15 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.evaluation.ApiTask import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.submissions.BatchedSubmission import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.Verdict import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence -typealias TaskId = String +typealias TaskId = String /** * Represents a [Task], i.e., a concrete instance of a [TaskTemplate], as executed by DRES. @@ -23,9 +25,6 @@ class Task(entity: Entity) : PersistentEntity(entity) { get() = this.id set(value) { this.id = value } - /** The [RunType] of this [Evaluation]. */ - var type by xdLink1(RunType) - /** Timestamp of when this [Evaluation] started. */ var started by xdRequiredLongProp() @@ -33,14 +32,26 @@ class Task(entity: Entity) : PersistentEntity(entity) { var ended by xdNullableLongProp() /** The [TaskTemplate] this [Task] is an instance of. */ - var description by xdLink1(TaskTemplate) + var template by xdLink1(TaskTemplate) /** The [Evaluation] this [Task] belongs to. */ var evaluation by xdParent(Evaluation::tasks) /** List of [Submission]s received by this [Task]. */ - val submissions by xdChildren0_N(Submission::task) - - /** List of [BatchedSubmission]s received by this [Task]. */ - val batched by xdChildren0_N(BatchedSubmission::task) + val submissions by xdChildren0_N(Verdict::task) + + /** + * Converts this [Task] to a RESTful API representation [ApiTask]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiTask] + */ + fun toApi(): ApiTask = ApiTask( + taskId = this.taskId, + templateId = this.template.id, + started = this.started, + ended = this.ended, + submissions = this.submissions.asSequence().map { it.toApi() }.toList() + ) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt deleted file mode 100644 index baa76f057..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/BatchedSubmission.kt +++ /dev/null @@ -1,34 +0,0 @@ -package dev.dres.data.model.submissions - -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.team.Team -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.simple.min -import kotlinx.dnq.xdLink1 -import kotlinx.dnq.xdRequiredLongProp - -/** - * A [BatchedSubmission] as submitted by a competition participant and received by DRES. - * - * @author Luca Rossetto - * @version 2.0.0 - */ -class BatchedSubmission(entity: Entity) : PersistentEntity(entity) { - /** The [SubmissionId] of this [User]. */ - var submissionId: SubmissionId - get() = this.id - set(value) { this.id = value } - - /** The timestamp of this [Submission]. */ - var timestamp by xdRequiredLongProp { min(0L) } - - /** The [SubmissionStatus] of this [Submission]. */ - var status by xdLink1(SubmissionStatus) - - /** The [Team] that submitted this [Submission] */ - var team by xdLink1(Team) - - /** The [User] that submitted this [Submission] */ - var user by xdLink1(User) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 963bbbd37..7369d8b2f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -4,20 +4,20 @@ import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User import dev.dres.data.model.template.team.Team -import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.run.Task import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence import kotlinx.dnq.simple.min -import kotlinx.dnq.simple.requireIf typealias SubmissionId = String /** * A [Submission] as submitted by a competition participant and received by DRES. * + * Contains one to N [Verdict]s regarding a [Task]. + * * @author Ralph Gasser * @author Luca Rossetto * @version 2.0.0 @@ -33,46 +33,21 @@ sealed class Submission(entity: Entity) : PersistentEntity(entity) { /** The timestamp of this [Submission]. */ var timestamp by xdRequiredLongProp { min(0L) } - /** The [SubmissionStatus] of this [Submission]. */ - var status by xdLink1(SubmissionStatus) - - /** The [Task] this [Submission] belongs to. */ - var task by xdParent(Task::submissions) - /** The [Team] that submitted this [Submission] */ var team by xdLink1(Team) /** The [User] that submitted this [Submission] */ var user by xdLink1(User) - /** The [SubmissionType] of this [Submission]. */ - var type by xdLink1(SubmissionType) - - /** The [MediaItem] submitted. Only for [SubmissionType.ITEM] or [SubmissionType.TEMPORAL]. */ - var item by xdLink0_1(MediaItem) - - /** The start frame number of this [Submission]. */ - var start by xdNullableLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } - - /** The end frame number of this [Submission]. */ - var end by xdNullableLongProp { requireIf { this.type == SubmissionType.TEMPORAL } } - - /** The text submitted. Only for [SubmissionType.TEXT] . */ - var text by xdStringProp { requireIf { this.type == SubmissionType.TEXT } } - - /** Returns the [TemporalRange] for this [Submission]. */ - val temporalRange: TemporalRange? - get() = try { - TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) - } catch (e: NullPointerException) { - null - } + /** The [Verdict]s that make-up this [Submission]. For batched submissions, more than one verdict can be possible. */ + val verdicts by xdChildren1_N(Verdict::submission) /** * Converts this [Submission] to a RESTful API representation [ApiSubmission]. * * This is a convenience method and requires an active transaction context. * + * @param blind True, if a "blind" Submission (without [verdicts]) should be generated. * @return [ApiSubmission] */ fun toApi(blind: Boolean = false): ApiSubmission = ApiSubmission( @@ -81,12 +56,7 @@ sealed class Submission(entity: Entity) : PersistentEntity(entity) { teamName = this.team.name, memberId = this.user.id, memberName = this.user.username, - status = this.status, timestamp = this.timestamp, - item = if (!blind) { this.item?.toApi() } else { null }, - text = if (!blind) { this.text } else { null }, - start = if (!blind) { this.start } else { null }, - end = if (!blind) { this.end } else { null } + verdicts = if (blind) { emptyList() } else { this.verdicts.asSequence().map { it.toApi() }.toList()} ) - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt new file mode 100644 index 000000000..a82dc38f3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt @@ -0,0 +1,70 @@ +package dev.dres.data.model.submissions + +import dev.dres.api.rest.types.evaluation.ApiVerdict +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.run.Task +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.requireIf + +/** + * A [VerdictStatus] as submitted by a competition participant. Makes a statement about a [Task]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @version 2.0.0 + */ +class Verdict(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The [VerdictStatus] of this [Verdict]. */ + var status by xdLink1(VerdictStatus) + + /** The [VerdictType] of this [Verdict]. */ + var type by xdLink1(VerdictType) + + /** The [Submission] this [Verdict] belongs to. */ + var submission by xdParent(Submission::verdicts) + + /** The [Task] this [Verdict] belongs to. */ + var task by xdParent(Task::submissions) + + /** The [MediaItem] submitted. Only for [VerdictType.ITEM] or [VerdictType.TEMPORAL]. */ + var item by xdLink0_1(MediaItem) + + /** The start frame number of this [Submission]. */ + var start by xdNullableLongProp { requireIf { this.type == VerdictType.TEMPORAL } } + + /** The end frame number of this [Submission]. */ + var end by xdNullableLongProp { requireIf { this.type == VerdictType.TEMPORAL } } + + /** The text submitted. Only for [VerdictType.TEXT] . */ + var text by xdStringProp { requireIf { this.type == VerdictType.TEXT } } + + /** Returns the [TemporalRange] for this [Verdict]. */ + val temporalRange: TemporalRange? + get() = try { + TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) + } catch (e: NullPointerException) { + null + } + + /** + * Converts this [VerdictStatus] to a RESTful API representation [ApiVerdict]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiVerdict] + */ + fun toApi(): ApiVerdict = ApiVerdict( + status = this.status.toApi(), + type = this.type.toApi(), + item = this.item?.toApi(), + text = this.text, + start = this.start, + end = this.end + ) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt similarity index 68% rename from backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt rename to backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt index a3bc60f92..3c080c45c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt @@ -1,6 +1,9 @@ package dev.dres.data.model.submissions +import dev.dres.api.rest.types.competition.tasks.options.ApiTargetOption +import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.data.model.admin.Role +import dev.dres.data.model.template.task.options.HintOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -12,8 +15,8 @@ import kotlinx.dnq.xdRequiredStringProp * @author Luca Rossetto * @version 2.0.0 */ -class SubmissionStatus(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class VerdictStatus(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val CORRECT by enumField { description = "CORRECT" } /** Submission has been deemed as correct. */ val WRONG by enumField { description = "WRONG" } val INDETERMINATE by enumField { description = "INDETERMINATE" } /** Submission has been deemed as wrong. */ @@ -38,7 +41,14 @@ class SubmissionStatus(entity: Entity) : XdEnumEntity(entity) { } } - /** Name / description of the [SubmissionType]. */ + /** Name / description of the [VerdictType]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [VerdictStatus] to a RESTful API representation [ApiVerdictStatus]. + * + * @return [ApiVerdictStatus] + */ + fun toApi() = ApiVerdictStatus.values().find { it.status == this } ?: throw IllegalStateException("Verdict status ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictType.kt similarity index 65% rename from backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt rename to backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictType.kt index c2c9773bf..75535eaf6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/SubmissionType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictType.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.submissions +import dev.dres.api.rest.types.evaluation.ApiVerdictType import dev.dres.data.model.admin.Role import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity @@ -7,13 +8,13 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * The type of [Submission] with respect to its content + * The type of [Verdict] with respect to its content * * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class SubmissionType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class VerdictType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val ITEM by enumField { description = "ITEM" } val TEMPORAL by enumField { description = "TEMPORAL" } val TEXT by enumField { description = "TEXT" } @@ -36,7 +37,14 @@ class SubmissionType(entity: Entity) : XdEnumEntity(entity) { } } - /** Name / description of the [SubmissionType]. */ + /** Name / description of the [VerdictType]. */ var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [VerdictType] to a RESTful API representation [VerdictType]. + * + * @return [VerdictType] + */ + fun toApi() = ApiVerdictType.values().find { it.type == this } ?: throw IllegalStateException("Verdict type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt deleted file mode 100644 index 530e4b95a..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseResultBatch.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.data.model.submissions.batch - -import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.run.interfaces.TaskId - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -data class BaseResultBatch( - override val task: TaskId, - override val name: String, - override val teamId: TeamId, - override val results: List -) : ResultBatch \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt deleted file mode 100644 index 3b94de055..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/BaseSubmissionBatch.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.data.model.submissions.batch - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -data class BaseSubmissionBatch( - override val uid: EvaluationId, - override val teamId: EvaluationId, - override val memberId: EvaluationId, - override val results: Collection> -) : SubmissionBatch> diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt deleted file mode 100644 index 3e4bef769..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ItemBatchElement.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.data.model.submissions.batch - -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.SubmissionStatus - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -data class ItemBatchElement(override val item: MediaItem): BaseResultBatchElement { - override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt deleted file mode 100644 index 68705ffdc..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/ResultBatch.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.data.model.submissions.batch - -import dev.dres.data.model.template.TeamId -import dev.dres.data.model.run.interfaces.TaskId - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -interface ResultBatch { - val task: TaskId - val teamId: TeamId - val name: String - val results: List -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt deleted file mode 100644 index 3428adeb1..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/SubmissionBatch.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.dres.data.model.submissions.batch - -import dev.dres.data.model.submissions.aspects.OriginAspect - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -interface SubmissionBatch> : OriginAspect { - val results : Collection -} diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt deleted file mode 100644 index b534e86dc..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalBatchElement.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.data.model.submissions.batch - -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.TemporalAspect - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -data class TemporalBatchElement(override val item: MediaItem, override val start: Long, override val end: Long, ) : BaseResultBatchElement, TemporalAspect { - override var status: SubmissionStatus = SubmissionStatus.INDETERMINATE - override val temporalRange: TemporalRange - get() = TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt deleted file mode 100644 index 3c1f2a266..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/batch/TemporalSubmissionBatch.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.data.model.submissions.batch - -/** - * - * @author Luca Rossetto - * @version 1.0.0 - */ -data class TemporalSubmissionBatch( - override val teamId: EvaluationId, - override val memberId: EvaluationId, - override val uid: EvaluationId, - override val results: List>, -) : SubmissionBatch> \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt index b8df25cb2..7b8ed3542 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.template -import dev.dres.api.rest.types.competition.ApiCompetitionDescription +import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.User import dev.dres.data.model.template.task.TaskTemplate @@ -56,22 +56,22 @@ class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ val tasks by xdChildren0_N(TaskTemplate::competition) /** The [Team]s that are part of this [EvaluationTemplate]. */ - val teams by xdChildren0_N(Team::competition) + val teams by xdChildren0_N(Team::template) /** The [Team]s that are part of this [EvaluationTemplate]. */ - val teamsGroups by xdChildren0_N(TeamGroup::competition) + val teamsGroups by xdChildren0_N(TeamGroup::template) /** The [User]s that act as judge for this [EvaluationTemplate] */ val judges by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) /** - * Converts this [EvaluationTemplate] to a RESTful API representation [ApiCompetitionDescription]. + * Converts this [EvaluationTemplate] to a RESTful API representation [ApiEvaluationTemplate]. * * This is a convenience method and it requires and active transaction context. * - * @return [ApiCompetitionDescription] + * @return [ApiEvaluationTemplate] */ - fun toApi(): ApiCompetitionDescription = ApiCompetitionDescription( + fun toApi(): ApiEvaluationTemplate = ApiEvaluationTemplate( id = this.id, name = this.name, description = this.description, diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt index fed703da0..b7a26b31a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt @@ -55,7 +55,7 @@ class Hint(entity: Entity) : XdEntity(entity) { /** The start of a (potential) range. */ var temporalRangeEnd by xdNullableLongProp { requireIf { type == HintType.VIDEO } } - /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ + /** Returns the [TemporalRange] of this [TaskTemplateTarget]. */ val range: TemporalRange? get() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { TemporalRange(TemporalPoint.Millisecond(this.temporalRangeStart!!), TemporalPoint.Millisecond(this.temporalRangeEnd!!)) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt deleted file mode 100644 index 9149193da..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskDescriptionTarget.kt +++ /dev/null @@ -1,115 +0,0 @@ -package dev.dres.data.model.template.task - -import dev.dres.api.rest.types.collection.time.ApiTemporalRange -import dev.dres.api.rest.types.competition.tasks.ApiTarget -import dev.dres.api.rest.types.task.ApiContentElement -import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.Config -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* -import kotlinx.dnq.simple.requireIf -import java.io.FileNotFoundException -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths -import java.nio.file.StandardOpenOption -import java.util.* - -/** - * Represents the target of a [TaskTemplate], i.e., the [MediaItem] or part thereof that is considered correct. - * - * @author Luca Rossetto & Ralph Gasser - * @version 2.0.0 - */ -class TaskDescriptionTarget(entity: Entity) : XdEntity(entity) { - companion object: XdNaturalEntityType() - - /** The [TargetType] of this [TaskDescriptionTarget]. */ - var type by xdLink1(TargetType) - - /** The parent [TaskTemplate] this [TaskDescriptionTarget] belongs to. */ - var task by xdParent(TaskTemplate::targets) - - /** The targeted [MediaItem]. Can be null. */ - var item by xdLink0_1(MediaItem) - - /** The target text. Can be null. */ - var text by xdStringProp() { requireIf { item == null }} - - /** The start of a (potential) range. */ - var start by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } - - /** The start of a (potential) range. */ - var end by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } - - /** Returns the [TemporalRange] of this [TaskDescriptionTarget]. */ - val range: TemporalRange? - get() = if (this.start != null && this.end != null) { - TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) - } else { - null - } - - - /** - * Generates and returns a textual description of this [TaskDescriptionTarget]. - * - * @return Text - */ - fun textDescription(): String = when (this.type) { - TargetType.JUDGEMENT -> "Judgement" - TargetType.JUDGEMENT_WITH_VOTE -> "Judgement with vote" - TargetType.MEDIA_ITEM -> "Media item ${this.item?.name}" - TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.start} - ${this.end}" - TargetType.TEXT -> "Text: ${this.text}" - else -> throw IllegalStateException("The task description type ${this.type.description} is currently not supported.") - } - - /** - * - */ - fun toApi(): ApiTarget = when(this.type) { - TargetType.JUDGEMENT, - TargetType.JUDGEMENT_WITH_VOTE -> ApiTarget(this.type.toApi(), null) - TargetType.MEDIA_ITEM, - TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget(this.type.toApi(), this.item?.id, this.range?.let { ApiTemporalRange(it) }) - TargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) - else -> throw IllegalStateException("Task description of type ${this.type.description} is not supported.") - } - - /** - * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. - * - * @param config The [Config] used of path resolution. - * @return [ApiContentElement] - * - * @throws FileNotFoundException - * @throws IOException - */ - fun toQueryContentElement(config: Config): ApiContentElement { - val (content, type) = when (this.type) { - TargetType.JUDGEMENT, - TargetType.JUDGEMENT_WITH_VOTE -> null to ApiContentType.EMPTY - TargetType.MEDIA_ITEM -> { - val type = when (this.item?.type) { - MediaType.VIDEO -> ApiContentType.VIDEO - MediaType.IMAGE -> ApiContentType.IMAGE - else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") - } - val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.start, this.end)) - ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") - val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> - stream.readAllBytes() - } - Base64.getEncoder().encodeToString(data) to type - } - TargetType.TEXT -> this.text to ApiContentType.TEXT - else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") - } - return ApiContentElement(contentType = type, content = content, offset = 0L) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt index 5f8df8332..3c4081067 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt @@ -26,7 +26,7 @@ import java.lang.Long.max /** * Basic description of a [TaskRun] as executed in DRES. Defines basic attributes such as its name, its duration, - * the [TaskDescriptionTarget] and the [Hint]s, that should be presented to the user. + * the [TaskTemplateTarget] and the [Hint]s, that should be presented to the user. * * @version 2.0.0 * @author Luca Rossetto @@ -58,8 +58,8 @@ class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory /** The duration of the [TaskTemplate] in seconds. */ var duration by xdRequiredLongProp { min(0L) } - /** The [TaskDescriptionTarget]s that identify the target. Multiple entries indicate the existence of multiple targets. */ - val targets by xdChildren1_N(TaskDescriptionTarget::task) + /** The [TaskTemplateTarget]s that identify the target. Multiple entries indicate the existence of multiple targets. */ + val targets by xdChildren1_N(TaskTemplateTarget::task) /** The [Hint]s that act as clues to find the target media. */ val hints by xdChildren0_N(Hint::task) @@ -143,13 +143,13 @@ class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory fun textualDescription(): String = this.hints.asSequence().filter { it.type == HintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name /** - * Converts this [TaskTemplate] to a RESTful API representation [ApiTaskDescription]. + * Converts this [TaskTemplate] to a RESTful API representation [ApiTaskTemplate]. * * This is a convenience method and requires an active transaction context. * * @return [ApiTeam] */ - fun toApi(): ApiTaskDescription = ApiTaskDescription( + fun toApi(): ApiTaskTemplate = ApiTaskTemplate( this.id, this.name, this.taskGroup.name, diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt index 40539efef..4fe58018e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt @@ -42,7 +42,7 @@ class Team(entity: Entity) : PersistentEntity(entity) { var logo by xdBlobProp() /** The [EvaluationTemplate] this [Team] belongs to. */ - var competition by xdParent(EvaluationTemplate::teams) + var template by xdParent(EvaluationTemplate::teams) /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ var group by xdLink0_1(TeamGroup::teams) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt index 1dc1c722a..be81e9f4f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt @@ -35,7 +35,7 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { var defaultAggregator by xdLink1(TeamAggregator) /** The [EvaluationTemplate] this [Team] belongs to. */ - var competition by xdParent(EvaluationTemplate::teamsGroups) + var template by xdParent(EvaluationTemplate::teamsGroups) /** The [Team]s that belong to this [TeamGroup]. */ val teams by xdLink0_N(Team::group) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 3abeb2601..b83de1110 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -97,7 +97,7 @@ object UserManager { * @return True on success, false otherwise. */ fun update(id: UserId?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.let { Role.convertApiRole(it) }) + = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.role) /** * Deletes the [User] for the given [UserId]. diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 420e2e64c..a400c1ed6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.run.exceptions.IllegalRunStateException @@ -68,9 +68,6 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) - /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ - private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) - /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable, this.daoUpdatable) @@ -144,7 +141,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous /** Re-enqueue pending submissions for judgement (if any). */ this.run.tasks.forEach { run -> - run.submissions.filter { it.status == SubmissionStatus.INDETERMINATE }.forEach { + run.submissions.filter { it.status == VerdictStatus.INDETERMINATE }.forEach { run.validator.validate(it) } } @@ -514,10 +511,10 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @param context The [RunActionContext] used for the invocation * @param sub The [Submission] to be posted. * - * @return [SubmissionStatus] of the [Submission] + * @return [VerdictStatus] of the [Submission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, sub: Submission): VerdictStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } //checkTeamStatus(context.teamId, RunManagerStatus.RUNNING_TASK) require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } @@ -544,7 +541,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous } /** - * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [SubmissionStatus]. + * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [VerdictStatus]. * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveAsynchronousRunManager]. * * This method will not throw an exception and instead returns false if a [Submission] was @@ -553,14 +550,14 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * * @param context The [RunActionContext] used for the invocation * @param submissionId The [EvaluationId] of the [Submission] to update. - * @param submissionStatus The new [SubmissionStatus] + * @param submissionStatus The new [VerdictStatus] * * @return Whether the update was successful or not */ override fun updateSubmission( context: RunActionContext, submissionId: EvaluationId, - submissionStatus: SubmissionStatus + submissionStatus: VerdictStatus ): Boolean = this.stateLock.read { val found = this.allSubmissions.find { it.uid == submissionId } ?: return false diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index c8a89f48e..a01ec4c3b 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -5,7 +5,7 @@ import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard @@ -166,26 +166,9 @@ interface InteractiveRunManager : RunManager { */ fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean - /** - * Invoked by an external caller to post a new [Submission] for the [TaskRun] that is currently being - * executed by this [InteractiveRunManager]. [Submission]s usually cause updates to the internal state and/or - * the [Scoreboard] of this [InteractiveRunManager]. - * - * This method will not throw an exception and instead returns false if a [Submission] was - * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke - * this method again. - * - * - * @param context The [RunActionContext] used for the invocation - * @param sub The [Submission] to be posted. - * - * @return [SubmissionStatus] of the [Submission] - * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. - */ - fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus /** - * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [SubmissionStatus]. + * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [VerdictStatus]. * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveRunManager]. * * This method will not throw an exception and instead returns false if a [Submission] was @@ -194,10 +177,10 @@ interface InteractiveRunManager : RunManager { * * @param context The [RunActionContext] used for the invocation * @param submissionId The [EvaluationId] of the [Submission] to update. - * @param submissionStatus The new [SubmissionStatus] + * @param submissionStatus The new [VerdictStatus] * * @return Whether the update was successful or not * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: SubmissionStatus): Boolean + fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: VerdictStatus): Boolean } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 561f182ed..e0fc7b365 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunProperties import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent @@ -47,6 +47,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv /** Number of consecutive errors which have to occur within the main execution loop before it tries to gracefully terminate */ private val maxErrorCount = 5 + /** */ override val runProperties: RunProperties get() = run.properties @@ -65,7 +66,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv /** The list of all [Submission]s tracked ever received by this [InteractiveSynchronousRunManager]. */ override val allSubmissions: List get() = this.stateLock.read { - this.run.tasks.flatMap { it.submissions } + this.run.tasks.flatMap { it.ve } } /** The status of this [RunManager]. */ @@ -102,9 +103,6 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) - /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ - private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) - /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable, this.daoUpdatable) @@ -140,7 +138,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv /** Re-enqueue pending submissions (if any). */ this.run.tasks.forEach { run -> - run.submissions.filter { it.status == SubmissionStatus.INDETERMINATE }.forEach { + run.submissions.filter { it.status == VerdictStatus.INDETERMINATE }.forEach { run.validator.validate(it) } } @@ -471,7 +469,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * @param context The [RunActionContext] used for the invocation * @param sub [Submission] that should be registered. */ - override fun postSubmission(context: RunActionContext, sub: Submission): SubmissionStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, sub: Submission): VerdictStatus = this.stateLock.read { assureTaskRunning() /* Register submission. */ @@ -507,13 +505,13 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * * @param context The [RunActionContext] used for the invocation * @param submissionId The [EvaluationId] of the [Submission] to update. - * @param submissionStatus The new [SubmissionStatus] + * @param submissionStatus The new [VerdictStatus] * @return True on success, false otherwise. */ override fun updateSubmission( context: RunActionContext, submissionId: EvaluationId, - submissionStatus: SubmissionStatus + submissionStatus: VerdictStatus ): Boolean = this.stateLock.read { /* Sanity check. */ val found = this.allSubmissions.find { it.uid == submissionId } ?: return false @@ -667,7 +665,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_DEFAULT val correctOnly = option.getAsBool(SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT - if (correctOnly && sub.status != SubmissionStatus.CORRECT) { + if (correctOnly && sub.status != VerdictStatus.CORRECT) { return } val timeLeft = Math.floorDiv(this.timeLeft(context), 1000) diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 31504249c..47caa1bea 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -7,9 +7,7 @@ import dev.dres.data.model.run.* import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.data.model.submissions.batch.SubmissionBatch import dev.dres.run.score.scoreboard.Scoreboard -import dev.dres.run.updatables.DAOUpdatable import dev.dres.run.updatables.ScoreboardsUpdatable import dev.dres.run.validation.interfaces.JudgementValidator import org.slf4j.LoggerFactory @@ -30,9 +28,6 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() - /** The internal [DAOUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ - private val daoUpdatable = DAOUpdatable(RunExecutor.runs, this.run) - /** Run ID of this [InteractiveSynchronousRunManager]. */ override val id: EvaluationId get() = this.run.id @@ -180,23 +175,6 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { private val updatedTasks = LinkedBlockingQueue>>>() - /** - * - */ - fun addSubmissionBatch(batch: SubmissionBatch<*>) = this.stateLock.read{ - - //check(this.status == RunManagerStatus.RUNNING_TASK) { "SynchronousNonInteractiveRunManager is in status ${this.status} and can currently not accept submissions." } //FIXME - - this.run.tasks.forEach { task -> - val taskResultBatches = batch.results.filter { it.task == task.uid } - if (taskResultBatches.isNotEmpty()){ - task.addSubmissionBatch(batch, taskResultBatches) - updatedTasks.add(task.uid to taskResultBatches.map { batch.teamId to it.name }) - } - } - - } - /** * */ diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 5b635d258..44356a477 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -8,6 +8,8 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunProperties import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator @@ -17,7 +19,7 @@ import dev.dres.run.validation.interfaces.JudgementValidator * @see InteractiveSynchronousEvaluation * * @author Ralph Gasser - * @version 1.5.0 + * @version 2.0.0 */ interface RunManager : Runnable { /** Unique, public [EvaluationId] for this [RunManager]. */ @@ -88,6 +90,24 @@ interface RunManager : Runnable { */ fun tasks(context: RunActionContext): List + /** + * Invoked by an external caller to post a new [Submission] for the [TaskRun] that is currently being + * executed by this [InteractiveRunManager]. [Submission]s usually cause updates to the internal state and/or + * the [Scoreboard] of this [InteractiveRunManager]. + * + * This method will not throw an exception and instead returns false if a [Submission] was + * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke + * this method again. + * + * + * @param context The [RunActionContext] used for the invocation + * @param sub The [Submission] to be posted. + * + * @return [VerdictStatus] of the [Submission] + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + */ + fun postSubmission(context: RunActionContext, sub: Submission): VerdictStatus + /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. * diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index ab66ab8a6..2bd99bb98 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -10,7 +10,8 @@ import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.template.task.TaskDescriptionId import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionValidator @@ -211,23 +212,19 @@ object AuditLogger { /** * Logs a submission override to DRES. * - * @param submission The [Submission] that was overriden (new snapshot). + * @param verdict The [Submission] that was overriden (new snapshot). * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system */ - fun prepareJudgement(submission: Submission, validator: JudgementValidator, token: String) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.PREPARE_JUDGEMENT - this.source = AuditLogSource.INTERNAL - this.timestamp = DateTime.now() - this.submissionId = submission.uid.string - this.competitionId = submission.task?.competition?.id?.string - this.taskId = submission.task?.uid?.string - this.verdict = submission.status.toString() - this.validatorName = validator.id - this.description = "Token: $token" - } + fun prepareJudgement(verdict: Verdict, validator: JudgementValidator, token: String) { + AuditLogEntry.new { + this.type = AuditLogType.PREPARE_JUDGEMENT + this.source = AuditLogSource.INTERNAL + this.timestamp = DateTime.now() + this.submissionId = verdict.submission.id + this.competitionId = verdict.task.evaluation.evaluationId + this.taskId = verdict.task.taskId + this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.status.description}" } } @@ -237,11 +234,11 @@ object AuditLogger { * @param competitionId [EvaluationId] that identifies the competition * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system - * @param verdict The [SubmissionStatus] submitted by the judge. + * @param verdict The [VerdictStatus] submitted by the judge. * @param api The [AuditLogSource] * @param sessionId The identifier of the user session. */ - fun judgement(competitionId: EvaluationId, validator: JudgementValidator, token: String, verdict: SubmissionStatus, api: AuditLogSource, sessionId: SessionId?) { + fun judgement(competitionId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionId: SessionId?) { AuditLogEntry.new { this.type = AuditLogType.JUDGEMENT this.source = api diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index 660c83848..a1ceb27a2 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -5,18 +5,15 @@ import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.submissions.Submission @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") sealed class StreamEvent(var timeStamp : Long = System.currentTimeMillis(), var session: String? = null) - - class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val taskTemplate: TaskTemplate) : StreamEvent() class TaskEndEvent(val runId: EvaluationId, val taskId: EvaluationId) : StreamEvent() class RunStartEvent(val runId: EvaluationId, val description: EvaluationTemplate) : StreamEvent() class RunEndEvent(val runId: EvaluationId) : StreamEvent() - - class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : Submission) : StreamEvent(session = session) class QueryEventLogEvent(session: String, val runId: EvaluationId, val queryEventLog: QueryEventLog) : StreamEvent(session = session) class QueryResultLogEvent(session: String, val runId: EvaluationId, val queryResultLog: QueryResultLog) : StreamEvent(session = session) diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt index 5756d6422..60e11def3 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt @@ -1,21 +1,16 @@ package dev.dres.run.eventstream.handlers -import dev.dres.data.dbo.DaoIndexer import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaItemSegmentList -import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.TaskDescriptionTarget -import dev.dres.run.eventstream.QueryResultLogEvent import dev.dres.run.eventstream.StreamEvent import dev.dres.run.eventstream.StreamEventHandler -import dev.dres.run.eventstream.TaskStartEvent -import dev.dres.utilities.TimeUtil +import jetbrains.exodus.database.TransientEntityStore import java.io.File import java.io.PrintWriter -class ResultLogStatisticsHandler(private val segmentIndex: DaoIndexer) : StreamEventHandler { +class ResultLogStatisticsHandler(private val store: TransientEntityStore) : StreamEventHandler { private val writer = PrintWriter(File("statistics/result_log_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) @@ -29,6 +24,8 @@ class ResultLogStatisticsHandler(private val segmentIndex: DaoIndexer { lastActiveTask[event.runId] = event.taskTemplate @@ -84,6 +81,6 @@ class ResultLogStatisticsHandler(private val segmentIndex: DaoIndexer { /* ignore */ } - } + } */ } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 922eb1c79..4bb447b69 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -1,7 +1,7 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.eventstream.* import org.slf4j.LoggerFactory import java.io.File @@ -59,12 +59,12 @@ class SubmissionStatisticsHandler : StreamEventHandler { (teamId, count) -> writer.println("$task,${teamId.string},\"totalSubmissionsPerTeam\",$count") } submissionsByTeam.mapValues { - it.value.firstOrNull { s -> s.status == SubmissionStatus.CORRECT }?.timestamp?.minus(taskStart) } + it.value.firstOrNull { s -> s.status == VerdictStatus.CORRECT }?.timestamp?.minus(taskStart) } .filter { it.value != null }.forEach{ (teamId, time) -> writer.println("$task,${teamId.string},\"timeUntilCorrectSubmission\",$time") } submissionsByTeam.mapValues { - it.value.indexOfFirst { s -> s.status == SubmissionStatus.CORRECT } }.forEach{ + it.value.indexOfFirst { s -> s.status == VerdictStatus.CORRECT } }.forEach{ (teamId, count) -> writer.println("$task,${teamId.string},\"incorrectBeforeCorrectSubmissions\",$count") } writer.flush() diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index bf46bdecb..e4f013c4b 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -1,5 +1,6 @@ package dev.dres.run.eventstream.handlers +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* @@ -10,12 +11,27 @@ import dev.dres.run.score.interfaces.TeamTaskScorer import java.io.File import java.io.PrintWriter +/** + * + */ class TeamCombinationScoreHandler : StreamEventHandler { private val writer = PrintWriter(File("statistics/combined_team_scores_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) + /** + * + */ private val tasks = mutableMapOf() + + /** + * + */ + private val taskStartMap = mutableMapOf() + + /** + * + */ private val submissionTaskMap = mutableMapOf>() init { @@ -45,7 +61,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { val submissions = submissionTaskMap[event.taskId] ?: return - val teams = submissions.map { it.teamId }.toSet().toList().sortedBy { it.string } + val teams = submissions.map { it.team.teamId }.toSet().toList().sortedBy { it } val combinations = teams.mapIndexed { firstIndex, uidA -> teams.mapIndexed {secondIndex, uidB -> if (firstIndex > secondIndex) (uidA to uidB) else null} @@ -53,7 +69,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { val combinedSubmissions = submissions.flatMap { submission -> combinations.map { - if (it.value.first == submission.teamId || it.value.second == submission.teamId) { + if (it.value.first == submission.team.teamId || it.value.second == submission.team.teamId) { when (submission) { is Submission.Item -> submission.copy(teamId = it.key).apply { this.status = submission.status } is Submission.Temporal -> submission.copy(teamId = it.key).apply { this.status = submission.status } @@ -97,9 +113,6 @@ class TeamCombinationScoreHandler : StreamEventHandler { } else -> { /* ignore */ } - } - } - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index 90675377d..0c66f42b9 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -1,12 +1,26 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct +import kotlinx.dnq.query.size +/** + * A [SubmissionFilter] that filters correct [Submission]s if the number of correct [Submission] for the team exceed the limit. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @version 1.1.0 + */ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { - constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) - - override val reason = "Maximum number of correct submissions ($limit) exceeded for the team" + override val reason = "Maximum number of correct submissions ($limit) exceeded for the team." - override fun test(submission: Submission): Boolean = submission.task!!.submissions.count { it.status == SubmissionStatus.CORRECT && it.teamId == submission.teamId } < limit + constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) + override fun test(submission: Submission): Boolean { + return submission.verdicts.asSequence().all { verdict -> + verdict.task.submissions.filter { (it.status eq VerdictStatus.CORRECT).and(it.submission.team eq submission.team) }.size() < limit + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index eed5f21ba..264f33cdc 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -1,13 +1,28 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus - +import dev.dres.data.model.submissions.VerdictStatus +import kotlinx.dnq.query.FilteringContext.eq +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.size +/** + * A [SubmissionFilter] that filters correct [Submission]s if the number of correct [Submission] for the team member exceeds the limit. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @version 1.1.0 + */ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter { constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) - override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member" - - override fun test(submission: Submission): Boolean = submission.task!!.submissions.count { it.status == SubmissionStatus.CORRECT && it.teamId == submission.teamId && it.memberId == submission.memberId } < limit + override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member." + override fun test(submission: Submission): Boolean { + return submission.verdicts.asSequence().all { verdict -> + verdict.task.submissions.filter { + (it.status eq VerdictStatus.CORRECT).and(it.submission.team eq submission.team).and(it.submission.user eq submission.user) + }.size() < this.limit + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index c06e247c0..96f384d89 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -1,25 +1,26 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.isEmpty + +/** + * A [SubmissionFilter] that filters duplicate [Submission]s in terms of content. + * + * @author Luca Rossetto + * @version 1.1.0 + */ class DuplicateSubmissionFilter : SubmissionFilter { - override val reason = "Duplicate submission" + override val reason = "Duplicate submission received." - override fun test(submission: Submission): Boolean = submission.task!!.submissions.none { - it.teamId == submission.teamId && - if(it is ItemAspect && submission is ItemAspect) { - it.item == submission.item && - if (submission is TemporalSubmissionAspect && it is TemporalSubmissionAspect) { - /*(*/(submission.start <= it.start && submission.end >= it.end) /*|| */ - } else { - true - } - } else if (it is TextAspect && submission is TextAspect) { - it.text == submission.text - } else true + override fun test(submission: Submission): Boolean { + return submission.verdicts.asSequence().all { verdict -> + verdict.task.submissions.filter { + (it.text eq verdict.text) and (it.item eq verdict.item) and (it.start le (verdict.start ?: Long.MAX_VALUE)) and (it.end ge (verdict.end ?: Long.MIN_VALUE)) + }.isEmpty + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt index f5318d6cc..1bba7ab08 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt @@ -1,10 +1,17 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect +import dev.dres.data.model.submissions.VerdictType +import kotlinx.dnq.query.asSequence +/** + * A [SubmissionFilter] that filters temporal submissions. + * + * @author Luca Rossetto + * @version 1.1.0 + */ class ItemSubmissionFilter : SubmissionFilter { override val reason = "Submission does include temporal information, but whole item was expected" - - override fun test(submission: Submission): Boolean = submission !is TemporalSubmissionAspect + override fun test(submission: Submission): Boolean + = submission.verdicts.asSequence().any { it.type == VerdictType.TEMPORAL } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 0afc3c8dc..ac439e10d 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -1,7 +1,7 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus /** * @@ -13,5 +13,5 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi override val reason = "Maximum number of wrong submissions ($max) exceeded for the team" - override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId && it.status == SubmissionStatus.WRONG }.size < max + override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId && it.status == VerdictStatus.WRONG }.size < max } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt index 7547783fa..1105d6d8b 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt @@ -1,5 +1,9 @@ package dev.dres.run.score +import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.team.TeamId -data class TaskContext(val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) +/** + * + */ +data class TaskContext(val taskId: TaskId, val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt index 6e6459d03..17622d40f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt @@ -1,12 +1,13 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.Verdict /** * A [TaskScorer] implementation that can update scores incrementally on a [Submission] by [Submission] basis. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ interface IncrementalSubmissionTaskScorer: TaskScorer { /** diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt index 87c3cd5d9..136ccf709 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt @@ -1,26 +1,24 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext /** * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [TaskRun] based on the * entire [Submission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.2 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 */ interface RecalculatingSubmissionTaskScorer: TaskScorer { /** * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [Submission] history. * * @param submissions The [Submission]s used to update this [RecalculatingSubmissionTaskScorer] with. - * @param taskStartTime Time the [TaskRun] started. - * @param taskDuration Duration of the [TaskRun]. - * @param taskEndTime Time the [TaskRun] ended. - * + * @param context The [TaskContext] in which scoring takes place. */ fun computeScores(submissions: Collection, context: TaskContext): Map } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt deleted file mode 100644 index c57672f7a..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/ResultBatchTaskScorer.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.submissions.batch.ResultBatch - -interface ResultBatchTaskScorer : TaskScorer { - - fun computeScores(batch: ResultBatch<*>) : Double - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 9faa46f03..961d542ed 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,26 +1,33 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.TaskDescriptionId import dev.dres.data.model.template.team.Team import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.template.TemplateId import dev.dres.run.score.interfaces.TaskScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max +/** + * A [Scoreboard] that keeps track of the maximum score per [TaskTemplate] as identified by it [TemplateId]. + * + * @author Luca Rossett + * @version 1.1.0 + */ class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (TaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 100.0) : Scoreboard { - private val scorePerTaskMap = ConcurrentHashMap>() + /** Tracks the score per [TemplateId] (references a [TaskTemplate]). */ + private val scorePerTaskMap = ConcurrentHashMap>() private val teamIds = teams.map { it.teamId } - private fun overallScoreMap(): Map { + private fun overallScoreMap(): Map { val scoreSums = scorePerTaskMap.values .flatMap {it.entries} //all team to score pairs independent of task .groupBy { it.key } //individual scores per team .mapValues { - it.value.map { i -> i.value }.sum() + it.value.sumOf { i -> i.value } } val maxScore = max(1.0, scoreSums.values.maxOrNull() ?: return emptyMap()) @@ -35,7 +42,7 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri override fun score(teamId: TeamId) = overallScoreMap()[teamId] ?: 0.0 - override fun update(scorers: Map) { + override fun update(scorers: Map) { this.scorePerTaskMap.clear() this.scorePerTaskMap.putAll(scorers.map { it.key to it.value.scores().groupBy { it.first }.mapValues { @@ -45,13 +52,9 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, pri ) } - override fun update(runs: List) { - update( - runs - .filter { taskFilter(it.template) && (it.started != null) } - .map { it.uid to it.scorer }.toMap() - ) - } + override fun update(runs: List) = update( + runs.filter { taskFilter(it.template) && (it.started != null) }.associate { it.id to it.scorer } + ) override fun overview() = ScoreOverview(name, taskGroupName, scores()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt new file mode 100644 index 000000000..b7d04e899 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt @@ -0,0 +1,11 @@ +package dev.dres.run.score.scoreboard + +import dev.dres.data.model.template.team.TeamId + +/** + * A container class to track scores per team. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class Score(val teamId: TeamId, val score: Double) diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt new file mode 100644 index 000000000..64723a2df --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt @@ -0,0 +1,9 @@ +package dev.dres.run.score.scoreboard + +/** + * A container class to scores for a specific score board. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ScoreOverview(val name: String, val taskGroup: String?, val scores: List) diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index e03cbdbb4..d3c4fcbda 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -1,18 +1,11 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.template.team.Team -import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.interfaces.TaskScorer -/** - * Container for [Scoreboard]. - */ -data class Score(val teamId: String, val score: Double) - -data class ScoreOverview(val name: String, val taskGroup: String?, val scores: List) - /** * A [Scoreboard] tracks the [Score]s for different [Team]s * @@ -32,9 +25,9 @@ interface Scoreboard { fun scores(): List /** - * Retrieves and returns the score of the given [Team] [EvaluationId] + * Retrieves and returns the score of the given [Team] * - * @param teamId The [Team]'s [EvaluationId]. + * @param teamId The [Team]'s [TeamId]. * @return The score for the given [Team]. */ fun score(teamId: TeamId): Double @@ -45,7 +38,7 @@ interface Scoreboard { fun update(runs: List) /** - * Updates using a map of the [TaskRun] ids to the corresponding [TaskScorer]s + * Updates using a map of the [TaskId] ids to the corresponding [TaskScorer]s */ fun update(scorers: Map) @@ -53,5 +46,4 @@ interface Scoreboard { * Returns a summary of all current scores in a [ScoreOverview] */ fun overview(): ScoreOverview - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index a16283704..b36cf4a52 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -1,27 +1,31 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.template.task.TaskDescriptionId import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.template.TemplateId import dev.dres.run.score.interfaces.TaskScorer +/** + * A [Scoreboard] that keeps tracks the total score per team and task group. + * + * @author Luca Rossett + * @version 1.1.0 + */ class SumAggregateScoreBoard(override val name: String, private val boards: List, private val taskGroupName: String? = null) : Scoreboard { override fun scores(): List = this.boards.map { it.scores() } .flatten().groupBy { it.teamId }.values - .map { Score(it.first().teamId, it.map { it.score }.sum()) } - - override fun score(teamId: TeamId) = boards.map { it.score(teamId) }.sum() + .map { Score(it.first().teamId, it.sumOf { it.score }) } + override fun score(teamId: TeamId) = boards.sumOf { it.score(teamId) } override fun update(runs: List) { //since calls are delegated, nothing needs to be done here } - override fun update(scorers: Map) { + override fun update(scorers: Map) { //since calls are delegated, nothing needs to be done here } - override fun overview() = ScoreOverview(this.name, this.taskGroupName, scores()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 9364246aa..bb38154b4 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -3,7 +3,7 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.media.MediaType import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.ScoreEntry @@ -25,8 +25,8 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { private val lastScoresLock = ReentrantReadWriteLock() override fun computeScores(submissions: Collection, context: TaskContext): Map { - val correctSubmissions = submissions.filter { it.status == SubmissionStatus.CORRECT } - val wrongSubmissions = submissions.filter { it.status == SubmissionStatus.WRONG } + val correctSubmissions = submissions.filter { it.status == VerdictStatus.CORRECT } + val wrongSubmissions = submissions.filter { it.status == VerdictStatus.WRONG } val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.teamId } val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.teamId } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index 8aa007b06..74b69d508 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,25 +1,24 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.StatusAspect -import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.score.interfaces.ResultBatchTaskScorer import dev.dres.run.score.interfaces.ScoreEntry +import dev.dres.run.score.interfaces.TaskScorer import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read -import kotlin.concurrent.write -class InferredAveragePrecisionScorer : ResultBatchTaskScorer { +/** + * @author Luca Rossetto + * @version 1.0.0 + */ +class InferredAveragePrecisionScorer : TaskScorer { companion object { private val epsilon = 0.01 //TODO check what TRECVID uses - + // TODO: Fix and make purpose and functionality of class explicit through proper documentation. //see https://www-nlpir.nist.gov/projects/tv2006/infap/inferredAP.pdf - fun infAP(elements: List): Double { + /*fun infAP(elements: List): Double { if (elements.isEmpty()) { return 0.0 @@ -34,7 +33,7 @@ class InferredAveragePrecisionScorer : ResultBatchTaskScorer { val k = index + 1.0 when(statusAspect.status) { - SubmissionStatus.CORRECT -> { + VerdictStatus.CORRECT -> { ++judgements // |d100| ++correct // |rel| @@ -49,7 +48,7 @@ class InferredAveragePrecisionScorer : ResultBatchTaskScorer { infAPSum += ap } - SubmissionStatus.WRONG -> { + VerdictStatus.WRONG -> { ++judgements ++wrong // |nonrel| } @@ -68,20 +67,22 @@ class InferredAveragePrecisionScorer : ResultBatchTaskScorer { fun score(submissions: List): Double = infAP(submissions) fun score(batch: ResultBatch<*>): Double = infAP(batch.results) - + */ } - private var lastScores: MutableMap, Double> = mutableMapOf() - private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(batch: ResultBatch<*>): Double = this.lastScoresLock.write { + /*override fun computeScores(batch: ResultBatch<*>): Double = this.lastScoresLock.write { val score = score(batch) this.lastScores[batch.teamId to batch.name] = score return@write score - } + }*/ + + private var lastScores: MutableMap, Double> = mutableMapOf() + + private val lastScoresLock = ReentrantReadWriteLock() + override fun scores(): List = this.lastScoresLock.read { this.lastScores.map { ScoreEntry(it.key.first, it.key.second, it.value) } } - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 4b6f2a89e..8fe6fd514 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -2,11 +2,13 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.ScoreEntry import dev.dres.run.score.interfaces.TeamTaskScorer +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.toList import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -34,20 +36,19 @@ class KisTaskScorer( private val lastScoresLock = ReentrantReadWriteLock() override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { - - val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("no task start time specified") - val taskDuration = context.taskDuration ?: throw IllegalArgumentException("no task duration specified") - + val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("No task start time specified.") + val taskDuration = context.taskDuration ?: throw IllegalArgumentException("No task duration specified.") val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition - this.lastScores = context.teamIds.associateWith { teamId -> - val sbs = submissions.filter { it.teamId == teamId && (it.status == SubmissionStatus.CORRECT || it.status == SubmissionStatus.WRONG) }.sortedBy { it.timestamp } - val firstCorrect = sbs.indexOfFirst { it.status == SubmissionStatus.CORRECT } + val verdicts = submissions.filter { it.team.id == teamId }.sortedBy { it.timestamp }.flatMap { sub -> + sub.verdicts.filter { (it.status eq VerdictStatus.CORRECT) or (it.status eq VerdictStatus.WRONG) }.toList() + } + val firstCorrect = verdicts.indexOfFirst { it.status == VerdictStatus.CORRECT } val score = if (firstCorrect > -1) { - val timeFraction = 1.0 - (sbs[firstCorrect].timestamp - taskStartTime) / tDur + val timeFraction = 1.0 - (verdicts[firstCorrect].submission.timestamp - taskStartTime) / tDur max( 0.0, - maxPointsAtTaskEnd + + this.maxPointsAtTaskEnd + ((maxPointsPerTask - maxPointsAtTaskEnd) * timeFraction) - (firstCorrect * penaltyPerWrongSubmission) //index of first correct submission is the same as number of not correct submissions ) diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt deleted file mode 100644 index 298d7f07d..000000000 --- a/backend/src/main/kotlin/dev/dres/run/updatables/DAOUpdatable.kt +++ /dev/null @@ -1,47 +0,0 @@ -package dev.dres.run.updatables - -import dev.dres.data.dbo.DAO -import dev.dres.data.model.PersistentEntity -import dev.dres.run.RunManagerStatus - -/** - * A [StatefulUpdatable] that takes care of storing the object it holds whenever [DAOUpdatable.update] - * is called and changes have been registered. - * - * @author Ralph Gasser - * @version 1.0 - */ -class DAOUpdatable(val dao: DAO, val obj: T): StatefulUpdatable { - - - companion object { - val ELIGIBLE_RUNNING_STATES = arrayOf( - RunManagerStatus.CREATED, - RunManagerStatus.ACTIVE, - //RunManagerStatus.TASK_ENDED, - RunManagerStatus.TERMINATED - ) - } - - /** The [Phase] this [DAOUpdatable] belongs to. */ - override val phase: Phase = Phase.FINALIZE - - @Volatile - override var dirty: Boolean = false - override fun update(status: RunManagerStatus) { - if (this.dirty) { - this.dao.update(this.obj) - this.dirty = false - } - } - - /** - * Checks if [RunManagerStatus] is contained in [ELIGIBLE_RUNNING_STATES]. This should prevent - * [DAOUpdatable] to be invoked while a task run is running, which potentially involves a lot - * of changes to the object and therefore a lot of unnecessar updates. - * - * @param status The [RunManagerStatus] to check. - * @return True if [DAOUpdatable] should be run, false otherwise. - */ - override fun shouldBeUpdated(status: RunManagerStatus): Boolean = (status in ELIGIBLE_RUNNING_STATES) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 72afe836f..87eec8faf 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -1,52 +1,50 @@ package dev.dres.run.updatables -import dev.dres.data.model.template.options.SubmissionFilterOption import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.task.options.SubmissionOption import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManagerStatus +import kotlinx.dnq.query.* import java.util.concurrent.atomic.AtomicInteger +/** + * An [Updatable] that takes care of ending a task if enough submissions have been received. + * + * @author Luca Rossetto + * @version 1.1.0 + */ class EndTaskUpdatable(private val run: InteractiveRunManager, private val context: RunActionContext) : Updatable { + /** The [EndTaskUpdatable] always belongs to the [Phase.MAIN]. */ override val phase: Phase = Phase.MAIN /** Number of submissions seen during the last update. */ private var submissions = AtomicInteger(0) - val isAsync = run is InteractiveAsynchronousRunManager + /** Internal flag indicating whether the provided [InteractiveRunManager] is asynchronous. */ + private val isAsync = this.run is InteractiveAsynchronousRunManager override fun update(status: RunManagerStatus) { val taskRun = this.run.currentTask(this.context) if (taskRun != null) { - val limitingFilter = - taskRun.template.taskType.filter.find { it.option == SubmissionFilterOption.LIMIT_CORRECT_PER_TEAM } - ?: return - val limit = limitingFilter.getAsInt("limit") ?: 1 - if (this.run.timeLeft(context) > 0) { - if (this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { - - if (isAsync) { - - if (this.run.submissions(this.context) - .count { it.teamId == context.teamId && it.status == SubmissionStatus.CORRECT } >= limit - ) { - this.run.abortTask(context) - this.submissions.set(0) + if (taskRun.template.taskGroup.type.submission.contains(SubmissionOption.LIMIT_CORRECT_PER_TEAM)) { + val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq SubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 + if (this.run.timeLeft(this.context) > 0) { + if (this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { + val allDone = if (this.isAsync) { + val numberOfSubmissions = this.run.submissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } + numberOfSubmissions >= limit + } else { + /* Determine of all teams have submitted . */ + this.run.template.teams.asSequence().all { team -> + val numberOfSubmissions = this.run.submissions(this.context).count { it.team.id == team.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } + numberOfSubmissions >= limit + } } - - } else { - - /* Determine of all teams have submitted . */ - val allDone = this.run.template.teams.all { team -> - this.run.submissions(this.context) - .count { it.teamId == team.uid && it.status == SubmissionStatus.CORRECT } >= limit - } - - /* Do all teams have reached the limit of correct submissions ? */ if (allDone) { - this.run.abortTask(context) + this.run.abortTask(this.context) this.submissions.set(0) } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt index 48526622a..a6bb07777 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt @@ -1,21 +1,21 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.websocket.ServerMessage -import dev.dres.data.model.template.TeamId +import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunExecutor +import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus -import dev.dres.utilities.extensions.UID import java.util.concurrent.ConcurrentLinkedQueue /** - * A internal queue of [ServerMessage]s that are due for sending by the [RunManager]. + * An internal queue of [ServerMessage]s that are due for sending by the [RunManager]. * * @author Ralph Gasser - * @version 1.0 + * @version 1.1.0 */ class MessageQueueUpdatable(private val executor: RunExecutor) : Updatable { - /** The [Phase] this [MessageQueueUpdatable] belongs to. */ + /** The [MessageQueueUpdatable] always belongs to the [Phase.FINALIZE]. */ override val phase: Phase = Phase.FINALIZE /** Internal queue of all [ServerMessage] that are due for sending. */ @@ -26,9 +26,9 @@ class MessageQueueUpdatable(private val executor: RunExecutor) : Updatable { var message: Pair? = this.messageQueue.poll() while (message != null) { if (message.first == null) { - this.executor.broadcastWsMessage(message.second.runId.UID(), message.second) + this.executor.broadcastWsMessage(message.second.evaluationId, message.second) } else { - this.executor.broadcastWsMessage(message.second.runId.UID(), message.first!!, message.second) + this.executor.broadcastWsMessage(message.second.evaluationId, message.first!!, message.second) } message = this.messageQueue.poll() } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/Phase.kt b/backend/src/main/kotlin/dev/dres/run/updatables/Phase.kt index 9626a7815..c9dd80b0d 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/Phase.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/Phase.kt @@ -5,10 +5,10 @@ package dev.dres.run.updatables * in the order [PREPARE], [MAIN] and [FINALIZE]. * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ enum class Phase { - PREPARE, /** Preparation [Phase] in the run loop. Used to build data structures or prepare data. */ - MAIN, /** Main [Phase] in the run loop. Used to update internal state e.g. recalculate scoreboards. */ - FINALIZE /** Finalization [Phase] in the run loop. Used to send out messages or persist information. */ + PREPARE, /** Preparation [Phase] in the run loop. Used to build data structures or prepare data. */ + MAIN, /** Main [Phase] in the run loop. Used to update internal state e.g. recalculate scoreboards. */ + FINALIZE /** Finalization [Phase] in the run loop. Used to send out messages or persist information. */ } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index 8d3c9021b..51726a197 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -18,7 +18,7 @@ import java.util.* class ScoreboardsUpdatable(val scoreboards: List, private val updateIntervalMs: Long, private val competition: EvaluationRun): StatefulUpdatable { companion object { - val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE/*, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED, RunManagerStatus.PREPARING_TASK*/) + private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) } /** The [Phase] this [ScoreboardsUpdatable] belongs to. */ diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 175e6e10c..935a6dc04 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -3,59 +3,57 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.Verdict import dev.dres.run.RunManagerStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.IncrementalSubmissionTaskScorer import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import kotlinx.dnq.query.asSequence import java.util.* /** - * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated and - * updates the scores for the respective [InteractiveSynchronousEvaluation.Task]. + * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated and updates the scores for the respective [TaskContext]. * * @author Ralph Gasser * @version 1.2.0 */ -class ScoresUpdatable(val runId: EvaluationId, val scoreboardsUpdatable: ScoreboardsUpdatable, val messageQueueUpdatable: MessageQueueUpdatable, val daoUpdatable: DAOUpdatable<*>): Updatable { +class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreboardsUpdatable: ScoreboardsUpdatable, private val messageQueueUpdatable: MessageQueueUpdatable): Updatable { companion object { - val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE/*, RunManagerStatus.RUNNING_TASK, RunManagerStatus.PREPARING_TASK, RunManagerStatus.TASK_ENDED*/) + private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) } - /** Internal list of [Submission] that pend processing. */ + /** Internal list of [Verdict] that pend processing. */ private val list = LinkedList>() /** The [Phase] this [ScoresUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN - /** Enqueues a new [Submission] for post-processing. */ + /** Enqueues a new [Verdict] for post-processing. */ fun enqueue(submission: Pair) = this.list.add(submission) override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { val scorersToUpdate = mutableSetOf>() val removed = this.list.removeIf { - val scorer = it.first.scorer - if (it.second.status != SubmissionStatus.INDETERMINATE) { - when(scorer) { - is RecalculatingSubmissionTaskScorer -> scorersToUpdate.add(Pair(it.first, scorer)) - is IncrementalSubmissionTaskScorer -> scorer.update(it.second) - else -> { } - } - true - } else { - false + when(val scorer = it.first.scorer) { + is RecalculatingSubmissionTaskScorer -> scorersToUpdate.add(Pair(it.first, scorer)) + is IncrementalSubmissionTaskScorer -> scorer.update(it.second) + else -> { } } + true } /* Update scorers. */ scorersToUpdate.forEach { val task = it.first if (it.first.started != null) { - val scores = it.second.computeScores(task.submissions, TaskContext(task.competition.description.teams.map { t -> t.uid }, task.started, task.template.duration, task.ended)) + val scores = it.second.computeScores( + task.submissions, + TaskContext(task.id, task.competition.description.teams.asSequence().map { t -> t.id }.toList(), task.started, task.template.duration, task.ended) + ) it.first.updateTeamAggregation(scores) } } @@ -63,8 +61,7 @@ class ScoresUpdatable(val runId: EvaluationId, val scoreboardsUpdatable: Scorebo /* If elements were removed, then update scoreboards and tasks. */ if (removed) { this.scoreboardsUpdatable.dirty = true - this.daoUpdatable.dirty = true - this.messageQueueUpdatable.enqueue(ServerMessage(this.runId.string, ServerMessageType.TASK_UPDATED)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.evaluationId, ServerMessageType.TASK_UPDATED)) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt index bf570f85c..7221e0339 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt @@ -7,7 +7,7 @@ import dev.dres.run.RunManagerStatus * Interface implemented by classes that are updated during the lifecycle of a [RunManager]. * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ interface Updatable { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt index 738df101e..7528fd522 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt @@ -1,13 +1,21 @@ package dev.dres.run.validation import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.asSequence -class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { +/** + * A [SubmissionValidator] class that allows for the combination of two [SubmissionValidator]s. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 + */ +class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { companion object{ - fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { + fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { return when { validator.size < 2 -> throw IllegalArgumentException("Chain needs at least two validators") validator.size == 2 -> ChainedSubmissionValidator(validator[0], continueStates, validator[1]) @@ -16,17 +24,22 @@ class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator } } + override val deferring: Boolean + get() = this.secondValidator.deferring + init { - require(!firstValidator.deferring) {"first validator cannot defer validation"} + require(!this.firstValidator.deferring) {"First validator cannot be a deferring validation."} } + /** + * Validates a [Submission] based on two [SubmissionValidator]s. + * + * @param submission The [Submission] to validate. + */ override fun validate(submission: Submission) { - firstValidator.validate(submission) - if (continueStates.contains(submission.status)){ - secondValidator.validate(submission) + this.firstValidator.validate(submission) + if (submission.verdicts.asSequence().any { this.continueStates.contains(it.status) }) { + this.secondValidator.validate(submission) } } - - override val deferring: Boolean - get() = secondValidator.deferring } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt deleted file mode 100644 index 9fc0c1f0e..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionBatchValidator.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.validation.interfaces.SubmissionBatchValidator - -class MediaItemsSubmissionBatchValidator(private val items : Set) : SubmissionBatchValidator { - - override fun validate(batch: ResultBatch<*>) { - - batch.results.forEach { - it.status = if (it.item in items) SubmissionStatus.CORRECT else SubmissionStatus.WRONG - } - - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index 2fe80583b..14b5a007a 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -2,8 +2,9 @@ package dev.dres.run.validation import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.asSequence /** * A [SubmissionValidator] that checks if the items specified by a [Submission] match the items in the provided set. @@ -22,10 +23,12 @@ class MediaItemsSubmissionValidator(private val items : Set) : Submis * @param submission The [Submission] to validate. */ override fun validate(submission: Submission) { - if (submission.item == null || submission.item !in this.items) { - submission.status = SubmissionStatus.WRONG - } else { - submission.status = SubmissionStatus.CORRECT + submission.verdicts.asSequence().forEach {verdict -> + if (verdict.item == null || verdict.item !in this.items) { + verdict.status = VerdictStatus.WRONG + } else { + verdict.status = VerdictStatus.CORRECT + } } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 878d81eb5..714d83d90 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,20 +1,24 @@ package dev.dres.run.validation import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.VerdictType import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.asSequence /** * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the * complete containment of the [Submission] within the provided [MediaSegmentTaskDescription]. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.1 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 */ -class TemporalContainmentSubmissionValidator(private val task: MediaSegment) : SubmissionValidator { +class TemporalContainmentSubmissionValidator(private val targetSegment: TransientMediaSegment) : SubmissionValidator { + + override val deferring: Boolean + get() = false /** * Validates a [Submission] based on the target segment and the temporal overlap of the @@ -23,24 +27,35 @@ class TemporalContainmentSubmissionValidator(private val task: MediaSegment) : S * @param submission The [Submission] to validate. */ override fun validate(submission: Submission) { - if (submission !is TemporalSubmissionAspect){ - submission.status = SubmissionStatus.WRONG - return - } - submission.status = when { - submission.start > submission.end -> SubmissionStatus.WRONG - submission.item != task.item -> SubmissionStatus.WRONG - else -> { - val outer = this.task.range.toMilliseconds() - if (outer.first <= submission.start && outer.second >= submission.end) { - SubmissionStatus.CORRECT - } else { - SubmissionStatus.WRONG - } + submission.verdicts.asSequence().forEach { verdict -> + /* Perform sanity checks. */ + if (verdict.type != VerdictType.TEMPORAL) { + verdict.status = VerdictStatus.WRONG + return@forEach + } + + val start = verdict.start + val end = verdict.end + val item = verdict.item + if (item == null || start == null || end == null || start > end) { + verdict.status = VerdictStatus.WRONG + return@forEach + + } + + /* Perform item validation. */ + if (verdict.item != this.targetSegment.first) { + verdict.status = VerdictStatus.WRONG + return@forEach + } + + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if (outer.first <= start && outer.second >= end) { + verdict.status = VerdictStatus.CORRECT + } else { + verdict.status = VerdictStatus.WRONG } } } - - override val deferring: Boolean - get() = false } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt deleted file mode 100644 index 6ce6b7374..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionBatchValidator.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.TemporalAspect -import dev.dres.data.model.submissions.batch.ResultBatch -import dev.dres.run.validation.interfaces.SubmissionBatchValidator - -/** - * A [SubmissionBatchValidator] class that checks, if a submission is correct based on the target segment and the - * temporal overlap of the [Submission] with the provided [TransientMediaSegment]. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 - */ -class TemporalOverlapSubmissionBatchValidator(private val targetSegment: TransientMediaSegment) : SubmissionBatchValidator { - - override fun validate(batch: ResultBatch<*>) { - batch.results.forEach { - if (it is TemporalAspect){ - it.status = when { - it.start > it.end -> SubmissionStatus.WRONG - it.item != targetSegment.first -> SubmissionStatus.WRONG - else -> { - val outer = - this.targetSegment.second.toMilliseconds() - if ((outer.first <= it.start && outer.second >= it.start) || (outer.first <= it.end && outer.second >= it.end)) { - SubmissionStatus.CORRECT - } else { - SubmissionStatus.WRONG - } - } - } - } else { - it.status = SubmissionStatus.WRONG - } - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 8d464a1be..f94205478 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -4,9 +4,10 @@ import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.SubmissionType +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.VerdictType import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.asSequence /** */ typealias TransientMediaSegment = Pair @@ -15,8 +16,9 @@ typealias TransientMediaSegment = Pair * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the * temporal overlap of the [Submission] with the provided [TransientMediaSegment]. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.1 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 */ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMediaSegment) : SubmissionValidator { @@ -29,28 +31,34 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed * @param submission The [Submission] to validate. */ override fun validate(submission: Submission) { - if (submission.type != SubmissionType.TEMPORAL) { - submission.status = SubmissionStatus.WRONG - return - } + submission.verdicts.asSequence().forEach { verdict -> + /* Perform sanity checks. */ + if (verdict.type != VerdictType.TEMPORAL) { + verdict.status = VerdictStatus.WRONG + return@forEach + } - if (submission.start > submission.end) { - submission.status = SubmissionStatus.WRONG - return - } + val start = verdict.start + val end = verdict.end + val item = verdict.item + if (item == null || start == null || end == null || start > end) { + verdict.status = VerdictStatus.WRONG + return@forEach + } - /* Perform item validation. */ - if (submission.item != this.targetSegment.first) { - submission.status = SubmissionStatus.WRONG - return - } + /* Perform item validation. */ + if (verdict.item != this.targetSegment.first) { + verdict.status = VerdictStatus.WRONG + return@forEach + } - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if ((outer.first <= submission.start && outer.second >= submission.start) || (outer.first <= submission.end && outer.second >= submission.end)) { - submission.status = SubmissionStatus.CORRECT - } else { - submission.status = SubmissionStatus.WRONG + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { + verdict.status = VerdictStatus.CORRECT + } else { + verdict.status = VerdictStatus.WRONG + } } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 1e4096d6e..3f9dd7f77 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -1,13 +1,22 @@ package dev.dres.run.validation import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.TextAspect +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.VerdictType import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.asSequence - +/** + * A [SubmissionValidator] class that valiadates textual submissions based on [Regex]. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 + */ class TextValidator(targets: List) : SubmissionValidator { + override val deferring = false + /** * Transforms the targets to [Regex]s. * There is the convention introduced, that targets padded in backslashes (single) (\) @@ -32,19 +41,31 @@ class TextValidator(targets: List) : SubmissionValidator { } } + /** + * Validates a textual [Submission] based on the provided [Regex]. + * + * @param submission The [Submission] to validate. + */ override fun validate(submission: Submission) { + submission.verdicts.asSequence().forEach { verdict -> + /* Perform sanity checks. */ + if (verdict.type != VerdictType.TEXT) { + verdict.status = VerdictStatus.WRONG + return@forEach + } - if (submission !is TextAspect) { - submission.status = SubmissionStatus.WRONG - return - } + /* Perform text validation. */ + val text = verdict.text + if (text == null) { + verdict.status = VerdictStatus.WRONG + return@forEach + } - if (regex.any { it matches submission.text }) { - submission.status = SubmissionStatus.CORRECT - } else { - submission.status = SubmissionStatus.WRONG + if (regex.any { it matches text }) { + verdict.status = VerdictStatus.CORRECT + } else { + verdict.status = VerdictStatus.WRONG + } } } - - override val deferring = false } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index 3882b9674..d38d91ae5 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -1,14 +1,16 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.BaseSubmissionAspect +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus /** - * A [SubmissionValidator] that bases validation on human (manual) verdicts. This kind of [SubmissionValidator] - * is inherently asynchronous. + * A [SubmissionValidator] that bases validation on human (manual) verdicts. + * + * This kind of [SubmissionValidator] is inherently asynchronous. * * @author Luca Rossetto & Ralph Gasser - * @version 1.0 + * @version 1.1.0 */ interface JudgementValidator { /** unique id to identify the [JudgementValidator]*/ @@ -24,16 +26,14 @@ interface JudgementValidator { val hasOpen: Boolean get() = open > 0 - - /** * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]' * internal queue. If such an element exists, then the [Submission] is returned alongside a - * unique token, that can be used to update the [Submission]'s [SubmissionStatus]. + * unique token, that can be used to update the [Submission]'s [VerdictStatus]. * * @return Optional [Pair] containing a string token and the [Submission] that should be judged. */ - fun next(queue: String): Pair? + fun next(queue: String): Pair? /** * Places a verdict for the [Submission] identified by the given token. @@ -41,6 +41,6 @@ interface JudgementValidator { * @param token The token used to identify the [Submission]. * @param verdict The verdict of the judge. */ - fun judge(token: String, verdict: SubmissionStatus) + fun judge(token: String, verdict: VerdictStatus) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt deleted file mode 100644 index 7797437cd..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionBatchValidator.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.dres.run.validation.interfaces - -import dev.dres.data.model.submissions.batch.ResultBatch - -interface SubmissionBatchValidator { - fun validate(batch: ResultBatch<*>) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt deleted file mode 100644 index d2a1cd647..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionJudgementValidator.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.run.validation.interfaces - -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus - -interface SubmissionJudgementValidator : SubmissionValidator, JudgementValidator { - - /** - * Enqueues a [Submission] with the internal judgment queue and updates its [SubmissionStatus] - * to [SubmissionStatus.INDETERMINATE]. - * - * @param submission The [Submission] to validate. - */ - override fun validate(submission: Submission) - override val deferring: Boolean - get() = true - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt index 24647d8c5..0bdc2fccb 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.interfaces import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus /** * A validator class that checks, if a [Submission] is correct. @@ -11,7 +11,7 @@ import dev.dres.data.model.submissions.SubmissionStatus */ interface SubmissionValidator { /** - * Validates the [Submission] and updates its [SubmissionStatus]. + * Validates the [Submission] and updates its [VerdictStatus]. * * @param submission The [Submission] to validate. */ diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index d1dadf8b5..c2fc75dc9 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,8 +1,11 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus +/** + * + */ interface VoteValidator : JudgementValidator { /** @@ -18,8 +21,10 @@ interface VoteValidator : JudgementValidator { /** * Places a verdict for the currently active Submission */ - fun vote(verdict: SubmissionStatus) - - fun nextSubmissionToVoteOn() : Submission? + fun vote(verdict: VerdictStatus) + /** + * + */ + fun nextSubmissionToVoteOn() : Verdict? } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index c9dbf3197..d45a4e33a 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,11 +1,14 @@ package dev.dres.run.validation.judged import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator -import dev.dres.run.validation.interfaces.SubmissionJudgementValidator +import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.first import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -17,38 +20,44 @@ import kotlin.concurrent.write * A validator class that checks, if a submission is correct based on a manual judgement by a user. * * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 */ -open class BasicJudgementValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList()): - SubmissionJudgementValidator { +open class BasicJudgementValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList()): SubmissionValidator, JudgementValidator { companion object { private val counter = AtomicInteger() private const val judgementTimeout = 60_000 //ms until a request is re-scheduled } + /** The [BasicJudgementValidator]'s ID is simply an auto-incrementing number. */ override val id = "bjv${counter.incrementAndGet()}" + /** A [BasicJudgementValidator] is always deferring. */ + override val deferring: Boolean = true + + /** Internal lock on relevant data structures. */ private val updateLock = ReentrantReadWriteLock() - /** Internal queue that keeps track of all the [Submission]s in need of a verdict. */ - private val queue: Queue = LinkedList() + /** Internal queue that keeps track of all the [Verdict]s in need of judgement. */ + private val queue: Queue = LinkedList() - private val queuedItemRanges: MutableMap> = HashMap() + /** Internal queue that keeps track of all the [Verdict]s in need of judgement. */ + private val queuedItemRanges: MutableMap> = HashMap() - /** Internal map of all [Submission]s that have been retrieved by a judge and are pending a verdict. */ - private val waiting = HashMap() + /** Internal map of all [Verdict]s that have been retrieved by a judge and are pending a verdict. */ + private val waiting = HashMap() /** Helper structure to keep track when a request needs to be re-scheduled */ private val timeouts = mutableListOf>() /** Internal map of already judged [Submission]s, independent of their source. */ - private val cache: MutableMap = ConcurrentHashMap() + private val cache: MutableMap = ConcurrentHashMap() init { - knownCorrectRanges.forEach { cache[it] = SubmissionStatus.CORRECT } - knownWrongRanges.forEach { cache[it] = SubmissionStatus.WRONG } + knownCorrectRanges.forEach { cache[it] = VerdictStatus.CORRECT } + knownWrongRanges.forEach { cache[it] = VerdictStatus.WRONG } } private fun checkTimeOuts() = updateLock.write { @@ -80,33 +89,33 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e } /** - * Enqueues a [Submission] with the internal judgment queue and updates its [SubmissionStatus] - * to [SubmissionStatus.INDETERMINATE]. + * Enqueues a [Submission] with the internal judgment queue and updates its [VerdictStatus] + * to [VerdictStatus.INDETERMINATE]. * * @param submission The [Submission] to validate. */ - override fun validate(submission: Submission) = updateLock.read { - - //only validate submissions which are not already validated - if (submission.status != SubmissionStatus.INDETERMINATE){ - return@read - } - - //check cache first - val itemRange = ItemRange(submission) - val cachedStatus = this.cache[itemRange] - if (cachedStatus != null) { - submission.status = cachedStatus - } else if (itemRange !in queuedItemRanges.keys) { - updateLock.write { - this.queue.offer(submission) - - submission.status = SubmissionStatus.INDETERMINATE - queuedItemRanges[itemRange] = mutableListOf(submission) + override fun validate(submission: Submission) = this.updateLock.read { + for (verdict in submission.verdicts.asSequence()) { + //only validate submissions which are not already validated + if (verdict.status != VerdictStatus.INDETERMINATE){ + continue } - } else { - updateLock.write { - queuedItemRanges[itemRange]!!.add(submission) + + //check cache first + val itemRange = ItemRange(submission.verdicts.first()) + val cachedStatus = this.cache[itemRange] + if (cachedStatus != null) { + verdict.status = cachedStatus + } else if (itemRange !in queuedItemRanges.keys) { + updateLock.write { + this.queue.offer(verdict) + verdict.status = VerdictStatus.INDETERMINATE + this.queuedItemRanges[itemRange] = mutableListOf(verdict) + } + } else { + this.updateLock.write { + this.queuedItemRanges[itemRange]!!.add(verdict) + } } } } @@ -114,11 +123,11 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e /** * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]' * internal queue. If such an element exists, then the [Submission] is returned alongside a - * unique token, that can be used to update the [Submission]'s [SubmissionStatus]. + * unique token, that can be used to update the [Submission]'s [VerdictStatus]. * * @return Optional [Pair] containing a string token and the [Submission] that should be judged. */ - override fun next(queue: String): Pair? = updateLock.write { + override fun next(queue: String): Pair? = updateLock.write { checkTimeOuts() val next = this.queue.poll() return if (next != null) { @@ -138,34 +147,35 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * @param token The token used to identify the [Submission]. * @param verdict The verdict of the judge. */ - override fun judge(token: String, verdict: SubmissionStatus) { - processSubmission(token, verdict)?.status = verdict + override fun judge(token: String, verdict: VerdictStatus) { + processSubmission(token, verdict).status = verdict } - internal fun processSubmission(token: String, verdict: SubmissionStatus) : Submission? = updateLock.write { - val submission = this.waiting[token] ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged - - val itemRange = ItemRange(submission) + /** + * + */ + fun processSubmission(token: String, status: VerdictStatus) : Verdict = this.updateLock.write { + val verdict = this.waiting[token] + ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged + val itemRange = ItemRange(verdict) //add to cache - this.cache[itemRange] = verdict + this.cache[itemRange] = status //remove from waiting map this.waiting.remove(token) //remove from queue set val otherSubmissions = this.queuedItemRanges.remove(itemRange) - otherSubmissions?.forEach { - it.status = verdict - } + otherSubmissions?.forEach { it.status = status } - return@write submission + return@write verdict } /** * Clears this [JudgementValidator] and all the associated queues and maps. */ - fun clear() = updateLock.write { + fun clear() = this.updateLock.write { this.waiting.clear() this.queue.clear() } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 4ce941ba3..6c3065a43 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.VoteValidator import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue @@ -17,7 +17,7 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() ) init { - require(minimumVotes > 0) {"minimum vote count cannot be <= 0"} + require(this.minimumVotes > 0) { "Minimum vote count cannot be <= 0" } } companion object { @@ -25,8 +25,8 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() private val defaultVoteDifference = 1 } - private val submissionQueue = ConcurrentLinkedQueue() - private val voteCountMap = ConcurrentHashMap() + private val submissionQueue = ConcurrentLinkedQueue() + private val voteCountMap = ConcurrentHashMap() private val updateLock = ReentrantReadWriteLock() override val isActive: Boolean @@ -35,24 +35,19 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() override val voteCount: Map get() = voteCountMap.mapKeys { it.toString() } - override fun vote(verdict: SubmissionStatus) = updateLock.write { - - if (verdict == SubmissionStatus.INDETERMINATE || verdict == SubmissionStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does + override fun vote(status: VerdictStatus) = updateLock.write { + if (status == VerdictStatus.INDETERMINATE || status == VerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does return@write } - val submission = submissionQueue.firstOrNull() ?: return@write - - voteCountMap[verdict] = 1 + voteCountMap.getOrDefault(verdict, 0) + val verdict = this.submissionQueue.firstOrNull() ?: return@write + this.voteCountMap[status] = 1 + this.voteCountMap.getOrDefault(status, 0) if (enoughVotes()){ - - val finalVerdict = voteCountMap.entries.maxByOrNull { it.value }!!.key - submission.status = finalVerdict - - submissionQueue.poll() - voteCountMap.clear() - + val finalVerdict = this.voteCountMap.entries.maxByOrNull { it.value }!!.key + verdict.status = finalVerdict + this.submissionQueue.poll() + this.voteCountMap.clear() } } @@ -64,19 +59,16 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() return max - others >= voteDifference } - override fun nextSubmissionToVoteOn(): Submission? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? + override fun nextSubmissionToVoteOn(): Verdict? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? //siphon of undecidable submission from logic of super class - override fun judge(token: String, verdict: SubmissionStatus) { - val submission = super.processSubmission(token, verdict) - if (submission != null) { - when(verdict){ - SubmissionStatus.CORRECT, - SubmissionStatus.WRONG -> submission.status = verdict - SubmissionStatus.INDETERMINATE -> {} - SubmissionStatus.UNDECIDABLE -> submissionQueue.add(submission) - } - + override fun judge(token: String, status: VerdictStatus) { + val verdict = super.processSubmission(token, status) + when (status){ + VerdictStatus.CORRECT, + VerdictStatus.WRONG -> verdict.status = status + VerdictStatus.INDETERMINATE -> {} + VerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index c7dae01ea..fc00c1ed4 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -2,31 +2,24 @@ package dev.dres.run.validation.judged import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect -import dev.dres.data.model.submissions.aspects.TextAspect - -/** Helper class to store submission information independent of source */ +import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.VerdictType + +/** + * Helper class to store submission information independent of source. + * + * @author Luca Rossetto + * @version 2.0.0 + */ data class ItemRange(val element: String, val start: Long, val end: Long){ - constructor(submission: TemporalSubmissionAspect): this(submission.item.id, submission.start, submission.end) - constructor(submission: TextAspect): this(submission.text, 0, 0) - constructor(submission: ItemAspect): this(submission.item) constructor(item: MediaItem): this(item.id, 0, 0) constructor(item: MediaItem, start: Long, end: Long): this(item.id, start, end) - constructor(submission: Submission): this(fromSubmission(submission), - if (submission is TemporalSubmissionAspect) submission.start else 0, - if (submission is TemporalSubmissionAspect) submission.end else 0) - - companion object { - private fun fromSubmission(submission: Submission): String { - return when (submission){ - is ItemAspect -> submission.item.id - is TextAspect -> submission.text - else -> throw IllegalStateException("Submission contains neither item nor text") - } - - } - } + constructor(verdict: Verdict): this(when (verdict.type){ + VerdictType.ITEM, + VerdictType.TEMPORAL -> verdict.item!!.id + VerdictType.TEXT -> verdict.text!! + else -> throw IllegalStateException("Submission contains neither item nor text.") + }, verdict.start ?: 0, verdict.end ?: 0) override fun equals(other: Any?): Boolean { if (this === other) return true @@ -47,6 +40,4 @@ data class ItemRange(val element: String, val start: Long, val end: Long){ result = 31 * result + end.hashCode() return result } - - } \ No newline at end of file diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 6bac94782..a736f421b 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -2,7 +2,7 @@ package dres.run.score.scorer import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals @@ -57,9 +57,9 @@ class AvsTaskScorerTest { fun allWrongSameImage() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -71,9 +71,9 @@ class AvsTaskScorerTest { fun allWrongDifferentImages() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = VerdictStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -85,9 +85,9 @@ class AvsTaskScorerTest { fun allSameImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -99,9 +99,9 @@ class AvsTaskScorerTest { fun allDifferentImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) @@ -120,20 +120,20 @@ class AvsTaskScorerTest { */ //3 out of 4 correct - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = VerdictStatus.WRONG }, //1 out of 3 correct - Submission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = VerdictStatus.WRONG }, //1 out of 2 correct - Submission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = VerdictStatus.WRONG } ) @@ -172,9 +172,9 @@ class AvsTaskScorerTest { fun allSameVideoSameSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -187,9 +187,9 @@ class AvsTaskScorerTest { fun allSameVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) @@ -202,9 +202,9 @@ class AvsTaskScorerTest { fun allDifferentVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) @@ -216,17 +216,17 @@ class AvsTaskScorerTest { fun allSameVideoOverlappingSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT } + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -238,21 +238,21 @@ class AvsTaskScorerTest { fun allSameVideoOverlappingSegmentSomeCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = SubmissionStatus.WRONG } + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = VerdictStatus.WRONG }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = VerdictStatus.WRONG }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = VerdictStatus.WRONG } ) @@ -270,40 +270,40 @@ class AvsTaskScorerTest { val submissions = listOf( //team 1 //gets merged to one - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = VerdictStatus.CORRECT }, //plus 2 independent correct ones - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = VerdictStatus.CORRECT }, //and an incorrect one directly next to it - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = VerdictStatus.WRONG }, //c = 5, q(c) = 3, i = 1 //team 2 //the center part is missing, so it's counted as two in the quantization - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = VerdictStatus.CORRECT }, //another correct one, same as team 1 - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = VerdictStatus.CORRECT }, //and two wrong ones - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = VerdictStatus.WRONG }, + Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = VerdictStatus.WRONG }, //c = 3, q(c) = 3, i = 2 //team 3 //same as team 2, but with all 3 segments of the 1st video, same as team 1 - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = SubmissionStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = VerdictStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = VerdictStatus.CORRECT }, //another correct one, same as team 1 - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = SubmissionStatus.CORRECT }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = VerdictStatus.CORRECT }, //and two wrong ones - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = SubmissionStatus.WRONG }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = SubmissionStatus.WRONG }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = VerdictStatus.WRONG }, + Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = VerdictStatus.WRONG }, //c = 4, q(c) = 2, i = 2 diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index b5ab61dc2..f52d99b19 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -2,7 +2,7 @@ package dres.run.score.scorer import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.KisTaskScorer import org.junit.jupiter.api.Assertions.assertEquals @@ -36,9 +36,9 @@ class KisTaskScorerTest { fun allWrong() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG } + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -50,7 +50,7 @@ class KisTaskScorerTest { fun immediatelyRight() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -62,7 +62,7 @@ class KisTaskScorerTest { fun rightAtTheEnd() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) @@ -72,7 +72,7 @@ class KisTaskScorerTest { fun rightInTheMiddle() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT } + Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) @@ -84,19 +84,19 @@ class KisTaskScorerTest { val submissions = listOf( //incorrect submissions - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = SubmissionStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, //correct submissions at 1/2 the task time - Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = SubmissionStatus.CORRECT }, + Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + Submission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) From 92f4d12feae51521a2dcfa6be6f9e0a2593b21a7 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 7 Nov 2022 05:26:18 +0100 Subject: [PATCH 024/498] Fixed errors in AuditLogger. --- .../dres/data/model/audit/AuditLogEntry.kt | 8 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 189 ++++++++---------- 2 files changed, 89 insertions(+), 108 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt index e93e1ff9c..24048e055 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt @@ -23,10 +23,10 @@ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { /** The timestamp of this [AuditLogEntry]. */ var timestamp by xdRequiredDateTimeProp() - /** The ID of the competition this [AuditLogEntry] belongs to. */ - var competitionId by xdStringProp() + /** The ID of the evaluation this [AuditLogEntry] belongs to. */ + var evaluationId by xdStringProp() - /** The ID of the competition this [AuditLogEntry] belongs to. */ + /** The ID of the task this [AuditLogEntry] belongs to. */ var taskId by xdStringProp() /** The ID of the submission this [AuditLogEntry] belongs to. Only valid if [type] is equal to [AuditLogType.SUBMISSION], [AuditLogType.SUBMISSION_VALIDATION] or [AuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ @@ -51,5 +51,5 @@ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { * * @return [ApiAuditLogEntry] */ - fun toApi(): ApiAuditLogEntry = ApiAuditLogEntry(this.id, this.type.toApi(), this.source.toApi(), this.timestamp.millis, this.competitionId, this.userId, this.submissionId, this.session, this.address, this.description) + fun toApi(): ApiAuditLogEntry = ApiAuditLogEntry(this.id, this.type.toApi(), this.source.toApi(), this.timestamp.millis, this.evaluationId, this.userId, this.submissionId, this.session, this.address, this.description) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 2bd99bb98..9e48a8dbc 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -5,10 +5,10 @@ import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.TaskDescriptionId -import dev.dres.data.model.run.interfaces.CompetitionId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.Verdict import dev.dres.data.model.submissions.VerdictStatus @@ -16,6 +16,7 @@ import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.first import org.joda.time.DateTime /** @@ -40,105 +41,95 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionStart(competitionId: CompetitionId, description: EvaluationTemplate, api: AuditLogSource, session: SessionId?) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.COMPETITION_START - this.source = api - this.timestamp = DateTime.now() - this.competitionId = competitionId.string - this.session = session - } + fun competitionStart(evaluationId: EvaluationId, description: EvaluationTemplate, api: AuditLogSource, session: SessionId?) { + AuditLogEntry.new { + this.type = AuditLogType.COMPETITION_START + this.source = api + this.timestamp = DateTime.now() + this.evaluationId = evaluationId + this.session = session } - EventStreamProcessor.event(RunStartEvent(competitionId, description)) + EventStreamProcessor.event(RunStartEvent(evaluationId, description)) } /** * Logs the end of a DRES competition. * - * @param competitionId [EvaluationId] that identifies the competition + * @param evaluationId [EvaluationId] that identifies the competition * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionEnd(competitionId: CompetitionId, api: AuditLogSource, session: SessionId?) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.COMPETITION_END - this.source = api - this.timestamp = DateTime.now() - this.competitionId = competitionId.string - this.session = session - } + fun competitionEnd(evaluationId: EvaluationId, api: AuditLogSource, session: SessionId?) { + AuditLogEntry.new { + this.type = AuditLogType.COMPETITION_END + this.source = api + this.timestamp = DateTime.now() + this.evaluationId = evaluationId + this.session = session } - EventStreamProcessor.event(RunEndEvent(competitionId)) + EventStreamProcessor.event(RunEndEvent(evaluationId)) } /** * Logs the start of a DRES task. * - * @param competitionId [EvaluationId] that identifies the competition + * @param evaluationId [EvaluationId] that identifies the competition * @param taskId [EvaluationId] that identifies the task * @param description The [TaskTemplate]. * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(competitionId: CompetitionId, taskId: TaskId, description: TaskTemplate, api: AuditLogSource, session: SessionId?) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.TASK_START - this.source = api - this.timestamp = DateTime.now() - this.competitionId = competitionId.string - this.taskId = taskId.string - this.session = session - } + fun taskStart(evaluationId: EvaluationId, taskId: TaskId, description: TaskTemplate, api: AuditLogSource, session: SessionId?) { + AuditLogEntry.new { + this.type = AuditLogType.TASK_START + this.source = api + this.timestamp = DateTime.now() + this.evaluationId = evaluationId + this.taskId = taskId + this.session = session } - EventStreamProcessor.event(TaskStartEvent(competitionId, taskId, description)) + EventStreamProcessor.event(TaskStartEvent(evaluationId, taskId, description)) } /** * Logs the start of a DRES task. * - * @param competitionId [EvaluationId] that identifies the competition + * @param evaluationId [EvaluationId] that identifies the competition * @param taskId [EvaluationId] that identifies the task * @param modification Description of the modification. * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskModified(competitionId: CompetitionId, taskId: TaskDescriptionId, modification: String, api: AuditLogSource, session: String?) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.TASK_MODIFIED - this.source = api - this.timestamp = DateTime.now() - this.competitionId = competitionId.string - this.taskId = taskId.string - this.description = modification - this.session = session - } + fun taskModified(evaluationId: EvaluationId, taskId: TaskId, modification: String, api: AuditLogSource, session: String?) { + AuditLogEntry.new { + this.type = AuditLogType.TASK_MODIFIED + this.source = api + this.timestamp = DateTime.now() + this.evaluationId = evaluationId + this.taskId = taskId + this.description = modification + this.session = session } } /** * Logs the end of a DRES task. * - * @param competitionId [EvaluationId] that identifies the competition + * @param evaluationId [EvaluationId] that identifies the competition * @param taskId [EvaluationId] that identifies the task * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(competitionId: CompetitionId, taskId: TaskId, api: AuditLogSource, session: SessionId?) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.TASK_END - this.source = api - this.timestamp = DateTime.now() - this.competitionId = competitionId.string - this.taskId = taskId.string - this.session = session - } + fun taskEnd(evaluationId: EvaluationId, taskId: TaskId, api: AuditLogSource, session: SessionId?) { + AuditLogEntry.new { + this.type = AuditLogType.TASK_END + this.source = api + this.timestamp = DateTime.now() + this.evaluationId = evaluationId + this.taskId = taskId + this.session = session } - EventStreamProcessor.event(TaskEndEvent(competitionId, taskId)) + EventStreamProcessor.event(TaskEndEvent(evaluationId, taskId)) } /** @@ -150,20 +141,17 @@ object AuditLogger { * @param address The IP address of the submitter. */ fun submission(submission: Submission, api: AuditLogSource, sessionId: SessionId?, address: String) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.SUBMISSION - this.source = api - this.timestamp = DateTime.now() - this.submissionId = submission.uid.string - this.competitionId = submission.task?.competition?.id?.string - this.taskId = submission.task?.uid?.string - this.submissionId = submission.uid.string - this.session = sessionId - this.address = address - } + AuditLogEntry.new { + this.type = AuditLogType.SUBMISSION + this.source = api + this.timestamp = DateTime.now() + this.submissionId = submission.id + this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId + this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ + this.session = sessionId + this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionId ?: "na", submission.task?.competition?.id!!, submission.task?.uid, submission)) + EventStreamProcessor.event(SubmissionEvent(sessionId ?: "na", submission.verdicts.first().task.evaluation.evaluationId, submission.verdicts.first().task.id, submission)) } /** @@ -178,11 +166,10 @@ object AuditLogger { this.type = AuditLogType.SUBMISSION_VALIDATION this.source = AuditLogSource.INTERNAL this.timestamp = DateTime.now() - this.submissionId = submission.uid.string - this.competitionId = submission.task?.competition?.id?.string - this.taskId = submission.task?.uid?.string - this.verdict = submission.status.toString() - this.validatorName = validator::class.simpleName + this.submissionId = submission.id + this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId + this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ + this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.verdicts.first().status.description}" /* TODO: Here name, there ID. Why? */ } } } @@ -200,10 +187,10 @@ object AuditLogger { this.type = AuditLogType.SUBMISSION_STATUS_OVERWRITE this.source = api this.timestamp = DateTime.now() - this.submissionId = submission.uid.string - this.competitionId = submission.task?.competition?.id?.string - this.taskId = submission.task?.uid?.string - this.verdict = submission.status.toString() + this.submissionId = submission.id + this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId + this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ + this.description = "Verdict: ${submission.verdicts.first().status.description}" this.session = sessionId } } @@ -222,7 +209,7 @@ object AuditLogger { this.source = AuditLogSource.INTERNAL this.timestamp = DateTime.now() this.submissionId = verdict.submission.id - this.competitionId = verdict.task.evaluation.evaluationId + this.evaluationId = verdict.task.evaluation.evaluationId this.taskId = verdict.task.taskId this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.status.description}" } @@ -231,22 +218,20 @@ object AuditLogger { /** * Logs a submission override to DRES. * - * @param competitionId [EvaluationId] that identifies the competition + * @param evaluationId [EvaluationId] that identifies the competition * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system * @param verdict The [VerdictStatus] submitted by the judge. * @param api The [AuditLogSource] * @param sessionId The identifier of the user session. */ - fun judgement(competitionId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionId: SessionId?) { + fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionId: SessionId?) { AuditLogEntry.new { this.type = AuditLogType.JUDGEMENT this.source = api this.timestamp = DateTime.now() - this.competitionId = competitionId.string - this.verdict = verdict.toString() - this.validatorName = validator.id - this.description = "Token: $token" + this.evaluationId = evaluationId + this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.description}" this.session = sessionId } } @@ -259,14 +244,12 @@ object AuditLogger { * @param sessionId The [SessionId] */ fun login(userId: UserId, api: AuditLogSource, sessionId: SessionId) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.LOGIN - this.source = api - this.timestamp = DateTime.now() - this.userId = userId - this.session = sessionId - } + AuditLogEntry.new { + this.type = AuditLogType.LOGIN + this.source = api + this.timestamp = DateTime.now() + this.userId = userId + this.session = sessionId } } @@ -278,14 +261,12 @@ object AuditLogger { * @param sessionId The [SessionId] */ fun logout(userId: UserId, api: AuditLogSource, sessionId: SessionId) { - this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.LOGOUT - this.source = api - this.timestamp = DateTime.now() - this.userId = userId - this.session = sessionId - } + AuditLogEntry.new { + this.type = AuditLogType.LOGOUT + this.source = api + this.timestamp = DateTime.now() + this.userId = userId + this.session = sessionId } } } \ No newline at end of file From cb9c963c6bce1adb4cf5be3af55312dbed5da892 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 7 Nov 2022 16:01:55 +0100 Subject: [PATCH 025/498] Started refactoring of competition related handlers. --- .../dev/dres/api/cli/EvaluationRunCommand.kt | 4 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 17 +- .../api/rest/handler/ContextExtensions.kt | 16 +- .../download/EvaluationDownloadHandler.kt | 2 +- .../EvaluationTemplateDownloadHandler.kt | 2 +- .../handler/download/ScoreDownloadHandler.kt | 58 ++++ .../evaluation/admin/AdjustDurationHandler.kt | 2 +- .../evaluation/admin/ListPastTaskHandler.kt | 10 +- .../evaluation/admin/NextTaskHandler.kt | 6 +- .../evaluation/admin/PreviousTaskHandler.kt | 2 +- .../evaluation/admin/StartTaskHandler.kt | 4 +- .../evaluation/admin/StopTaskHandler.kt | 6 +- .../evaluation/admin/SwitchTaskHandler.kt | 2 +- .../client/ClientTaskInfoHandler.kt | 18 +- .../evaluation/scores/AbstractScoreHandler.kt | 23 ++ .../scores/CurrentTaskScoreHandler.kt | 62 ++++ .../scores/HistoryTaskScoreHandler.kt | 66 ++++ .../scores/ListCompetitionScoreHandler.kt | 42 +++ .../scores/ListScoreSeriesHandler.kt | 48 +++ .../scores/ListScoreboardsHandler.kt | 35 +++ .../scores/TeamGroupScoreHandler.kt | 51 ++++ .../viewer/CompetitionRunHandler.kt | 282 +---------------- .../viewer/CompetitionRunScoreHandler.kt | 288 ------------------ .../viewer/GetCurrentTaskHandler.kt | 58 ++++ .../viewer/GetEvaluationInfoHandler.kt | 42 +++ .../evaluation/viewer/GetEvaluationState.kt | 46 +++ .../evaluation/viewer/GetTaskHintHandler.kt | 79 +++++ .../evaluation/viewer/GetTaskTargetHandler.kt | 72 +++++ .../viewer/ListEvaluationInfoHandler.kt | 35 +++ .../viewer/ListEvaluationStatesHandler.kt | 37 +++ .../evaluation/viewer/ScoreDownloadHandler.kt | 61 ---- .../handler/submission/SubmissionHandler.kt | 7 +- .../rest/types/evaluation/AdminRunOverview.kt | 2 +- .../types/evaluation/ApiEvaluationInfo.kt | 40 ++- .../{RunState.kt => ApiEvaluationState.kt} | 39 +-- .../api/rest/types/evaluation/ApiTaskInfo.kt | 21 -- .../rest/types/evaluation/ApiTaskStatus.kt | 15 + .../types/evaluation/ApiTaskTemplateInfo.kt | 22 ++ .../{TeamInfo.kt => ApiTeamInfo.kt} | 7 +- .../api/rest/types/evaluation/TaskInfo.kt | 32 -- .../rest/types/evaluation/scores/ApiScore.kt | 11 + .../evaluation/scores/ApiScoreOverview.kt | 9 + .../types/evaluation/scores/ApiScoreSeries.kt | 6 + .../evaluation/scores/ApiScoreSeriesPoint.kt | 6 + .../evaluation/scores/ApiTeamGroupValue.kt | 6 + .../evaluation/websocket/ClientMessage.kt | 6 - .../evaluation/websocket/ClientMessageType.kt | 14 + .../evaluation/websocket/ServerMessage.kt | 11 - .../evaluation/websocket/ServerMessageType.kt | 18 ++ .../dres/data/model/run/AbstractTaskRun.kt | 12 +- .../dres/data/model/run/interfaces/TaskRun.kt | 4 +- .../dev/dres/data/model/template/team/Team.kt | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 8 +- .../dev/dres/run/InteractiveRunManager.kt | 9 +- .../run/InteractiveSynchronousRunManager.kt | 26 +- .../main/kotlin/dev/dres/run/TaskRunStatus.kt | 19 -- .../main/kotlin/dev/dres/run/TaskStatus.kt | 28 ++ .../dev/dres/run/filter/SubmissionFilter.kt | 8 +- .../run/filter/SubmissionRejectedException.kt | 22 +- .../run/filter/TemporalSubmissionFilter.kt | 14 +- .../run/filter/TextualSubmissionFilter.kt | 14 +- .../dev/dres/run/score/scoreboard/Score.kt | 12 +- .../run/score/scoreboard/ScoreOverview.kt | 12 +- .../dev/dres/run/updatables/Updatable.kt | 4 +- 64 files changed, 1076 insertions(+), 866 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/{RunState.kt => ApiEvaluationState.kt} (50%) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/{TeamInfo.kt => ApiTeamInfo.kt} (67%) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScore.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreOverview.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeries.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeriesPoint.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiTeamGroupValue.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessageType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessageType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/TaskRunStatus.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/TaskStatus.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt index fb7e77ce2..4ad29a0e2 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt @@ -73,7 +73,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt } if (this.plain) { RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - println("${RunSummary(it.id, it.name, it.template.description, it.currentTaskDescription(RunActionContext.INTERNAL).name)} (${it.status})") + println("${RunSummary(it.id, it.name, it.template.description, it.currentTaskTemplate(RunActionContext.INTERNAL).name)} (${it.status})") } } else { println( @@ -94,7 +94,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt "Synchronous", it.name, it.template.description, - it.currentTaskDescription(RunActionContext.INTERNAL).name, + it.currentTaskTemplate(RunActionContext.INTERNAL).name, it.status ) is InteractiveAsynchronousRunManager -> row( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 74d54c301..7bbced171 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -10,6 +10,7 @@ import dev.dres.api.rest.handler.download.EvaluationTemplateDownloadHandler import dev.dres.api.rest.handler.evaluation.admin.* import dev.dres.api.rest.handler.evaluation.client.ClientListEvaluationsHandler import dev.dres.api.rest.handler.evaluation.client.ClientTaskInfoHandler +import dev.dres.api.rest.handler.evaluation.scores.* import dev.dres.api.rest.handler.judgement.* import dev.dres.api.rest.handler.log.QueryLogHandler import dev.dres.api.rest.handler.log.ResultLogHandler @@ -17,6 +18,7 @@ import dev.dres.api.rest.handler.template.* import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.MediaPreviewHandler import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler +import dev.dres.api.rest.handler.scores.ListCompetitionScoreHandler import dev.dres.api.rest.handler.submission.BatchSubmissionHandler import dev.dres.api.rest.handler.submission.SubmissionHandler import dev.dres.api.rest.handler.system.CurrentTimeHandler @@ -49,7 +51,7 @@ import java.io.File /** * This is a singleton instance of the RESTful API * - * @version 1.0.1 + * @version 1.1.0 * @author Luca Rossetto */ object RestApi { @@ -63,7 +65,6 @@ object RestApi { val runExecutor = RunExecutor - /** * The list of API operations, each as a handler. * Did you follow our convention? @@ -137,12 +138,12 @@ object RestApi { HistorySubmissionInfoHandler(), // Competition run scores - ListCompetitionScoreHandler(), - CurrentTaskScoreHandler(), - HistoryTaskScoreHandler(), - ListScoreSeriesHandler(), - ListScoreboardsHandler(), - TeamGroupScoreHandler(), + ListCompetitionScoreHandler(store), + CurrentTaskScoreHandler(store), + HistoryTaskScoreHandler(store), + ListScoreSeriesHandler(store), + ListScoreboardsHandler(store), + TeamGroupScoreHandler(store), // Evaluation administration CreateEvaluationHandler(store, config), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt index 1ff395ed2..a51127c34 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt @@ -66,7 +66,21 @@ fun Context.eligibleManagerForId(): RunManager { throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) } } - return manager + + if (this.isAdmin()) { + return manager + } + + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) +} +/** + * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. + * + * @return True if current user has [ApiRole.PARTICIPANT] + */ +fun Context.isAdmin(): Boolean { + val roles = AccessManager.rolesOfSession(this.sessionId()) + return roles.contains(ApiRole.ADMIN) } /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index 8a9da27ed..0e931829a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -28,7 +28,7 @@ class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadH path = "/api/v1/download/evaluation/{evaluationId}", tags = ["Download"], pathParams = [ - OpenApiParam("runId", String::class, "The evaluation ID.") + OpenApiParam("runId", String::class, "The evaluation ID.", required = true) ], responses = [ OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index d09a44a38..16505c9b2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -29,7 +29,7 @@ class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractD path = "/api/v1/download/template/{templateId}", tags = ["Download"], pathParams = [ - OpenApiParam("templateId", String::class, "The evaluation template ID") + OpenApiParam("templateId", String::class, "The evaluation template ID", required = true) ], responses = [ OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt new file mode 100644 index 000000000..6ff5d435c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.download + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.firstOrNull + +/** + * An [AbstractDownloadHandler] that allows for download of score information. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandler(store), GetRestHandler { + + override val route = "download/evaluation/{evaluationId}/scores" + + @OpenApi( + summary = "Provides a CSV download with the scores for a given evaluation.", + path = "/api/v1/download/evaluation/{evaluationId}/scores", + tags = ["Download"], + pathParams = [ + OpenApiParam("runId", String::class, "The evaluation ID.", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(String::class, type = "text/csv")]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): String { + val manager = ctx.eligibleManagerForId() + + /* Update response header. */ + ctx.contentType("text/csv") + ctx.header("Content-Disposition", "attachment; filename=\"scores-${manager.id}.csv\"") + + return this.store.transactional(false) { + val rac = RunActionContext.runActionContext(ctx, manager) + "startTime,task,group,team,score\n" + manager.tasks(rac).filter { + it.started != null + }.sortedBy { + it.started + } + .flatMap { task -> + task.scorer.scores().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.filter { t -> t.id eq it.first }.firstOrNull()?.name ?: "???"}\",${it.third}" } + }.joinToString(separator = "\n") + } + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 57d8eca53..05f0124e2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -49,7 +49,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.adjustDuration(rac, duration) - AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskDescription(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionId()) + AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionId()) SuccessStatus("Duration for run $evaluationId was successfully adjusted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index 883519ed0..7ccfead0e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.types.evaluation.ApiTaskInfo +import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { +class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { override val route: String = "evaluation/admin/{evaluationId}/task/past/list" @OpenApi( @@ -28,19 +28,19 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH ], tags = ["Evaluation Administrator"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): List { + override fun doGet(ctx: Context): List { val evaluationId = ctx.evaluationId() val runManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional (true) { val rac = RunActionContext.runActionContext(ctx, runManager) runManager.tasks(rac).filter { it.hasEnded }.map { - ApiTaskInfo( + ApiTaskTemplateInfo( taskId = it.id, templateId = it.template.id, name = it.template.name, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index ae9765cf6..7b367c90d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -10,7 +10,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveAsynchronousRunManager -import dev.dres.run.TaskRunStatus +import dev.dres.run.TaskStatus import dev.dres.utilities.extensions.sessionId import io.javalin.http.Context import io.javalin.openapi.* @@ -55,13 +55,13 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl val rac = RunActionContext.runActionContext(ctx, evaluationManager) if (evaluationManager is InteractiveAsynchronousRunManager && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) - && evaluationManager.currentTask(rac)?.status != TaskRunStatus.ENDED) { + && evaluationManager.currentTask(rac)?.status != TaskStatus.ENDED) { throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) } try { if (evaluationManager.next(rac)) { - SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskDescription(rac).name}'.") + SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") } else { throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because there are no tasks left.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index 49541fc76..768dc1127 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -41,7 +41,7 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { if (evaluationManager.previous(rac)) { - SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskDescription(rac).name}'.") + SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") } else { throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because there are no tasks left.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index b2e0ee4d0..deab10d17 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -54,8 +54,8 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.startTask(rac) - AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskDescription(rac), AuditLogSource.REST, ctx.sessionId()) - SuccessStatus("Task '${evaluationManager.currentTaskDescription(rac).name}' for evaluation $evaluationId was successfully started.") + AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), AuditLogSource.REST, ctx.sessionId()) + SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) } catch (e: IllegalAccessError) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 2f738debb..17bb5a79e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -43,12 +43,12 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl return this.store.transactional { val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { - val task = evaluationManager.currentTaskDescription(rac) + val task = evaluationManager.currentTaskTemplate(rac) evaluationManager.abortTask(rac) AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionId()) - SuccessStatus("Task '${evaluationManager.currentTaskDescription(rac).name}' for evaluation $evaluationId was successfully aborted.") + SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskDescription(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) + throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) } catch (e: IllegalAccessError) { throw ErrorStatusException(403, e.message!!, ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index a554ae05b..fcde7b8a3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -44,7 +44,7 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.goTo(rac, idx) - SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskDescription(rac).name}'.") + SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because run is in the wrong state (state = ${evaluationManager.status}).", ctx) } catch (e: IndexOutOfBoundsException) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index 88d7903b8..d4147dc61 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler.evaluation.client import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.types.evaluation.ApiTaskInfo +import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClientHandler(store), GetRestHandler { +class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClientHandler(store), GetRestHandler { override val route = "client/evaluation/currentTask/{runId}" @OpenApi( @@ -30,13 +30,13 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) ], responses = [ - OpenApiResponse("200", [OpenApiContent(ApiTaskInfo::class)]), + OpenApiResponse("200", [OpenApiContent(ApiTaskTemplateInfo::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiTaskInfo = this.store.transactional(true) { tx -> + override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional(true) { tx -> val run = ctx.eligibleManagerForId() val rac = RunActionContext.runActionContext(ctx, run) @@ -44,7 +44,7 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) - ApiTaskInfo( + ApiTaskTemplateInfo( taskId = task.id, templateId = task.template.id, name = task.template.name, @@ -55,10 +55,10 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie RunManagerStatus.CREATED -> 0 RunManagerStatus.ACTIVE -> { when(task.status) { - TaskRunStatus.CREATED, - TaskRunStatus.PREPARING -> task.duration - TaskRunStatus.RUNNING ->run.timeLeft(rac) / 1000 - TaskRunStatus.ENDED -> 0 + TaskStatus.CREATED, + TaskStatus.PREPARING -> task.duration + TaskStatus.RUNNING ->run.timeLeft(rac) / 1000 + TaskStatus.ENDED -> 0 } } RunManagerStatus.TERMINATED -> 0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt new file mode 100644 index 000000000..e13bd2c05 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt @@ -0,0 +1,23 @@ +package dev.dres.api.rest.handler.evaluation.scores + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.Evaluation +import dev.dres.run.score.scoreboard.ScoreOverview +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore + +/** + * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing [Evaluation]s. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +abstract class AbstractScoreHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { + /** By default [AbstractScoreHandler] can only be used by [ApiRole.VIEWER]. */ + override val permittedRoles: Set = setOf(ApiRole.VIEWER) + + /** All [AbstractScoreHandler]s are part of the v1 API. */ + override val apiVersion = "v1" +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt new file mode 100644 index 000000000..f521ea990 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -0,0 +1,62 @@ +package dev.dres.api.rest.handler.evaluation.scores + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.scores.ApiScore +import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.Task +import dev.dres.run.InteractiveRunManager +import dev.dres.run.score.interfaces.TeamTaskScorer +import dev.dres.run.score.scoreboard.ScoreOverview +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence + +/** + * Generates and lists the [ScoreOverview] for the currently active [Task]. + * + * Only valid for [InteractiveRunManager]s. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler { + + override val route = "score/evaluation/{evaluationId}/current" + + @OpenApi( + summary = "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + path = "/api/v1/score/evaluation/{evaluationId}/current", + tags = ["Evaluation Scores"], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiScoreOverview::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiScoreOverview { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not scores for a current task.", ctx) + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + + return this.store.transactional(true) { + val rac = RunActionContext.runActionContext(ctx, manager) + val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No active task run in evaluation ${ctx.evaluationId()}.", ctx) + val scores = (scorer as? TeamTaskScorer)?.teamScoreMap() ?: throw ErrorStatusException(400, "Scorer has more than one score per team for evaluation ${ctx.evaluationId()}.", ctx) + ApiScoreOverview("task", + manager.currentTaskTemplate(rac).taskGroup.name, + manager.template.teams.asSequence().map { team -> ApiScore(team.id, scores[team.id] ?: 0.0) }.toList() + ) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt new file mode 100644 index 000000000..2b0c82ea9 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -0,0 +1,66 @@ +package dev.dres.api.rest.handler.evaluation.scores + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isAdmin +import dev.dres.api.rest.types.evaluation.scores.ApiScore +import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.Task +import dev.dres.run.InteractiveRunManager +import dev.dres.run.score.interfaces.TeamTaskScorer +import dev.dres.run.score.scoreboard.ScoreOverview +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence + +/** + * Generates and lists the [ScoreOverview] for the specified [Task]. + * + * + * Only valid for [InteractiveRunManager]s.Can only be invoked by admins. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler { + + override val route = "score/evaluation/{evaluationId}/history/{taskId}" + + @OpenApi( + summary = "Returns the overviews of all score boards for the specified task.", + path = "/api/v1/score/evaluation/{evaluationId}/history/{taskId}", + tags = ["Evaluation Scores"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("taskId", String::class, "The task ID", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiScoreOverview::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiScoreOverview { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a score history.'", ctx) + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' is missing!'", ctx) + if (!ctx.isAdmin()) throw ErrorStatusException(403, "Access denied.", ctx) + + return this.store.transactional(true) { + val rac = RunActionContext.runActionContext(ctx, manager) + val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run ${manager.id}.", ctx) + val scores = (scorer as? TeamTaskScorer)?.teamScoreMap() ?: throw ErrorStatusException(400, "Scorer has more than one score per team for evaluation ${ctx.evaluationId()}.", ctx) + ApiScoreOverview("task", + manager.currentTaskTemplate(rac).taskGroup.name, + manager.template.teams.asSequence().map { ApiScore(it.teamId, scores[it.teamId] ?: 0.0) }.toList() + ) + } + } +} + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt new file mode 100644 index 000000000..2180bd47f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt @@ -0,0 +1,42 @@ +package dev.dres.api.rest.handler.scores + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluation.scores.AbstractScoreHandler +import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.run.score.scoreboard.ScoreOverview +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + + +/** + * Generates and lists all [ScoreOverview]s for the provided [Evaluation]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ListCompetitionScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { + + override val route = "score/evaluation/{evaluationId}" + + @OpenApi( + summary = "Returns the score overviews of a specific evaluation run.", + path = "/api/v1/score/evaluation/{evaluationId}", + tags = ["Evaluation Scores"], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val manager = ctx.eligibleManagerForId() + return this.store.transactional (true) { + manager.scoreboards.map { it.overview().toApi() } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt new file mode 100644 index 000000000..cd7a0b3ab --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt @@ -0,0 +1,48 @@ +package dev.dres.api.rest.handler.evaluation.scores + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.types.evaluation.scores.ApiScoreSeries +import dev.dres.api.rest.types.evaluation.scores.ApiScoreSeriesPoint +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] that returns a time series of all data points for a given run and scoreboard. + */ +class ListScoreSeriesHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { + override val route = "score/evaluation/{evaluationId}/series/{scoreboard}" + + @OpenApi( + summary = "Returns a time series for a given run and scoreboard.", + path = "/api/v1/score/evaluation/{evaluationId}/series/{scoreboard}", + tags = ["Evaluation Scores"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("scoreboard", String::class, "Name of the scoreboard to return the time series for.", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val scoreboard = ctx.pathParamMap().getOrElse("scoreboard") { throw ErrorStatusException(400, "Parameter 'scoreboard' is missing!'", ctx) } + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a score history.'", ctx) + return this.store.transactional(true) { + manager.scoreHistory + .filter { it.name == scoreboard } + .groupBy { it.team } + .mapValues { it.value.map { p -> ApiScoreSeriesPoint(p.score, p.timestamp) } } + .map { ApiScoreSeries(it.key, scoreboard, it.value) } + } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt new file mode 100644 index 000000000..bd0ade4fc --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt @@ -0,0 +1,35 @@ +package dev.dres.api.rest.handler.evaluation.scores + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.types.status.ErrorStatus +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [GetRestHandler] that returns the names of all available scoreboards for a given run. + */ +class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { + override val route = "score/evaluation/{evaluationId}/scoreboard/list" + + @OpenApi( + summary = "Returns a list of available scoreboard names for the given evaluation.", + path = "/api/v1/evaluation/{evaluationId}/scoreboard/list", + tags = ["Evaluation Scores"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val manager = ctx.eligibleManagerForId() + return manager.scoreboards.map { it.name } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt new file mode 100644 index 000000000..62c8061a5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -0,0 +1,51 @@ +package dev.dres.api.rest.handler.evaluation.scores + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.scores.ApiTeamGroupValue +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.toList + +/** + * + */ +class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { + override val route = "score/evaluation/{evaluationId}/teamGroup/list" + + @OpenApi( + summary = "Returns team group aggregated values of the current task.", + path = "/api/v1/score/evaluation/{evaluationId}/teamGroup/list", + tags = ["Competition Run Scores"], + pathParams = [ + OpenApiParam("runId", String::class, "ID of the competition run.", required = true), + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a score history.'", ctx) + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + + return this.store.transactional(true) { + val rac = RunActionContext.runActionContext(ctx, manager) + val aggregators = manager.currentTask(rac)?.teamGroupAggregators ?: throw ErrorStatusException(404, "No active task in evaluation ${ctx.evaluationId()}.", ctx) + val teamGroups = manager.template.teamsGroups.toList() + teamGroups.map { ApiTeamGroupValue(it.name, aggregators[it.teamGroupId]?.lastValue ?: 0.0) } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt index 29f0389d0..6f78297dd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt @@ -1,11 +1,6 @@ package dev.dres.api.rest.handler -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo -import dev.dres.api.rest.types.evaluation.RunState import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.api.rest.types.evaluation.TaskInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.competition.tasks.ApiHintContent @@ -16,290 +11,17 @@ import dev.dres.data.model.media.MediaCollection import dev.dres.data.model.template.options.SimpleOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.Submission -import dev.dres.run.TaskRunStatus +import dev.dres.run.TaskStatus import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId -import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.openapi.* import java.io.FileNotFoundException import java.io.IOException -abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRestHandler { - override val permittedRoles: Set = setOf(ApiRole.VIEWER) - override val apiVersion = "v1" - private fun userId(ctx: Context): EvaluationId = AccessManager.userIdForSession(ctx.sessionId())!! - - private fun isJudge(ctx: Context): Boolean { - val roles = AccessManager.rolesOfSession(ctx.sessionId()) - return roles.contains(ApiRole.JUDGE) && !roles.contains(ApiRole.ADMIN) - } - - fun isParticipant(ctx: Context): Boolean { - val roles = AccessManager.rolesOfSession(ctx.sessionId()) - return roles.contains(ApiRole.PARTICIPANT) && !roles.contains(ApiRole.ADMIN) - } - - - - fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { - throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) - }.UID() - -} - -class ListCompetitionRunInfosHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { - - override val route = "run/info/list" - - @OpenApi( - summary = "Lists an overview of all competition runs visible to the current user", - path = "/api/v1/run/info/list", - tags = ["Competition Run"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List = getRelevantManagers(ctx).map { ApiEvaluationInfo(it) } -} - -class ListCompetitionRunStatesHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { - - override val route = "run/state/list" - - @OpenApi( - summary = "Lists an overview of all competition runs visible to the current user", - path = "/api/v1/run/state/list", - tags = ["Competition Run"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List = - getRelevantManagers(ctx).map { - val rac = runActionContext(ctx, it) - RunState(it, rac) - } - - -} - - -class GetCompetitionRunInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler { - - override val route = "run/{runId}/info" - - @OpenApi( - summary = "Returns a specific competition run.", - path = "/api/v1/run/{runId}/info", - tags = ["Competition Run"], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - responses = [ - OpenApiResponse("200", [OpenApiContent(ApiEvaluationInfo::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ApiEvaluationInfo { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access Denied", ctx) - } - - return ApiEvaluationInfo(run) - } -} - -class GetCompetitionRunStateHandler : AbstractCompetitionRunRestHandler(), GetRestHandler { - - override val route = "run/{runId}/state" - - @OpenApi( - summary = "Returns the state of a specific competition run.", - path = "/api/v1/run/{runId}/state", - tags = ["Competition Run"], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - responses = [ - OpenApiResponse("200", [OpenApiContent(RunState::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): RunState { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access Denied", ctx) - } - - val rac = runActionContext(ctx, run) - - return RunState(run, rac) - } -} - -class CurrentTaskInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler { - - override val route = "run/{runId}/task" - - @OpenApi( - summary = "Returns the information for the current task (i.e. the one that is currently selected).", - path = "/api/v1/run/{runId}/task", - tags = ["Competition Run"], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - responses = [ - OpenApiResponse("200", [OpenApiContent(TaskInfo::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): TaskInfo { - - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - - val rac = runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - - return TaskInfo(run.currentTaskDescription(rac)) - } -} - -class CurrentTaskHintHandler(private val config: Config) : AbstractCompetitionRunRestHandler(), - GetRestHandler { - - override val route = "run/{runId}/hint/{taskId}" - - @OpenApi( - summary = "Returns the task hint for the current task run (i.e. the one that is currently selected).", - path = "/api/v1/run/{runId}/hint/{taskId}", - tags = ["Competition Run"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("taskId", String::class, "Task Description ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(ApiHintContent::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ApiHintContent { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - - val taskId = ctx.pathParam("taskId").UID() - - val rac = runActionContext(ctx, run) - - val currentTaskDescription = run.currentTaskDescription(rac) - - val task = if (currentTaskDescription.id == taskId) { - currentTaskDescription - } else { - run.taskForId(rac, taskId)?.template - } - - if (task == null) { //request to a task id that is either invalid or not yet available - throw ErrorStatusException(403, "Access denied.", ctx) - } - - try { - ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes - return task.toTaskHint(config) - } catch (e: FileNotFoundException) { - throw ErrorStatusException(404, "Query object cache file not found!", ctx) - } catch (ioe: IOException) { - throw ErrorStatusException(500, "Exception when reading query object cache file.", ctx) - } - } -} - -class CurrentTaskTargetHandler(private val config: Config, private val collections: DAO) : - AbstractCompetitionRunRestHandler(), GetRestHandler { - - override val route = "run/{runId}/target/{taskId}" - - @OpenApi( - summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", - path = "/api/v1/run/{runId}/target/{taskId}", - tags = ["Competition Run"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("taskId", String::class, "Task Description ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(ApiTargetContent::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ApiTargetContent { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - - /* Test for access rights. */ - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - val rac = runActionContext(ctx, run) - - /* Test for correct state. */ - if (run.currentTask(rac)?.status != TaskRunStatus.ENDED) { - throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) - } - - val taskId = ctx.pathParam("taskId").UID() - - val currentTaskDescription = run.currentTaskDescription(rac) - - val task = if (currentTaskDescription.id == taskId) { - currentTaskDescription - } else { - run.taskForId(rac, taskId)?.template - } - - if (task == null) { //request to a task id that is either invalid or not yet available - throw ErrorStatusException(403, "Access denied.", ctx) - } - - try { - ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes - return task.toTaskTarget(config, collections) - } catch (e: FileNotFoundException) { - throw ErrorStatusException(404, "Query object cache file not found!", ctx) - } catch (ioe: IOException) { - throw ErrorStatusException(500, "Exception when reading query object cache file.", ctx) - } - } -} +class CurrentTaskTargetHandler class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt deleted file mode 100644 index 5a1937c0e..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunScoreHandler.kt +++ /dev/null @@ -1,288 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.RunActionContext -import dev.dres.run.InteractiveRunManager -import dev.dres.run.RunExecutor -import dev.dres.run.score.interfaces.TeamTaskScorer -import dev.dres.run.score.scoreboard.Score -import dev.dres.run.score.scoreboard.ScoreOverview -import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId -import io.javalin.security.RouteRole -import io.javalin.http.Context -import io.javalin.openapi.* - -/** - * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing - * [dev.dres.data.model.run.InteractiveSynchronousEvaluation]s. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler { - - override val permittedRoles: Set = setOf(ApiRole.VIEWER) - override val apiVersion = "v1" - - private fun userId(ctx: Context): EvaluationId = AccessManager.userIdForSession(ctx.sessionId())!! - - /** - * Checks if the current session has the [ApiRole.PARTICIPANT]. - * - * @param ctx The [Context] to check. - */ - fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) - - - fun getRun(ctx: Context, runId: EvaluationId): InteractiveRunManager? { - if (isParticipant(ctx)) { - val userId = userId(ctx) - val run = RunExecutor.managerForId(runId) ?: return null - if (run is InteractiveRunManager && run.template.teams.any { it.users.contains(userId) }) { - return run - } - return null - } - val run = RunExecutor.managerForId(runId) - if (run != null && run is InteractiveRunManager){ - return run - } - return null - } -} - -/** - * Generates and lists all [ScoreOverview]s for the provided [dev.dres.data.model.run.InteractiveSynchronousEvaluation]. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class ListCompetitionScoreHandler : AbstractScoreRestHandler(), GetRestHandler> { - - override val route = "score/run/{runId}" - - @OpenApi( - summary = "Returns the score overviews of a specific competition run.", - path = "/api/v1/score/run/{runId}", - tags = ["Competition Run Scores"], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - return run.scoreboards.map { it.overview() } - } -} - -/** - * Generates and lists the [ScoreOverview] for the currently active [dev.dres.data.model.run.InteractiveSynchronousEvaluation.Task]. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class CurrentTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler { - - override val route = "score/run/{runId}/current" - - @OpenApi( - summary = "Returns the overviews of all score boards for the current task run, if it is either running or has just ended.", - path = "/api/v1/score/run/{runId}/current", - tags = ["Competition Run Scores"], - pathParams = [OpenApiParam("runId", String::class, "Competition run ID")], - responses = [ - OpenApiResponse("200", [OpenApiContent(ScoreOverview::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ScoreOverview { - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - val rac = RunActionContext.runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - - val scorer = run.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No active task run in run $runId.", ctx) - val scores = (scorer as? TeamTaskScorer)?.teamScoreMap() ?: throw ErrorStatusException(400, "Scorer has more than one score per team for run $runId.", ctx) - - return ScoreOverview("task", - run.currentTaskDescription(rac).taskGroup.name, - run.template.teams.map { team -> - Score(team.uid.string, scores[team.uid] ?: 0.0) - } - ) - } -} - -/** - * Generates and lists the [ScoreOverview] for the specified [dev.dres.data.model.run.InteractiveSynchronousEvaluation.Task]. - * Can only be invoked by admins. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class HistoryTaskScoreHandler : AbstractScoreRestHandler(), GetRestHandler { - - override val route = "score/run/{runId}/history/{taskId}" - - @OpenApi( - summary = "Returns the overviews of all score boards for the specified task run.", - path = "/api/v1/score/run/{runId}/history/{taskId}", - tags = ["Competition Run Scores"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition run ID"), - OpenApiParam("taskId", String::class, "Task run ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(ScoreOverview::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): ScoreOverview { - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val taskId = ctx.pathParamMap().getOrElse("taskId") { throw ErrorStatusException(400, "Parameter 'taskId' is missing!'", ctx) }.UID() - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - - val rac = RunActionContext.runActionContext(ctx, run) - - if (rac.isAdmin) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - - - - /* Fetch the relevant scores and generate score overview. */ - - val scorer = run.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run $runId.", ctx) - val scores = (scorer as? TeamTaskScorer)?.teamScoreMap() ?: throw ErrorStatusException(400, "Scorer has more than one score per team for run $runId.", ctx) - - return ScoreOverview("task", - run.currentTaskDescription(rac).taskGroup.name, - run.template.teams.map { - Score(it.uid.string, scores[it.uid] ?: 0.0) - } - ) - } -} - -/** - * A [GetRestHandler] that returns the names of all available scoreboards for a given run. - */ -class ListScoreboardsHandler : AbstractScoreRestHandler(), GetRestHandler> { - override val route = "score/run/{runId}/scoreboard/list" - - @OpenApi( - summary = "Returns a list of available scoreboard names for the given run.", - path = "/api/v1/score/run/{runId}/scoreboard/list", - tags = ["Competition Run Scores"], - pathParams = [ - OpenApiParam("runId", String::class, "ID of the competition run.", required = true) - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - return run.scoreboards.map { it.name } - } -} - -/** - * A [GetRestHandler] that returns a time series of all data points for a given run and scoreboard. - */ -class ListScoreSeriesHandler : AbstractScoreRestHandler(), GetRestHandler> { - override val route = "score/run/{runId}/series/{scoreboard}" - - @OpenApi( - summary = "Returns a time series for a given run and scoreboard.", - path = "/api/v1/score/run/{runId}/series/{scoreboard}", - tags = ["Competition Run Scores"], - pathParams = [ - OpenApiParam("runId", String::class, "ID of the competition run.", required = true), - OpenApiParam("scoreboard", String::class, "Name of the scoreboard to return the time series for.", required = true) - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val scoreboard = ctx.pathParamMap().getOrElse("scoreboard") { throw ErrorStatusException(400, "Parameter 'scoreboard' is missing!'", ctx) } - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - return run.scoreHistory - .filter { it.name == scoreboard } - .groupBy { it.team } - .mapValues { it.value.map { p -> ScoreSeriesPoint(p.score, p.timestamp) } } - .map { ScoreSeries(it.key, scoreboard, it.value) } - } -} - -data class ScoreSeries(val team: String, val name: String, val points: List) - -data class ScoreSeriesPoint(val score: Double, val timestamp: Long) - - -class TeamGroupScoreHandler : AbstractScoreRestHandler(), GetRestHandler> { - override val route = "score/run/{runId}/teamGroup/list" - - @OpenApi( - summary = "Returns team group aggregated values of the current task.", - path = "/api/v1/score/run/{runId}/teamGroup/list", - tags = ["Competition Run Scores"], - pathParams = [ - OpenApiParam("runId", String::class, "ID of the competition run.", required = true), - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - val rac = RunActionContext.runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - - val aggregators = run.currentTask(rac)?.teamGroupAggregators ?: throw ErrorStatusException(404, "No active task run in run $runId.", ctx) - - val teamGroups = run.template.teamGroups - - return teamGroups.map { TeamGroupValue(it.name, aggregators[it.uid]?.lastValue ?: 0.0) } - } - -} - -data class TeamGroupValue(val name: String, val value: Double) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt new file mode 100644 index 000000000..b2d45a7df --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.Task +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * A [AbstractEvaluationViewerHandler] that returns the currently active [TaskTemplate]. + * + * If a [Task] is being executed, the method returns the [TaskTemplate] for that [Task]. + * Otherwise, the selected [TaskTemplate] is returned (active task vs. task template navigation). + * + * Only eligible for [InteractiveRunManager]s. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler { + + override val route = "evaluation/{evaluationId}/task" + + @OpenApi( + summary = "Returns the information for the currently active task template (i.e., the one that is currently selected).", + path = "/api/v1/evaluation/{evaluationId}/task", + tags = ["Competition Run"], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiTaskTemplateInfo::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiTaskTemplateInfo { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a current task.'", ctx) + return this.store.transactional (true) { + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + val rac = RunActionContext.runActionContext(ctx, manager) + ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt new file mode 100644 index 000000000..271f50a7c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -0,0 +1,42 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + */ +class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluationViewerHandler(store), GetRestHandler { + + override val route = "evaluation/{evaluationId}/info" + + @OpenApi( + summary = "Returns basic information about a specific evaluation.", + path = "/api/v1/evaluation/{evaluationId}/info", + tags = ["Competition Run"], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiEvaluationInfo::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiEvaluationInfo { + val manager = ctx.eligibleManagerForId() + return this.store.transactional (true) { + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } + ApiEvaluationInfo(manager) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt new file mode 100644 index 000000000..b832d2de7 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt @@ -0,0 +1,46 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +/** + * + */ +class GetEvaluationState(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler { + + override val route = "evaluation/{evaluationId}/state" + + @OpenApi( + summary = "Returns the state of a specific evaluation.", + path = "/api/v1/evaluation/{evaluationId}/state", + tags = ["Evaluation"], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiEvaluationState::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiEvaluationState { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) + return this.store.transactional (true) { + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } + val rac = RunActionContext.runActionContext(ctx, manager) + ApiEvaluationState(manager, rac) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt new file mode 100644 index 000000000..dc20c430f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -0,0 +1,79 @@ +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluation.viewer.AbstractEvaluationViewerHandler +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.competition.tasks.ApiHintContent +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.Config +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.Task +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import java.io.FileNotFoundException +import java.io.IOException + +/** + * A [AbstractEvaluationViewerHandler] that returns the currently active [TaskTemplate]. + * + * If a [Task] is being executed, the method returns the [TaskTemplate] for that [Task]. + * Otherwise, the selected [TaskTemplate] is returned (active task vs. task template navigation). + * + * Only eligible for [InteractiveRunManager]s. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class GetTaskHintHandler(store: TransientEntityStore, private val config: Config) : AbstractEvaluationViewerHandler(store), GetRestHandler { + + override val route = "run/{evaluationId}/hint/{taskId}" + + @OpenApi( + summary = "Returns the task hint for the specified task.", + path = "/api/v1/run/{evaluationId}/hint/{taskId}", + tags = ["Competition Run"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("taskId", String::class, "The task ID.", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiHintContent::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiHintContent { + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) + return this.store.transactional (true) { + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } + val rac = RunActionContext.runActionContext(ctx, manager) + + val currentTaskDescription = manager.currentTaskTemplate(rac) + val task = if (currentTaskDescription.id == taskId) { + currentTaskDescription + } else { + manager.taskForId(rac, taskId)?.template ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) + } + + try { + ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes + task.toTaskHint(this.config) + } catch (e: FileNotFoundException) { + throw ErrorStatusException(404, "Query object cache file not found!", ctx) + } catch (ioe: IOException) { + throw ErrorStatusException(500, "Exception when reading query object cache file.", ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt new file mode 100644 index 000000000..a3a24489a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -0,0 +1,72 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.competition.tasks.ApiTargetContent +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.Config +import dev.dres.data.model.run.RunActionContext +import dev.dres.run.InteractiveRunManager +import dev.dres.run.TaskStatus +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import java.io.FileNotFoundException +import java.io.IOException + +/** + * + */ +class GetTaskTargetHandler(store: TransientEntityStore, private val config: Config) : AbstractEvaluationViewerHandler(), GetRestHandler { + + override val route = "run/{evaluationId}/target/{taskId}" + + @OpenApi( + summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", + path = "/api/v1/evaluation/{evaluationId}/target/{taskId}", + tags = ["Competition Run"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("taskId", String::class, "The task ID.", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiTargetContent::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): ApiTargetContent { + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) + return this.store.transactional (true) { + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } + val rac = RunActionContext.runActionContext(ctx, manager) + + /* Test for correct state. */ + var task = manager.currentTask(rac) + if (task == null) { + task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) + + } + if (task.status != TaskStatus.ENDED) { + throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) + } + + try { + ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes + task.toTaskTarget(config, collections) + } catch (e: FileNotFoundException) { + throw ErrorStatusException(404, "Query object cache file not found!", ctx) + } catch (ioe: IOException) { + throw ErrorStatusException(500, "Exception when reading query object cache file.", ctx) + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt new file mode 100644 index 000000000..807cb702f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt @@ -0,0 +1,35 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo +import dev.dres.api.rest.types.status.ErrorStatus +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore + +/** + * + */ +class ListEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluationViewerHandler(store), GetRestHandler> { + + override val route = "evaluation/info/list" + + @OpenApi( + summary = "Lists an overview of all evaluations visible to the current user.", + path = "/api/v1/evaluation/info/list", + tags = ["Evaluation"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List = this.store.transactional(true) { + this.getRelevantManagers(ctx).map { manager -> + ApiEvaluationInfo(manager) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt new file mode 100644 index 000000000..b611db4fa --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt @@ -0,0 +1,37 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.HttpMethod +import io.javalin.openapi.OpenApi +import io.javalin.openapi.OpenApiContent +import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore + +/** + * + */ +class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { + + override val route = "evaluation/state/list" + + @OpenApi( + summary = "Lists an overview of all evaluation visible to the current user.", + path = "/api/v1/evaluation/state/list", + tags = ["Evaluation"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List = this.store.transactional(true) { + this.getRelevantManagers(ctx).map { + val rac = RunActionContext.runActionContext(ctx, it) + ApiEvaluationState(it, rac) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt deleted file mode 100644 index a2a4fe258..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ScoreDownloadHandler.kt +++ /dev/null @@ -1,61 +0,0 @@ -package dev.dres.api.rest.handler.evaluation.viewer - -import dev.dres.api.rest.handler.AbstractScoreRestHandler -import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.InteractiveAsynchronousEvaluation -import io.javalin.http.Context -import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -class ScoreDownloadHandler(store: TransientEntityStore) : AbstractScoreRestHandler(), GetRestHandler { - - override val route = "download/run/{runId}/scores" - - @OpenApi( - summary = "Provides a CSV download with the scores for a given competition run.", - path = "/api/v1/download/run/{runId}/scores", - tags = ["Download"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition run ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(String::class, type = "text/csv")]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): String { - - val runId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'runId' is missing!'", ctx) }.UID() - val run = this.runs[runId] ?: throw ErrorStatusException(404, "Run $runId not found", ctx) - - /* Update response header. */ - ctx.contentType("text/csv") - ctx.header("Content-Disposition", "attachment; filename=\"scores-${runId.string}.csv\"") - - if (run is InteractiveAsynchronousEvaluation) { - - return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } - .flatMap { task -> - task.scorer.scores().filter { it.first == task.teamId } - .map { "${task.started},\"${task.description.name}\",\"${task.description.taskGroup.name}\",\"${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"}\",${it.third}" } - }.joinToString(separator = "\n") - - } - - return "startTime,task,group,team,score\n" + run.tasks.filter { it.started != null }.sortedBy { it.started } - .flatMap { task -> - task.scorer.scores().map { "${task.started},\"${task.description.name}\",\"${task.description.taskGroup.name}\",\"${run.description.teams.find { t -> t.uid == it.first }?.name ?: "???"}\",${it.third}" } - }.joinToString(separator = "\n") - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index daf23be5c..5396040fa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -4,7 +4,6 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.preview.AbstractPreviewHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus @@ -111,7 +110,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionId(), ctx.req().remoteAddr) - if (run.currentTaskDescription(rac).taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS)) { //pre-generate preview + if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS)) { //pre-generate preview generatePreview(submission.verdicts.first()) } submission to result @@ -190,8 +189,8 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con verdict.text = textParam return submission } else if (itemParam != null) { - val collection = runManager.currentTaskDescription(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ - val mapToSegment = runManager.currentTaskDescription(rac).taskGroup.type.options.contains(TaskOption.MAP_TO_SEGMENT) + val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ + val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(TaskOption.MAP_TO_SEGMENT) val item = MediaItem.query((MediaItem::name eq itemParam) and (MediaItem::collection eq collection)).firstOrNull() ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) val range: Pair? = when { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt index 4792ee387..e4cf6caf4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt @@ -37,7 +37,7 @@ data class TaskRunOverview( val group: String, val duration: Long, val taskId: String, - val status: TaskRunStatus, + val status: TaskStatus, val started: Long?, val ended: Long?) { constructor(task: TaskRun) : this( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt index 8ac611050..899d5552c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -2,7 +2,11 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunProperties +import dev.dres.run.InteractiveAsynchronousRunManager +import dev.dres.run.InteractiveSynchronousRunManager +import dev.dres.run.NonInteractiveRunManager import dev.dres.run.RunManager +import kotlinx.dnq.query.asSequence /** * Contains the basic and most importantly static information about a [InteractiveSynchronousEvaluation] and the @@ -10,15 +14,31 @@ import dev.dres.run.RunManager * it allows for local caching and other optimizations. * * @author Ralph Gasser - * @version 1.0.2 + * @version 1.1.0 */ data class ApiEvaluationInfo( - val id: String, - val name: String, - val templateId: String, - val templateDescription: String?, - val type: ApiRunType, - val properties: RunProperties, - val teams: List, - val tasks: List, -) \ No newline at end of file + val id: String, + val name: String, + val templateId: String, + val templateDescription: String?, + val type: ApiRunType, + val properties: RunProperties, + val teams: List, + val tasks: List, +) { + constructor(manager: RunManager): this( + manager.id, + manager.name, + manager.template.id, + manager.template.name, + when(manager) { + is InteractiveSynchronousRunManager -> ApiRunType.SYNCHRONOUS + is InteractiveAsynchronousRunManager -> ApiRunType.ASYNCHRONOUS + is NonInteractiveRunManager -> ApiRunType.NON_INTERACTIVE + else -> throw IllegalStateException("Incompatible type of run manager.") + }, + manager.runProperties, + manager.template.teams.asSequence().map { team -> ApiTeamInfo(team) }.toList(), + manager.template.tasks.asSequence().map { task -> ApiTaskTemplateInfo(task) }.toList() + ) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt similarity index 50% rename from backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt index c2c219510..4486fb88e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/RunState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt @@ -9,45 +9,32 @@ import dev.dres.run.* * * This is information that changes in the course of a run and therefore must be updated frequently. * - * @version 1.1.1 + * @version 1.2.0 */ -data class RunState( +data class ApiEvaluationState( val id: String, val runStatus: RunManagerStatus, - val taskRunStatus: RestTaskRunStatus, - val currentTask: TaskInfo?, + val taskRunStatus: ApiTaskStatus, + val currentTask: ApiTaskTemplateInfo?, val timeLeft: Long, val timeElapsed: Long ) { constructor(run: InteractiveRunManager, context: RunActionContext) : this( run.id, - //RestRunManagerStatus.getState(run, context), run.status, - RestTaskRunStatus.fromTaskRunStatus(run.currentTask(context)?.status), + when(run.currentTask(context)?.status) { + TaskStatus.CREATED -> ApiTaskStatus.CREATED + TaskStatus.PREPARING -> ApiTaskStatus.PREPARING + TaskStatus.RUNNING -> ApiTaskStatus.RUNNING + TaskStatus.ENDED -> ApiTaskStatus.ENDED + null -> ApiTaskStatus.NO_TASK + }, try { - TaskInfo(run.currentTaskDescription(context)) + ApiTaskTemplateInfo(run.currentTaskTemplate(context)) } catch (e: IllegalArgumentException) { - TaskInfo.EMPTY_INFO + ApiTaskTemplateInfo.EMPTY_INFO }, run.timeLeft(context) / 1000, run.timeElapsed(context) / 1000 ) -} - -enum class RestTaskRunStatus { - NO_TASK, - CREATED, - PREPARING, - RUNNING, - ENDED; - - companion object { - fun fromTaskRunStatus(taskRunStatus: TaskRunStatus?): RestTaskRunStatus = when(taskRunStatus) { - TaskRunStatus.CREATED -> CREATED - TaskRunStatus.PREPARING -> PREPARING - TaskRunStatus.RUNNING -> RUNNING - TaskRunStatus.ENDED -> ENDED - null -> NO_TASK - } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt deleted file mode 100644 index b3118894d..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskInfo.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -/** - * Information of past task in a [dev.dres.data.model.run.InteractiveSynchronousEvaluation]. - * The information includes the [dev.dres.data.model.template.TaskDescription], - * the actual task, its name, [dev.dres.data.model.template.TaskGroup], - * [dev.dres.data.model.template.TaskType] and the number of submissions. - * - * @author Loris Sauter - * @version 1.0.0 - */ -data class ApiTaskInfo( - val taskId: String, - val templateId: String, - val name: String, - val taskGroup: String, - val taskType: String, - val numberOfSubmissions: Int, - val remainingTime: Long, - val running: Boolean -) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt new file mode 100644 index 000000000..2879e28e5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt @@ -0,0 +1,15 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.run.TaskStatus + + +/** + * + */ +enum class ApiTaskStatus(val status: TaskStatus?) { + NO_TASK(null), + CREATED(TaskStatus.CREATED), + PREPARING(TaskStatus.PREPARING), + RUNNING(TaskStatus.PREPARING), + ENDED(TaskStatus.ENDED); +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt new file mode 100644 index 000000000..1f869abce --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt @@ -0,0 +1,22 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.run.InteractiveSynchronousEvaluation + +/** + * Basic and most importantly static information about a [TaskTemplate]. + * + * Since this information usually doesn't change in the course of a run, it allows for local caching and other optimizations. + * + * @author Ralph Gasser + * @author Loris Sauter + * @version 1.1.0 + */ +data class ApiTaskTemplateInfo(val templateId: String, val name: String, val taskGroup: String, val taskType: String, val duration: Long) { + + constructor(task: TaskTemplate) : this(task.id, task.name, task.taskGroup.name, task.taskGroup.type.name, task.duration) + + companion object { + val EMPTY_INFO = ApiTaskTemplateInfo("", "N/A", "N/A", "N/A", 0) + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt similarity index 67% rename from backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TeamInfo.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt index ad9e6af76..6c4ad5fc1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt @@ -2,6 +2,7 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.template.team.Team import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.template.team.TeamId /** * Basic and most importantly static information about the [Team] partaking in a [InteractiveSynchronousEvaluation]. @@ -9,8 +10,8 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation * and other optimizations. * * @author Ralph Gasser - * @version 1.0.2 + * @version 1.1.0 */ -data class TeamInfo(val uid: String, val name: String, val color: String, val logoId: String) { - constructor(team: Team) : this(team.uid.string, team.name, team.color, team.logoId.string) +data class ApiTeamInfo(val id: TeamId, val name: String, val color: String) { + constructor(team: Team) : this(team.id, team.name, team.color) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt deleted file mode 100644 index 049f9e7a5..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/TaskInfo.kt +++ /dev/null @@ -1,32 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.run.InteractiveSynchronousEvaluation - -/** - * Basic and most importantly static information about the [TaskTemplate] - * of a [InteractiveSynchronousEvaluation]. Since this information usually doesn't change in the course of a run, it - * allows for local caching and other optimizations. - * - * @author Ralph Gasser and Loris Sauter - * @version 1.0.2 - */ -data class TaskInfo( - val id: String, - val name: String, - val taskGroup: String, - val taskType: String, - val duration: Long) { - - constructor(task: TaskTemplate) : this( - task.id.string, - task.name, - task.taskGroup.name, - task.taskType.name, - task.duration - ) - - companion object { - val EMPTY_INFO = TaskInfo(EvaluationId.EMPTY.string, "N/A", "N/A", "N/A", 0) - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScore.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScore.kt new file mode 100644 index 000000000..b86f5b451 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScore.kt @@ -0,0 +1,11 @@ +package dev.dres.api.rest.types.evaluation.scores + +import dev.dres.data.model.template.team.TeamId + +/** + * A container class to track scores per team. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiScore(val teamId: TeamId, val score: Double) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreOverview.kt new file mode 100644 index 000000000..966eccaff --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreOverview.kt @@ -0,0 +1,9 @@ +package dev.dres.api.rest.types.evaluation.scores + +/** + * A container class to scores for a specific score board. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiScoreOverview(val name: String, val taskGroup: String?, val scores: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeries.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeries.kt new file mode 100644 index 000000000..e6e876e12 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeries.kt @@ -0,0 +1,6 @@ +package dev.dres.api.rest.types.evaluation.scores + +/** + * + */ +data class ApiScoreSeries(val team: String, val name: String, val points: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeriesPoint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeriesPoint.kt new file mode 100644 index 000000000..83539910f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiScoreSeriesPoint.kt @@ -0,0 +1,6 @@ +package dev.dres.api.rest.types.evaluation.scores + +/** + * + */ +class ApiScoreSeriesPoint(val score: Double, val timestamp: Long) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiTeamGroupValue.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiTeamGroupValue.kt new file mode 100644 index 000000000..fd163bbb0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/scores/ApiTeamGroupValue.kt @@ -0,0 +1,6 @@ +package dev.dres.api.rest.types.evaluation.scores + +/** + * + */ +data class ApiTeamGroupValue(val name: String, val value: Double) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt index f5f4828d0..3f11e44c1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt @@ -8,9 +8,3 @@ package dev.dres.api.rest.types.evaluation.websocket */ data class ClientMessage(val runId: String, val type: ClientMessageType) -enum class ClientMessageType { - ACK, /** Acknowledgement of the last message received. */ - REGISTER, - UNREGISTER, - PING -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessageType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessageType.kt new file mode 100644 index 000000000..46df6a05e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessageType.kt @@ -0,0 +1,14 @@ +package dev.dres.api.rest.types.evaluation.websocket + +/** + * Enumeration of the types of [ClientMessage]s. + * + * @version 1.0.0 + * @author Ralph Gasser + */ +enum class ClientMessageType { + ACK, /** Acknowledgement of the last message received. */ + REGISTER, + UNREGISTER, + PING +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt index 2d9692d67..c5a01ff8d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt @@ -2,7 +2,6 @@ package dev.dres.api.rest.types.evaluation.websocket import dev.dres.data.model.run.EvaluationId - /** * Message send by the DRES server via WebSocket to inform clients about the state of the run. * @@ -11,13 +10,3 @@ import dev.dres.data.model.run.EvaluationId */ data class ServerMessage(val evaluationId: EvaluationId, val type: ServerMessageType, val timestamp: Long = System.currentTimeMillis()) -enum class ServerMessageType { - COMPETITION_START, /** Competition run started. */ - COMPETITION_UPDATE, /** State of competition run was updated. */ - COMPETITION_END, /** Competition run ended. */ - TASK_PREPARE, /** Prepare for a task run to start. */ - TASK_START, /** Task run started. */ - TASK_UPDATED, /** State of task run has changed; mostly handles arrival of or changes to submissions. */ - TASK_END, /** Tasks run ended. */ - PING /** Keep alive */ -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessageType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessageType.kt new file mode 100644 index 000000000..cad6f81d0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessageType.kt @@ -0,0 +1,18 @@ +package dev.dres.api.rest.types.evaluation.websocket + +/** + * Enumeration of the types of [ServerMessage]s. + * + * @version 1.0.0 + * @author Ralph Gasser + */ +enum class ServerMessageType { + COMPETITION_START, /** Competition run started. */ + COMPETITION_UPDATE, /** State of competition run was updated. */ + COMPETITION_END, /** Competition run ended. */ + TASK_PREPARE, /** Prepare for a task run to start. */ + TASK_START, /** Task run started. */ + TASK_UPDATED, /** State of task run has changed; mostly handles arrival of or changes to submissions. */ + TASK_END, /** Tasks run ended. */ + PING /** Keep alive */ +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt index e2feddfdc..e82fc2731 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt @@ -3,7 +3,7 @@ package dev.dres.data.model.run import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.run.TaskRunStatus +import dev.dres.run.TaskStatus import kotlinx.dnq.util.findById /** @@ -51,7 +51,7 @@ abstract class AbstractTaskRun(task: Task): TaskRun { get() = this.task.template @Volatile - override var status: TaskRunStatus = TaskRunStatus.CREATED + override var status: TaskStatus = TaskStatus.CREATED protected set fun prepare() { @@ -61,7 +61,7 @@ abstract class AbstractTaskRun(task: Task): TaskRun { if (this.hasStarted) { throw IllegalStateException("Run has already been started.") } - this.status = TaskRunStatus.PREPARING + this.status = TaskStatus.PREPARING } /** @@ -72,7 +72,7 @@ abstract class AbstractTaskRun(task: Task): TaskRun { throw IllegalStateException("Run has already been started.") } this.started = System.currentTimeMillis() - this.status = TaskRunStatus.RUNNING + this.status = TaskStatus.RUNNING } /** @@ -83,7 +83,7 @@ abstract class AbstractTaskRun(task: Task): TaskRun { this.started = System.currentTimeMillis() } this.ended = System.currentTimeMillis() - this.status = TaskRunStatus.ENDED + this.status = TaskStatus.ENDED } @@ -95,6 +95,6 @@ abstract class AbstractTaskRun(task: Task): TaskRun { throw IllegalStateException("Run has not yet ended.") } this.ended = null - this.status = TaskRunStatus.RUNNING + this.status = TaskStatus.RUNNING } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 4dcca77e1..d24b1a922 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -2,7 +2,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task -import dev.dres.run.TaskRunStatus +import dev.dres.run.TaskStatus import dev.dres.run.score.interfaces.TaskScorer typealias TaskId = String @@ -29,5 +29,5 @@ interface TaskRun: Run { val scorer: TaskScorer /** The current status of this [Task]. */ - val status: TaskRunStatus + val status: TaskStatus } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt index 4fe58018e..ce042d93e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt @@ -23,7 +23,7 @@ class Team(entity: Entity) : PersistentEntity(entity) { companion object: XdNaturalEntityType() { /** Combination of [Team] name / competition must be unique. */ override val compositeIndices = listOf( - listOf(Team::name, Team::competition) + listOf(Team::name, Team::template) ) } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index a400c1ed6..b9e9390b0 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -221,7 +221,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @param context The [RunActionContext] used for the invocation. * @return The [TaskTemplate] for the given team. */ - override fun currentTaskDescription(context: RunActionContext): TaskTemplate { + override fun currentTaskTemplate(context: RunActionContext): TaskTemplate { require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager." } return this.run.currentTaskDescription(context.teamId) } @@ -239,7 +239,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.template.tasks.indexOf(this.currentTaskDescription(context)) - 1 + val newIndex = this.template.tasks.indexOf(this.currentTaskTemplate(context)) - 1 return try { this.goTo(context, newIndex) true @@ -260,7 +260,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.template.tasks.indexOf(this.currentTaskDescription(context)) + 1 + val newIndex = this.template.tasks.indexOf(this.currentTaskTemplate(context)) + 1 return try { this.goTo(context, newIndex) true @@ -737,5 +737,5 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous private fun teamHasRunningTask(teamId: TeamId) = this.run.currentTaskForTeam(teamId)?.isRunning == true private fun teamHasPreparingTask(teamId: TeamId) = - this.run.currentTaskForTeam(teamId)?.status == TaskRunStatus.PREPARING + this.run.currentTaskForTeam(teamId)?.status == TaskStatus.PREPARING } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index a01ec4c3b..eae08d171 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -67,7 +67,7 @@ interface InteractiveRunManager : RunManager { * @param context The [RunActionContext] used for the invocation. * @return [TaskTemplate] */ - fun currentTaskDescription(context: RunActionContext): TaskTemplate + fun currentTaskTemplate(context: RunActionContext): TaskTemplate /** * Starts the [currentTask] and thus moves the [RunManager.status] from @@ -141,13 +141,12 @@ interface InteractiveRunManager : RunManager { fun currentTask(context: RunActionContext): AbstractInteractiveTask? /** - * Returns [AbstractInteractiveTask]s for the specified index. The index is zero based, i.e., an index of 0 returns the - * first [AbstractInteractiveTask], index of 1 the second etc. + * Returns [AbstractInteractiveTask]s for the specified [TaskId]. * * @param context The [RunActionContext] used for the invocation. - * @param taskId The [EvaluationId] of the desired [AbstractInteractiveTask]. + * @param taskId The [TaskId] of the desired [AbstractInteractiveTask]. */ - fun taskForId(context: RunActionContext, taskId: EvaluationId): AbstractInteractiveTask? + fun taskForId(context: RunActionContext, taskId: TaskId): AbstractInteractiveTask? /** * List of [Submission]s for the current [AbstractInteractiveTask]. diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index e0fc7b365..39ba8c553 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -190,7 +190,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv LOGGER.info("SynchronousRunManager ${this.id} terminated") } - override fun currentTaskDescription(context: RunActionContext): TaskTemplate = this.stateLock.write { + override fun currentTaskTemplate(context: RunActionContext): TaskTemplate = this.stateLock.write { checkStatus( RunManagerStatus.CREATED, RunManagerStatus.ACTIVE/*, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED*/ @@ -249,7 +249,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv assureNoRunningTask() checkContext(context) - val currentTaskDescription = this.currentTaskDescription(context) + val currentTaskDescription = this.currentTaskTemplate(context) /* Check for duplicate task runs */ if (!runProperties.allowRepeatedTasks && this.run.tasks.any { it.template.id == currentTaskDescription.id }) { @@ -316,9 +316,9 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv // } when (this.run.currentTask?.status) { - TaskRunStatus.PREPARING, - TaskRunStatus.RUNNING, - TaskRunStatus.ENDED -> this.run.currentTask + TaskStatus.PREPARING, + TaskStatus.RUNNING, + TaskStatus.ENDED -> this.run.currentTask else -> null } @@ -377,7 +377,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - return if (this.run.currentTask?.status == TaskRunStatus.RUNNING) { + return if (this.run.currentTask?.status == TaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") max( @@ -396,7 +396,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { - return if (this.run.currentTask?.status == TaskRunStatus.RUNNING) { + return if (this.run.currentTask?.status == TaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) @@ -446,7 +446,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv this.stateLock.read { when (message.type) { ClientMessageType.ACK -> { - if (this.run.currentTask?.status == TaskRunStatus.PREPARING) { + if (this.run.currentTask?.status == TaskStatus.PREPARING) { this.readyLatch.setReady(connection) } } @@ -612,7 +612,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv */ private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ - if (this.run.currentTask?.status == TaskRunStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { + if (this.run.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { this.run.currentTask!!.start() //this.status = RunManagerStatus.RUNNING_TASK @@ -627,7 +627,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ - if (this.run.currentTask?.status == TaskRunStatus.RUNNING) { + if (this.run.currentTask?.status == TaskStatus.RUNNING) { val task = this.run.currentTask!! val timeLeft = max( 0L, @@ -684,15 +684,15 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv } private fun assureTaskRunning() { - if (this.run.currentTask?.status != TaskRunStatus.RUNNING) throw IllegalStateException("Task not running") + if (this.run.currentTask?.status != TaskStatus.RUNNING) throw IllegalStateException("Task not running") } private fun assureTaskPreparingOrRunning() { val status = this.run.currentTask?.status - if (status != TaskRunStatus.RUNNING && status != TaskRunStatus.PREPARING) throw IllegalStateException("Task not preparing or running") + if (status != TaskStatus.RUNNING && status != TaskStatus.PREPARING) throw IllegalStateException("Task not preparing or running") } private fun assureNoRunningTask() { - if (this.run.tasks.any { it.status == TaskRunStatus.RUNNING }) throw IllegalStateException("Task running!") + if (this.run.tasks.any { it.status == TaskStatus.RUNNING }) throw IllegalStateException("Task running!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/TaskRunStatus.kt b/backend/src/main/kotlin/dev/dres/run/TaskRunStatus.kt deleted file mode 100644 index 0321f95fa..000000000 --- a/backend/src/main/kotlin/dev/dres/run/TaskRunStatus.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.dres.run - -enum class TaskRunStatus { - - CREATED, - /** - * A [Task] is currently being prepared for execution by the [RunManager]. - * - * This is an optional [RunManagerStatus] and can or cannot be used by the [RunManager] implementation. - */ - PREPARING, - - /** A [Task] is currently being executed by the [RunManager]. */ - RUNNING, - - /** A [Task] has been completed by the [RunManager]. */ - ENDED, - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt b/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt new file mode 100644 index 000000000..a96d48b19 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt @@ -0,0 +1,28 @@ +package dev.dres.run + +import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.Task + +/** + * The status of a [Task] within an [Evaluation]. + * + * @author Luca Rossetto + * @version 1.0.0. + */ +enum class TaskStatus { + /** + * A [Task] was freshly created and is ready for execution. + */ + CREATED, + + /** + * A [Task] is currently being prepared for execution. + */ + PREPARING, + + /** A [Task] is currently being executed. */ + RUNNING, + + /** A [Task] has been completed. */ + ENDED; +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt index 601bccc7e..59a92d0b4 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt @@ -1,6 +1,7 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission +import org.slf4j.LoggerFactory import java.util.function.Predicate /** @@ -11,9 +12,9 @@ import java.util.function.Predicate * @version 1.1.0 */ interface SubmissionFilter : Predicate { -// override infix fun and(other: Predicate): SubmissionFilter = SubmissionFilter { s -> this@SubmissionFilter.test(s) && other.test(s) } -// override infix fun or(other: Predicate): SubmissionFilter = SubmissionFilter { s -> this@SubmissionFilter.test(s) || other.test(s) } -// operator fun not(): SubmissionFilter = SubmissionFilter { s -> !this@SubmissionFilter.test(s) } + companion object { + private val LOGGER = LoggerFactory.getLogger(this::class.java) + } val reason: String @@ -25,6 +26,7 @@ interface SubmissionFilter : Predicate { */ fun acceptOrThrow(submission: Submission) { if (!this.test(submission)) { + LOGGER.info("Submission $${submission.submissionId} was rejected by filter: $reason") throw SubmissionRejectedException(submission, reason) } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt index 4dfd79e7a..fd9632e9f 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt @@ -1,8 +1,6 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.data.model.submissions.aspects.TextAspect import org.slf4j.LoggerFactory /** @@ -11,22 +9,4 @@ import org.slf4j.LoggerFactory * @author Ralph Gasser * @version 1.0.0 */ -class SubmissionRejectedException(s: Submission, reason: String) : Throwable( - "Submission ${ - when (s) { - is ItemAspect -> "for item ${s.item.name}" - is TextAspect -> "with text '${s.text}'" - else -> "" - } - } was rejected by filter: $reason" -) { - - companion object { - private val logger = LoggerFactory.getLogger(this::class.java) - } - - init { - logger.info("Submission $s was rejected by filter: $reason") - } - -} \ No newline at end of file +class SubmissionRejectedException(s: Submission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt index 7fb208d1d..b27c09ef0 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt @@ -1,11 +1,19 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.aspects.TemporalSubmissionAspect +import dev.dres.data.model.submissions.VerdictType +import kotlinx.dnq.query.asSequence +/** + * A [SubmissionFilter} that checks if a [Submission] contains temporal information. + * + * @author Luca Rossetto + * @version 1.1.0 + */ class TemporalSubmissionFilter : SubmissionFilter { - override val reason = "Submission does not include temporal information" + override val reason = "Submission does not include temporal information." - override fun test(submission: Submission): Boolean = submission is TemporalSubmissionAspect + override fun test(submission: Submission): Boolean + = submission.verdicts.asSequence().all { it.type == VerdictType.TEMPORAL && it.start != null && it.end != null } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt index 50c6dd85b..19519ab55 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt @@ -1,11 +1,19 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.aspects.TextAspect +import dev.dres.data.model.submissions.VerdictType +import kotlinx.dnq.query.asSequence +/** + * A [SubmissionFilter} that checks if a [Submission] contains text information. + * + * @author Luca Rossetto + * @version 1.1.0 + */ class TextualSubmissionFilter : SubmissionFilter { - override val reason = "Submission does not include textual information (or is an emppty submission)" + override val reason = "Submission does not include textual information (or is an empty submission)" - override fun test(submission: Submission): Boolean = submission is TextAspect && submission.text.isNotEmpty() + override fun test(submission: Submission): Boolean + = submission.verdicts.asSequence().all { it.text != null && it.type == VerdictType.TEXT } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt index b7d04e899..f92986a8d 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Score.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.template.team.TeamId +import dev.dres.api.rest.types.evaluation.scores.ApiScore import dev.dres.data.model.template.team.TeamId /** * A container class to track scores per team. @@ -8,4 +8,12 @@ import dev.dres.data.model.template.team.TeamId * @author Ralph Gasser * @version 1.0.0 */ -data class Score(val teamId: TeamId, val score: Double) +data class Score(val teamId: TeamId, val score: Double) { + + /** + * Converts this [Score] to a RESTful API representation [ApiScore]. + * + * @return [ApiScore] + */ + fun toApi(): ApiScore = ApiScore(this.teamId, this.score) +} diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt index 64723a2df..056ad39e0 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/ScoreOverview.kt @@ -1,9 +1,19 @@ package dev.dres.run.score.scoreboard +import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview + /** * A container class to scores for a specific score board. * * @author Ralph Gasser * @version 1.0.0 */ -data class ScoreOverview(val name: String, val taskGroup: String?, val scores: List) +data class ScoreOverview(val name: String, val taskGroup: String?, val scores: List) { + + /** + * Converts this [ScoreOverview] to a RESTful API representation [ApiScoreOverview]. + * + * @return [ApiScoreOverview] + */ + fun toApi() = ApiScoreOverview(this.name, this.taskGroup, this.scores.map { it.toApi() }) +} diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt index 7221e0339..888b8682e 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt @@ -1,6 +1,6 @@ package dev.dres.run.updatables -import dev.dres.api.rest.types.evaluation.RunState +import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.run.RunManagerStatus /** @@ -22,7 +22,7 @@ interface Updatable { fun update(status: RunManagerStatus) /** - * Returns true, if this [Updatable] should be updated given the [RunState] and false otherwise. + * Returns true, if this [Updatable] should be updated given the [ApiEvaluationState] and false otherwise. * * @param status The [RunManagerStatus] to check * @return True if update is required, false otherwise. From cc65ab1c48bf007be973d1363bad10a3d898fbc2 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 7 Nov 2022 17:24:38 +0100 Subject: [PATCH 026/498] Added missing file. --- .../model/template/task/TaskTemplateTarget.kt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt new file mode 100644 index 000000000..23f5ff255 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt @@ -0,0 +1,115 @@ +package dev.dres.data.model.template.task + +import dev.dres.api.rest.types.collection.time.ApiTemporalRange +import dev.dres.api.rest.types.competition.tasks.ApiTarget +import dev.dres.api.rest.types.task.ApiContentElement +import dev.dres.api.rest.types.task.ApiContentType +import dev.dres.data.model.Config +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaType +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.requireIf +import java.io.FileNotFoundException +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardOpenOption +import java.util.* + +/** + * Represents the target of a [TaskTemplate], i.e., the [MediaItem] or part thereof that is considered correct. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { + companion object: XdNaturalEntityType() + + /** The [TargetType] of this [TaskTemplateTarget]. */ + var type by xdLink1(TargetType) + + /** The parent [TaskTemplate] this [TaskTemplateTarget] belongs to. */ + var task by xdParent(TaskTemplate::targets) + + /** The targeted [MediaItem]. Can be null. */ + var item by xdLink0_1(MediaItem) + + /** The target text. Can be null. */ + var text by xdStringProp() { requireIf { item == null }} + + /** The start of a (potential) range. */ + var start by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } + + /** The start of a (potential) range. */ + var end by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } + + /** Returns the [TemporalRange] of this [TaskTemplateTarget]. */ + val range: TemporalRange? + get() = if (this.start != null && this.end != null) { + TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) + } else { + null + } + + + /** + * Generates and returns a textual description of this [TaskTemplateTarget]. + * + * @return Text + */ + fun textDescription(): String = when (this.type) { + TargetType.JUDGEMENT -> "Judgement" + TargetType.JUDGEMENT_WITH_VOTE -> "Judgement with vote" + TargetType.MEDIA_ITEM -> "Media item ${this.item?.name}" + TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.start} - ${this.end}" + TargetType.TEXT -> "Text: ${this.text}" + else -> throw IllegalStateException("The task description type ${this.type.description} is currently not supported.") + } + + /** + * + */ + fun toApi(): ApiTarget = when(this.type) { + TargetType.JUDGEMENT, + TargetType.JUDGEMENT_WITH_VOTE -> ApiTarget(this.type.toApi(), null) + TargetType.MEDIA_ITEM, + TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget(this.type.toApi(), this.item?.id, this.range?.let { ApiTemporalRange(it) }) + TargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) + else -> throw IllegalStateException("Task description of type ${this.type.description} is not supported.") + } + + /** + * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. + * + * @param config The [Config] used of path resolution. + * @return [ApiContentElement] + * + * @throws FileNotFoundException + * @throws IOException + */ + fun toQueryContentElement(config: Config): ApiContentElement { + val (content, type) = when (this.type) { + TargetType.JUDGEMENT, + TargetType.JUDGEMENT_WITH_VOTE -> null to ApiContentType.EMPTY + TargetType.MEDIA_ITEM -> { + val type = when (this.item?.type) { + MediaType.VIDEO -> ApiContentType.VIDEO + MediaType.IMAGE -> ApiContentType.IMAGE + else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") + } + val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.start, this.end)) + ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") + val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> + stream.readAllBytes() + } + Base64.getEncoder().encodeToString(data) to type + } + TargetType.TEXT -> this.text to ApiContentType.TEXT + else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") + } + return ApiContentElement(contentType = type, content = content, offset = 0L) + } +} \ No newline at end of file From de56eab11312e3ae61810d9a410a3eb69a533c7c Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 13 Dec 2022 09:02:17 +0100 Subject: [PATCH 027/498] Delete Playground.kt --- .../src/main/kotlin/dev/dres/Playground.kt | 163 ------------------ 1 file changed, 163 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/Playground.kt diff --git a/backend/src/main/kotlin/dev/dres/Playground.kt b/backend/src/main/kotlin/dev/dres/Playground.kt deleted file mode 100644 index 412a8a03b..000000000 --- a/backend/src/main/kotlin/dev/dres/Playground.kt +++ /dev/null @@ -1,163 +0,0 @@ -package dev.dres - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.data.dbo.DAO -import dev.dres.data.model.UID -import dev.dres.data.model.run.interfaces.Competition -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.StatusAspect -import dev.dres.data.serializers.CompetitionRunSerializer -import dev.dres.data.serializers.CompetitionSerializer -import dev.dres.data.serializers.MediaItemSerializer -import java.io.File -import java.nio.file.Paths - - -object Playground { - - data class StatusConrainer(override var status: SubmissionStatus) : StatusAspect - - @JvmStatic - fun main(args: Array) { - val competitionId = UID("8c57b76e-416f-44ea-a783-7d0e8becf7a3") - - val mapper = jacksonObjectMapper() - - val mediaItems = DAO(Paths.get("C:/Users/Lucaro/Desktop/vbs21/data/mediaItems.db"), MediaItemSerializer) - val competitionSerializer = CompetitionSerializer(mediaItems) - - val runs: DAO = DAO(Paths.get("C:/Users/Lucaro/Desktop/vbs21/data/runs.db"), CompetitionRunSerializer(competitionSerializer)) - - val writer = File("C:/Users/Lucaro/Desktop/vbs21/mediaItems.csv").printWriter() - - mediaItems.sortedBy { it.name }.forEach { - writer.println("${it.id.string},${it.name}") - } - - writer.flush() - writer.close() - - -// val writer = File("C:\\Users\\Lucaro\\Desktop\\vbs21\\run.json").printWriter() -// -// writer.println(mapper.writeValueAsString(runs.filter { it.id == competitionId }.first())) -// -// writer.flush() -// writer.close() - - -// val audit = DAO(Paths.get("C:\\Users\\Lucaro\\Desktop\\vbs21\\data\\auditLog.db"), AuditLogEntrySerializer) -// -// -// -// var flag = false -// -// val logEntries = audit.sortedBy { it.timestamp }.filter { -// flag || if (it is CompetitionStartAuditLogEntry && it.competition == competitionId) { -// flag = true -// true -// } else false -// } -// -// val writer = File("C:\\Users\\Lucaro\\Desktop\\vbs21\\audits.json").printWriter() -// -// logEntries.forEach { -// writer.println(mapper.writeValueAsString(it)) -// } -// -// writer.flush() -// writer.close() - -// val elements = listOf( -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT), -// StatusConrainer(SubmissionStatus.CORRECT) -// -// ) -// -// println(InferredAveragePrecisionScorer.infAP(elements)) - - -// -// -// val audit = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/auditLog.db"), AuditLogEntrySerializer) -// -// val mediaItems = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/mediaItems.db"), MediaItemSerializer) -// val competitionSerializer = CompetitionSerializer(mediaItems) -// -// val competitions = DAO(Paths.get("C:\\Users\\Lucaro\\Downloads\\LSC2020\\data_branch/competitions.db"), competitionSerializer) -// -// -// val competition = competitions[UID("db2ef71a-df42-4fbd-a2ea-2e1c8b393edd")]!! -// -// val targets = competition.tasks.map { -// it.name to (it.target as TaskDescriptionTarget.MultipleMediaItemTarget).items.map { it.name } -// }.toMap() -// -// val teams = competition.teams.map { it.name } -// -//// println(audit.find { it.id.string == "b54fc9dd-5a86-4c92-be7b-7642b35c887b" }!!.timestamp) -//// -//// return -// -// val events = audit.filter { it.timestamp > 1604053744241 }.sortedBy { it.timestamp } -// -// val taskStartTimes = mutableMapOf() -// -// val submissionWriter = File("submissions.csv").printWriter() -// val taskStartWriter = File("taskStart.csv").printWriter() -// -// submissionWriter.println("timestamp,task,time,team,member,session,item,correct") -// taskStartWriter.println("timestamp,task") -// -// for (event in events) { -// -// when(event) { -// -// is TaskStartAuditLogEntry -> { -// -// taskStartTimes[event.taskName] = event.timestamp -// taskStartWriter.println("${event.timestamp},${event.taskName}") -// -// } -// -// -// is SubmissionAuditLogEntry -> { -// -// val taskName = event.taskName -// -// if (!targets.containsKey(taskName)){ -// println("ignoring $taskName") -// continue -// } -// -// val time = event.submission.timestamp - taskStartTimes[taskName]!! -// val item = event.submission.item.name -// val correct = (targets[taskName] ?: -// error("$taskName not found")).contains(item) -// -// -// -// //submissionWriter.println("${event.timestamp},$taskName,$time,${teams[event.submission.teamId]},${event.submission.member.string},${event.user},$item,$correct") -// -// } -// -// else -> { -// println("ignoring $event") -// } -// -// } -// -// } -// -// submissionWriter.flush() -// submissionWriter.close() -// taskStartWriter.flush() -// taskStartWriter.close() -// - - } - -} \ No newline at end of file From 234ebbef2c9be97577c5544a2c72ad78979e6e9f Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 22 Dec 2022 15:11:31 +0100 Subject: [PATCH 028/498] Replaced Jetty session handler with more lightweight mechanism --- .../kotlin/dev/dres/api/rest/AccessManager.kt | 19 +++++++--- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 36 ++++++++++--------- .../rest/handler/BatchSubmissionHandler.kt | 4 +-- .../handler/CompetitionRunAdminHandler.kt | 18 +++++----- .../CompetitionRunClientInfoHandler.kt | 6 ++-- .../api/rest/handler/CompetitionRunHandler.kt | 9 +++-- .../handler/CompetitionRunScoreHandler.kt | 6 ++-- .../dres/api/rest/handler/JudgementHandler.kt | 10 +++--- .../dev/dres/api/rest/handler/LogHandler.kt | 14 ++++---- .../dev/dres/api/rest/handler/LoginHandler.kt | 11 ++++-- .../dres/api/rest/handler/LogoutHandler.kt | 6 ++-- .../api/rest/handler/SubmissionHandler.kt | 6 ++-- .../dev/dres/api/rest/handler/UserHandler.kt | 9 ++--- .../dres/data/model/run/RunActionContext.kt | 6 ++-- .../dev/dres/run/eventstream/StreamEvent.kt | 6 ++-- .../utilities/extensions/ContextExtensions.kt | 19 +++++++++- 16 files changed, 110 insertions(+), 75 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 13f1f4fa6..e632db3ed 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest import dev.dres.data.model.UID import dev.dres.data.model.admin.User import dev.dres.run.RunManager -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.http.Handler @@ -12,11 +12,17 @@ import java.util.concurrent.ConcurrentHashMap object AccessManager { + const val SESSION_COOKIE_NAME = "SESSIONID" + const val SESSION_COOKIE_LIFETIME = 60 * 60 * 24 //a day + + val SESSION_TOKEN_CHAR_POOL : List = ('a'..'z') + ('A'..'Z') + ('0'..'9') + '-' + '_' + const val SESSION_TOKEN_LENGTH = 32 + fun manage(handler: Handler, ctx: Context, permittedRoles: Set) { when { permittedRoles.isEmpty() -> handler.handle(ctx) //fallback in case no roles are set, none are required permittedRoles.contains(RestApiRole.ANYONE) -> handler.handle(ctx) - rolesOfSession(ctx.sessionId()).any { it in permittedRoles } -> handler.handle(ctx) + rolesOfSession(ctx.sessionToken()).any { it in permittedRoles } -> handler.handle(ctx) else -> ctx.status(401) } } @@ -49,14 +55,17 @@ object AccessManager { } - fun clearUserSession(sessionId: String){ + fun clearUserSession(sessionId: String?){ + if (sessionId == null) { + return + } sessionRoleMap.remove(sessionId) sessionUserMap.remove(sessionId) } - fun rolesOfSession(sessionId: String): Set = sessionRoleMap[sessionId] ?: emptySet() + fun rolesOfSession(sessionId: String?): Set = if(sessionId == null) emptySet() else sessionRoleMap[sessionId] ?: emptySet() - fun getUserIdForSession(sessionId: String): UID? = sessionUserMap[sessionId] + fun getUserIdForSession(sessionId: String?): UID? = if (sessionId == null) null else sessionUserMap[sessionId] /** * Registers a [RunManager] for quick lookup of user ID to eligible [RunManager]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 5c365b2e3..dcb403597 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -220,7 +220,6 @@ object RestApi { it.http.defaultContentType = "application/json" it.http.prefer405over404 = true it.jetty.server { setupHttpServer() } - it.jetty.sessionHandler { fileSessionHandler(config) } it.accessManager(AccessManager::manage) it.staticFiles.add("html", Location.CLASSPATH) it.spaRoot.addFile("/vote", "vote/index.html") @@ -238,6 +237,26 @@ object RestApi { it.plugins.register(ssl) } + }.before { ctx -> + + //check for session cookie + val cookieId = ctx.cookie(AccessManager.SESSION_COOKIE_NAME) + + if (cookieId != null) { + //update cookie lifetime + ctx.cookie(AccessManager.SESSION_COOKIE_NAME, cookieId, AccessManager.SESSION_COOKIE_LIFETIME) + //store id in attribute for later use + ctx.attribute("session", cookieId) + } + + //check for query parameter + val paramId = ctx.queryParam("session") + + if (paramId != null) { + //store id in attribute for later use + ctx.attribute("session", paramId) + } + }.routes { path("api") { apiRestHandlers.groupBy { it.apiVersion }.forEach { apiGroup -> @@ -297,21 +316,6 @@ object RestApi { javalin = null } - private fun fileSessionHandler(config: Config) = SessionHandler().apply { - sessionCache = DefaultSessionCache(this).apply { - sessionDataStore = FileSessionDataStore().apply { - val baseDir = File(".") - this.storeDir = File(baseDir, "session-store").apply { mkdir() } - } - } - - if (config.enableSsl) { - sameSite = HttpCookie.SameSite.NONE - sessionCookieConfig.isSecure = true - isSecureRequestOnly = true - } - - } private val pool = QueuedThreadPool( 1000, 8, 60000, -1, null, null, NamedThreadFactory("JavalinPool") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt index 3c61dbe1c..8be46732c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/BatchSubmissionHandler.kt @@ -19,7 +19,7 @@ import dev.dres.run.InteractiveRunManager import dev.dres.run.NonInteractiveRunManager import dev.dres.utilities.TimeUtil import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* @@ -30,7 +30,7 @@ abstract class BatchSubmissionHandler(internal val collections: DAO = setOf(RestApiRole.PARTICIPANT) - internal fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + internal fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) fun runId(ctx: Context) = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(404, "Parameter 'runId' is missing!'", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt index 65e12ba1e..323d9315c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunAdminHandler.kt @@ -28,7 +28,7 @@ import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -80,7 +80,7 @@ abstract class AbstractCompetitionRunAdminRestHandler( return } - if (!AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN)) { + if (!AccessManager.rolesOfSession(ctx.sessionToken()).contains(RestApiRole.ADMIN)) { throw ErrorStatusException(403, "Access Denied.", ctx); } @@ -224,7 +224,7 @@ class StartCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandler(s try { run.start(rac) - AuditLogger.competitionStart(run.id, run.description, LogEventSource.REST, ctx.sessionId()) + AuditLogger.competitionStart(run.id, run.description, LogEventSource.REST, ctx.sessionToken()) return SuccessStatus("Run $runId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -266,7 +266,7 @@ class NextTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandle val rac = runActionContext(ctx, run) if (run is InteractiveAsynchronousRunManager - && !AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN) + && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(RestApiRole.ADMIN) && run.currentTask(rac)?.status != TaskRunStatus.ENDED) { throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) } @@ -442,7 +442,7 @@ class StartTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl run.currentTask(rac)!!.uid, run.currentTaskDescription(rac), LogEventSource.REST, - ctx.sessionId() + ctx.sessionToken() ) return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully started.") } catch (e: IllegalStateException) { @@ -483,7 +483,7 @@ class AbortTaskCompetitionRunAdminHandler : AbstractCompetitionRunAdminRestHandl try { val task = run.currentTaskDescription(rac) run.abortTask(rac) - AuditLogger.taskEnd(run.id, task.id, task, LogEventSource.REST, ctx.sessionId()) + AuditLogger.taskEnd(run.id, task.id, task, LogEventSource.REST, ctx.sessionToken()) return SuccessStatus("Task '${run.currentTaskDescription(rac).name}' for run $runId was successfully aborted.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -523,7 +523,7 @@ class TerminateCompetitionRunAdminHandler : val rac = runActionContext(ctx, run) try { run.end(rac) - AuditLogger.competitionEnd(run.id, LogEventSource.REST, ctx.sessionId()) + AuditLogger.competitionEnd(run.id, LogEventSource.REST, ctx.sessionToken()) return SuccessStatus("Run $runId was successfully terminated.") } catch (e: IllegalStateException) { throw ErrorStatusException( @@ -575,7 +575,7 @@ class AdjustDurationRunAdminHandler : run.currentTaskDescription(rac).name, "Task duration adjusted by ${duration}s.", LogEventSource.REST, - ctx.sessionId() + ctx.sessionToken() ) return SuccessStatus("Duration for run $runId was successfully adjusted.") } catch (e: IllegalStateException) { @@ -758,7 +758,7 @@ class OverwriteSubmissionStatusRunAdminHandler : submissionId, submission.status, LogEventSource.REST, - ctx.sessionId() + ctx.sessionToken() ) return SubmissionInfo(submission) } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt index 2a912a3c3..528cb8569 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunClientInfoHandler.kt @@ -8,7 +8,7 @@ import dev.dres.data.model.UID import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.* import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.openapi.* @@ -18,9 +18,9 @@ abstract class AbstractCompetitionRunClientInfoHandler : RestHandler, AccessMana override val permittedRoles: Set = setOf(RestApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionToken())!! - fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains( + fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionToken()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionToken()).contains( RestApiRole.ADMIN) fun getRelevantManagers(ctx: Context): List { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt index 25330be60..ade1f4011 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunHandler.kt @@ -16,12 +16,11 @@ import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaCollection import dev.dres.data.model.competition.options.SimpleOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.data.model.submissions.Submission import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.TaskRunStatus import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.openapi.* @@ -34,15 +33,15 @@ abstract class AbstractCompetitionRunRestHandler : RestHandler, AccessManagedRes override val permittedRoles: Set = setOf(RestApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionToken())!! private fun isJudge(ctx: Context): Boolean { - val roles = AccessManager.rolesOfSession(ctx.sessionId()) + val roles = AccessManager.rolesOfSession(ctx.sessionToken()) return roles.contains(RestApiRole.JUDGE) && !roles.contains(RestApiRole.ADMIN) } fun isParticipant(ctx: Context): Boolean { - val roles = AccessManager.rolesOfSession(ctx.sessionId()) + val roles = AccessManager.rolesOfSession(ctx.sessionToken()) return roles.contains(RestApiRole.PARTICIPANT) && !roles.contains(RestApiRole.ADMIN) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt index 2a77a0c4f..be3f81216 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/CompetitionRunScoreHandler.kt @@ -12,7 +12,7 @@ import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.score.scoreboard.Score import dev.dres.run.score.scoreboard.ScoreOverview import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.security.RouteRole import io.javalin.http.Context import io.javalin.openapi.* @@ -29,14 +29,14 @@ abstract class AbstractScoreRestHandler : RestHandler, AccessManagedRestHandler override val permittedRoles: Set = setOf(RestApiRole.VIEWER) override val apiVersion = "v1" - private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionId())!! + private fun userId(ctx: Context): UID = AccessManager.getUserIdForSession(ctx.sessionToken())!! /** * Checks if the current session has the [RestApiRole.PARTICIPANT]. * * @param ctx The [Context] to check. */ - fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN) + fun isParticipant(ctx: Context): Boolean = AccessManager.rolesOfSession(ctx.sessionToken()).contains(RestApiRole.PARTICIPANT) && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(RestApiRole.ADMIN) fun getRun(ctx: Context, runId: UID): InteractiveRunManager? { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt index 450f4bc02..7eb52b6b7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/JudgementHandler.kt @@ -20,7 +20,7 @@ import dev.dres.run.audit.LogEventSource import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.VoteValidator import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.security.RouteRole import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -37,12 +37,12 @@ abstract class AbstractJudgementHandler : RestHandler, AccessManagedRestHandler companion object { fun checkRunManagerAccess(ctx: Context, runManager: RunManager) { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException( + val userId = AccessManager.getUserIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( 403, "No valid user.", ctx ) - if (AccessManager.rolesOfSession(ctx.sessionId()).contains(RestApiRole.ADMIN)) { + if (AccessManager.rolesOfSession(ctx.sessionToken()).contains(RestApiRole.ADMIN)) { return //Admins require no further check } if (userId !in runManager.description.judges) { @@ -89,7 +89,7 @@ class NextOpenJudgementHandler(val collections: DAO) : Abstract checkRunManagerAccess(ctx, run) val validator = run.judgementValidators.find { it.hasOpen } ?: throw ErrorStatusException(202, "There is currently no submission awaiting judgement", ctx, true) - val next = validator.next(ctx.sessionId()) ?: throw ErrorStatusException(202, "There is currently no submission awaiting judgement", ctx) + val next = validator.next(ctx.sessionToken() ?: throw ErrorStatusException(403, "No valid session", ctx)) ?: throw ErrorStatusException(202, "There is currently no submission awaiting judgement", ctx) val taskDescription = next.second.task?.description?.textualDescription() ?: next.second.task?.description?.name ?: "no task description available" @@ -157,7 +157,7 @@ class PostJudgementHandler : AbstractJudgementHandler(), PostRestHandler() } catch (e: BadRequestResponse){ - EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), run.id, ctx.body())) + EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionToken(), run.id, ctx.body())) throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) }.copy(serverTimeStamp = System.currentTimeMillis()) - EventStreamProcessor.event(QueryEventLogEvent(ctx.sessionId(), run.id, queryEventLog)) + EventStreamProcessor.event(QueryEventLogEvent(ctx.sessionToken(), run.id, queryEventLog)) return SuccessStatus("Log received") } @@ -98,17 +98,17 @@ class ResultLogHandler : LogHandler() { ) override fun doPost(ctx: Context): SuccessStatus { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.getUserIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val run = getActiveRun(userId, ctx) val queryResultLog = try { ctx.bodyAsClass(QueryResultLog::class.java) } catch (e: BadRequestResponse){ - EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), run.id, ctx.body())) + EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionToken(), run.id, ctx.body())) throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) }.copy(serverTimeStamp = System.currentTimeMillis()) - EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionId(), run.id, queryResultLog)) + EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionToken(), run.id, queryResultLog)) return SuccessStatus("Log received") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt index ded69b604..b701c8820 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LoginHandler.kt @@ -11,7 +11,7 @@ import dev.dres.mgmt.admin.UserManager.getMatchingUser import dev.dres.run.audit.AuditLogEntry import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.getOrCreateSessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -47,8 +47,13 @@ class LoginHandler(private val audit: DAO) : RestHandler, PostRes val user = getMatchingUser(username, password) ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) - AccessManager.setUserForSession(ctx.sessionId(), user) - AuditLogger.login(loginRequest.username, ctx.sessionId(), LogEventSource.REST) + val sessionToken = ctx.getOrCreateSessionToken() + + AccessManager.setUserForSession(sessionToken, user) + AuditLogger.login(loginRequest.username, sessionToken, LogEventSource.REST) + + //explicitly set cookie on login + ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) return UserDetails.create(UserManager.get(username)!!, ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt index a4ae6e3da..c357de4df 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/LogoutHandler.kt @@ -7,7 +7,7 @@ import dev.dres.data.dbo.DAO import dev.dres.run.audit.AuditLogEntry import dev.dres.run.audit.AuditLogger import dev.dres.run.audit.LogEventSource -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* @@ -28,8 +28,8 @@ class LogoutHandler(private val audit: DAO) : RestHandler, GetRes methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessStatus { - AccessManager.clearUserSession(ctx.sessionId()) - AuditLogger.logout(ctx.sessionId(), LogEventSource.REST) + AccessManager.clearUserSession(ctx.sessionToken()) + AuditLogger.logout(ctx.sessionToken() ?: "no session", LogEventSource.REST) return SuccessStatus("Logged out") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt index e09b5266e..56e0a0bab 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/SubmissionHandler.kt @@ -29,7 +29,7 @@ import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.TimeUtil -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import org.slf4j.LoggerFactory @@ -175,7 +175,7 @@ class SubmissionHandler (val collections: DAO, private val item methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.getUserIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val run = getActiveRun(userId, ctx) val time = System.currentTimeMillis() val submission = toSubmission(ctx, userId, run, time) @@ -193,7 +193,7 @@ class SubmissionHandler (val collections: DAO, private val item throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, run.currentTask(rac)?.uid, submission, LogEventSource.REST, ctx.sessionId(), ctx.req().remoteAddr) + AuditLogger.submission(run.id, run.currentTaskDescription(rac).name, run.currentTask(rac)?.uid, submission, LogEventSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { //pre-generate preview generatePreview(submission) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt index 6b436f32a..e9d01f39a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/UserHandler.kt @@ -11,7 +11,7 @@ import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserName import dev.dres.mgmt.admin.UserManager import dev.dres.utilities.extensions.UID -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import dev.dres.utilities.extensions.toSessionId import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -25,7 +25,7 @@ data class UserDetails(val id: String, val username: String, val role: Role, val companion object { fun of(user: User): UserDetails = UserDetails(user.id.string, user.username.name, user.role) - fun create(user: User, ctx: Context): UserDetails = UserDetails(user.id.string, user.username.name, user.role, ctx.sessionId()) + fun create(user: User, ctx: Context): UserDetails = UserDetails(user.id.string, user.username.name, user.role, ctx.sessionToken()) } } @@ -34,7 +34,8 @@ abstract class UserHandler : RestHandler { override val apiVersion = "v1" protected fun getFromSessionOrDie(ctx: Context): User { - return UserManager.get(id = AccessManager.getUserIdForSession(ctx.sessionId())!!) + val sessionToken = ctx.sessionToken() ?: throw ErrorStatusException(404, "No valid session", ctx) + return UserManager.get(id = AccessManager.getUserIdForSession(sessionToken)!!) ?: throw ErrorStatusException(404, "User could not be found!", ctx) } @@ -240,7 +241,7 @@ class CurrentUsersSessionIdHandler : UserHandler(), GetRestHandler, A methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SessionId { - return ctx.sessionId().toSessionId() + return ctx.sessionToken()!!.toSessionId() } override val permittedRoles = setOf(RestApiRole.VIEWER) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 32270d8ef..c009b3c2a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -7,7 +7,7 @@ import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.UserId import dev.dres.data.model.competition.TeamId import dev.dres.run.RunManager -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context /** @@ -34,8 +34,8 @@ data class RunActionContext(val userId: UserId, val teamId: TeamId?, val roles: * @param runManager The [RunManager] to construct the [RunActionContext] for. */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { - val userId = AccessManager.getUserIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionId()).map { Role.fromRestRole(it) }.toSet() + val userId = AccessManager.getUserIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) + val roles = AccessManager.rolesOfSession(ctx.sessionToken()).map { Role.fromRestRole(it) }.toSet() val teamId = runManager.description.teams.find { it.users.contains(userId) }?.uid return RunActionContext(userId, teamId, roles) } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index e6a19d6b3..99c65096b 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -19,6 +19,6 @@ class RunEndEvent(val runId: UID) : StreamEvent() class SubmissionEvent(session: String, val runId: UID, val taskId: UID?, val submission : Submission) : StreamEvent(session = session) -class QueryEventLogEvent(session: String, val runId: UID, val queryEventLog: QueryEventLog) : StreamEvent(session = session) -class QueryResultLogEvent(session: String, val runId: UID, val queryResultLog: QueryResultLog) : StreamEvent(session = session) -class InvalidRequestEvent(session: String, val runId: UID, val requestData: String) : StreamEvent(session = session) \ No newline at end of file +class QueryEventLogEvent(session: String?, val runId: UID, val queryEventLog: QueryEventLog) : StreamEvent(session = session) +class QueryResultLogEvent(session: String?, val runId: UID, val queryResultLog: QueryResultLog) : StreamEvent(session = session) +class InvalidRequestEvent(session: String?, val runId: UID, val requestData: String) : StreamEvent(session = session) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 0caa5f753..37ae891ac 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -1,5 +1,6 @@ package dev.dres.utilities.extensions +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.util.MimeTypeHelper @@ -8,6 +9,7 @@ import java.io.File import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption +import kotlin.random.Random fun Context.errorResponse(status: Int, errorMessage: String) { this.status(status) @@ -49,4 +51,19 @@ fun Context.sendFile(file: File) { this.result(file.inputStream()) } -fun Context.sessionId(): String = this.queryParam("session") ?: this.req().session.id \ No newline at end of file +fun Context.sessionToken(): String? = this.attribute("session") + +fun Context.getOrCreateSessionToken(): String { + val attributeId = this.attribute("session") + if (attributeId != null) { + return attributeId + } + + val random = Random(System.nanoTime()) + val id = List(AccessManager.SESSION_TOKEN_LENGTH) { AccessManager.SESSION_TOKEN_CHAR_POOL.random(random) }.joinToString("") + + this.attribute("session", id) + + return id + +} \ No newline at end of file From 99a9177755195e5f6b5db5f85383168bcf9e4fb8 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 10 Jan 2023 13:28:03 +0100 Subject: [PATCH 029/498] Added a specific unit test for the new (2023) avs scorer --- .../run/score/scorer/NewAvsTaskScorerTest.kt | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt index 426e4d137..d87d18c5a 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt @@ -36,7 +36,7 @@ class NewAvsTaskScorerTest { @Test @DisplayName("Three teams all without a submission. Expected score: 0.0") - fun noSubmissions(){ + fun testNoSubmissions(){ val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) Assertions.assertEquals(0.0, scores[teams[0]]) @@ -46,7 +46,7 @@ class NewAvsTaskScorerTest { @Test @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") - fun onlyTeamOneWithAllEqualsOneCorrect(){ + fun testOnlyTeamOneWithAllEqualsOneCorrect(){ val taskStart = 100_000L val subs = listOf( Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} @@ -59,7 +59,7 @@ class NewAvsTaskScorerTest { @Test @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") - fun allTeamsWithAllEuqalsOneCorrect(){ + fun testAllTeamsWithAllEuqalsOneCorrect(){ val taskStart = 100_000L val subs = listOf( Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, @@ -74,7 +74,7 @@ class NewAvsTaskScorerTest { @Test @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") - fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ + fun testTeamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ val taskStart = 100_000L val subs = listOf( Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, @@ -89,7 +89,7 @@ class NewAvsTaskScorerTest { @Test @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") - fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ + fun testTeamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ val taskStart = 100_000L val subs = listOf( /* Team One: All correct */ @@ -133,4 +133,19 @@ class NewAvsTaskScorerTest { */ Assertions.assertEquals(400.0, scores[teams[2]]!!, 0.001) } + + @Test() + @DisplayName("Only wrong and correct submissions are considered") + fun testOnlyCorrectAndWrongSubmissionsAreConsidered(){ + val taskStart = 100_000L + val subs = listOf( + Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.INDETERMINATE}, + Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.INDETERMINATE} + ) + val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) + Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) + Assertions.assertEquals(0.0, scores[teams[1]]) + Assertions.assertEquals(0.0, scores[teams[2]]) + } } From 4a602f296a15e2932082241361d41f58dca7828d Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 10 Jan 2023 13:46:50 +0100 Subject: [PATCH 030/498] Applied yarn up -- update packages where possible --- frontend/yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b7dafdd0d..ea894aff1 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -73,7 +73,7 @@ __metadata: less: 4.1.2 less-loader: 10.2.0 license-webpack-plugin: 4.0.1 - loader-utils: 3.2.0 + loader-utils: 3.2.1 mini-css-extract-plugin: 2.5.3 minimatch: 3.0.4 open: 8.4.0 @@ -7588,10 +7588,10 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:3.2.0": - version: 3.2.0 - resolution: "loader-utils@npm:3.2.0" - checksum: c7b9a8dc4b3bc19e9ef563c48e3a18ea9f8bb2da1ad38a12e4b88358cfba5f148a7baf12d78fe78ffcb718ce1e062ab31fcf5c148459f1247a672a4213471e80 +"loader-utils@npm:3.2.1": + version: 3.2.1 + resolution: "loader-utils@npm:3.2.1" + checksum: 4e3ea054cdc8be1ab1f1238f49f42fdf0483039eff920fb1d442039f3f0ad4ebd11fb8e584ccdf2cb7e3c56b3d40c1832416e6408a55651b843da288960cc792 languageName: node linkType: hard From ba999eec73d0f827fea205fb9e150aeca994afb1 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 25 Jan 2023 17:08:44 +0100 Subject: [PATCH 031/498] Resumed cleanup after months of inactivity. --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 24 +-- .../evaluation/admin/ListPastTaskHandler.kt | 11 +- .../client/ClientTaskInfoHandler.kt | 25 +-- .../viewer/CompetitionRunHandler.kt | 179 ------------------ ...nState.kt => GetEvaluationStateHandler.kt} | 2 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 54 ++++++ .../viewer/GetSubmissionHistoryInfoHandler.kt | 58 ++++++ .../viewer/GetSubmissionInfoHandler.kt | 78 ++++++++ .../evaluation/viewer/GetTaskTargetHandler.kt | 4 +- .../handler/template/GetTeamLogoHandler.kt | 3 +- .../template/UpdateCompetitionHandler.kt | 8 +- .../rest/types/evaluation/ApiSubmission.kt | 5 +- .../kotlin/dev/dres/data/model/admin/Role.kt | 2 - .../dev/dres/data/model/template/team/Team.kt | 2 +- .../data/model/template/team/TeamGroup.kt | 1 - 15 files changed, 218 insertions(+), 238 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/{GetEvaluationState.kt => GetEvaluationStateHandler.kt} (93%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 7bbced171..84739a166 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest import GetAuditLogInfoHandler +import GetTaskHintHandler import dev.dres.api.rest.handler.* import dev.dres.api.rest.handler.audit.ListAuditLogsHandler import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler @@ -11,6 +12,7 @@ import dev.dres.api.rest.handler.evaluation.admin.* import dev.dres.api.rest.handler.evaluation.client.ClientListEvaluationsHandler import dev.dres.api.rest.handler.evaluation.client.ClientTaskInfoHandler import dev.dres.api.rest.handler.evaluation.scores.* +import dev.dres.api.rest.handler.evaluation.viewer.* import dev.dres.api.rest.handler.judgement.* import dev.dres.api.rest.handler.log.QueryLogHandler import dev.dres.api.rest.handler.log.ResultLogHandler @@ -125,17 +127,17 @@ object RestApi { QueryLogHandler(), ResultLogHandler(), - // Competition run - ListCompetitionRunInfosHandler(), - ListCompetitionRunStatesHandler(), - GetCompetitionRunInfoHandler(), - GetCompetitionRunStateHandler(), - CurrentTaskHintHandler(config), - CurrentTaskTargetHandler(config, store), - CurrentTaskInfoHandler(), - SubmissionInfoHandler(), - RecentSubmissionInfoHandler(), - HistorySubmissionInfoHandler(), + // Evaluation + ListEvaluationInfoHandler(store), + ListEvaluationStatesHandler(store), + GetEvaluationInfoHandler(store), + GetEvaluationStateHandler(store), + GetTaskHintHandler(store, config), + GetTaskTargetHandler(store, config), + GetCurrentTaskHandler(store), + GetSubmissionInfoHandler(store), + GetSubmissionAfterInfoHandler(store), + GetSubmissionHistoryInfoHandler(store), // Competition run scores ListCompetitionScoreHandler(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index 7ccfead0e..b55b87f49 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -40,16 +40,7 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH return this.store.transactional (true) { val rac = RunActionContext.runActionContext(ctx, runManager) runManager.tasks(rac).filter { it.hasEnded }.map { - ApiTaskTemplateInfo( - taskId = it.id, - templateId = it.template.id, - name = it.template.name, - taskGroup = it.template.taskGroup.name, - taskType = it.template.taskGroup.type.name, - numberOfSubmissions = it.submissions.size, - remainingTime = 0L, - running = false - ) + ApiTaskTemplateInfo(it.template) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index d4147dc61..bfad34e99 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -37,33 +37,10 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional(true) { tx -> - val run = ctx.eligibleManagerForId() val rac = RunActionContext.runActionContext(ctx, run) - if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) - - ApiTaskTemplateInfo( - taskId = task.id, - templateId = task.template.id, - name = task.template.name, - taskGroup = task.template.taskGroup.name, - taskType = task.template.taskGroup.type.name, - running = task.isRunning, - remainingTime = when(run.status){ - RunManagerStatus.CREATED -> 0 - RunManagerStatus.ACTIVE -> { - when(task.status) { - TaskStatus.CREATED, - TaskStatus.PREPARING -> task.duration - TaskStatus.RUNNING ->run.timeLeft(rac) / 1000 - TaskStatus.ENDED -> 0 - } - } - RunManagerStatus.TERMINATED -> 0 - }, - numberOfSubmissions = -1 - ) + ApiTaskTemplateInfo(task.template) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt deleted file mode 100644 index 6f78297dd..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/CompetitionRunHandler.kt +++ /dev/null @@ -1,179 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.competition.tasks.ApiHintContent -import dev.dres.api.rest.types.competition.tasks.ApiTargetContent -import dev.dres.data.dbo.DAO -import dev.dres.data.model.Config -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.template.options.SimpleOption -import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.data.model.submissions.Submission -import dev.dres.run.TaskStatus -import dev.dres.utilities.extensions.UID -import io.javalin.http.Context -import io.javalin.openapi.* -import java.io.FileNotFoundException -import java.io.IOException - - - - -class CurrentTaskTargetHandler - - -class SubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { - override val route = "run/{runId}/submission/list" - - @OpenApi( - summary = "Returns the submissions for the current task run, if it is either running or has just ended.", - path = "/api/v1/run/{runId}/submission/list", - tags = ["Competition Run"], - pathParams = [OpenApiParam("runId", String::class, "Competition Run ID")], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - val rac = runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - - fun limitSubmissions(submissions: List, limit: Int, blind: Boolean = false): List = - submissions.groupBy { it.teamId }.values - .map { - it.sortedBy { s -> s.timestamp }.take(limit) - }.flatMap { - it.map { s -> - if (blind) { - ApiSubmission.blind(s) - } else { - ApiSubmission(s) - } - } - } - - - val limit = run.runProperties.limitSubmissionPreviews - val blind = run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS } - - /* Obtain current task run and check status. */ - return if (run.currentTask(rac)?.isRunning == true) { - if (limit > 0) { - limitSubmissions(run.submissions(rac), limit, blind) - } else { - if (blind) { - run.submissions(rac).map { ApiSubmission.blind(it) } - } else { - run.submissions(rac).map { ApiSubmission(it) } - } - } - } else { - if (limit > 0) { - limitSubmissions(run.submissions(rac), limit, blind) - } else { - run.submissions(rac).map { ApiSubmission(it) } - } - } - } -} - -class RecentSubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { - override val route = "run/{runId}/submission/list/after/{timestamp}" - - @OpenApi( - summary = "Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.", - path = "/api/v1/run/{runId}/submission/list/after/{timestamp}", - tags = ["Competition Run"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("timestamp", Long::class, "Minimum Timestamp of returned submissions.") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - val rac = runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied", ctx) - } - - - val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() - return if (run.currentTask(rac)?.isRunning == true) { - if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { - run.submissions(rac).filter { it.timestamp >= timestamp }.map { ApiSubmission.blind(it) } - } else { - run.submissions(rac).filter { it.timestamp >= timestamp }.map { ApiSubmission(it) } - } - } else { - run.submissions(rac).filter { it.timestamp >= timestamp }.map { ApiSubmission.blind(it) } - } - } -} - -class HistorySubmissionInfoHandler : AbstractCompetitionRunRestHandler(), GetRestHandler> { - - override val route = "run/{runId}/task/{taskRunId}/submission/list" - - @OpenApi( - summary = "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - path = "/api/v1/run/{runId}/task/{taskRunId}/submission/list", - tags = ["Competition Run"], - pathParams = [ - OpenApiParam("runId", String::class, "Competition Run ID"), - OpenApiParam("taskRunId", String::class, "Task run ID") - ], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - val rac = runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied", ctx) - } - - - val taskRunId = - ctx.pathParamMap()["taskRunId"]?.UID() ?: throw ErrorStatusException(404, "Missing task id", ctx) - - val task = run.currentTask(rac) - - return if (task?.template?.id == taskRunId && task.isRunning) { - if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { - run.submissions(rac).map { ApiSubmission.blind(it) } - } else { - run.submissions(rac).map { ApiSubmission(it) } - } - } else { - run.taskForId(rac, taskRunId)?.submissions?.map { ApiSubmission(it) } ?: emptyList() - } - } -} - diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt similarity index 93% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index b832d2de7..d86681a28 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore /** * */ -class GetEvaluationState(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler { +class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler { override val route = "evaluation/{evaluationId}/state" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt new file mode 100644 index 000000000..2bd5bd091 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -0,0 +1,54 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.contains + +/** + * + */ +class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { + override val route = "evaluation/{evaluationId}/submission/list/after/{timestamp}" + + @OpenApi( + summary = "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + path = "/api/v1/evaluation/{evaluationId}/submission/list/after/{timestamp}", + tags = ["Evaluation"], + pathParams = [ + OpenApiParam("evaluationId", String::class,"The evaluation ID.", required = true), + OpenApiParam("timestamp", Long::class, "Timestamp that marks the lower bound for returned submissions.", required = false) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) + return this.store.transactional (true) { + val rac = RunActionContext.runActionContext(ctx, manager) + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + + val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() + val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) + val blind = currentTask.template.taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS) && currentTask.isRunning + manager.submissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt new file mode 100644 index 000000000..1339118eb --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -0,0 +1,58 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { + + override val route = "evaluation/{evaluationId}/task/{taskRunId}/submission/list" + + @OpenApi( + summary = "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + path = "/api/v1/evaluation/{evaluationId}/task/{taskRunId}/submission/list", + tags = ["Competition Run"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("taskRunId", String::class, "Task run ID") + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val runId = runId(ctx) + val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) + val rac = RunActionContext.runActionContext(ctx, run) + + if (!run.runProperties.participantCanView && isParticipant(ctx)) { + throw ErrorStatusException(403, "Access denied", ctx) + } + + + val taskRunId = + ctx.pathParamMap()["taskRunId"]?.UID() ?: throw ErrorStatusException(404, "Missing task id", ctx) + + val task = run.currentTask(rac) + + return if (task?.template?.id == taskRunId && task.isRunning) { + if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { + run.submissions(rac).map { ApiSubmission.blind(it) } + } else { + run.submissions(rac).map { ApiSubmission(it) } + } + } else { + run.taskForId(rac, taskRunId)?.submissions?.map { ApiSubmission(it) } ?: emptyList() + } + } +} + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt new file mode 100644 index 000000000..94c643f0a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -0,0 +1,78 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.run.InteractiveRunManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.contains + +/** + * + */ +class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { + override val route = "evaluation/{evaluationId}/submission/list" + + @OpenApi( + summary = "Returns all submissions for the current task run, if one is either running or has just ended.", + path = "/api/v1/evaluation/{evaluationId}/submission/list/{timestamp}", + tags = ["Evaluation"], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) + return this.store.transactional (true) { + val rac = RunActionContext.runActionContext(ctx, manager) + + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + + val limit = manager.runProperties.limitSubmissionPreviews + val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) + val blind = currentTask.template.taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS) && currentTask.isRunning + + /* Obtain current task run and check status. */ + if (limit > 0) { + limitSubmissions(manager.submissions(rac), limit, blind) + } else { + manager.submissions(rac).map { it.toApi(blind) } + } + } + } + + /** + * Implements a manual limit on the provided list of [Submission]s. + * + * TODO: Delegate to database? + * + * @param submissions The [List] of [Submission]s to limit. + * @param limit The number of items to limit to. + * @param blind If [Submission] should be anonymised. + * @return Limited [List] of [Submission] + */ + private fun limitSubmissions(submissions: List, limit: Int, blind: Boolean = false): List + = submissions.groupBy { it.team.id }.values.map { + it.sortedBy { s -> s.timestamp }.take(limit) + }.flatMap { + it.map { s -> s.toApi(blind) } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index a3a24489a..d94849519 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -20,7 +20,7 @@ import java.io.IOException /** * */ -class GetTaskTargetHandler(store: TransientEntityStore, private val config: Config) : AbstractEvaluationViewerHandler(), GetRestHandler { +class GetTaskTargetHandler(store: TransientEntityStore, private val config: Config) : AbstractEvaluationViewerHandler(store), GetRestHandler { override val route = "run/{evaluationId}/target/{taskId}" @@ -61,7 +61,7 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf try { ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes - task.toTaskTarget(config, collections) + task.template.toTaskTarget(config) } catch (e: FileNotFoundException) { throw ErrorStatusException(404, "Query object cache file not found!", ctx) } catch (ioe: IOException) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index b9d8b1ae5..e820bbc4d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -1,6 +1,5 @@ package dev.dres.api.rest.handler.template -import dev.dres.api.rest.handler.AbstractCompetitionRunRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.template.team.Team @@ -15,7 +14,7 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * A [AbstractCompetitionRunRestHandler] that can be used to list all [Team] logos. + * A [AbstractCompetitionDescriptionHandler] that can be used to list all [Team] logos. * * @author Ralph Gasser * @author Luca Rossetto diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt index adb160f7a..971f3a42c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt @@ -146,10 +146,10 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) /* Update team information. */ val teamIds = apiValue.teams.map { it.teamId }.toTypedArray() - existing.teams.removeAll(Team.query(Team::competition eq existing and not(Team::id.containsIn(*teamIds)))) + existing.teams.removeAll(Team.query(Team::template eq existing and not(Team::id.containsIn(*teamIds)))) for (team in apiValue.teams) { val t = Team.findOrNew { - (Team::name eq team.name) and (Team::competition eq existing) + (Team::name eq team.name) and (Team::template eq existing) } t.name = team.name t.color = team.color @@ -162,10 +162,10 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) /* Update teamGroup information */ val teamGroupIds = apiValue.teamGroups.map { it.id }.toTypedArray() - existing.teamsGroups.removeAll(TeamGroup.query(TeamGroup::competition eq existing and not(TeamGroup::id.containsIn(*teamGroupIds)))) + existing.teamsGroups.removeAll(TeamGroup.query(TeamGroup::template eq existing and not(TeamGroup::id.containsIn(*teamGroupIds)))) for (teamGroup in apiValue.teamGroups) { val t = TeamGroup.findOrNew { - (Team::name eq teamGroup.name) and (Team::competition eq existing) + (Team::name eq teamGroup.name) and (Team::template eq existing) } t.name = teamGroup.name t.teams.clear() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index e78794391..a316719f5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -18,4 +18,7 @@ data class ApiSubmission( val memberName: String, val timestamp: Long, val verdicts: List, -) \ No newline at end of file +) { + + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 688bd9dc1..47f3bad96 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -1,8 +1,6 @@ package dev.dres.data.model.admin -import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.users.ApiUser import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt index ce042d93e..8da61270d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt @@ -45,7 +45,7 @@ class Team(entity: Entity) : PersistentEntity(entity) { var template by xdParent(EvaluationTemplate::teams) /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ - var group by xdLink0_1(TeamGroup::teams) + var group by xdLink0_1(TeamGroup::teams) /** The [User]s that belong to this [Team]. */ val users by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt index be81e9f4f..aeb3244a6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt @@ -40,7 +40,6 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { /** The [Team]s that belong to this [TeamGroup]. */ val teams by xdLink0_N(Team::group) - /** * Converts this [TeamGroup] to a RESTful API representation [ApiTeamGroup]. * From 80efb2b04be7f4d46ae312de443c0bb9e958d0be Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 31 Jan 2023 17:45:10 +0100 Subject: [PATCH 032/498] Reconciliated the abstract task run implementations. --- .../viewer/GetCurrentTaskHandler.kt | 2 +- .../viewer/GetEvaluationInfoHandler.kt | 2 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 2 +- .../evaluation/viewer/GetTaskHintHandler.kt | 2 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 2 +- .../handler/template/GetTeamLogoHandler.kt | 2 +- .../dres/data/model/run/AbstractEvaluation.kt | 16 ++ .../data/model/run/AbstractInteractiveTask.kt | 70 +++--- .../model/run/AbstractNonInteractiveTask.kt | 51 ++--- .../dres/data/model/run/AbstractTaskRun.kt | 100 -------- .../run/InteractiveAsynchronousEvaluation.kt | 214 ++++++------------ .../run/InteractiveSynchronousEvaluation.kt | 45 ++-- .../model/run/NonInteractiveEvaluation.kt | 35 +-- .../kotlin/dev/dres/data/model/run/Task.kt | 4 + .../dres/data/model/run/interfaces/TaskRun.kt | 16 +- .../run/InteractiveAsynchronousRunManager.kt | 2 +- 16 files changed, 198 insertions(+), 367 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index b2d45a7df..4ef344c4d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -35,7 +35,7 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView @OpenApi( summary = "Returns the information for the currently active task template (i.e., the one that is currently selected).", path = "/api/v1/evaluation/{evaluationId}/task", - tags = ["Competition Run"], + tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ OpenApiResponse("200", [OpenApiContent(ApiTaskTemplateInfo::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt index 271f50a7c..5b393c98c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -20,7 +20,7 @@ class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluation @OpenApi( summary = "Returns basic information about a specific evaluation.", path = "/api/v1/evaluation/{evaluationId}/info", - tags = ["Competition Run"], + tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ OpenApiResponse("200", [OpenApiContent(ApiEvaluationInfo::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index 1339118eb..ebcef4009 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -16,7 +16,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval @OpenApi( summary = "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", path = "/api/v1/evaluation/{evaluationId}/task/{taskRunId}/submission/list", - tags = ["Competition Run"], + tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), OpenApiParam("taskRunId", String::class, "Task run ID") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index dc20c430f..2f9c97d8f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -37,7 +37,7 @@ class GetTaskHintHandler(store: TransientEntityStore, private val config: Config @OpenApi( summary = "Returns the task hint for the specified task.", path = "/api/v1/run/{evaluationId}/hint/{taskId}", - tags = ["Competition Run"], + tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), OpenApiParam("taskId", String::class, "The task ID.", required = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index d94849519..4cc2c1599 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -27,7 +27,7 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf @OpenApi( summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", path = "/api/v1/evaluation/{evaluationId}/target/{taskId}", - tags = ["Competition Run"], + tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), OpenApiParam("taskId", String::class, "The task ID.", required = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index e820bbc4d..80d6e6536 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -32,7 +32,7 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractCompetitionDescr @OpenApi( summary = "Returns the logo for the given team ID.", path = "/api/v1/template/logo/{teamId}", - tags = ["Competition Run", "Media"], + tags = ["Evaluation", "Media"], pathParams = [OpenApiParam("teamId", String::class, "The ID of the team to list load the logo for.")], responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], ignore = true, diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 6beee06e7..1a6dfeed3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -3,7 +3,21 @@ package dev.dres.data.model.run import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.options.TargetOption +import dev.dres.run.validation.MediaItemsSubmissionValidator +import dev.dres.run.validation.TemporalOverlapSubmissionValidator +import dev.dres.run.validation.TextValidator +import dev.dres.run.validation.interfaces.SubmissionValidator +import dev.dres.run.validation.judged.BasicJudgementValidator +import dev.dres.run.validation.judged.BasicVoteValidator +import dev.dres.run.validation.judged.ItemRange +import kotlinx.dnq.query.* +import kotlinx.dnq.query.FilteringContext.eq +import kotlinx.dnq.query.FilteringContext.ne import kotlinx.dnq.util.findById +import java.util.concurrent.ConcurrentLinkedQueue /** * An abstract [Run] implementation that can be used by different subtypes. @@ -13,6 +27,8 @@ import kotlinx.dnq.util.findById */ abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { + + /** The internal [xdId] of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index 824674d4a..bc01b6577 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -6,7 +6,6 @@ import dev.dres.data.model.template.team.TeamAggregatorImpl import dev.dres.data.model.template.team.TeamGroupId import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.Submission -import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.MediaItemsSubmissionValidator import dev.dres.run.validation.TemporalOverlapSubmissionValidator import dev.dres.run.validation.TextValidator @@ -16,7 +15,6 @@ import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.BasicVoteValidator import dev.dres.run.validation.judged.ItemRange import kotlinx.dnq.query.* -import java.util.concurrent.ConcurrentLinkedQueue /** * An abstract [Task] implementation for interactive [Task], i.e. [Task]s that rely on human interaction, such as [Submission]s @@ -24,59 +22,43 @@ import java.util.concurrent.ConcurrentLinkedQueue * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -abstract class AbstractInteractiveTask(task: Task): AbstractTaskRun(task) { - /** List of [Submission]s* registered for this [Task]. */ - val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() +abstract class AbstractInteractiveTask(task: Task): AbstractTask(task) { + /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ abstract var duration: Long - /** The [SubmissionFilter] used to filter [Submission]s. */ - abstract val filter: SubmissionFilter - - /** The [SubmissionValidator] used to validate [Submission]s. */ - abstract val validator: SubmissionValidator - /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ val teamGroupAggregators: Map by lazy { - // TODO: Check if transaction context is available when this is called. this.competition.description.teamsGroups.asSequence().associate { it.id to it.newAggregator() } } - /** - * Adds a new [Submission] to this [AbstractInteractiveTask]. - * - * @param submission The [Submission] to append. - */ - abstract fun postSubmission(submission: Submission) + /** The [SubmissionValidator] used to validate [Submission]s. */ + final override val validator: SubmissionValidator - /** - * Generates and returns a new [SubmissionValidator] for this [TaskTemplate]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * @return [SubmissionValidator]. - */ - internal fun newValidator(): SubmissionValidator = when (val targetOption = this.template.taskGroup.type.target) { - TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) - TargetOption.MEDIA_SEGMENT -> { - val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() - TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) - } - TargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) - TargetOption.JUDGEMENT -> { - val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { - ItemRange(it.item?.name!!, it.start!!, it.end!!) - }.toSet() - BasicJudgementValidator(knownCorrectRanges = knownRanges) - } - TargetOption.VOTE -> { - val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { - ItemRange(it.item?.name!!, it.start!!, it.end!!) - }.toSet() - val parameters = this.template.taskGroup.type.configurations.filter { it.key eq targetOption.description }.asSequence().associate { it.key to it.value } - BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) + init { + this.validator = when (val targetOption = this.template.taskGroup.type.target) { + TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + TargetOption.MEDIA_SEGMENT -> { + val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() + TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) + } + TargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + TargetOption.JUDGEMENT -> { + val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + BasicJudgementValidator(knownCorrectRanges = knownRanges) + } + TargetOption.VOTE -> { + val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + val parameters = this.template.taskGroup.type.configurations.filter { it.key eq targetOption.description }.asSequence().associate { it.key to it.value } + BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) + } + else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } - else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index 21bf45e23..ebf26df44 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,10 +1,13 @@ package dev.dres.data.model.run -import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.task.options.TargetOption -import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.submissions.batch.ResultBatch +import dev.dres.run.validation.MediaItemsSubmissionValidator +import dev.dres.run.validation.TemporalOverlapSubmissionValidator +import dev.dres.run.validation.TextValidator import dev.dres.run.validation.TransientMediaSegment +import dev.dres.run.validation.interfaces.SubmissionValidator +import dev.dres.run.validation.judged.BasicJudgementValidator +import dev.dres.run.validation.judged.ItemRange import kotlinx.dnq.query.* /** @@ -14,31 +17,25 @@ import kotlinx.dnq.query.* * @author Ralph Gasser * @version 2.0.0 */ -abstract class AbstractNonInteractiveTask(task: Task): AbstractTaskRun(task) { +abstract class AbstractNonInteractiveTask(task: Task): AbstractTask(task) { + /** The [SubmissionValidator] used by this [AbstractNonInteractiveTask]. */ + final override val validator: SubmissionValidator - /** The [SubmissionBatchValidator] used by this [AbstractNonInteractiveTask]. */ - @Transient - val validator: SubmissionBatchValidator = newValidator() - - /** - * Generates a new [SubmissionBatchValidator]. - * - * @return [SubmissionBatchValidator] - */ - fun newValidator(): SubmissionBatchValidator = when(this.template.taskGroup.type.target){ - TargetOption.MEDIA_ITEM -> MediaItemsSubmissionBatchValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) - TargetOption.MEDIA_SEGMENT -> { - val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() - TemporalOverlapSubmissionBatchValidator(TransientMediaSegment(target.item!!, target.range!!)) + init { + this.validator = when (val targetOption = this.template.taskGroup.type.target) { + TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + TargetOption.MEDIA_SEGMENT -> { + val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() + TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) + } + TargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + TargetOption.JUDGEMENT -> { + val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + BasicJudgementValidator(knownCorrectRanges = knownRanges) + } + else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } - TargetOption.JUDGEMENT -> TODO() - TargetOption.VOTE -> TODO() - TargetOption.TEXT -> TODO() - else -> throw IllegalStateException("The provided target option ${this.template.taskGroup.type.target.description} is not supported by non-interactive tasks.") } - - /** - * Submits a batch of [Submissions]. - */ - abstract fun addSubmissionBatch(teamId: TeamId, memberId: UserId, batches: List>) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt deleted file mode 100644 index e82fc2731..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTaskRun.kt +++ /dev/null @@ -1,100 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.run.TaskStatus -import kotlinx.dnq.util.findById - -/** - * An abstract [Run] implementation that can be used by different subtypes. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -abstract class AbstractTaskRun(task: Task): TaskRun { - /** The internal [xdId] of this [AbstractEvaluation]. - * - * Since this cannot change during the lifetime of an evaluation, it is kept in memory. - */ - private val xdId = task.xdId - - /** - * Accessor for the [Task] underpinning this [AbstractTaskRun] - */ - protected val task: Task - get() = Task.findById(this.xdId) - - /** - * The [TaskId] of this [AbstractTaskRun]. - * - * Since this cannot change during the lifetime of an evaluation, it is kept in memory. - */ - override val id: TaskId = this.task.id - - /** Timestamp of when this [AbstractTaskRun] was started. */ - override var started: Long - get() = this.task.started - protected set(value) { - this.task.started = value - } - - /** Timestamp of when this [AbstractTaskRun] was ended. */ - override var ended: Long? - get() = this.task.ended - protected set(value) { - this.task.ended = value - } - - /** Reference to the [TaskTemplate] describing this [AbstractTaskRun]. */ - override val template: TaskTemplate - get() = this.task.template - - @Volatile - override var status: TaskStatus = TaskStatus.CREATED - protected set - - fun prepare() { - if (this.hasEnded) { - throw IllegalStateException("Run has already ended.") - } - if (this.hasStarted) { - throw IllegalStateException("Run has already been started.") - } - this.status = TaskStatus.PREPARING - } - - /** - * Starts this [AbstractTaskRun]. - */ - override fun start() { - if (this.hasStarted) { - throw IllegalStateException("Run has already been started.") - } - this.started = System.currentTimeMillis() - this.status = TaskStatus.RUNNING - } - - /** - * Ends this [AbstractTaskRun]. - */ - override fun end() { - if (!this.isRunning) { - this.started = System.currentTimeMillis() - } - this.ended = System.currentTimeMillis() - this.status = TaskStatus.ENDED - } - - - /** - * Reactivates this [AbstractTaskRun]. - */ - override fun reactivate() { - if (this.ended == null){ - throw IllegalStateException("Run has not yet ended.") - } - this.ended = null - this.status = TaskStatus.RUNNING - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 54c7a1a09..c874bccd2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -1,39 +1,33 @@ package dev.dres.data.model.run -import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.TaskDescriptionId import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.Submission import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TeamTaskScorer -import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap /** * Represents a concrete, interactive and asynchronous [Run] of a [EvaluationTemplate]. * - * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [Task]s and access the current [Task]. + * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [IATaskRun]s and access the current [IATaskRun]. * */ -class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: Map>) : AbstractEvaluation(evaluation) { +class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val permutation: Map>) : AbstractEvaluation(evaluation) { companion object { - fun generatePermutation( - description: EvaluationTemplate, - shuffle: Boolean - ): Map> = + fun generatePermutation(description: EvaluationTemplate, shuffle: Boolean): Map> = if (shuffle) { - description.teams.associate { it.uid to makeLoop(description.tasks.size) } + description.teams.asSequence().associate { it.id to makeLoop(description.tasks.size()) } } else { - description.teams.associate { it.uid to description.tasks.indices.toList() } + description.teams.asSequence().associate { it.id to description.tasks.asSequence().toList().indices.toList() } } /** @@ -86,112 +80,73 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: } } - constructor( - id: EvaluationId, - name: String, - evaluationTemplate: EvaluationTemplate, - properties: RunProperties - ) : this( - id, - name, - evaluationTemplate, - properties, - generatePermutation(evaluationTemplate, properties.shuffleTasks) - ) - - internal constructor( - id: EvaluationId, - name: String, - evaluationTemplate: EvaluationTemplate, - runProperties: RunProperties, - started: Long, - ended: Long, - permutation: Map> - ) : this(id, name, evaluationTemplate, runProperties, permutation) { - this.started = if (started == -1L) { - null - } else { - started - } - this.ended = if (ended == -1L) { - null - } else { - ended - } - } - - /** A [ConcurrentHashMap] that maps a list of [Task]s to the [TeamId]s they belong to.*/ - private val tasksMap = ConcurrentHashMap>() - - /** A [List] of all active [Task]s.*/ - override val tasks: List + /** + * Internal constructor to create an [InteractiveAsynchronousEvaluation] from an [EvaluationTemplate]. + * Requires a transaction context! + * + * @param name The name of the new [InteractiveSynchronousEvaluation] + * @param shuffle Flag indicating if [IATaskRun]s should be shuffled. + * @param template The [EvaluationTemplate] + */ + constructor(name: String, shuffle: Boolean, template: EvaluationTemplate) : this(Evaluation.new { + this.id = UUID.randomUUID().toString() + this.type = RunType.INTERACTIVE_ASYNCHRONOUS + this.name = name + this.template = template + this.shuffleTasks = shuffle + this.started = System.currentTimeMillis() + }, generatePermutation(template, shuffle)) + + /** A [List] of all active [IATaskRun]s.*/ + override val tasks: List get() = this.tasksMap.values.flatten() + /** A [ConcurrentHashMap] that maps a list of [IATaskRun]s to the [TeamId]s they belong to.*/ + private val tasksMap = ConcurrentHashMap>() + /** Tracks the current [TaskTemplate] per [TeamId]. */ private val navigationMap: MutableMap = HashMap() + init { + /* TODO: Reconstruct TaskRuns from stored data. */ + } + fun goTo(teamId: TeamId, index: Int) { - navigationMap[teamId] = this.description.tasks[ - permutation[teamId]!![index] - ] + this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).single() } fun currentTaskDescription(teamId: TeamId): TaskTemplate = navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) init { - require(description.tasks.size > 0) { "Cannot create a run from a competition that doesn't have any tasks. " } - this.description.teams.forEach { - this.tasksMap[it.uid] = ArrayList(this.description.tasks.size) - goTo(it.uid, 0) - } - } - - /** - * When a run is deserialized, the pointers for individual teams need to be recalculated in order to be able to resume where they left of - */ - fun reconstructNavigationMap() { - this.description.teams.forEach { - val tasks = this.tasksMap[it.uid] - if (tasks != null && tasks.isNotEmpty()) { - val lastTask = tasks.last() - val taskIndex = this.description.tasks.indexOf(lastTask.template) - - if (lastTask.ended != null) { - this.navigationMap[it.uid] = - this.description.tasks[if (lastTask.descriptionId == description.tasks.last().id) description.tasks.size - 1 else taskIndex + 1] - } else { - this.navigationMap[it.uid] = this.description.tasks[taskIndex] - } - - } + val numberOfTasks = this.description.tasks.size() + require(numberOfTasks > 0) { "Cannot create a run from a competition that doesn't have any tasks. " } + this.description.teams.asSequence().forEach { + this.tasksMap[it.id] = ArrayList(numberOfTasks) + goTo(it.id, 0) } } /** - * Returns the current [Task] for the given [TeamId]. + * Returns the current [IATaskRun] for the given [TeamId]. * * @param teamId The [TeamId] to lookup. */ - fun currentTaskForTeam(teamId: TeamId): Task? { - - val currentTaskDescriptionId = navigationMap[teamId]!!.id - + fun currentTaskForTeam(teamId: TeamId): IATaskRun? { + val currentTaskTemplateId = this.navigationMap[teamId]!!.id return this.tasksForTeam(teamId).findLast { - it.descriptionId == currentTaskDescriptionId + it.template.id == currentTaskTemplateId } - } /** - * Returns all [Task]s for the given [TeamId]. + * Returns all [IATaskRun]s for the given [TeamId]. * * @param teamId The [TeamId] to lookup. * @return List [] */ - fun tasksForTeam(teamId: TeamId) = - this.tasksMap[teamId] - ?: throw IllegalArgumentException("Given $teamId is unknown to this competition $id.") + fun tasksForTeam(teamId: TeamId) + = this.tasksMap[teamId] ?: throw IllegalArgumentException("Given $teamId is unknown to this competition $id.") /** * Generates and returns a [String] representation for this [InteractiveAsynchronousEvaluation]. @@ -200,73 +155,44 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: /** * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousEvaluation]. - * - * @author Ralph Gasser - * @version 1.0.0 */ - inner class Task internal constructor( - override val uid: TaskId = EvaluationId(), - val teamId: TeamId, - val descriptionId: TaskDescriptionId - ) : AbstractInteractiveTask() { - - internal constructor( - uid: TaskId, - teamId: TeamId, - taskId: TaskDescriptionId, - started: Long, - ended: Long - ) : this( - uid, - teamId, - taskId - ) { - this.started = if (started == -1L) { - null - } else { - started - } - this.ended = if (ended == -1L) { - null - } else { - ended - } - } + inner class IATaskRun internal constructor(task: Task, val teamId: TeamId) : AbstractInteractiveTask(task) { - /** The [InteractiveAsynchronousEvaluation] this [Task] belongs to.*/ + /** + * Constructor used to generate an [IATaskRun] from a [TaskTemplate]. + * + * @param template [TaskTemplate] to generate [IATaskRun] from. + * @param teamId The [TeamId] this [IATaskRun] is created for. + */ + internal constructor(template: TaskTemplate, teamId: TeamId) : this(Task.new { + this.id = UUID.randomUUID().toString() + this.evaluation = this@InteractiveAsynchronousEvaluation.evaluation + this.template = template + this.team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId }.singleOrNull() + ?: throw IllegalArgumentException("Cannot start a new task run for team with ID ${teamId}. Team is not registered for competition.") + }, teamId) + + /** The [InteractiveAsynchronousEvaluation] this [IATaskRun] belongs to.*/ override val competition: InteractiveAsynchronousEvaluation - @JsonIgnore get() = this@InteractiveAsynchronousEvaluation + get() = this@InteractiveAsynchronousEvaluation - /** The position of this [Task] within the [InteractiveAsynchronousEvaluation]. */ + /** The position of this [IATaskRun] within the [InteractiveAsynchronousEvaluation]. */ override val position: Int - get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.teamId]?.indexOf(this) - ?: -1 + get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.teamId]?.indexOf(this) ?: -1 - @Transient - override val template: TaskTemplate = - this@InteractiveAsynchronousEvaluation.description.tasks.find { it.id == this.descriptionId } - ?: throw IllegalArgumentException("Task with taskId ${this.descriptionId} not found.") - - @Transient + /** The [SubmissionFilter] instance used by this [IATaskRun]. */ override val filter: SubmissionFilter = this.template.newFilter() - @Transient + /** The [TeamTaskScorer] instance used by this [InteractiveAsynchronousEvaluation].*/ override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") - @Transient - override val validator: SubmissionValidator = this.newValidator() - /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration - init { - check(this@InteractiveAsynchronousEvaluation.description.teams.any { it.uid == this.teamId }) { - "Cannot start a new task run for team with ID ${this.teamId}. Team is not registered for competition." - } this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.teamId) { _, v -> - val list = v ?: LinkedList() + val list = v ?: LinkedList() check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } list.add(this) list @@ -274,7 +200,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: } /** - * Adds a [Submission] to this [InteractiveAsynchronousEvaluation.Task]. + * Adds a [Submission] to this [InteractiveAsynchronousEvaluation.IATaskRun]. * * @param submission The [Submission] to add. * @throws IllegalArgumentException If [Submission] could not be added for any reason. @@ -282,7 +208,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, val permutation: @Synchronized override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveAsynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } + check(this.teamId == submission.team.id) { "Team ${submission.team.id} is not eligible to submit to this task. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index d46d93c17..5265aa6e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -8,7 +8,6 @@ import dev.dres.data.model.submissions.Submission import dev.dres.run.audit.AuditLogger import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TeamTaskScorer -import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.* import java.util.* @@ -18,7 +17,7 @@ import java.util.* * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. * * @author Ralph Gasser - * @param 1.3.0 + * @param 2.0.0 */ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { @@ -28,6 +27,21 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } + /** + * Internal constructor to create an [InteractiveSynchronousEvaluation] from an [EvaluationTemplate]. + * + * Requires a transaction context! + * + * @param name The name of the new [InteractiveSynchronousEvaluation] + * @param template The [EvaluationTemplate] + */ + constructor(name: String, template: EvaluationTemplate) : this(Evaluation.new { + this.id = UUID.randomUUID().toString() + this.type = RunType.INTERACTIVE_SYNCHRONOUS + this.template = template + this.name = name + }) + /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ override val tasks: List = this.evaluation.tasks.asSequence().map { ISTaskRun(it) @@ -55,23 +69,18 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat /** * Represents a concrete [Run] of a [TaskTemplate]. [Task]s always exist within a [InteractiveSynchronousEvaluation]. * As a [InteractiveSynchronousEvaluation], [Task]s can be started and ended and they can be used to register [Submission]s. - * - * @version 1.2.0 - * @author Ralph Gasser */ inner class ISTaskRun(task: Task): AbstractInteractiveTask(task) { /** * Constructor used to generate an [ISTaskRun] from a [TaskTemplate]. * - * @param description [TaskTemplate] to generate [ISTaskRun] from. + * @param template [TaskTemplate] to generate [ISTaskRun] from. */ - constructor(description: TaskTemplate) : this(Task.new { + constructor(template: TaskTemplate) : this(Task.new { this.id = UUID.randomUUID().toString() - this.type = RunType.INTERACTIVE_SYNCHRONOUS this.evaluation = this@InteractiveSynchronousEvaluation.evaluation - this.started = System.currentTimeMillis() - this.template = description + this.template = template }) /** The [InteractiveSynchronousEvaluation] this [Task] belongs to.*/ @@ -82,10 +91,6 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat override val position: Int get() = this@InteractiveSynchronousEvaluation.tasks.indexOf(this) - /** Reference to the [TaskTemplate] describing this [Task]. */ - override val template: TaskTemplate - get() = this.task.template - /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ override val filter: SubmissionFilter = this.template.newFilter() @@ -93,19 +98,11 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") - /** The [SubmissionValidator] used by this [ISTaskRun]. */ - override val validator: SubmissionValidator = newValidator() - /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration init { - check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { - "Cannot create a new task. Another task is currently running." - } - check(this.task.type == RunType.INTERACTIVE_SYNCHRONOUS) { - "Incompatible competition type ${this.task.type}. This is a programmer's error!" - } + check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { "Cannot create a new task. Another task is currently running." } (this@InteractiveSynchronousEvaluation.tasks as MutableList).add(this) } @@ -118,7 +115,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this@InteractiveSynchronousEvaluation.description.teams.filter { it eq submission.team }.any()) { - "Team ${submission.team.teamId} does not exists for competition run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" + "Team ${submission.team.teamId} does not exists for evaluation run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 4f6e6a0de..0a4d71cf9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -1,14 +1,14 @@ package dev.dres.data.model.run -import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.InteractiveSynchronousEvaluation.ISTaskRun import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.batch.ResultBatch -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.size +import dev.dres.data.model.submissions.Submission +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.score.interfaces.TeamTaskScorer +import kotlinx.dnq.query.* /** @@ -45,18 +45,27 @@ class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(eval override val position: Int get() = this@NonInteractiveEvaluation.tasks.indexOf(this) + /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ + override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer + ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") - internal val submissions: MutableMap, ResultBatch<*>> = mutableMapOf() - - @Transient - override val scorer: ResultBatchTaskScorer = template.newScorer() as? ResultBatchTaskScorer - ?: throw IllegalArgumentException("specified scorer is not of type ResultBatchTaskScorer") + /** */ + override val filter: SubmissionFilter + get() = TODO("Can there be submission filters for non-interactive tasks?") @Synchronized - override fun addSubmissionBatch(teamId: TeamId, memberId: UserId, batches: List>) { - batches.forEach { resultBatch -> - submissions[teamId to resultBatch.name] = resultBatch + override fun postSubmission(submission: Submission) { + check(this@NonInteractiveEvaluation.description.teams.filter { it eq submission.team }.any()) { + "Team ${submission.team.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" } + + /* Execute submission filters. */ + this.filter.acceptOrThrow(submission) + + /* Process Submission. */ + this.submissions.add(submission) + + /* TODO: Validation? */ } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index 7e5141eb5..54505f1bf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.template.team.Team import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -34,6 +35,9 @@ class Task(entity: Entity) : PersistentEntity(entity) { /** The [TaskTemplate] this [Task] is an instance of. */ var template by xdLink1(TaskTemplate) + /** Link to a [Team] this [Task] was created for. Can be NULL!*/ + var team by xdLink0_1(Team) + /** The [Evaluation] this [Task] belongs to. */ var evaluation by xdParent(Evaluation::tasks) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index d24b1a922..8332ac8fc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,33 +1,33 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.Task +import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.run.TaskStatus import dev.dres.run.score.interfaces.TaskScorer typealias TaskId = String /** - * Represents a [Task] solved by a DRES user or client. + * Represents a [IATaskRun] solved by a DRES user or client. * * @author Ralph Gasser * @version 1.0.0 */ interface TaskRun: Run { - /** The unique [TaskId] that identifies this [Task]. Used by the persistence layer. */ + /** The unique [TaskId] that identifies this [IATaskRun]. Used by the persistence layer. */ val id: TaskId - /** Reference to the [EvaluationRun] this [Task] belongs to. */ + /** Reference to the [EvaluationRun] this [IATaskRun] belongs to. */ val competition: EvaluationRun - /** The position of this [Task] within the enclosing [EvaluationRun]. */ + /** The position of this [IATaskRun] within the enclosing [EvaluationRun]. */ val position: Int - /** Reference to the [TaskTemplate] describing this [Task]. */ + /** Reference to the [TaskTemplate] describing this [IATaskRun]. */ val template: TaskTemplate - /** The [TaskScorer] used to update score for this [Task]. */ + /** The [TaskScorer] used to update score for this [IATaskRun]. */ val scorer: TaskScorer - /** The current status of this [Task]. */ + /** The current status of this [IATaskRun]. */ val status: TaskStatus } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index b9e9390b0..d38b18533 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -332,7 +332,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous } val currentTaskRun = - this.run.Task(teamId = context.teamId, descriptionId = currentTaskDescription.id) + this.run.IATaskRun(teamId = context.teamId, descriptionId = currentTaskDescription.id) currentTaskRun.prepare() /* Mark scoreboards and DAO for update. */ From 1b7c1d9f657013c08ec801f625b3b73b86b9c2e8 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 15:08:43 +0100 Subject: [PATCH 033/498] Finalized refactoring to a point that the backend once again compiles. --- backend/src/main/kotlin/dev/dres/DRES.kt | 12 +- .../dev/dres/api/cli/EvaluationRunCommand.kt | 19 +- .../admin/CreateEvaluationHandler.kt | 2 +- .../admin/EvaluationOverviewHandler.kt | 10 +- .../admin/ListSubmissionsHandler.kt | 2 +- .../admin/OverrideSubmissionHandler.kt | 4 +- .../scores/TeamGroupScoreHandler.kt | 8 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 2 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 46 +-- .../viewer/GetSubmissionInfoHandler.kt | 4 +- .../preview/SubmissionPreviewHandler.kt | 44 +-- .../template/UpdateCompetitionHandler.kt | 20 +- .../rest/handler/users/CreateUsersHandler.kt | 2 +- .../rest/types/evaluation/AdminRunOverview.kt | 55 --- .../types/evaluation/ApiEvaluationOverview.kt | 31 ++ .../api/rest/types/evaluation/ApiRunType.kt | 10 +- .../types/evaluation/ApiSubmissionInfo.kt | 3 +- .../dres/api/rest/types/evaluation/ApiTask.kt | 4 +- .../rest/types/evaluation/ApiTaskOverview.kt | 26 ++ .../types/evaluation/ApiTeamTaskOverview.kt | 3 + .../dev/dres/api/rest/types/users/ApiRole.kt | 19 +- .../dev/dres/data/model/PersistentEntity.kt | 3 + .../kotlin/dev/dres/data/model/admin/Role.kt | 3 +- .../dev/dres/data/model/media/MediaItem.kt | 2 +- .../dev/dres/data/model/media/MediaSegment.kt | 2 +- .../dres/data/model/run/AbstractEvaluation.kt | 28 +- .../dev/dres/data/model/run/AbstractTask.kt | 126 +++++++ .../dev/dres/data/model/run/Evaluation.kt | 14 +- .../run/InteractiveAsynchronousEvaluation.kt | 2 +- .../run/InteractiveSynchronousEvaluation.kt | 8 +- .../model/run/NonInteractiveEvaluation.kt | 4 +- .../dres/data/model/run/RunActionContext.kt | 2 +- .../kotlin/dev/dres/data/model/run/RunType.kt | 33 -- .../kotlin/dev/dres/data/model/run/Task.kt | 8 +- .../model/run/interfaces/EvaluationRun.kt | 8 +- .../dres/data/model/run/interfaces/TaskRun.kt | 13 + .../dres/data/model/submissions/Verdict.kt | 4 +- .../data/model/template/EvaluationTemplate.kt | 14 +- .../dev/dres/data/model/template/task/Hint.kt | 6 +- .../dres/data/model/template/task/HintType.kt | 18 +- .../data/model/template/task/TaskGroup.kt | 4 +- .../data/model/template/task/TaskTemplate.kt | 2 +- .../model/template/task/TaskTemplateTarget.kt | 2 +- .../dres/data/model/template/task/TaskType.kt | 4 +- .../template/task/options/ConfiguredOption.kt | 2 +- .../dev/dres/data/model/template/team/Team.kt | 6 +- .../data/model/template/team/TeamGroup.kt | 4 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 338 ++++++++---------- .../dev/dres/run/InteractiveRunManager.kt | 36 +- .../run/InteractiveSynchronousRunManager.kt | 306 +++++++--------- .../dev/dres/run/NonInteractiveRunManager.kt | 71 ++-- .../main/kotlin/dev/dres/run/RunExecutor.kt | 9 +- .../main/kotlin/dev/dres/run/RunManager.kt | 17 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 7 +- .../handlers/SubmissionStatisticsHandler.kt | 36 +- .../handlers/TeamCombinationScoreHandler.kt | 8 +- .../run/filter/MaximumTotalPerTeamFilter.kt | 12 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 15 +- .../dres/run/filter/SubmissionRateFilter.kt | 5 +- .../kotlin/dev/dres/run/score/TaskContext.kt | 4 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 33 +- .../dres/run/updatables/EndTaskUpdatable.kt | 6 +- .../dres/run/updatables/ScoresUpdatable.kt | 12 +- 64 files changed, 811 insertions(+), 754 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamTaskOverview.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 3c84f60a2..b4e4aac25 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -3,6 +3,7 @@ package dev.dres import dev.dres.api.cli.Cli import dev.dres.api.cli.OpenApiCommand import dev.dres.api.rest.RestApi +import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.Config import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User @@ -20,6 +21,7 @@ import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaSegment import dev.dres.data.model.media.MediaType import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.EvaluationType import dev.dres.data.model.run.Task import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.Verdict @@ -110,6 +112,7 @@ object DRES { AuditLogType, ConfiguredOption, Evaluation, + EvaluationType, EvaluationTemplate, Hint, HintOption, @@ -122,12 +125,14 @@ object DRES { ScoreOption, Submission, SubmissionOption, - TargetType, - TargetOption, Task, + TaskGroup, + TaskType, + TaskOption, TaskTemplate, TaskTemplateTarget, - TaskOption, + TargetType, + TargetOption, Team, TeamAggregator, TeamGroup, @@ -138,6 +143,7 @@ object DRES { ) val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") initMetaData(XdModel.hierarchy, store) + return store } } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt index 4ad29a0e2..46e93f1dd 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt @@ -13,7 +13,6 @@ import dev.dres.data.model.run.* import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionId import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.* import dev.dres.utilities.extensions.toDateString @@ -130,7 +129,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt it.id, it.name, it.template.description, - if (it.type == RunType.INTERACTIVE_SYNCHRONOUS) { + if (it.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" @@ -156,7 +155,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt it.id, it.name, it.template.description, - if (it.type == RunType.INTERACTIVE_SYNCHRONOUS) { + if (it.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" @@ -198,7 +197,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt println("Evaluated Tasks:") it.tasks.asSequence().forEach { t -> println(t.template) - if (t.evaluation.type == RunType.INTERACTIVE_SYNCHRONOUS) { + if (t.evaluation.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { println("Submissions") t.submissions.asSequence().forEach { s -> println(s) } } @@ -294,7 +293,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt /* Create run and reactivate. */ val run = evaluation.toRun() run.reactivate() - RunExecutor.schedule(run) + RunExecutor.schedule(run, this@EvaluationRunCommand.store) println("Evaluation ${this.id} was reactivated.") } } @@ -307,11 +306,11 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt /** [EvaluationId] of the [Evaluation] that should be reactivated. .*/ private val id: EvaluationId by option("-i", "--id").required() - /** The [SubmissionId]s to reset. */ - private val submissionIds: kotlin.collections.List by option("-s", "--submissions", help = "IDs of the submissions to reset.").multiple() + /** The [EvaluationId]s to reset. */ + private val submissionIds: kotlin.collections.List by option("-s", "--submissions", help = "IDs of the submissions to reset.").multiple() - /** The [TaskId]s to reset [Submission]s for. */ - private val taskIds: kotlin.collections.List by option("-t", "--tasks", help = "IDs of the tasks to resetsubmissions for.").multiple() + /** The [EvaluationId]s to reset [Submission]s for. */ + private val taskIds: kotlin.collections.List by option("-t", "--tasks", help = "IDs of the tasks to resetsubmissions for.").multiple() /** The names of the task groups to reset [Submission]s for. */ private val taskGroups: kotlin.collections.List by option("-g", "--groups", help = "Names of the task groups to reset submissions for.").multiple() @@ -324,7 +323,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt return@transactional } - if (evaluation.type == RunType.INTERACTIVE_SYNCHRONOUS) { + if (evaluation.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { /* Prepare query. */ var query = if (this.taskIds.isNotEmpty()) { evaluation.tasks.filter { it.id.isIn(this@ResetSubmission.taskIds) }.flatMapDistinct { it.submissions } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index c89dceb33..f1e3b8420 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -116,7 +116,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs ApiRunType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ ApiRunType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) ApiRunType.NON_INTERACTIVE -> TODO() - }) + }, this.store) evaluation } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt index 3e648e778..4e12f7359 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.types.evaluation.AdminRunOverview +import dev.dres.api.rest.types.evaluation.ApiEvaluationOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import io.javalin.http.Context @@ -14,7 +14,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler { +class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler { override val route = "run/admin/{runId}/overview" @OpenApi( summary = "Provides a complete overview of a run.", @@ -25,17 +25,17 @@ class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluation ], tags = ["Competition Run Admin"], responses = [ - OpenApiResponse("200", [OpenApiContent(AdminRunOverview::class)]), + OpenApiResponse("200", [OpenApiContent(ApiEvaluationOverview::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): AdminRunOverview { + override fun doGet(ctx: Context): ApiEvaluationOverview { val evaluationId = ctx.evaluationId() val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional(true) { - AdminRunOverview.of(evaluationManager) + ApiEvaluationOverview.of(evaluationManager) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index fe47e580d..a632b8332 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -40,7 +40,7 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional(true) { evaluationManager.tasks(RunActionContext.runActionContext(ctx, evaluationManager)).filter { it.template.templateId == templateId }.map { - ApiSubmissionInfo(evaluationId, it.id, it.submissions.map { sub -> sub.toApi() }) + ApiSubmissionInfo(evaluationId, it.id, it.getSubmissions().map { sub -> sub.toApi() }) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 87fc1a5bf..9a35051f2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -66,11 +66,11 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation val rac = RunActionContext.runActionContext(ctx, evaluationManager) /* Sanity check to see, whether the submission exists */ - if (evaluationManager.allSubmissions.none { it.id == submissionInfo.id }) { + if (evaluationManager.allSubmissions(rac).none { it.id == submissionInfo.id }) { throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) } if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.status)) { - val submission = evaluationManager.allSubmissions.single { it.id == submissionInfo.id } + val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.id } AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionId()) submission.toApi() } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index 62c8061a5..64d915101 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -43,9 +43,11 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, manager) - val aggregators = manager.currentTask(rac)?.teamGroupAggregators ?: throw ErrorStatusException(404, "No active task in evaluation ${ctx.evaluationId()}.", ctx) - val teamGroups = manager.template.teamsGroups.toList() - teamGroups.map { ApiTeamGroupValue(it.name, aggregators[it.teamGroupId]?.lastValue ?: 0.0) } + /* TODO: Not suite sure where the teamGroupAggregator got lost.*/ + //val aggregators = manager.currentTask(rac)?.teamGroupAggregators ?: throw ErrorStatusException(404, "No active task in evaluation ${ctx.evaluationId()}.", ctx) + //val teamGroups = manager.template.teamsGroups.toList() + //teamGroups.map { ApiTeamGroupValue(it.name, aggregators[it.teamGroupId]?.lastValue ?: 0.0) } + emptyList() } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index 2bd5bd091..d9387150a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -48,7 +48,7 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) val blind = currentTask.template.taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS) && currentTask.isRunning - manager.submissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } + manager.currentSubmissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index ebcef4009..9d2097048 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -1,13 +1,20 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.api.rest.handler.evaluationId +import dev.dres.api.rest.handler.isParticipant import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.any +import kotlinx.dnq.query.filter class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { @@ -19,7 +26,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), - OpenApiParam("taskRunId", String::class, "Task run ID") + OpenApiParam("taskId", String::class, "Task ID", required = true, allowEmptyValue = false) ], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), @@ -30,28 +37,23 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { - val runId = runId(ctx) - val run = getRun(ctx, runId) ?: throw ErrorStatusException(404, "Run $runId not found.", ctx) - val rac = RunActionContext.runActionContext(ctx, run) - - if (!run.runProperties.participantCanView && isParticipant(ctx)) { - throw ErrorStatusException(403, "Access denied", ctx) - } - - - val taskRunId = - ctx.pathParamMap()["taskRunId"]?.UID() ?: throw ErrorStatusException(404, "Missing task id", ctx) - - val task = run.currentTask(rac) - - return if (task?.template?.id == taskRunId && task.isRunning) { - if (run.currentTaskDescription(rac).taskType.options.any { it.option == SimpleOption.HIDDEN_RESULTS }) { - run.submissions(rac).map { ApiSubmission.blind(it) } - } else { - run.submissions(rac).map { ApiSubmission(it) } + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) + return this.store.transactional (true) { + val rac = RunActionContext.runActionContext(ctx, manager) + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(404, "Missing task id", ctx) + val task = manager.currentTask(rac) + if (task?.template?.id == taskId && task.isRunning) { + if (task.isRunning) { + val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq TaskOption.HIDDEN_RESULTS }.any() + manager.currentSubmissions(rac).map { it.toApi(hidden) } + } else { + manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() } ?: emptyList() + } } - } else { - run.taskForId(rac, taskRunId)?.submissions?.map { ApiSubmission(it) } ?: emptyList() + emptyList() } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index 94c643f0a..e7932ed46 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -52,9 +52,9 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV /* Obtain current task run and check status. */ if (limit > 0) { - limitSubmissions(manager.submissions(rac), limit, blind) + limitSubmissions(manager.currentSubmissions(rac), limit, blind) } else { - manager.submissions(rac).map { it.toApi(blind) } + manager.currentSubmissions(rac).map { it.toApi(blind) } } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index 94d1ba8f7..1392c93e5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.handler.preview import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.utilities.extensions.errorResponse @@ -39,31 +40,33 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab override fun get(ctx: Context) { try { val params = ctx.pathParamMap() - val runId = params["runId"] ?: throw ErrorStatusException(404, "Parameter 'runId' is invalid", ctx) + val runId = params["evaluationId"] ?: throw ErrorStatusException(404, "Parameter 'evaluationId' is invalid", ctx) val submissionId = params["submissionId"] ?: throw ErrorStatusException(404, "Parameter 'submissionId' is missing", ctx) - val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Competition Run $runId not found", ctx) + val run = RunExecutor.managerForId(runId) ?: throw ErrorStatusException(404, "Competition run $runId not found", ctx) if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Competition Run $runId is not interactive", ctx) /* TODO: Make this work for batched submissions ? */ + this.store.transactional (true) { + val rac = RunActionContext.runActionContext(ctx, run) + val submission = run.allSubmissions(rac).find { it.id == submissionId } + ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) + val verdict = submission.verdicts.firstOrNull() + ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) - val submission = run.allSubmissions.find { it.id == submissionId } - ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) - val verdict = submission.verdicts.firstOrNull() - ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) - - when { - verdict.item != null -> { - handlePreviewRequest(verdict.item!!, if (verdict.start != null) verdict.start else null, ctx) - } - verdict.text != null -> { - ctx.header("Cache-Control", "max-age=31622400") - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) - } - else -> { - ctx.header("Cache-Control", "max-age=31622400") - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) + when { + verdict.item != null -> { + handlePreviewRequest(verdict.item!!, if (verdict.start != null) verdict.start else null, ctx) + } + verdict.text != null -> { + ctx.header("Cache-Control", "max-age=31622400") + ctx.contentType("image/png") + ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) + } + else -> { + ctx.header("Cache-Control", "max-age=31622400") + ctx.contentType("image/png") + ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) + } } } } catch (e: ErrorStatusException) { @@ -71,7 +74,6 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab } } - override fun doGet(ctx: Context): Any { throw UnsupportedOperationException("SubmissionPreviewHandler::doGet() is not supported and should not be executed!") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt index 971f3a42c..8731181ec 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt @@ -66,10 +66,10 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) /* Update task type information. */ val taskTypes = apiValue.taskTypes.map { it.name }.toTypedArray() - existing.taskTypes.removeAll(TaskType.query(TaskType::competition eq existing and not(TaskType::name.containsIn(*taskTypes)))) + existing.taskTypes.removeAll(TaskType.query(TaskType::evaluation eq existing and not(TaskType::name.containsIn(*taskTypes)))) for (type in apiValue.taskTypes) { val t = TaskType.findOrNew { - (TaskType::name eq type.name) and (TaskType::competition eq existing) + (TaskType::name eq type.name) and (TaskType::evaluation eq existing) } t.name = type.name t.duration = type.duration @@ -92,18 +92,18 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) /* Update task group information. */ val taskGroups = apiValue.taskGroups.map { it.name }.toTypedArray() - existing.taskGroups.removeAll(TaskGroup.query(TaskGroup::competition eq existing and not(TaskGroup::name.containsIn(*taskGroups)))) + existing.taskGroups.removeAll(TaskGroup.query(TaskGroup::evaluation eq existing and not(TaskGroup::name.containsIn(*taskGroups)))) for (group in apiValue.taskGroups) { val g = TaskGroup.findOrNew { - (TaskGroup::name eq type.name) and (TaskGroup::competition eq existing) + (TaskGroup::name eq type.name) and (TaskGroup::evaluation eq existing) } g.name = group.name - g.type = TaskType.query((TaskType::name eq group.name) and (TaskGroup::competition eq existing)).first() + g.type = TaskType.query((TaskType::name eq group.name) and (TaskGroup::evaluation eq existing)).first() } /* Update task information. */ val taskIds = apiValue.tasks.mapNotNull { it.id }.toTypedArray() - existing.tasks.removeAll(TaskTemplate.query(TaskTemplate::competition eq existing and not(TaskTemplate::id.containsIn(*taskIds)))) + existing.tasks.removeAll(TaskTemplate.query(TaskTemplate::evaluation eq existing and not(TaskTemplate::id.containsIn(*taskIds)))) for (task in apiValue.tasks) { val t = if (task.id != null) { existing.tasks.filter { it.id eq task.id }.first() @@ -146,10 +146,10 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) /* Update team information. */ val teamIds = apiValue.teams.map { it.teamId }.toTypedArray() - existing.teams.removeAll(Team.query(Team::template eq existing and not(Team::id.containsIn(*teamIds)))) + existing.teams.removeAll(Team.query(Team::evaluation eq existing and not(Team::id.containsIn(*teamIds)))) for (team in apiValue.teams) { val t = Team.findOrNew { - (Team::name eq team.name) and (Team::template eq existing) + (Team::name eq team.name) and (Team::evaluation eq existing) } t.name = team.name t.color = team.color @@ -162,10 +162,10 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) /* Update teamGroup information */ val teamGroupIds = apiValue.teamGroups.map { it.id }.toTypedArray() - existing.teamsGroups.removeAll(TeamGroup.query(TeamGroup::template eq existing and not(TeamGroup::id.containsIn(*teamGroupIds)))) + existing.teamsGroups.removeAll(TeamGroup.query(TeamGroup::evaluation eq existing and not(TeamGroup::id.containsIn(*teamGroupIds)))) for (teamGroup in apiValue.teamGroups) { val t = TeamGroup.findOrNew { - (Team::name eq teamGroup.name) and (Team::template eq existing) + (Team::name eq teamGroup.name) and (Team::evaluation eq existing) } t.name = teamGroup.name t.teams.clear() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index 2fd273158..eb04d5de0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -51,7 +51,7 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, Acce if (req.role == null) throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) - val success = UserManager.create(req.username, Password.Plain(req.password), req.role.role ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) + val success = UserManager.create(req.username, Password.Plain(req.password), req.role.getRole() ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) if (success) { return UserManager.get(username = req.username)!!.toApi() } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt deleted file mode 100644 index e4cf6caf4..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/AdminRunOverview.kt +++ /dev/null @@ -1,55 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.run.* - -data class AdminRunOverview(val state: RunManagerStatus, val teamOverviews: List) { - - companion object { - fun of(run: InteractiveRunManager): AdminRunOverview { - - val teamOverviews = when (run) { - is InteractiveSynchronousRunManager -> { - val overview = run.run.tasks.map { TaskRunOverview(it) } - run.template.teams.map { - TeamTaskOverview(it.uid.string, overview) - } - } - is InteractiveAsynchronousRunManager -> { - run.run.tasks.groupBy { it.teamId }.map { (teamId, tasks) -> - val overview = tasks.map { TaskRunOverview(it) } - TeamTaskOverview(teamId.string, overview) - } - } - else -> throw IllegalStateException("Unsupported run manager type") //should never happen - } - - return AdminRunOverview(run.status, teamOverviews) - } - } - -} - -data class TaskRunOverview( - val id:String, - val name: String, - val type: String, - val group: String, - val duration: Long, - val taskId: String, - val status: TaskStatus, - val started: Long?, - val ended: Long?) { - constructor(task: TaskRun) : this( - task.template.id.string, - task.template.name, - task.template.taskGroup.name, - task.template.taskType.name, - task.template.duration, - task.uid.string, - task.status, - task.started, - task.ended) -} - -data class TeamTaskOverview(val teamId: String, val tasks: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt new file mode 100644 index 000000000..b7a86d97e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt @@ -0,0 +1,31 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.run.* +import kotlinx.dnq.query.asSequence + +data class ApiEvaluationOverview(val state: RunManagerStatus, val teamOverviews: List) { + companion object { + fun of(manager: InteractiveRunManager): ApiEvaluationOverview { + val teamOverviews = when (manager) { + is InteractiveSynchronousRunManager -> { + val overview = manager.evaluation.tasks.asSequence().map { ApiTaskOverview(it) }.toList() + manager.template.teams.asSequence().map { + ApiTeamTaskOverview(it.teamId, overview) + }.toList() + } + is InteractiveAsynchronousRunManager -> { + manager.evaluation.tasks.groupBy { it.teamId }.map { (teamId, tasks) -> + val overview = tasks.map { ApiTaskOverview(it) } + ApiTeamTaskOverview(teamId, overview) + } + } + else -> throw IllegalStateException("Unsupported run manager type") //should never happen + } + + return ApiEvaluationOverview(manager.status, teamOverviews) + } + } +} + + + diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt index 1f6b06a26..52008a285 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt @@ -1,12 +1,12 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.run.RunType +import dev.dres.data.model.run.EvaluationType /** * */ -enum class ApiRunType(val type: RunType) { - SYNCHRONOUS(RunType.INTERACTIVE_SYNCHRONOUS), - ASYNCHRONOUS(RunType.INTERACTIVE_ASYNCHRONOUS), - NON_INTERACTIVE(RunType.NON_INTERACTIVE) +enum class ApiRunType(val type: EvaluationType) { + SYNCHRONOUS(EvaluationType.INTERACTIVE_SYNCHRONOUS), + ASYNCHRONOUS(EvaluationType.INTERACTIVE_ASYNCHRONOUS), + NON_INTERACTIVE(EvaluationType.NON_INTERACTIVE) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt index ed88206ac..8a617199a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt @@ -1,11 +1,10 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.TemplateId /** - * Encodes [ApiSubmission] data for a specific [EvaluationId] and (optionally) [TaskId]. + * Encodes [ApiSubmission] data for a specific [EvaluationId] and (optionally) [EvaluationId]. * * @author Loris Sauter * @version 1.1.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index 66f2d2cc1..ae7d82202 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.Task -import dev.dres.data.model.run.TaskId +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.TemplateId /** @@ -12,7 +12,7 @@ import dev.dres.data.model.template.TemplateId * @version 2.0.0 */ data class ApiTask( - val taskId: TaskId, + val taskId: EvaluationId, val templateId: TemplateId, val started: Long?, val ended: Long?, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt new file mode 100644 index 000000000..ec292ba8c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt @@ -0,0 +1,26 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.run.TaskStatus + +data class ApiTaskOverview( + val id:String, + val name: String, + val type: String, + val group: String, + val duration: Long, + val taskId: String, + val status: TaskStatus, + val started: Long?, + val ended: Long?) { + constructor(task: TaskRun) : this( + task.template.id, + task.template.name, + task.template.taskGroup.name, + task.template.taskGroup.type.name, + task.template.duration, + task.id, + task.status, + task.started, + task.ended) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamTaskOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamTaskOverview.kt new file mode 100644 index 000000000..755bc4f85 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamTaskOverview.kt @@ -0,0 +1,3 @@ +package dev.dres.api.rest.types.evaluation + +data class ApiTeamTaskOverview(val teamId: String, val tasks: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt index b4258f896..273f90060 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt @@ -9,10 +9,17 @@ import io.javalin.security.RouteRole * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiRole(val role: Role?) : RouteRole { - ANYONE(null), - VIEWER(Role.VIEWER), - PARTICIPANT(Role.PARTICIPANT), - JUDGE(Role.JUDGE), - ADMIN(Role.ADMIN) +enum class ApiRole() : RouteRole { + ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN; + + /** + * + */ + fun getRole(): Role? = when(this) { + ANYONE -> null + VIEWER -> Role.VIEWER + PARTICIPANT -> Role.PARTICIPANT + JUDGE -> Role.JUDGE + ADMIN -> Role.ADMIN + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt index 487a3692a..7eb87f75b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt @@ -1,7 +1,9 @@ package dev.dres.data.model +import dev.dres.data.model.template.task.TaskTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEntity +import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.xdRequiredStringProp /** @@ -11,5 +13,6 @@ import kotlinx.dnq.xdRequiredStringProp * @version 1.0.0 */ abstract class PersistentEntity(entity: Entity): XdEntity(entity) { + companion object: XdNaturalEntityType() var id: String by xdRequiredStringProp(unique = true, trimmed = false) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 47f3bad96..8cd3f4653 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -47,6 +47,5 @@ class Role(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiRole] */ - fun toApi(): ApiRole - = ApiRole.values().find { it.role == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") + fun toApi(): ApiRole = ApiRole.values().find { it.getRole() == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index 9ec2ac9ff..9437e0538 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -40,7 +40,7 @@ class MediaItem(entity: Entity) : PersistentEntity(entity) { var durationMs by xdNullableLongProp() { requireIf { this.type == MediaType.VIDEO } } /** The [MediaCollection] this [MediaItem] belongs to. */ - var collection by xdParent(MediaCollection::items) + var collection: MediaCollection by xdParent(MediaCollection::items) /** List of [MediaSegment] that this [MediaItem] contains. */ val segments by xdChildren0_N(MediaSegment::item) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt index b991e8e47..6764bf19d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt @@ -25,7 +25,7 @@ class MediaSegment(entity: Entity) : PersistentEntity(entity) { var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [MediaType] of this [MediaItem]. */ - var item by xdParent(MediaItem::segments) + var item: MediaItem by xdParent(MediaItem::segments) /** The start frame number of this [MediaSegment]. */ var start by xdRequiredIntProp { min(0L) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 1a6dfeed3..91f6e27f8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -3,21 +3,7 @@ package dev.dres.data.model.run import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.options.TargetOption -import dev.dres.run.validation.MediaItemsSubmissionValidator -import dev.dres.run.validation.TemporalOverlapSubmissionValidator -import dev.dres.run.validation.TextValidator -import dev.dres.run.validation.interfaces.SubmissionValidator -import dev.dres.run.validation.judged.BasicJudgementValidator -import dev.dres.run.validation.judged.BasicVoteValidator -import dev.dres.run.validation.judged.ItemRange -import kotlinx.dnq.query.* -import kotlinx.dnq.query.FilteringContext.eq -import kotlinx.dnq.query.FilteringContext.ne import kotlinx.dnq.util.findById -import java.util.concurrent.ConcurrentLinkedQueue /** * An abstract [Run] implementation that can be used by different subtypes. @@ -27,20 +13,12 @@ import java.util.concurrent.ConcurrentLinkedQueue */ abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { - - /** The internal [xdId] of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ private val xdId = evaluation.xdId - /** - * Accessor for the [Evaluation] underpinning this [AbstractEvaluation] - */ - protected val evaluation: Evaluation - get() = Evaluation.findById(this.xdId) - /** The [EvaluationId] of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. @@ -59,6 +37,12 @@ abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { */ override val description: EvaluationTemplate = evaluation.template + /** + * Accessor for the [Evaluation] underpinning this [AbstractEvaluation] + */ + protected val evaluation: Evaluation + get() = Evaluation.findById(this.xdId) + /** Timestamp of when this [AbstractEvaluation] was started. */ override var started: Long get() = this.evaluation.started diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt new file mode 100644 index 000000000..c7012155e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -0,0 +1,126 @@ +package dev.dres.data.model.run + +import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.submissions.Submission +import dev.dres.run.TaskStatus +import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.util.findById +import java.util.concurrent.ConcurrentLinkedQueue + +/** + * An abstract [Run] implementation that can be used by different subtypes. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +abstract class AbstractTask(task: Task): TaskRun { + + /** The internal [xdId] of this [AbstractEvaluation]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + protected val xdId = task.xdId + + /** + * Accessor for the [Task] underpinning this [AbstractTask] + */ + protected val task: Task + get() = Task.findById(this.xdId) + + /** + * The [EvaluationId] of this [AbstractTask]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + final override val id: EvaluationId = this.task.id + + /** List of [Submission]s* registered for this [AbstractTask]. */ + protected val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() + + /** Timestamp of when this [AbstractTask] was started. */ + final override var started: Long + get() = this.task.started + protected set(value) { + this.task.started = value + } + + /** Timestamp of when this [AbstractTask] was ended. */ + final override var ended: Long? + get() = this.task.ended + protected set(value) { + this.task.ended = value + } + + /** Reference to the [TaskTemplate] describing this [AbstractTask]. */ + final override val template: TaskTemplate + get() = this.task.template + + @Volatile + final override var status: TaskStatus = TaskStatus.CREATED + protected set + + /** The [SubmissionFilter] used to filter [Submission]s. */ + abstract val filter: SubmissionFilter + + /** The [SubmissionValidator] used to validate [Submission]s. */ + abstract val validator: SubmissionValidator + + /** + * Prepares this [TaskRun] for later starting. + */ + override fun prepare() { + if (this.hasEnded) { + throw IllegalStateException("Run has already ended.") + } + if (this.hasStarted) { + throw IllegalStateException("Run has already been started.") + } + this.status = TaskStatus.PREPARING + } + + /** + * Starts this [AbstractTask]. + */ + override fun start() { + if (this.hasStarted) { + throw IllegalStateException("Run has already been started.") + } + this.started = System.currentTimeMillis() + this.status = TaskStatus.RUNNING + } + + /** + * Ends this [AbstractTask]. + */ + override fun end() { + if (!this.isRunning) { + this.started = System.currentTimeMillis() + } + this.ended = System.currentTimeMillis() + this.status = TaskStatus.ENDED + } + + /** + * Reactivates this [AbstractTask]. + */ + override fun reactivate() { + if (this.ended == null){ + throw IllegalStateException("Run has not yet ended.") + } + this.ended = null + this.status = TaskStatus.RUNNING + } + + /** Returns a [List] of all [Submission]s held by this [AbstractTask]. */ + override fun getSubmissions() = this.submissions.toList() + + /** + * Adds a new [Submission] to this [AbstractInteractiveTask]. + * + * @param submission The [Submission] to append. + */ + abstract fun postSubmission(submission: Submission) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt index bbe00e5a5..92b248cdb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt @@ -1,16 +1,14 @@ package dev.dres.data.model.run import dev.dres.api.rest.types.evaluation.ApiEvaluation -import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.data.model.submissions.Submission import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence -typealias EvaluationId = String +typealias TaskId = String /** * Represents a [Evaluation], i.e., a concrete instance of a [EvaluationTemplate], as executed by DRES. @@ -29,8 +27,8 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { /** The name held by this [Evaluation]. Must be unique!*/ var name by xdRequiredStringProp(unique = true, trimmed = true) - /** The [RunType] of this [Evaluation]. */ - var type by xdLink1(RunType) + /** The [EvaluationType] of this [Evaluation]. */ + var type by xdLink1(EvaluationType) /** The [EvaluationTemplate] backing this [Evaluation]. */ var template by xdLink1(EvaluationTemplate) @@ -79,9 +77,9 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { * @return [EvaluationRun] */ fun toRun(): EvaluationRun = when(this.type) { - RunType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) - RunType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this, emptyMap()) /* TODO: Not sure about semantics here. */ - RunType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) + EvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) + EvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this, emptyMap()) /* TODO: Not sure about semantics here. */ + EvaluationType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) else -> throw IllegalArgumentException("Unsupported run type ${this.type.description}. This is a programmer's error!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index c874bccd2..1508bdfab 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -90,7 +90,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm */ constructor(name: String, shuffle: Boolean, template: EvaluationTemplate) : this(Evaluation.new { this.id = UUID.randomUUID().toString() - this.type = RunType.INTERACTIVE_ASYNCHRONOUS + this.type = EvaluationType.INTERACTIVE_ASYNCHRONOUS this.name = name this.template = template this.shuffleTasks = shuffle diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 5265aa6e6..c657d2e0b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -22,7 +22,7 @@ import java.util.* class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { init { - require(this.evaluation.type == RunType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } + require(this.evaluation.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } @@ -37,13 +37,13 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat */ constructor(name: String, template: EvaluationTemplate) : this(Evaluation.new { this.id = UUID.randomUUID().toString() - this.type = RunType.INTERACTIVE_SYNCHRONOUS + this.type = EvaluationType.INTERACTIVE_SYNCHRONOUS this.template = template this.name = name }) /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ - override val tasks: List = this.evaluation.tasks.asSequence().map { + override val tasks = this.evaluation.tasks.asSequence().map { ISTaskRun(it) }.toMutableList() @@ -52,7 +52,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat private set /** Returns the last [TaskRun]. */ - val currentTask: TaskRun? + val currentTask: AbstractInteractiveTask? get() = this.tasks.firstOrNull { it.template.id == this.currentTaskTemplate.id } override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 0a4d71cf9..bf807ed41 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -22,13 +22,13 @@ import kotlinx.dnq.query.* class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { init { - require(this.evaluation.type == RunType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } + require(this.evaluation.type == EvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [TaskRun]s registered for this [NonInteractiveEvaluation]. */ - override val tasks: List = this.evaluation.tasks.asSequence().map { + override val tasks = this.evaluation.tasks.asSequence().map { NITaskRun(it) }.toList() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index cb7ecedc8..8a1425b02 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -39,7 +39,7 @@ data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionId()).mapNotNull { it.role }.toSet() + val roles = AccessManager.rolesOfSession(ctx.sessionId()).mapNotNull { it.getRole() }.toSet() val teamId = runManager.template.teams.filter { it.users.contains(User.query(User::id eq userId).first()) }.first().teamId return RunActionContext(userId, teamId, roles) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt deleted file mode 100644 index c78299834..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunType.kt +++ /dev/null @@ -1,33 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.api.rest.types.evaluation.ApiRunType -import dev.dres.api.rest.types.evaluation.ApiVerdictType -import dev.dres.data.model.submissions.VerdictType -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdEnumEntity -import kotlinx.dnq.XdEnumEntityType -import kotlinx.dnq.xdRequiredStringProp - -/** - * Enumeration of the type of [Evaluation]. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class RunType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { - val INTERACTIVE_SYNCHRONOUS by enumField { description = "INTERACTIVE_SYNCHRONOUS" } - val INTERACTIVE_ASYNCHRONOUS by enumField { description = "INTERACTIVE_ASYNCHRONOUS" } - val NON_INTERACTIVE by enumField { description = "NON_INTERACTIVE" } - } - - var description by xdRequiredStringProp(unique = true) - private set - - /** - * Converts this [RunType] to a RESTful API representation [ApiRunType]. - * - * @return [ApiRunType] - */ - fun toApi() = ApiRunType.values().find { it.type == this } ?: throw IllegalStateException("Run type ${this.description} is not supported.") -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index 54505f1bf..ebd37ecc8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -10,7 +10,7 @@ import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence -typealias TaskId = String +typealias EvaluationId = String /** * Represents a [Task], i.e., a concrete instance of a [TaskTemplate], as executed by DRES. @@ -21,8 +21,8 @@ typealias TaskId = String class Task(entity: Entity) : PersistentEntity(entity) { companion object : XdNaturalEntityType() - /** The [TaskId] of this [Task]. */ - var taskId: TaskId + /** The [EvaluationId] of this [Task]. */ + var taskId: EvaluationId get() = this.id set(value) { this.id = value } @@ -39,7 +39,7 @@ class Task(entity: Entity) : PersistentEntity(entity) { var team by xdLink0_1(Team) /** The [Evaluation] this [Task] belongs to. */ - var evaluation by xdParent(Evaluation::tasks) + var evaluation: Evaluation by xdParent(Evaluation::tasks) /** List of [Submission]s received by this [Task]. */ val submissions by xdChildren0_N(Verdict::task) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index 6745f74ad..f07b031bf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -10,16 +10,16 @@ import dev.dres.data.model.run.EvaluationId * @version 1.0.0 */ interface EvaluationRun: Run { - /** The unique [EvaluationId] that identifies this [Evaluation]. */ + /** The unique [EvaluationId] that identifies this [EvaluationRun]. */ val id: EvaluationId - /** The name human readable of this [Evaluation]. */ + /** The name human readable of this [EvaluationRun]. */ val name: String - /** Reference to the [EvaluationTemplate] that describes the content of this [Evaluation]. */ + /** Reference to the [EvaluationTemplate] that describes the content of this [EvaluationRun]. */ val description: EvaluationTemplate - /** Collection of [TaskRun]s that make up this [Evaluation]. */ + /** Collection of [TaskRun]s that make up this [EvaluationRun]. */ val tasks: List /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 8332ac8fc..05cef964b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -2,6 +2,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun +import dev.dres.data.model.submissions.Submission import dev.dres.run.TaskStatus import dev.dres.run.score.interfaces.TaskScorer typealias TaskId = String @@ -30,4 +31,16 @@ interface TaskRun: Run { /** The current status of this [IATaskRun]. */ val status: TaskStatus + + /** + * Prepares this [TaskRun] for later starting. + */ + fun prepare() + + /** + * Returns a [List] of all [Submission]s that belong to this [TaskRun]. + * + * @return [List] of [Submission]s + */ + fun getSubmissions(): List } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt index a82dc38f3..92cd06815 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt @@ -27,10 +27,10 @@ class Verdict(entity: Entity) : PersistentEntity(entity) { var type by xdLink1(VerdictType) /** The [Submission] this [Verdict] belongs to. */ - var submission by xdParent(Submission::verdicts) + var submission: Submission by xdParent(Submission::verdicts) /** The [Task] this [Verdict] belongs to. */ - var task by xdParent(Task::submissions) + var task: Task by xdParent(Task::submissions) /** The [MediaItem] submitted. Only for [VerdictType.ITEM] or [VerdictType.TEMPORAL]. */ var item by xdLink0_1(MediaItem) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt index 7b8ed3542..1960b30fb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt @@ -33,7 +33,7 @@ class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ companion object: XdNaturalEntityType() /** The [TemplateId] of this [EvaluationTemplate]. */ - var teamId: TemplateId + var templateId: TemplateId get() = this.id set(value) { this.id = value } @@ -41,25 +41,25 @@ class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ var name by xdRequiredStringProp(unique = true, trimmed = true) /** If set, this [EvaluationTemplate] is considered a template!*/ - var template by xdBooleanProp() + var isTemplate by xdBooleanProp() /** An optional description of this [EvaluationTemplate]. */ var description by xdStringProp(trimmed = false) /** The [TaskType]s defined within this [EvaluationTemplate]. */ - val taskTypes by xdChildren0_N(TaskType::competition) + val taskTypes by xdChildren0_N(TaskType::evaluation) /** The [TaskGroup]s that are part of this [EvaluationTemplate]. */ - val taskGroups by xdChildren0_N(TaskGroup::competition) + val taskGroups by xdChildren0_N(TaskGroup::evaluation) /** The [TaskTemplate]s contained in this [EvaluationTemplate]*/ - val tasks by xdChildren0_N(TaskTemplate::competition) + val tasks by xdChildren0_N(TaskTemplate::evaluation) /** The [Team]s that are part of this [EvaluationTemplate]. */ - val teams by xdChildren0_N(Team::template) + val teams by xdChildren0_N(Team::evaluation) /** The [Team]s that are part of this [EvaluationTemplate]. */ - val teamsGroups by xdChildren0_N(TeamGroup::template) + val teamsGroups by xdChildren0_N(TeamGroup::evaluation) /** The [User]s that act as judge for this [EvaluationTemplate] */ val judges by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt index b7a26b31a..5d6f79198 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt @@ -32,13 +32,13 @@ class Hint(entity: Entity) : XdEntity(entity) { var type by xdLink1(HintType) /** The start of a (potential) range. */ - var start by xdNullableLongProp() { min(0L) } + var start by xdNullableLongProp() /** The start of a (potential) range. */ - var end by xdNullableLongProp { min(this@Hint.start ?: 0L) } + var end by xdNullableLongProp() /** The parent [TaskTemplate] this [Hint] belongs to. */ - var task by xdParent(TaskTemplate::hints) + var task: TaskTemplate by xdParent(TaskTemplate::hints) /** The[MediaItem] shown as part of the [Hint]. Can be null. */ var item by xdLink0_1(MediaItem) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt index a487dd9df..7814d5cde 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt @@ -4,18 +4,14 @@ import dev.dres.api.rest.types.competition.tasks.ApiHintType import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.template.task.options.ScoreOption import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdEnumEntity -import kotlinx.dnq.XdEnumEntityType -import kotlinx.dnq.xdBooleanProp -import kotlinx.dnq.xdRequiredStringProp +import kotlinx.dnq.* class HintType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { - val EMPTY by enumField { description = "EMPTY"; mimeType = ""; suffix = ""; base64 = true } - val TEXT by enumField { description = "TEXT"; mimeType = "text/plain"; base64 = false } - val VIDEO by enumField { description = "VIDEO"; mimeType = "video/mp4"; base64 = true } - val IMAGE by enumField { description = "IMAGE"; mimeType = "image/jpg"; base64 = true } - + val EMPTY by enumField { description = "EMPTY"; base64 = false } + val TEXT by enumField { description = "TEXT"; mimeType = "text/plain"; suffix = "txt"; base64 = false } + val VIDEO by enumField { description = "VIDEO"; mimeType = "video/mp4"; suffix = "mp4"; base64 = true } + val IMAGE by enumField { description = "IMAGE"; mimeType = "image/jpg"; suffix = "jpg"; base64 = true } } /** Name / description of the [ScoreOption]. */ @@ -23,11 +19,11 @@ class HintType(entity: Entity) : XdEnumEntity(entity) { private set /** Name / description of the [ScoreOption]. */ - var mimeType by xdRequiredStringProp(unique = true) + var mimeType by xdStringProp() private set /** Name / description of the [ScoreOption]. */ - var suffix by xdRequiredStringProp(unique = true) + var suffix by xdStringProp() /** Name / description of the [ScoreOption]. */ var base64 by xdBooleanProp() diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt index 0e7e5cad4..b2f92ea20 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt @@ -16,7 +16,7 @@ class TaskGroup(entity: Entity) : XdEntity(entity) { companion object : XdNaturalEntityType() { /** Combination of [TaskGroup] name / competition must be unique. */ override val compositeIndices = listOf( - listOf(TaskGroup::name, TaskGroup::competition) + listOf(TaskGroup::name, TaskGroup::evaluation) ) } @@ -27,7 +27,7 @@ class TaskGroup(entity: Entity) : XdEntity(entity) { var type by xdLink1(TaskType) /** The [EvaluationTemplate] this [TaskGroup] belongs to. */ - var competition by xdParent(EvaluationTemplate::taskGroups) + var evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::taskGroups) /** * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt index 3c4081067..5b3e188a6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt @@ -50,7 +50,7 @@ class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory var taskGroup by xdLink1(TaskGroup) /** The [EvaluationTemplate] this [TaskTemplate] belongs to. */ - var competition by xdParent(EvaluationTemplate::tasks) + var evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::tasks) /** The [MediaCollection] this [TaskTemplate] operates upon. */ var collection by xdLink1(MediaCollection) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt index 23f5ff255..3029b8dd6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt @@ -32,7 +32,7 @@ class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { var type by xdLink1(TargetType) /** The parent [TaskTemplate] this [TaskTemplateTarget] belongs to. */ - var task by xdParent(TaskTemplate::targets) + var task: TaskTemplate by xdParent(TaskTemplate::targets) /** The targeted [MediaItem]. Can be null. */ var item by xdLink0_1(MediaItem) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt index 0c6b262b9..de959f808 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt @@ -24,7 +24,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { /** Combination of [TaskType] name / competition must be unique. */ companion object: XdNaturalEntityType() { override val compositeIndices = listOf( - listOf(TaskType::name, TaskType::competition) + listOf(TaskType::name, TaskType::evaluation) ) } @@ -32,7 +32,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [EvaluationTemplate] this [TaskType] belongs to. */ - var competition by xdParent(EvaluationTemplate::taskTypes) + var evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::taskTypes) /** The (default) duration of this [TaskType] in seconds. */ var duration by xdRequiredLongProp() { min(0L) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt index e6e9bca71..9294fb73f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt @@ -22,7 +22,7 @@ class ConfiguredOption(entity: Entity) : XdEntity(entity) { var value by xdRequiredStringProp() /** The [TaskTemplate] this [ConfiguredOption] belongs to. */ - val task by xdParent(TaskType::configurations) + val task: TaskType by xdParent(TaskType::configurations) /** * Tries to parse a named parameter as [Boolean]. Returns null, if the parameter is not set or cannot be converted. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt index 8da61270d..16f678759 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt @@ -23,7 +23,7 @@ class Team(entity: Entity) : PersistentEntity(entity) { companion object: XdNaturalEntityType() { /** Combination of [Team] name / competition must be unique. */ override val compositeIndices = listOf( - listOf(Team::name, Team::template) + listOf(Team::name, Team::evaluation) ) } @@ -42,10 +42,10 @@ class Team(entity: Entity) : PersistentEntity(entity) { var logo by xdBlobProp() /** The [EvaluationTemplate] this [Team] belongs to. */ - var template by xdParent(EvaluationTemplate::teams) + val evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::teams) /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ - var group by xdLink0_1(TeamGroup::teams) + var group: TeamGroup? by xdLink0_1(TeamGroup::teams) /** The [User]s that belong to this [Team]. */ val users by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt index aeb3244a6..39e7d42c1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt @@ -35,10 +35,10 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { var defaultAggregator by xdLink1(TeamAggregator) /** The [EvaluationTemplate] this [Team] belongs to. */ - var template by xdParent(EvaluationTemplate::teamsGroups) + val evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::teamsGroups) /** The [Team]s that belong to this [TeamGroup]. */ - val teams by xdLink0_N(Team::group) + val teams by xdLink1_N(Team::group) /** * Converts this [TeamGroup] to a RESTful API representation [ApiTeamGroup]. diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index b83de1110..4689ccfaf 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -97,7 +97,7 @@ object UserManager { * @return True on success, false otherwise. */ fun update(id: UserId?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.role) + = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.getRole()) /** * Deletes the [User] for the given [UserId]. diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index d38b18533..b50a2687d 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -9,19 +9,22 @@ import dev.dres.data.model.admin.Role import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.submissions.Verdict import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.team.TeamId import dev.dres.run.audit.AuditLogger -import dev.dres.run.audit.LogEventSource import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -41,7 +44,7 @@ import kotlin.math.max * @version 1.0.0 * @author Ralph Gasser */ -class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronousEvaluation): InteractiveRunManager { +class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsynchronousEvaluation, private val store: TransientEntityStore): InteractiveRunManager { companion object { private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) @@ -49,47 +52,46 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous private const val MAXIMUM_ERROR_COUNT = 5 } + /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ override val runProperties: RunProperties - get() = run.properties + get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) /** Tracks the current [TaskTemplate] per [TeamId]. */ private val statusMap: MutableMap = HashMap() - /** A [Map] of all viewers, i.e., DRES cliets currently registered with this [InteractiveAsynchronousRunManager]. */ + /** A [Map] of all viewers, i.e., DRES clients currently registered with this [InteractiveAsynchronousRunManager]. */ private val viewers = ConcurrentHashMap() /** A lock for state changes to this [InteractiveAsynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = - ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.evaluation) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoresUpdatable = - ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable, this.daoUpdatable) + private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable) /** List of [Updatable] held by this [InteractiveAsynchronousRunManager]. */ private val updatables = mutableListOf() /** Run ID of this [InteractiveAsynchronousEvaluation]. */ - override val id: EvaluationId - get() = this.run.id + override val id: SubmissionId + get() = this.evaluation.id /** Name of this [InteractiveAsynchronousEvaluation]. */ override val name: String - get() = this.run.name + get() = this.evaluation.name /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ override val template: EvaluationTemplate - get() = this.run.description + get() = this.evaluation.description /** The global [RunManagerStatus] of this [InteractiveAsynchronousRunManager]. */ @Volatile - override var status: RunManagerStatus = if (this.run.hasStarted) { + override var status: RunManagerStatus = if (this.evaluation.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED @@ -97,7 +99,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous private set override val judgementValidators: List - get() = this.run.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } + get() = this.evaluation.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } override val scoreboards: List get() = this.scoreboardsUpdatable.scoreboards @@ -105,61 +107,51 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous override val scoreHistory: List get() = this.scoreboardsUpdatable.timeSeries - override val allSubmissions: List - get() = this.stateLock.read { this.run.tasks.flatMap { it.submissions } } + init { /* Register relevant Updatables. */ this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) this.updatables.add(this.messageQueueUpdatable) - this.updatables.add(this.daoUpdatable) - - this.template.teams.forEach { - - val teamContext = RunActionContext(EvaluationId.EMPTY, it.uid, setOf(Role.ADMIN)) - - this.updatables.add( - EndTaskUpdatable(this, teamContext) - ) - - } - /* Initialize map and set all tasks pointers to the first task. */ - this.template.teams.forEach { - this.statusMap[it.uid] = if (this.run.hasStarted) { - RunManagerStatus.ACTIVE - } else { - RunManagerStatus.CREATED - } + this.store.transactional(true) { + this.template.teams.asSequence().forEach { + val teamContext = RunActionContext("", it.teamId, setOf(Role.ADMIN)) + this.updatables.add(EndTaskUpdatable(this, teamContext)) - /** End ongoing runs upon initialization (in case server crashed during task execution). */ - if (this.run.tasksForTeam(it.uid).lastOrNull()?.isRunning == true) { - this.run.tasksForTeam(it.uid).last().end() - } - } + /* Initialize map and set all tasks pointers to the first task. */ + this.statusMap[it.teamId] = if (this.evaluation.hasStarted) { + RunManagerStatus.ACTIVE + } else { + RunManagerStatus.CREATED + } - /** Re-enqueue pending submissions for judgement (if any). */ - this.run.tasks.forEach { run -> - run.submissions.filter { it.status == VerdictStatus.INDETERMINATE }.forEach { - run.validator.validate(it) + /** End ongoing runs upon initialization (in case server crashed during task execution). */ + if (this.evaluation.tasksForTeam(it.teamId).lastOrNull()?.isRunning == true) { + this.evaluation.tasksForTeam(it.teamId).last().end() + } } - } - /** Re-calculate all the relevant scores. */ - this.run.tasks.forEach { run -> - run.submissions.forEach { sub -> - this.scoresUpdatable.enqueue(Pair(run, sub)) + /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ + this.evaluation.tasks.forEach { task -> + task.getSubmissions().forEach { sub -> + this.scoresUpdatable.enqueue(Pair(task, sub)) + if (sub.verdicts.filter { v -> v.status eq VerdictStatus.INDETERMINATE }.any()) { + task.validator.validate(sub) + } + } } + this.scoresUpdatable.update(this.status) } - this.scoresUpdatable.update(this.status) } /** * Starts this [InteractiveAsynchronousEvaluation] moving [RunManager.status] from [RunManagerStatus.CREATED] to - * [RunManagerStatus.ACTIVE] for all teams. This can only be executed by an administrator. + * [RunManagerStatus.ACTIVE] for the selected team. * - * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. + * As all state affecting methods, this method throws an [IllegalStateException] if invocation + * does not match the current state. * * @param context The [RunActionContext] for this invocation. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.CREATED] @@ -168,17 +160,14 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous checkGlobalStatus(RunManagerStatus.CREATED) if (context.isAdmin) { /* Start the run. */ - this.run.start() + this.evaluation.start() /* Update status. */ this.statusMap.forEach { (t, _) -> this.statusMap[t] = RunManagerStatus.ACTIVE } this.status = RunManagerStatus.ACTIVE - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) LOGGER.info("Run manager ${this.id} started") } @@ -186,7 +175,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous /** * Ends this [InteractiveAsynchronousEvaluation] moving [RunManager.status] from [RunManagerStatus.ACTIVE] to - * [RunManagerStatus.TERMINATED] for all teams. This can only be executed by an administrator. + * [RunManagerStatus.TERMINATED] for selected team. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation * does not match the current state. @@ -198,32 +187,33 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous checkGlobalStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE)//, RunManagerStatus.TASK_ENDED) if (context.isAdmin) { /* End the run. */ - this.run.end() + this.evaluation.end() /* Update status. */ this.statusMap.forEach { (t, _) -> this.statusMap[t] = RunManagerStatus.TERMINATED } this.status = RunManagerStatus.TERMINATED - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } } + override fun updateProperties(properties: RunProperties) { + TODO("Not yet implemented") + } + /** - * Returns the currently active [TaskTemplate] for the given team. Requires [RunManager.status] for the requesting team - * to be [RunManagerStatus.ACTIVE]. + * Returns the currently active [TaskTemplate] for the given team. Requires [RunManager.status] for the + * requesting team to be [RunManagerStatus.ACTIVE]. * * @param context The [RunActionContext] used for the invocation. * @return The [TaskTemplate] for the given team. */ override fun currentTaskTemplate(context: RunActionContext): TaskTemplate { require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager." } - return this.run.currentTaskDescription(context.teamId) + return this.evaluation.currentTaskDescription(context.teamId) } /** @@ -285,12 +275,12 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE)//, RunManagerStatus.TASK_ENDED) require(!teamHasRunningTask(context.teamId)) { "Cannot change task while task is active" } - val idx = (index + this.template.tasks.size) % this.template.tasks.size + val idx = (index + this.template.tasks.size()) % this.template.tasks.size() /* Update active task. */ //this.run.navigationMap[context.teamId] = this.description.tasks[index] - this.run.goTo(context.teamId, idx) + this.evaluation.goTo(context.teamId, idx) //FIXME since task run and competition run states are separated, this is not actually a state change this.statusMap[context.teamId] = RunManagerStatus.ACTIVE @@ -299,7 +289,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue( - ServerMessage(this.id.string, ServerMessageType.COMPETITION_UPDATE), + ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE), context.teamId ) @@ -324,65 +314,59 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE) /* Create task and update status. */ - val currentTaskDescription = this.run.currentTaskDescription(context.teamId) + val currentTaskTemplate = this.evaluation.currentTaskDescription(context.teamId) /* Check for duplicate task runs */ - if (!runProperties.allowRepeatedTasks && this.run.tasksForTeam(context.teamId).any { it.descriptionId == currentTaskDescription.id }) { - throw IllegalStateException("Task '${currentTaskDescription.name}' has already been used") + if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasksForTeam(context.teamId).any { it.template.id == currentTaskTemplate.id }) { + throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used") } - val currentTaskRun = - this.run.IATaskRun(teamId = context.teamId, descriptionId = currentTaskDescription.id) + val currentTaskRun = this.evaluation.IATaskRun(currentTaskTemplate, context.teamId) currentTaskRun.prepare() /* Mark scoreboards and DAO for update. */ this.scoreboardsUpdatable.dirty = true - this.daoUpdatable.dirty = true /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue( - ServerMessage(this.id.string, ServerMessageType.TASK_PREPARE), + ServerMessage(this.id, ServerMessageType.TASK_PREPARE), context.teamId ) - LOGGER.info("Run manager ${this.id} started task $currentTaskDescription.") + LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") } /** * Force-abort the [currentTask] and thus moves the [InteractiveAsynchronousRunManager.status] for the given team from - * [RunManagerStatus.PREPARING_TASK] or [RunManagerStatus.RUNNING_TASK] to [RunManagerStatus.ACTIVE] + * [RunManagerStatus.ACTIVE] or [RunManagerStatus.ACTIVE] to [RunManagerStatus.ACTIVE] * * TODO: Do we want users to be able to do this? If yes, I'd argue that the ability to repeat a task is a requirement. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ override fun abortTask(context: RunActionContext) = this.stateLock.write { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - //checkTeamStatus(context.teamId, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK) require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } /* End TaskRun and update status. */ - val currentTask = this.currentTask(context) - ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") + val currentTask = this.currentTask(context) ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") currentTask.end() - //this.statusMap[context.teamId] = RunManagerStatus.TASK_ENDED /* Mark scoreboards and DAO for update. */ this.scoreboardsUpdatable.dirty = true - this.daoUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END), context.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END), context.teamId) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } /** * Returns the time in milliseconds that is left until the end of the currently running task for the given team. - * Only works if the [InteractiveAsynchronousRunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, + * Only works if the [InteractiveAsynchronousRunManager] is in state [RunManagerStatus.ACTIVE]. If no task is running, * this method returns -1L. * * @param context The [RunActionContext] used for the invocation. @@ -394,7 +378,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous return if (currentTaskRun?.isRunning == true) { max( 0L, - currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!) + InteractiveRunManager.COUNTDOWN_DURATION + currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started) + InteractiveRunManager.COUNTDOWN_DURATION ) } else { -1L @@ -402,15 +386,15 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous } /** - * Returns the time in milliseconds that has elapsed since the start of the current [InteractiveSynchronousEvaluation.Task]. - * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, this method returns -1L. + * Returns the time in milliseconds that has elapsed since the start of the current [Task]. + * Only works if the [RunManager] is in state [RunManagerStatus.ACTIVE]. If no task is running, this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { val currentTaskRun = this.currentTask(context) return if (currentTaskRun?.isRunning == true) { - System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) + System.currentTimeMillis() - (currentTaskRun.started + InteractiveRunManager.COUNTDOWN_DURATION) } else { -1L } @@ -425,9 +409,9 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @return [List] of [AbstractInteractiveTask]s */ override fun taskCount(context: RunActionContext): Int { - if (context.isAdmin) return this.run.tasks.size + if (context.isAdmin) return this.evaluation.tasks.size require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.run.tasksForTeam(context.teamId).size + return this.evaluation.tasksForTeam(context.teamId).size } /** @@ -439,19 +423,19 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * @return [List] of [AbstractInteractiveTask]s */ override fun tasks(context: RunActionContext): List { - if (context.isAdmin) return this.run.tasks + if (context.isAdmin) return this.evaluation.tasks require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.run.tasksForTeam(context.teamId) + return this.evaluation.tasksForTeam(context.teamId) } /** - * Returns [AbstractInteractiveTask]s for a specific task [EvaluationId]. May be empty. + * Returns [AbstractInteractiveTask]s for a specific task [SubmissionId]. May be empty. * * @param context The [RunActionContext] used for the invocation. - * @param taskId The [EvaluationId] of the [AbstractInteractiveTask]. + * @param taskId The [SubmissionId] of the [AbstractInteractiveTask]. */ - override fun taskForId(context: RunActionContext, taskId: EvaluationId): AbstractInteractiveTask? = - this.tasks(context).find { it.uid == taskId } + override fun taskForId(context: RunActionContext, taskId: SubmissionId): AbstractInteractiveTask? = + this.tasks(context).find { it.id == taskId } /** * Returns a reference to the currently active [AbstractInteractiveTask]. @@ -461,17 +445,28 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous */ override fun currentTask(context: RunActionContext): AbstractInteractiveTask? = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.run.currentTaskForTeam(context.teamId) + return this.evaluation.currentTaskForTeam(context.teamId) + } + + /** + * List of all [Submission]s for this [InteractiveAsynchronousRunManager], irrespective of the [Task] it belongs to. + * + * @param context The [RunActionContext] used for the invocation. + * @return List of [Submission]s. + */ + override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { + this.evaluation.tasks.flatMap { it.getSubmissions() } } /** * Returns the [Submission]s for all currently active [AbstractInteractiveTask]s or an empty [List], if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s for the currently active [AbstractInteractiveTask] + * @return List of [Submission]s. */ - override fun submissions(context: RunActionContext): List = - this.currentTask(context)?.submissions?.toList() ?: emptyList() + override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { + this.currentTask(context)?.getSubmissions()?.toList() ?: emptyList() + } /** * Adjusting task durations is not supported by the [InteractiveAsynchronousRunManager]s. @@ -484,9 +479,9 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("No active TaskRun found. This is a serious error!") val newDuration = currentTaskRun.duration + s - check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } + check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration - return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) + return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) } @@ -509,39 +504,31 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * this method again. * * @param context The [RunActionContext] used for the invocation - * @param sub The [Submission] to be posted. + * @param submission The [Submission] to be posted. * * @return [VerdictStatus] of the [Submission] - * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ - override fun postSubmission(context: RunActionContext, sub: Submission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - //checkTeamStatus(context.teamId, RunManagerStatus.RUNNING_TASK) require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } - + require(submission.verdicts.size() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") - task.postSubmission(sub) - - /* Mark dao for update. */ - this.daoUpdatable.dirty = true + task.postSubmission(submission) /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, sub)) + this.scoresUpdatable.enqueue(Pair(task, submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue( - ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED), - context.teamId - ) - - return sub.status + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId) + return submission.verdicts.first().status } /** - * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [VerdictStatus]. + * Invoked by an external caller to update an existing [Submission] by its [Submission.submissionId] with a new [VerdictStatus]. * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveAsynchronousRunManager]. * * This method will not throw an exception and instead returns false if a [Submission] was @@ -549,33 +536,24 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [EvaluationId] of the [Submission] to update. + * @param submissionId The [SubmissionId] of the [Submission] to update. * @param submissionStatus The new [VerdictStatus] * * @return Whether the update was successful or not */ - override fun updateSubmission( - context: RunActionContext, - submissionId: EvaluationId, - submissionStatus: VerdictStatus - ): Boolean = this.stateLock.read { - val found = this.allSubmissions.find { it.uid == submissionId } ?: return false + override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: VerdictStatus): Boolean = this.stateLock.read { + val verdict = Verdict.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false + val task = this.taskForId(context, verdict.task.id) ?: return false /* Actual update - currently, only status update is allowed */ - if (found.status != submissionStatus) { - found.status = submissionStatus - - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true + if (verdict.status != submissionStatus) { + verdict.status = submissionStatus /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(found.task!!, found)) + this.scoresUpdatable.enqueue(Pair(task, verdict.submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue( - ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED), - context.teamId!! - ) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId!!) return true } @@ -607,6 +585,9 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous return true } + /** + * + */ override fun run() { /** Sort list of by [Phase] in ascending order. */ this.updatables.sortBy { it.phase } @@ -615,14 +596,16 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous var errorCounter = 0 while (this.status != RunManagerStatus.TERMINATED) { try { - /* 1) Invoke all relevant [Updatable]s. */ - this.invokeUpdatables() + this.store.transactional { + /* 1) Invoke all relevant [Updatable]s. */ + this.invokeUpdatables() - /* 2) Process internal state updates (if necessary). */ - this.internalStateUpdate() + /* 2) Process internal state updates (if necessary). */ + this.internalStateUpdate() - /* 3) Reset error counter and yield to other threads. */ - errorCounter = 0 + /* 3) Reset error counter and yield to other threads. */ + errorCounter = 0 + } Thread.sleep(10) } catch (ie: InterruptedException) { LOGGER.info("Interrupted run manager thread; exiting...") @@ -636,7 +619,7 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous // oh shit, something went horribly horribly wrong if (errorCounter >= MAXIMUM_ERROR_COUNT) { LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_ERROR_COUNT; terminating loop...") - RunExecutor.dump(this.run) + RunExecutor.dump(this.evaluation) break } else { LOGGER.error("This is the ${++errorCounter}-th in a row. Run manager will terminate loop after $MAXIMUM_ERROR_COUNT errors") @@ -653,21 +636,17 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ private fun invokeUpdatables() = this.stateLock.read { - this.statusMap.values.toSet() - .forEach { status -> //call update once for every possible status which is currently set for any team - this.updatables.forEach { - if (it.shouldBeUpdated(status)) { - try { - it.update(status) - } catch (e: Throwable) { - LOGGER.error( - "Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", - e - ) - } + this.statusMap.values.toSet().forEach { status -> //call update once for every possible status which is currently set for any team + this.updatables.forEach { + if (it.shouldBeUpdated(status)) { + try { + it.update(status) + } catch (e: Throwable) { + LOGGER.error("Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) } } } + } } /** @@ -675,40 +654,29 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous * i.e., status updates that are not triggered by an outside interaction. */ private fun internalStateUpdate() = this.stateLock.read { - for (teamId in this.run.description.teams.map { it.uid }) { - if (teamHasRunningTask(teamId)) { - val task = this.run.currentTaskForTeam(teamId) - ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") + for (team in this.evaluation.description.teams.asSequence()) { + if (teamHasRunningTask(team.teamId)) { + val task = this.evaluation.currentTaskForTeam(team.teamId) + ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") val timeLeft = max( 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION + task.duration * 1000L - (System.currentTimeMillis() - task.started) + InteractiveRunManager.COUNTDOWN_DURATION ) if (timeLeft <= 0) { this.stateLock.write { task.end() - //this.statusMap[teamId] = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, task.uid, task.template, LogEventSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, task.id, AuditLogSource.INTERNAL, null) } - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue( - ServerMessage(this.id.string, ServerMessageType.TASK_END), - teamId - ) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END), team.teamId) } - } else if (teamHasPreparingTask(teamId)) { - - //TODO check if all viewers are ready? - - val task = this.run.currentTaskForTeam(teamId) - ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") + } else if (teamHasPreparingTask(team.teamId)) { + val task = this.evaluation.currentTaskForTeam(team.teamId) + ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") task.start() - AuditLogger.taskStart(this.id, task.uid, task.template, AuditLogSource.REST, null) - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_START), teamId) - + AuditLogger.taskStart(this.id, task.teamId, task.template, AuditLogSource.REST, null) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_START), team.teamId) } } } @@ -734,8 +702,14 @@ class InteractiveAsynchronousRunManager(private val run: InteractiveAsynchronous if (s !in status) throw IllegalRunStateException(s) } - private fun teamHasRunningTask(teamId: TeamId) = this.run.currentTaskForTeam(teamId)?.isRunning == true + /** + * + */ + private fun teamHasRunningTask(teamId: TeamId) = this.evaluation.currentTaskForTeam(teamId)?.isRunning == true + /** + * + */ private fun teamHasPreparingTask(teamId: TeamId) = - this.run.currentTaskForTeam(teamId)?.status == TaskStatus.PREPARING + this.evaluation.currentTaskForTeam(teamId)?.status == TaskStatus.PREPARING } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index eae08d171..005f107c5 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -18,9 +18,6 @@ interface InteractiveRunManager : RunManager { /** List of [ScoreTimePoint]s tracking the states of the different [Scoreboard]s over time*/ val scoreHistory: List - /** List of all [Submission]s for this [InteractiveRunManager], irrespective of the [InteractiveSynchronousEvaluation.Task] it belongs to. */ - val allSubmissions: List - /** * Prepares this [InteractiveRunManager] for the execution of previous [TaskTemplate] as per order defined in * [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. @@ -124,37 +121,45 @@ interface InteractiveRunManager : RunManager { fun timeElapsed(context: RunActionContext): Long /** - * Returns a list of all [AbstractInteractiveTask]s for this [InteractiveRunManager]. Depending on the + * Returns a list of all [TaskRun]s for this [InteractiveRunManager]. Depending on the * implementation, that list may be filtered depending on the [RunActionContext]. * * @param context The [RunActionContext] used for the invocation. - * @return [List] of [AbstractInteractiveTask]s + * @return [List] of [TaskRun]s */ - override fun tasks(context: RunActionContext): List + override fun tasks(context: RunActionContext): List /** - * Returns a reference to the currently active [AbstractInteractiveTask]. + * Returns a reference to the currently active [TaskRun]. * * @param context The [RunActionContext] used for the invocation. - * @return [AbstractInteractiveTask] that is currently active or null, if no such task is active. + * @return [TaskRun] that is currently active or null, if no such task is active. */ - fun currentTask(context: RunActionContext): AbstractInteractiveTask? + fun currentTask(context: RunActionContext): TaskRun? /** - * Returns [AbstractInteractiveTask]s for the specified [TaskId]. + * Returns [TaskRun]s for the specified [EvaluationId]. * * @param context The [RunActionContext] used for the invocation. - * @param taskId The [TaskId] of the desired [AbstractInteractiveTask]. + * @param taskId The [EvaluationId] of the desired [TaskRun]. */ - fun taskForId(context: RunActionContext, taskId: TaskId): AbstractInteractiveTask? + fun taskForId(context: RunActionContext, taskId: EvaluationId): TaskRun? /** - * List of [Submission]s for the current [AbstractInteractiveTask]. + * List of all [Submission]s for this [InteractiveRunManager], irrespective of the [Task] it belongs to. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission] for the current, [AbstractInteractiveTask] + * @return List of [Submission]s */ - fun submissions(context: RunActionContext): List + fun allSubmissions(context: RunActionContext): List + + /** + * List of [Submission]s for the current [Task]. + * + * @param context The [RunActionContext] used for the invocation. + * @return List of [Submission]s + */ + fun currentSubmissions(context: RunActionContext): List /** * Override the ready state for a given viewer ID. @@ -165,7 +170,6 @@ interface InteractiveRunManager : RunManager { */ fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean - /** * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [VerdictStatus]. * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveRunManager]. diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 39ba8c553..826364540 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -6,14 +6,14 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.run.InteractiveSynchronousEvaluation -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.RunProperties import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.Verdict import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.task.options.TaskOption import dev.dres.run.audit.AuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent @@ -23,6 +23,8 @@ import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.ReadyLatch +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -31,12 +33,12 @@ import kotlin.math.max /** * An implementation of [RunManager] aimed at distributed execution having a single DRES Server instance and multiple - * viewers connected via WebSocket. Before starting a [InteractiveSynchronousEvaluation.Task], all viewer instances are synchronized. + * viewers connected via WebSocket. Before starting a [Task], all viewer instances are synchronized. * * @version 3.0.0 * @author Ralph Gasser */ -class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEvaluation) : InteractiveRunManager { +class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynchronousEvaluation, private val store: TransientEntityStore) : InteractiveRunManager { private val VIEWER_TIME_OUT = 30L //TODO make configurable @@ -47,43 +49,34 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv /** Number of consecutive errors which have to occur within the main execution loop before it tries to gracefully terminate */ private val maxErrorCount = 5 - /** */ + /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ override val runProperties: RunProperties - get() = run.properties + get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) - /** Run ID of this [InteractiveSynchronousRunManager]. */ + /** [EvaluationId] of this [InteractiveSynchronousRunManager]. */ override val id: EvaluationId - get() = this.run.id + get() = this.evaluation.id /** Name of this [InteractiveSynchronousRunManager]. */ override val name: String - get() = this.run.name + get() = this.evaluation.name /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ override val template: EvaluationTemplate - get() = this.run.description - - /** The list of all [Submission]s tracked ever received by this [InteractiveSynchronousRunManager]. */ - override val allSubmissions: List - get() = this.stateLock.read { - this.run.tasks.flatMap { it.ve } - } + get() = this.evaluation.description /** The status of this [RunManager]. */ @Volatile - override var status: RunManagerStatus = if (this.run.hasStarted) { + override var status: RunManagerStatus = if (this.evaluation.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED } - get() = this.stateLock.read { - return field - } - private set + private set /** Returns list [JudgementValidator]s associated with this [InteractiveSynchronousRunManager]. May be empty*/ override val judgementValidators: List - get() = this.run.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } + get() = this.evaluation.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } /** List of [Scoreboard]s for this [InteractiveSynchronousRunManager]. */ override val scoreboards: List @@ -93,21 +86,19 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv override val scoreHistory: List get() = this.scoreboardsUpdatable.timeSeries - /** Internal data structure that tracks all [WebSocketConnection]s and their ready state (for [RunManagerStatus.PREPARING_TASK]) */ + /** Internal data structure that tracks all [WebSocketConnection]s and their ready state. */ private val readyLatch = ReadyLatch() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = - ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.evaluation) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoresUpdatable = - ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable, this.daoUpdatable) + private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable) - /** The internal [DAOUpdatable] used to end a task once no more submissions are possible */ + /** The internal [EndTaskUpdatable] used to end a task once no more submissions are possible */ private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.INTERNAL) /** List of [Updatable] held by this [InteractiveSynchronousRunManager]. */ @@ -127,26 +118,21 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) this.updatables.add(this.messageQueueUpdatable) - this.updatables.add(this.daoUpdatable) this.updatables.add(this.endTaskUpdatable) /** End ongoing runs upon initialization (in case server crashed during task execution). */ - if (this.run.currentTask?.isRunning == true) { - this.run.currentTask?.end() + if (this.evaluation.currentTask?.isRunning == true) { + this.evaluation.currentTask?.end() } - /** Re-enqueue pending submissions (if any). */ - this.run.tasks.forEach { run -> - run.submissions.filter { it.status == VerdictStatus.INDETERMINATE }.forEach { - run.validator.validate(it) - } - } - - /** Re-calculate all the relevant scores. */ - this.run.tasks.forEach { run -> - run.submissions.forEach { sub -> - this.scoresUpdatable.enqueue(Pair(run, sub)) + /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ + this.evaluation.tasks.forEach { task -> + task.getSubmissions().forEach { sub -> + this.scoresUpdatable.enqueue(Pair(task, sub)) + if (sub.verdicts.filter { v -> v.status eq VerdictStatus.INDETERMINATE }.any()) { + task.validator.validate(sub) + } } } this.scoresUpdatable.update(this.status) @@ -157,16 +143,13 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv checkContext(context) /* Start the run. */ - this.run.start() + this.evaluation.start() /* Update status. */ this.status = RunManagerStatus.ACTIVE - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) LOGGER.info("SynchronousRunManager ${this.id} started") } @@ -176,31 +159,32 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv checkContext(context) /* End the run. */ - this.run.end() + this.evaluation.end() /* Update status. */ this.status = RunManagerStatus.TERMINATED - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } + override fun updateProperties(properties: RunProperties) { + TODO("Not yet implemented") + } + override fun currentTaskTemplate(context: RunActionContext): TaskTemplate = this.stateLock.write { checkStatus( RunManagerStatus.CREATED, RunManagerStatus.ACTIVE/*, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED*/ ) - this.run.currentTaskTemplate + this.evaluation.currentTaskTemplate } override fun previous(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.template.tasks.indexOf(this.run.currentTaskTemplate) - 1 + val newIndex = this.template.tasks.indexOf(this.evaluation.currentTaskTemplate) - 1 return try { this.goTo(context, newIndex) true @@ -211,7 +195,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv override fun next(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.template.tasks.indexOf(this.run.currentTaskTemplate) + 1 + val newIndex = this.template.tasks.indexOf(this.evaluation.currentTaskTemplate) + 1 return try { this.goTo(context, newIndex) true @@ -223,10 +207,10 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv override fun goTo(context: RunActionContext, index: Int) { checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() - if (index >= 0 && index < this.template.tasks.size) { + if (index >= 0 && index < this.template.tasks.size()) { /* Update active task. */ - this.run.goTo(index) + this.evaluation.goTo(index) //FIXME since task run and competition run states are separated, this is not actually a state change // /* Update RunManager status. */ @@ -236,7 +220,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.COMPETITION_UPDATE)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE)) LOGGER.info("SynchronousRunManager ${this.id} set to task $index") } else { @@ -249,35 +233,30 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv assureNoRunningTask() checkContext(context) - val currentTaskDescription = this.currentTaskTemplate(context) - - /* Check for duplicate task runs */ - if (!runProperties.allowRepeatedTasks && this.run.tasks.any { it.template.id == currentTaskDescription.id }) { - throw IllegalStateException("Task '${currentTaskDescription.name}' has already been used") + val currentTaskTemplate = this.currentTaskTemplate(context) + if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasks.any { it.template.id == currentTaskTemplate.id }) { + throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used.") } /* Create and prepare pipeline for submission. */ - this.run.Task(taskDescriptionId = currentTaskDescription.id) + this.evaluation.ISTaskRun(currentTaskTemplate) /* Update status. */ - //this.status = RunManagerStatus.PREPARING_TASK - this.run.currentTask!!.prepare() + this.evaluation.currentTask!!.prepare() /* Mark scoreboards and dao for update. */ this.scoreboardsUpdatable.dirty = true - this.daoUpdatable.dirty = true /* Reset the ReadyLatch. */ this.readyLatch.reset(VIEWER_TIME_OUT) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_PREPARE)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_PREPARE)) - LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.run.currentTaskTemplate}") + LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.evaluation.currentTaskTemplate}") } override fun abortTask(context: RunActionContext) = this.stateLock.write { - //checkStatus(RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK) checkStatus(RunManagerStatus.ACTIVE) assureTaskPreparingOrRunning() checkContext(context) @@ -285,77 +264,75 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv /* End TaskRun and persist. */ this.currentTask(context)?.end() -// /* Update state. */ -// this.status = RunManagerStatus.TASK_ENDED - /* Mark scoreboards and dao for update. */ this.scoreboardsUpdatable.dirty = true - this.daoUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END)) - - LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.run.currentTaskTemplate}") + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END)) + LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.evaluation.currentTaskTemplate}") } - /** List of [InteractiveSynchronousEvaluation.Task] for this [InteractiveSynchronousRunManager]. */ - override fun tasks(context: RunActionContext): List = this.run.tasks + /** List of [Task] for this [InteractiveSynchronousRunManager]. */ + override fun tasks(context: RunActionContext): List = this.evaluation.tasks /** - * Returns the currently active [InteractiveSynchronousEvaluation.Task]s or null, if no such task is active. + * Returns the currently active [Task]s or null, if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return [InteractiveSynchronousEvaluation.Task] or null + * @return [Task] or null */ override fun currentTask(context: RunActionContext) = this.stateLock.read { -// when (this.status) { -// RunManagerStatus.PREPARING_TASK, -// RunManagerStatus.RUNNING_TASK, -// RunManagerStatus.TASK_ENDED -> this.run.lastTask -// else -> null -// } - - when (this.run.currentTask?.status) { + when (this.evaluation.currentTask?.status) { TaskStatus.PREPARING, TaskStatus.RUNNING, - TaskStatus.ENDED -> this.run.currentTask + TaskStatus.ENDED -> this.evaluation.currentTask else -> null } - } /** - * Returns [InteractiveSynchronousEvaluation.Task]s for a specific task [EvaluationId]. May be empty. + * Returns [Task]s for a specific task [EvaluationId]. May be empty. * - * @param taskId The [EvaluationId] of the [InteractiveSynchronousEvaluation.Task]. + * @param taskId The [EvaluationId] of the [TaskRun]. */ - override fun taskForId(context: RunActionContext, taskId: EvaluationId): InteractiveSynchronousEvaluation.Task? = - this.run.tasks.find { it.uid == taskId } + override fun taskForId(context: RunActionContext, taskId: EvaluationId) = + this.evaluation.tasks.find { it.id == taskId } + + /** + * List of all [Submission]s for this [InteractiveAsynchronousRunManager], irrespective of the [Task] it belongs to. + * + * @param context The [RunActionContext] used for the invocation. + * @return List of [Submission]s. + */ + override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { + this.evaluation.tasks.flatMap { it.getSubmissions() } + } /** - * Returns the [Submission]s for all currently active [InteractiveSynchronousEvaluation.Task]s or an empty [List], if no such task is active. + * Returns the [Submission]s for all currently active [Task]s or an empty [List], if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s for the currently active [InteractiveSynchronousEvaluation.Task] + * @return List of [Submission]s for the currently active [Task] */ - override fun submissions(context: RunActionContext): List = - this.currentTask(context)?.submissions?.toList() ?: emptyList() + override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { + this.currentTask(context)?.getSubmissions()?.toList() ?: emptyList() + } /** - * Returns the number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager]. + * Returns the number of [Task]s held by this [RunManager]. * - * @return The number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager] + * @return The number of [Task]s held by this [RunManager] */ - override fun taskCount(context: RunActionContext): Int = this.run.tasks.size + override fun taskCount(context: RunActionContext): Int = this.evaluation.tasks.size /** - * Adjusts the duration of the current [InteractiveSynchronousEvaluation.Task] by the specified amount. Amount can be either positive or negative. + * Adjusts the duration of the current [Task] by the specified amount. Amount can be either positive or negative. * * @param s The number of seconds to adjust the duration by. * @return Time remaining until the task will end in milliseconds * * @throws IllegalArgumentException If the specified correction cannot be applied. - * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.RUNNING_TASK]. + * @throws IllegalStateException If [RunManager] was not in wrong [RunManagerStatus]. */ override fun adjustDuration(context: RunActionContext, s: Int): Long = this.stateLock.read { assureTaskRunning() @@ -364,25 +341,25 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") val newDuration = currentTaskRun.duration + s - check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } + check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration - return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) + return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) } /** - * Returns the time in milliseconds that is left until the end of the current [InteractiveSynchronousEvaluation.Task]. - * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, + * Returns the time in milliseconds that is left until the end of the current [Task]. + * Only works if the [RunManager] is in wrong [RunManagerStatus]. If no task is running, * this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - return if (this.run.currentTask?.status == TaskStatus.RUNNING) { + return if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") max( 0L, - currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!) + InteractiveRunManager.COUNTDOWN_DURATION + currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started) + InteractiveRunManager.COUNTDOWN_DURATION ) } else { -1L @@ -390,16 +367,16 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv } /** - * Returns the time in milliseconds that has elapsed since the start of the current [InteractiveSynchronousEvaluation.Task]. - * Only works if the [RunManager] is in state [RunManagerStatus.RUNNING_TASK]. If no task is running, this method returns -1L. + * Returns the time in milliseconds that has elapsed since the start of the current [Task]. + * Only works if the [RunManager] is in wrong [RunManagerStatus]. If no task is running, this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { - return if (this.run.currentTask?.status == TaskStatus.RUNNING) { + return if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") - System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) + System.currentTimeMillis() - (currentTaskRun.started + InteractiveRunManager.COUNTDOWN_DURATION) } else { -1L } @@ -418,7 +395,6 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * @param viewerId The ID of the viewer's WebSocket session. */ override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean = this.stateLock.read { - //checkStatus(RunManagerStatus.PREPARING_TASK) checkStatus(RunManagerStatus.ACTIVE) assureTaskPreparingOrRunning() checkContext(context) @@ -446,7 +422,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv this.stateLock.read { when (message.type) { ClientMessageType.ACK -> { - if (this.run.currentTask?.status == TaskStatus.PREPARING) { + if (this.evaluation.currentTask?.status == TaskStatus.PREPARING) { this.readyLatch.setReady(connection) } } @@ -459,45 +435,40 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv } /** - * Processes incoming [Submission]s. If a [InteractiveSynchronousEvaluation.Task] is running then that [Submission] will usually - * be associated with that [InteractiveSynchronousEvaluation.Task]. + * Processes incoming [Submission]s. If a [Task] is running then that [Submission] will usually + * be associated with that [Task]. * * This method will not throw an exception and instead return false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param sub [Submission] that should be registered. + * @param submission [Submission] that should be registered. */ - override fun postSubmission(context: RunActionContext, sub: Submission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { assureTaskRunning() /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") - task.postSubmission(sub) + task.postSubmission(submission) - /** Checks for the presence of the [SimpleOption.PROLONG_ON_SUBMISSION] and applies it. */ - val option = task.template.taskType.options.find { it.option == SimpleOption.PROLONG_ON_SUBMISSION } - if (option != null) { - this.prolongOnSubmit(context, option, sub) + /** Checks for the presence of the [TaskOption.PROLONG_ON_SUBMISSION] and applies it. */ + if (task.template.taskGroup.type.options.filter { it eq TaskOption.PROLONG_ON_SUBMISSION }.any()) { + this.prolongOnSubmit(context, submission) } - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, sub)) + this.scoresUpdatable.enqueue(Pair(task, submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED)) - - return sub.status + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED)) + return submission.verdicts.first().status } /** - * Processes incoming [Submission]s. If a [InteractiveSynchronousEvaluation.Task] is running then that [Submission] will usually - * be associated with that [InteractiveSynchronousEvaluation.Task]. + * Processes incoming [Submission]s. If a [Task] is running then that [Submission] will usually + * be associated with that [Task]. * * This method will not throw an exception and instead return false if a [Submission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke @@ -508,26 +479,19 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * @param submissionStatus The new [VerdictStatus] * @return True on success, false otherwise. */ - override fun updateSubmission( - context: RunActionContext, - submissionId: EvaluationId, - submissionStatus: VerdictStatus - ): Boolean = this.stateLock.read { - /* Sanity check. */ - val found = this.allSubmissions.find { it.uid == submissionId } ?: return false + override fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: VerdictStatus): Boolean = this.stateLock.read { + val verdict = Verdict.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false + val task = this.taskForId(context, verdict.task.id) ?: return false /* Actual update - currently, only status update is allowed */ - if (found.status != submissionStatus) { - found.status = submissionStatus - - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true + if (verdict.status != submissionStatus) { + verdict.status = submissionStatus /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(found.task!!, found)) + this.scoresUpdatable.enqueue(Pair(task, verdict.submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_UPDATED)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId!!) return true } @@ -574,7 +538,7 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv // oh shit, something went horribly, horribly wrong if (errorCounter >= maxErrorCount) { LOGGER.error("Reached maximum consecutive error count, terminating loop") - RunExecutor.dump(this.run) + RunExecutor.dump(this.evaluation) break //terminate loop } } @@ -612,40 +576,34 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv */ private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ - if (this.run.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { + if (this.evaluation.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { - this.run.currentTask!!.start() + this.evaluation.currentTask!!.start() //this.status = RunManagerStatus.RUNNING_TASK - AuditLogger.taskStart(this.id, this.run.currentTask!!.uid, this.run.currentTaskTemplate, AuditLogSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, AuditLogSource.INTERNAL, null) } - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_START)) } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ - if (this.run.currentTask?.status == TaskStatus.RUNNING) { - val task = this.run.currentTask!! + if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { + val task = this.evaluation.currentTask!! val timeLeft = max( 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION + task.duration * 1000L - (System.currentTimeMillis() - task.started) + InteractiveRunManager.COUNTDOWN_DURATION ) if (timeLeft <= 0) { this.stateLock.write { task.end() //this.status = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, this.run.currentTask!!.uid, AuditLogSource.INTERNAL, null) - EventStreamProcessor.event(TaskEndEvent(this.id, task.uid)) + AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, AuditLogSource.INTERNAL, null) + EventStreamProcessor.event(TaskEndEvent(this.id, task.id)) } - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id.string, ServerMessageType.TASK_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END)) } } } @@ -654,11 +612,10 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv * Applies the [SimpleOption.PROLONG_ON_SUBMISSION] [Option]. * * @param context [RunActionContext] used for invocation. - * @param option The [ConfiguredOption] option. * @param sub The [Submission] to apply the [Option] for. */ - private fun prolongOnSubmit(context: RunActionContext, option: ConfiguredOption, sub: Submission) { - require(option.option == SimpleOption.PROLONG_ON_SUBMISSION) { "Cannot process ${option.option} in prolongOnSubmit()." } + private fun prolongOnSubmit(context: RunActionContext, sub: Submission) { + /* require(option.option == SimpleOption.PROLONG_ON_SUBMISSION) { "Cannot process ${option.option} in prolongOnSubmit()." } val limit = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT val prolongBy = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_PARAM) @@ -671,7 +628,8 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv val timeLeft = Math.floorDiv(this.timeLeft(context), 1000) if (timeLeft in 0 until limit) { this.adjustDuration(context, prolongBy) - } + } */ + TODO("Fetch information from database and prolong.") } /** @@ -684,15 +642,15 @@ class InteractiveSynchronousRunManager(private val run: InteractiveSynchronousEv } private fun assureTaskRunning() { - if (this.run.currentTask?.status != TaskStatus.RUNNING) throw IllegalStateException("Task not running") + if (this.evaluation.currentTask?.status != TaskStatus.RUNNING) throw IllegalStateException("Task not running") } private fun assureTaskPreparingOrRunning() { - val status = this.run.currentTask?.status + val status = this.evaluation.currentTask?.status if (status != TaskStatus.RUNNING && status != TaskStatus.PREPARING) throw IllegalStateException("Task not preparing or running") } private fun assureNoRunningTask() { - if (this.run.tasks.any { it.status == TaskStatus.RUNNING }) throw IllegalStateException("Task running!") + if (this.evaluation.tasks.any { it.status == TaskStatus.RUNNING }) throw IllegalStateException("Task running!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 47caa1bea..b08e82f9c 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -5,61 +5,62 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.data.model.run.* import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.TeamId import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable import dev.dres.run.validation.interfaces.JudgementValidator +import jetbrains.exodus.database.TransientEntityStore import org.slf4j.LoggerFactory import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read -class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { +class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation, private val store: TransientEntityStore) : RunManager { private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable private val LOGGER = LoggerFactory.getLogger(this.javaClass) + /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ override val runProperties: RunProperties - get() = run.properties + get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() /** Run ID of this [InteractiveSynchronousRunManager]. */ - override val id: EvaluationId - get() = this.run.id + override val id: TaskId + get() = this.evaluation.id /** Name of this [InteractiveSynchronousRunManager]. */ override val name: String - get() = this.run.name + get() = this.evaluation.name /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ override val template: EvaluationTemplate - get() = this.run.description + get() = this.evaluation.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.run) //TODO requires some changes + private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.evaluation) //TODO requires some changes override val scoreboards: List get() = this.scoreboardsUpdatable.scoreboards @Volatile - override var status: RunManagerStatus = if (this.run.hasStarted) { + override var status: RunManagerStatus = if (this.evaluation.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED } - get() = this.stateLock.read { - return field - } private set /** */ override val judgementValidators: List - get() = this.run.tasks.map { it.validator }.filterIsInstance(JudgementValidator::class.java) + get() = this.evaluation.tasks.map { it.validator }.filterIsInstance(JudgementValidator::class.java) override fun start(context: RunActionContext) { check(this.status == RunManagerStatus.CREATED) { "NonInteractiveRunManager is in status ${this.status} and cannot be started." } @@ -67,15 +68,11 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { throw IllegalAccessError("functionality of NonInteractiveRunManager only available to administrators") /* Start the run. */ - this.run.start() + this.evaluation.start() /* Update status. */ this.status = RunManagerStatus.ACTIVE - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - - LOGGER.info("NonInteractiveRunManager ${this.id} started") } @@ -85,18 +82,19 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { throw IllegalAccessError("functionality of NonInteractiveRunManager only available to administrators") /* End the run. */ - this.run.end() + this.evaluation.end() /* Update status. */ this.status = RunManagerStatus.TERMINATED - /* Mark DAO for update. */ - this.daoUpdatable.dirty = true - LOGGER.info("SynchronousRunManager ${this.id} terminated") } - override fun taskCount(context: RunActionContext): Int = this.run.tasks.size + override fun updateProperties(properties: RunProperties) { + TODO("Not yet implemented") + } + + override fun taskCount(context: RunActionContext): Int = this.evaluation.tasks.size private val viewerMap: MutableMap = mutableMapOf() @@ -118,8 +116,6 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { try { this.stateLock.read { - - while (updatedTasks.isNotEmpty()) { val idNamePair = updatedTasks.poll(1, TimeUnit.SECONDS) @@ -128,14 +124,16 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { break } - val task = this.run.tasks.find { it.uid == idNamePair.first } + val task = this.evaluation.tasks.find { it.id == idNamePair.first } if (task == null) { LOGGER.error("Unable to retrieve task with changed id ${idNamePair.first}") break } - val batches = idNamePair.second.mapNotNull { task.submissions[it] } + + /* TODO: Redo. */ + /*val batches = idNamePair.second.mapNotNull { task.submissions[it] } val validator = task.validator val scorer = task.scorer @@ -143,17 +141,9 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { batches.forEach { validator.validate(it) scorer.computeScores(it) - } - + }*/ scoreboardsUpdatable.update(this.status) - - this.daoUpdatable.dirty = true - } - - this.daoUpdatable.update(this.status) - - } } catch (ie: InterruptedException) { LOGGER.info("Interrupted NonInteractiveRunManager, exiting") @@ -165,10 +155,6 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { } - this.stateLock.read { - this.daoUpdatable.update(this.status) - } - LOGGER.info("NonInteractiveRunManager ${this.id} reached end of run logic.") } @@ -178,5 +164,8 @@ class NonInteractiveRunManager(val run: NonInteractiveEvaluation) : RunManager { /** * */ - override fun tasks(context: RunActionContext): List = this.run.tasks + override fun tasks(context: RunActionContext): List = this.evaluation.tasks + override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 6a33fa401..6b1eb23f8 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -84,12 +84,13 @@ object RunExecutor : Consumer { * Schedules a new [EvaluationRun] with this [RunExecutor]. * * @param evaluation [EvaluationRun] to execute. + * @param store [TransientEntityStore] instanced used to interact with database. */ - fun schedule(evaluation: EvaluationRun) { + fun schedule(evaluation: EvaluationRun, store: TransientEntityStore) { val run = when(evaluation) { - is InteractiveSynchronousEvaluation -> InteractiveSynchronousRunManager(evaluation) - is InteractiveAsynchronousEvaluation -> InteractiveAsynchronousRunManager(evaluation) - is NonInteractiveEvaluation -> NonInteractiveRunManager(evaluation) + is InteractiveSynchronousEvaluation -> InteractiveSynchronousRunManager(evaluation, store) + is InteractiveAsynchronousEvaluation -> InteractiveAsynchronousRunManager(evaluation, store) + is NonInteractiveEvaluation -> NonInteractiveRunManager(evaluation, store) else -> throw NotImplementedError("No matching run manager found for $evaluation") } this.schedule(run) diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 44356a477..f125eea29 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -2,11 +2,9 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.run.InteractiveSynchronousEvaluation -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.RunProperties import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus @@ -28,6 +26,9 @@ interface RunManager : Runnable { /** A name for identifying this [RunManager]. */ val name: String + /** The [Evaluation] instance that backs this [RunManager]. */ + val evaluation: EvaluationRun + /** The [EvaluationTemplate] that is executed / run by this [RunManager]. */ val template: EvaluationTemplate @@ -75,10 +76,10 @@ interface RunManager : Runnable { fun updateProperties(properties: RunProperties) /** - * Returns the number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager]. + * Returns the number of [Task]s held by this [RunManager]. * * @param context The [RunActionContext] for this invocation. - * @return The number of [InteractiveSynchronousEvaluation.Task]s held by this [RunManager] + * @return The number of [Task]s held by this [RunManager] */ fun taskCount(context: RunActionContext): Int @@ -101,12 +102,12 @@ interface RunManager : Runnable { * * * @param context The [RunActionContext] used for the invocation - * @param sub The [Submission] to be posted. + * @param submission The [Submission] to be posted. * * @return [VerdictStatus] of the [Submission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(context: RunActionContext, sub: Submission): VerdictStatus + fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 9e48a8dbc..fbbd84547 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -6,7 +6,6 @@ import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.audit.AuditLogType import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.EvaluationTemplate import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.submissions.Submission @@ -79,7 +78,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(evaluationId: EvaluationId, taskId: TaskId, description: TaskTemplate, api: AuditLogSource, session: SessionId?) { + fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: TaskTemplate, api: AuditLogSource, session: SessionId?) { AuditLogEntry.new { this.type = AuditLogType.TASK_START this.source = api @@ -100,7 +99,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskModified(evaluationId: EvaluationId, taskId: TaskId, modification: String, api: AuditLogSource, session: String?) { + fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: AuditLogSource, session: String?) { AuditLogEntry.new { this.type = AuditLogType.TASK_MODIFIED this.source = api @@ -120,7 +119,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(evaluationId: EvaluationId, taskId: TaskId, api: AuditLogSource, session: SessionId?) { + fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionId?) { AuditLogEntry.new { this.type = AuditLogType.TASK_END this.source = api diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 4bb447b69..6090f3da2 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -1,8 +1,11 @@ package dev.dres.run.eventstream.handlers +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.eventstream.* +import kotlinx.dnq.query.first +import kotlinx.dnq.query.size import org.slf4j.LoggerFactory import java.io.File import java.io.PrintWriter @@ -51,26 +54,31 @@ class SubmissionStatisticsHandler : StreamEventHandler { } } + /** + * TODO: Check and maybe generalise. + * + * I assume here, that there this handler requires a single verdict per submission. Is this a valid assumption? + */ private fun computeStatistics(submissions: List, taskStart: Long, task: String) { - - val submissionsByTeam = submissions.groupBy { it.teamId } - + val submissionsByTeam = submissions.groupBy { it.team.teamId } submissionsByTeam.mapValues { it.value.size }.forEach{ - (teamId, count) -> writer.println("$task,${teamId.string},\"totalSubmissionsPerTeam\",$count") + (teamId, count) -> writer.println("$task,${teamId},\"totalSubmissionsPerTeam\",$count") } submissionsByTeam.mapValues { - it.value.firstOrNull { s -> s.status == VerdictStatus.CORRECT }?.timestamp?.minus(taskStart) } - .filter { it.value != null }.forEach{ - (teamId, time) -> writer.println("$task,${teamId.string},\"timeUntilCorrectSubmission\",$time") - } + it.value.firstOrNull { s -> + require(s.verdicts.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } + s.verdicts.first().status == VerdictStatus.CORRECT + }?.timestamp?.minus(taskStart) }.filter { it.value != null }.forEach{ + (teamId, time) -> writer.println("$task,${teamId},\"timeUntilCorrectSubmission\",$time") + } submissionsByTeam.mapValues { - it.value.indexOfFirst { s -> s.status == VerdictStatus.CORRECT } }.forEach{ - (teamId, count) -> writer.println("$task,${teamId.string},\"incorrectBeforeCorrectSubmissions\",$count") + it.value.indexOfFirst { s -> + require(s.verdicts.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } + s.verdicts.first().status == VerdictStatus.CORRECT + } + }.forEach{ + (teamId, count) -> writer.println("$task,${teamId},\"incorrectBeforeCorrectSubmissions\",$count") } writer.flush() - - } - - } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index e4f013c4b..7235242b6 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -4,9 +4,6 @@ import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* -import dev.dres.run.score.TaskContext -import dev.dres.run.score.interfaces.IncrementalSubmissionTaskScorer -import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.TeamTaskScorer import java.io.File import java.io.PrintWriter @@ -67,7 +64,8 @@ class TeamCombinationScoreHandler : StreamEventHandler { teams.mapIndexed {secondIndex, uidB -> if (firstIndex > secondIndex) (uidA to uidB) else null} }.flatten().filterNotNull().map { EvaluationId() to it }.toMap() - val combinedSubmissions = submissions.flatMap { submission -> + /* TODO: Fix. Not quite sure what is going on here. */ + /*val combinedSubmissions = submissions.flatMap { submission -> combinations.map { if (it.value.first == submission.team.teamId || it.value.second == submission.team.teamId) { when (submission) { @@ -109,7 +107,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { tasks.remove(event.taskId) submissionTaskMap.remove(event.taskId) - taskStartMap.remove(event.taskId) + taskStartMap.remove(event.taskId) */ } else -> { /* ignore */ } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index 7159968f2..83642faea 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -1,12 +1,22 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.size class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) override val reason = "Maximum total number of submissions ($max) exceeded for the team" - override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId }.size < max + /** + * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? + */ + override fun test(submission: Submission): Boolean { + return submission.verdicts.asSequence().all { v -> + v.task.submissions.filter { it.submission.team.id eq submission.team.id }.size() < max + } + } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index ac439e10d..ac8f153f0 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -2,6 +2,11 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus +import kotlinx.dnq.query.FilteringContext.eq +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.size /** * @@ -12,6 +17,12 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi constructor(parameters: Map) : this(parameters.getOrDefault("limit", "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) override val reason = "Maximum number of wrong submissions ($max) exceeded for the team" - - override fun test(submission: Submission): Boolean = submission.task!!.submissions.filter { it.teamId == submission.teamId && it.status == VerdictStatus.WRONG }.size < max + /** + * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? + */ + override fun test(submission: Submission): Boolean { + return submission.verdicts.asSequence().all { v -> + v.task.submissions.filter { (it.submission.team.id eq submission.team.id) and (it.status eq VerdictStatus.WRONG) }.size() < max + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt index c5670d313..8d26ee097 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt @@ -9,7 +9,8 @@ class SubmissionRateFilter(private val minDelayMS: Int = 500) : SubmissionFilter override val reason = "Not enough time has passed since last submission, gap needs to be at least $minDelayMS ms" override fun test(t: Submission): Boolean { - val mostRecentSubmissionTime = t.task!!.submissions.maxByOrNull { it.timestamp }?.timestamp ?: 0 - return (t.timestamp - mostRecentSubmissionTime) >= minDelayMS + TODO("Not sure about the semantic here. Do we need a data model extension for this?") + /*val mostRecentSubmissionTime = t.task!!.submissions.maxByOrNull { it.timestamp }?.timestamp ?: 0 + return (t.timestamp - mostRecentSubmissionTime) >= minDelayMS*/ } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt index 1105d6d8b..8f1179af7 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt @@ -1,9 +1,9 @@ package dev.dres.run.score -import dev.dres.data.model.run.TaskId +import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.team.TeamId /** * */ -data class TaskContext(val taskId: TaskId, val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) +data class TaskContext(val taskId: EvaluationId, val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index bb38154b4..0c312eff0 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -3,12 +3,15 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.media.MediaType import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.Verdict import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.ScoreEntry import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.utilities.TimeUtil +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.toList import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -17,46 +20,40 @@ import kotlin.concurrent.write * A [TeamTaskScorer] used for AVS tasks. * * @author Luca Rossetto - * @version 1.0.1 + * @version 1.1.0 */ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() + /** + * TODO: Check for correctness especially if a submission has more than one verdict. Maybe add sanity checks. + */ override fun computeScores(submissions: Collection, context: TaskContext): Map { - val correctSubmissions = submissions.filter { it.status == VerdictStatus.CORRECT } - val wrongSubmissions = submissions.filter { it.status == VerdictStatus.WRONG } - - val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.teamId } - val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.teamId } - + val correctSubmissions = submissions.flatMap {s -> s.verdicts.filter { v -> v.status eq VerdictStatus.CORRECT }.toList() } + val wrongSubmissions = submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq VerdictStatus.WRONG }.toList() } + val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.team.id } + val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.team.id } val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() - - lastScores = this.lastScoresLock.write { + this.lastScores = this.lastScoresLock.write { context.teamIds.map { teamid -> - val correctSubs = correctSubmissionsPerTeam[teamid] ?: return@map teamid to 0.0 - val correct = correctSubs.size - val wrong = wrongSubmissionsPerTeam[teamid]?.size ?: 0 - - teamid to 100.0 * - (correct / (correct + wrong / 2.0)) * - (countQuantized(correctSubs).toDouble() / totalCorrectQuantized) + teamid to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs).toDouble() / totalCorrectQuantized) }.toMap() } return teamScoreMap() } - private fun countQuantized(submissions: Collection): Int = submissions + private fun countQuantized(submissions: Collection): Int = submissions .filter { it.item != null } .groupBy { it.item }.map { when(it.key!!.type) { MediaType.IMAGE -> 1 MediaType.VIDEO -> { - val ranges = it.value.map { s -> s.temporalRange } + val ranges = it.value.map { s -> s.temporalRange!! } TimeUtil.merge(ranges, overlap = 1).size } else -> throw IllegalStateException("Unsupported media type ${it.key!!.type} for AVS task scorer.") diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 87eec8faf..54c46205b 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -32,14 +32,14 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte if (taskRun.template.taskGroup.type.submission.contains(SubmissionOption.LIMIT_CORRECT_PER_TEAM)) { val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq SubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 if (this.run.timeLeft(this.context) > 0) { - if (this.submissions.getAndSet(taskRun.submissions.size) < taskRun.submissions.size) { + if (this.submissions.getAndSet(taskRun.getSubmissions().size) < taskRun.getSubmissions().size) { val allDone = if (this.isAsync) { - val numberOfSubmissions = this.run.submissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } + val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } numberOfSubmissions >= limit } else { /* Determine of all teams have submitted . */ this.run.template.teams.asSequence().all { team -> - val numberOfSubmissions = this.run.submissions(this.context).count { it.team.id == team.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } + val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == team.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } numberOfSubmissions >= limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 935a6dc04..f23f6a623 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -49,13 +49,11 @@ class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreb /* Update scorers. */ scorersToUpdate.forEach { val task = it.first - if (it.first.started != null) { - val scores = it.second.computeScores( - task.submissions, - TaskContext(task.id, task.competition.description.teams.asSequence().map { t -> t.id }.toList(), task.started, task.template.duration, task.ended) - ) - it.first.updateTeamAggregation(scores) - } + val scores = it.second.computeScores( + task.getSubmissions(), + TaskContext(task.id, task.competition.description.teams.asSequence().map { t -> t.id }.toList(), task.started, task.template.duration, task.ended) + ) + it.first.updateTeamAggregation(scores) } /* If elements were removed, then update scoreboards and tasks. */ From 601234f430d1f5a9a722d04ac080cb1246532947 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 15:35:33 +0100 Subject: [PATCH 034/498] Removed double instances of the same handler. --- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 84739a166..59163baf5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -103,7 +103,6 @@ object RestApi { AddMediaItemHandler(store), UpdateMediaItemHandler(store), DeleteMediaItemHandler(store), - ShowCollectionHandler(store), RandomMediaItemHandler(store), // Must be before ListMediaItem ResolveMediaItemListByNameHandler(store), // Must be before ListMediaItem ListMediaItemHandler(store), @@ -229,15 +228,15 @@ object RestApi { it.jetty.server { setupHttpServer() } it.jetty.sessionHandler { fileSessionHandler(config) } it.accessManager(AccessManager::manage) - it.staticFiles.add("html", Location.CLASSPATH) - it.spaRoot.addFile("/vote", "vote/index.html") - it.spaRoot.addFile("/", "html/index.html") + //it.staticFiles.add("html", Location.CLASSPATH) + //it.spaRoot.addFile("/vote", "vote/index.html") + //it.spaRoot.addFile("/", "html/index.html") if (config.enableSsl) { val ssl = SSLPlugin { conf -> conf.keystoreFromPath(config.keystorePath, config.keystorePassword) conf.http2 = true - conf.secure = true + conf.secure = false conf.insecurePort = config.httpPort conf.securePort = config.httpsPort conf.sniHostCheck = false @@ -294,8 +293,7 @@ object RestApi { }.exception(Exception::class.java) { e, ctx -> ctx.status(500).json(ErrorStatus("Internal server error: ${e.localizedMessage}")) logger.error("Exception during handling of request to ${ctx.path()}", e) - } - .start(config.httpPort) + }.start(config.httpPort) } fun stop() { From eb35988f9f84cc0a2ce4066edb9bd719a7096d60 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 15:36:07 +0100 Subject: [PATCH 035/498] Changed conversion mechanism between ApiRole / ApiMediaType and Role and MediaType. --- .../dev/dres/api/cli/MediaCollectionCommand.kt | 4 ++-- .../handler/collection/AddMediaItemHandler.kt | 2 +- .../collection/UpdateMediaItemHandler.kt | 2 +- .../evaluation/admin/StartTaskHandler.kt | 2 +- .../rest/handler/users/CreateUsersHandler.kt | 2 +- .../api/rest/types/collection/ApiMediaType.kt | 17 +++++++++++++---- .../dev/dres/api/rest/types/users/ApiRole.kt | 8 ++++++-- .../kotlin/dev/dres/data/model/admin/Role.kt | 2 +- .../dev/dres/data/model/media/MediaType.kt | 2 +- .../dev/dres/data/model/run/RunActionContext.kt | 2 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 2 +- 11 files changed, 29 insertions(+), 16 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 8b22ab738..6216b9bde 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -462,7 +462,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /* Add new media item. */ collection.items.add(MediaItem.new { this.id = UUID.randomUUID().toString() - this.type = this@AddItem.type.type + this.type = this@AddItem.type.toMediaType() this.name = Paths.get(this@AddItem.path).nameWithoutExtension this.location = this@AddItem.path this.durationMs = this@AddItem.duration @@ -536,7 +536,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inserted += 1 collection.items.add(MediaItem.new { this.id = UUID.randomUUID().toString() - this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).type + this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).toMediaType() this.name = row.getValue("name") this.location = row.getValue("location") this.durationMs = row["duration"]?.toLongOrNull() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 1208e2e6f..6e052639f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -57,7 +57,7 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl val item = MediaItem.new { this.id = UUID.randomUUID().toString() - this.type = mediaItem.type.type + this.type = mediaItem.type.toMediaType() this.name = mediaItem.name this.location = mediaItem.location this.fps = mediaItem.fps diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 36ef1ccc6..88865b7e0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -48,7 +48,7 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa val item = MediaItem.query(MediaItem::id eq mediaItem.id).firstOrNull() ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.id} not found.", ctx) - item.type = mediaItem.type.type + item.type = mediaItem.type.toMediaType() item.name = item.name item.location = item.location item.fps = item.fps diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index deab10d17..0840161fc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -26,7 +26,7 @@ import jetbrains.exodus.database.TransientEntityStore */ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { - override val route: String = "evaluation/admin/{evaluationId}/task/next" + override val route: String = "evaluation/admin/{evaluationId}/task/start" /** The [StartTaskHandler] can be used by [ApiRole.ADMIN] and [ApiRole.PARTICIPANT] (in case of asynchronous evaluations). */ override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index eb04d5de0..8a4e3cca9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -51,7 +51,7 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, Acce if (req.role == null) throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) - val success = UserManager.create(req.username, Password.Plain(req.password), req.role.getRole() ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) + val success = UserManager.create(req.username, Password.Plain(req.password), req.role.toRole() ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) if (success) { return UserManager.get(username = req.username)!!.toApi() } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt index 9c91b2adb..584086520 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -9,8 +9,17 @@ import dev.dres.data.model.media.MediaType * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiMediaType(val type: MediaType) { - IMAGE(MediaType.IMAGE), - VIDEO(MediaType.VIDEO), - TEXT(MediaType.TEXT) +enum class ApiMediaType { + IMAGE, VIDEO, TEXT; + + /** + * Converts this [ApiMediaType] to a [MediaType] representation. Requires an ongoing transaction! + * + * @return [MediaType] + */ + fun toMediaType(): MediaType = when(this) { + IMAGE -> MediaType.IMAGE + VIDEO -> MediaType.VIDEO + TEXT -> MediaType.TEXT + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt index 273f90060..5fb8ea15e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt @@ -1,6 +1,8 @@ package dev.dres.api.rest.types.users +import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.data.model.admin.Role +import dev.dres.data.model.media.MediaType import io.javalin.security.RouteRole /** @@ -9,13 +11,15 @@ import io.javalin.security.RouteRole * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiRole() : RouteRole { +enum class ApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN; /** + * Converts this [ApiMediaType] to a [MediaType] representation. Requires an ongoing transaction. * + * @return [MediaType] */ - fun getRole(): Role? = when(this) { + fun toRole(): Role? = when(this) { ANYONE -> null VIEWER -> Role.VIEWER PARTICIPANT -> Role.PARTICIPANT diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 8cd3f4653..11ed592dc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -47,5 +47,5 @@ class Role(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiRole] */ - fun toApi(): ApiRole = ApiRole.values().find { it.getRole() == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") + fun toApi(): ApiRole = ApiRole.values().find { it.toRole() == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt index cb5feeead..637bc1795 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt @@ -31,5 +31,5 @@ class MediaType(entity: Entity) : XdEnumEntity(entity) { * This is a convenience method and requires an active transaction context. */ fun toApi(): ApiMediaType - = ApiMediaType.values().find { it.type == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") + = ApiMediaType.values().find { it.toMediaType() == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 8a1425b02..8d7e94833 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -39,7 +39,7 @@ data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionId()).mapNotNull { it.getRole() }.toSet() + val roles = AccessManager.rolesOfSession(ctx.sessionId()).mapNotNull { it.toRole() }.toSet() val teamId = runManager.template.teams.filter { it.users.contains(User.query(User::id eq userId).first()) }.first().teamId return RunActionContext(userId, teamId, roles) } diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 4689ccfaf..3011bdc22 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -97,7 +97,7 @@ object UserManager { * @return True on success, false otherwise. */ fun update(id: UserId?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.getRole()) + = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.toRole()) /** * Deletes the [User] for the given [UserId]. From 717ff6061ffdacbc0673b6104fc9125ed8aec7ac Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 15:40:09 +0100 Subject: [PATCH 036/498] Removed conflicting parameter names. --- .../main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 6216b9bde..cc4077206 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -509,7 +509,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class Import : AbstractCollectionCommand("import", help = "Imports a media collection from a CSV file.") { /** [Path] to the input file. */ - private val input: Path by option("-i", "--input", help = "Path of the file the media collection should be imported from.") + private val input: Path by option("--input", help = "Path of the file the media collection should be imported from.") .convert { Paths.get(it) }.required() override fun run() { @@ -559,7 +559,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class ImportSegments : AbstractCollectionCommand("importSegments", "Imports the Segment information for the Items in a Collection from a CSV file") { /** [Path] to the input file. */ - private val input: Path by option("-i", "--input", help = "Path of the file the media segments should be imported from.") + private val input: Path by option("--input", help = "Path of the file the media segments should be imported from.") .convert { Paths.get(it) }.required() override fun run() { From 3cf0537672172c17bdaf42ff0040e91c5c090e71 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 15:41:11 +0100 Subject: [PATCH 037/498] Reverted uncommenting of lines 231-233 --- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 59163baf5..24a32df41 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -228,9 +228,9 @@ object RestApi { it.jetty.server { setupHttpServer() } it.jetty.sessionHandler { fileSessionHandler(config) } it.accessManager(AccessManager::manage) - //it.staticFiles.add("html", Location.CLASSPATH) - //it.spaRoot.addFile("/vote", "vote/index.html") - //it.spaRoot.addFile("/", "html/index.html") + it.staticFiles.add("html", Location.CLASSPATH) + it.spaRoot.addFile("/vote", "vote/index.html") + it.spaRoot.addFile("/", "html/index.html") if (config.enableSsl) { val ssl = SSLPlugin { conf -> From 45c5ff064b02df9421e5812675cb166773505f13 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 15:49:42 +0100 Subject: [PATCH 038/498] Moved the entire API to v2. --- .../dres/api/rest/handler/audit/AbstractAuditLogHandler.kt | 2 +- .../dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt | 2 +- .../dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt | 2 +- .../api/rest/handler/audit/ListAuditLogsInRangeHandler.kt | 2 +- .../api/rest/handler/collection/AbstractCollectionHandler.kt | 2 +- .../dres/api/rest/handler/collection/AddCollectionHandler.kt | 2 +- .../dres/api/rest/handler/collection/AddMediaItemHandler.kt | 2 +- .../api/rest/handler/collection/DeleteCollectionHandler.kt | 2 +- .../api/rest/handler/collection/DeleteMediaItemHandler.kt | 2 +- .../dres/api/rest/handler/collection/ListCollectionHandler.kt | 2 +- .../api/rest/handler/collection/ListExternalItemHandler.kt | 4 ++-- .../dres/api/rest/handler/collection/ListMediaItemHandler.kt | 2 +- .../api/rest/handler/collection/RandomMediaItemHandler.kt | 2 +- .../handler/collection/ResolveMediaItemListByNameHandler.kt | 2 +- .../dres/api/rest/handler/collection/ShowCollectionHandler.kt | 2 +- .../dres/api/rest/handler/collection/ShowMediaItemHandler.kt | 2 +- .../api/rest/handler/collection/UpdateCollectionHandler.kt | 2 +- .../api/rest/handler/collection/UpdateMediaItemHandler.kt | 2 +- .../dres/api/rest/handler/download/AbstractDownloadHandler.kt | 2 +- .../api/rest/handler/download/EvaluationDownloadHandler.kt | 2 +- .../handler/download/EvaluationTemplateDownloadHandler.kt | 2 +- .../dres/api/rest/handler/download/ScoreDownloadHandler.kt | 2 +- .../evaluation/admin/AbstractEvaluationAdminHandler.kt | 2 +- .../rest/handler/evaluation/admin/AdjustDurationHandler.kt | 2 +- .../rest/handler/evaluation/admin/AdjustPropertiesHandler.kt | 2 +- .../rest/handler/evaluation/admin/CreateEvaluationHandler.kt | 2 +- .../handler/evaluation/admin/EvaluationOverviewHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/ForceViewerHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/ListPastTaskHandler.kt | 2 +- .../rest/handler/evaluation/admin/ListSubmissionsHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/ListViewersHandler.kt | 2 +- .../dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt | 2 +- .../handler/evaluation/admin/OverrideSubmissionHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/PreviousTaskHandler.kt | 2 +- .../rest/handler/evaluation/admin/StartEvaluationHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/StartTaskHandler.kt | 2 +- .../rest/handler/evaluation/admin/StopEvaluationHandler.kt | 2 +- .../dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/SwitchTaskHandler.kt | 2 +- .../evaluation/client/AbstractEvaluationClientHandler.kt | 2 +- .../handler/evaluation/client/ClientListEvaluationsHandler.kt | 2 +- .../rest/handler/evaluation/client/ClientTaskInfoHandler.kt | 2 +- .../rest/handler/evaluation/scores/AbstractScoreHandler.kt | 2 +- .../rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt | 2 +- .../rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt | 2 +- .../handler/evaluation/scores/ListCompetitionScoreHandler.kt | 2 +- .../rest/handler/evaluation/scores/ListScoreSeriesHandler.kt | 2 +- .../rest/handler/evaluation/scores/ListScoreboardsHandler.kt | 2 +- .../rest/handler/evaluation/scores/TeamGroupScoreHandler.kt | 2 +- .../evaluation/viewer/AbstractEvaluationViewerHandler.kt | 2 +- .../rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt | 2 +- .../handler/evaluation/viewer/GetEvaluationInfoHandler.kt | 2 +- .../handler/evaluation/viewer/GetEvaluationStateHandler.kt | 2 +- .../evaluation/viewer/GetSubmissionAfterInfoHandler.kt | 2 +- .../evaluation/viewer/GetSubmissionHistoryInfoHandler.kt | 2 +- .../handler/evaluation/viewer/GetSubmissionInfoHandler.kt | 2 +- .../api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt | 2 +- .../rest/handler/evaluation/viewer/GetTaskTargetHandler.kt | 2 +- .../handler/evaluation/viewer/ListEvaluationInfoHandler.kt | 2 +- .../handler/evaluation/viewer/ListEvaluationStatesHandler.kt | 2 +- .../api/rest/handler/judgement/AbstractJudgementHandler.kt | 2 +- .../api/rest/handler/judgement/DequeueJudgementHandler.kt | 2 +- .../dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt | 2 +- .../dres/api/rest/handler/judgement/JudgementStatusHandler.kt | 4 ++-- .../dres/api/rest/handler/judgement/PostJudgementHandler.kt | 2 +- .../dev/dres/api/rest/handler/judgement/PostVoteHandler.kt | 4 ++-- .../dev/dres/api/rest/handler/log/AbstractLogHandler.kt | 2 +- .../kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt | 2 +- .../kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt | 2 +- .../dres/api/rest/handler/preview/AbstractPreviewHandler.kt | 2 +- .../dev/dres/api/rest/handler/preview/GetMediaHandler.kt | 4 ++-- .../dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt | 2 +- .../dres/api/rest/handler/preview/SubmissionPreviewHandler.kt | 2 +- .../api/rest/handler/submission/BatchSubmissionHandler.kt | 4 ++-- .../dev/dres/api/rest/handler/submission/SubmissionHandler.kt | 4 ++-- .../dev/dres/api/rest/handler/system/CurrentTimeHandler.kt | 4 ++-- .../kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt | 4 ++-- .../kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt | 4 ++-- .../kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt | 4 ++-- .../handler/template/AbstractCompetitionDescriptionHandler.kt | 2 +- .../rest/handler/template/CreateEvaluationTemplateHandler.kt | 2 +- .../rest/handler/template/DeleteEvaluationTemplateHandler.kt | 2 +- .../dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt | 4 ++-- .../rest/handler/template/ListEvaluationTemplatesHandler.kt | 2 +- .../dev/dres/api/rest/handler/template/ListTasksHandler.kt | 2 +- .../dev/dres/api/rest/handler/template/ListTeamHandler.kt | 2 +- .../rest/handler/template/ShowEvaluationTemplateHandler.kt | 2 +- .../api/rest/handler/template/UpdateCompetitionHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/AbstractUserHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/CreateUsersHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/DeleteUsersHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt | 4 ++-- .../dev/dres/api/rest/handler/users/ListUsersHandler.kt | 2 +- .../dres/api/rest/handler/users/ShowCurrentSessionHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/UpdateUsersHandler.kt | 2 +- .../dev/dres/api/rest/handler/users/UserDetailsHandler.kt | 2 +- 97 files changed, 109 insertions(+), 109 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt index 7afa216dc..fcc345f03 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt @@ -17,5 +17,5 @@ abstract class AbstractAuditLogHandler(protected val store: TransientEntityStore override val permittedRoles: Set = setOf(ApiRole.ADMIN) /** All [AbstractCollectionHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt index 578b9fd55..ad23711e9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt @@ -16,7 +16,7 @@ class GetAuditLogInfoHandler(store: TransientEntityStore) : AbstractAuditLogHand @OpenApi( summary = "Gives information about the audit log. Namely size and latest timestamp of known entries.", - path = "/api/v1/audit/info", + path = "/api/v2/audit/info", tags = ["Audit"], responses = [ OpenApiResponse("200", [OpenApiContent(AuditLogInfo::class)], description = "The audit log info."), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index 06734bdb6..534a5d627 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -34,7 +34,7 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle @OpenApi( summary = "Lists all audit logs matching the query.", - path = "/api/v1/audit/log/list/limit/{limit}/{page}", + path = "/api/v2/audit/log/list/limit/{limit}/{page}", pathParams = [ OpenApiParam(LIMIT_PARAM, Int::class, "The maximum number of results. Default: 500"), OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt index 9506d4ed4..b7a0287b5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -23,7 +23,7 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog @OpenApi( summary = "Lists all audit logs matching the query", - path = "/api/v1/audit/log/list/since/{since}/{upto}", + path = "/api/v2/audit/log/list/since/{since}/{upto}", pathParams = [ OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include"), OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt index 99716856c..987b0055d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt @@ -23,7 +23,7 @@ abstract class AbstractCollectionHandler(protected val store: TransientEntitySto override val permittedRoles: Set = setOf(ApiRole.ADMIN) /** All [AbstractCollectionHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** Convenience method to extract [MediaCollection] from [Context]. */ protected fun collectionFromContext(ctx: Context): MediaCollection { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index e5150c88c..ec17e0502 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -24,7 +24,7 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand @OpenApi( summary = "Adds a new media collection.", - path = "/api/v1/collection", + path = "/api/v2/collection", tags = ["Collection"], methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 6e052639f..584252b86 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -26,7 +26,7 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl @OpenApi( summary = "Adds a media item to the specified media collection.", - path = "/api/v1/mediaItem", + path = "/api/v2/mediaItem", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaItem::class)]), tags = ["Collection"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt index 9487041b7..1aa707d58 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt @@ -16,7 +16,7 @@ class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionH @OpenApi( summary = "Deletes a media collection identified by a collection id.", - path = "/api/v1/collection/{collectionId}", + path = "/api/v2/collection/{collectionId}", pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], tags = ["Collection"], methods = [HttpMethod.DELETE], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt index 1f2ae9aeb..651670649 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -23,7 +23,7 @@ class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa @OpenApi( summary = "Tries to delete a specific media item.", - path = "/api/v1/mediaItem/{mediaId}", + path = "/api/v2/mediaItem/{mediaId}", methods = [HttpMethod.DELETE], pathParams = [ OpenApiParam("mediaId", String::class, "Media item ID") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt index 5493bd32e..162cab3e4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -23,7 +23,7 @@ class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan @OpenApi( summary = "Lists all available media collections with basic information about their content.", - path = "/api/v1/collection/list", + path = "/api/v2/collection/list", tags = ["Collection"], methods = [HttpMethod.GET], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index 6f428d61a..2357112b2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -20,7 +20,7 @@ import kotlin.streams.toList */ class ListExternalItemHandler(config: Config) : GetRestHandler> { - override val apiVersion = "v1" + override val apiVersion = "v2" /** Path to the directory that contains the external items. */ val path = Paths.get(config.externalPath) @@ -34,7 +34,7 @@ class ListExternalItemHandler(config: Config) : GetRestHandler> { @OpenApi( summary = "Lists items from the external media collection whose name start with the given string.", - path = "/api/v1/external/", + path = "/api/v2/external/", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("startsWith", String::class, "Name starts with.", required = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt index 781ed1a2d..2b28ae8c7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -22,7 +22,7 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand @OpenApi( summary = "Lists media items from a given media collection whose name start with the given string.", - path = "/api/v1/collection/{collectionId}/{startsWith}", + path = "/api/v2/collection/{collectionId}/{startsWith}", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID"), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt index d2e62b478..a23d575f4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -26,7 +26,7 @@ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa @OpenApi( summary = "Selects and returns a random media item from a given media collection.", - path = "/api/v1/collection/{collectionId}/random", + path = "/api/v2/collection/{collectionId}/random", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt index d3b8bf411..8a3b9d6dd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt @@ -21,7 +21,7 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC @OpenApi( summary = "Resolves a list of media item names to media items", - path = "/api/v1/collection/{collectionId}/resolve", + path = "/api/v2/collection/{collectionId}/resolve", methods = [HttpMethod.POST], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index 740ee833f..434765a82 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -24,7 +24,7 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan @OpenApi( summary = "Shows the content of the specified media collection.", - path = "/api/v1/collection/{collectionId}", + path = "/api/v2/collection/{collectionId}", pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], tags = ["Collection"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt index ff4433950..977c6a96a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt @@ -22,7 +22,7 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand @OpenApi( summary = "Selects and returns a specific media item.", - path = "/api/v1/mediaItem/{mediaId}", + path = "/api/v2/mediaItem/{mediaId}", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("mediaId", String::class, "Media item ID") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt index 087094922..9c09462f6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -26,7 +26,7 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH @OpenApi( summary = "Updates a media collection", - path = "/api/v1/collection", + path = "/api/v2/collection", tags = ["Collection"], methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 88865b7e0..28a949b50 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -25,7 +25,7 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa @OpenApi( summary = "Updates a Media Item to the specified Media Collection.", - path = "/api/v1/mediaitem", + path = "/api/v2/mediaitem", methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaItem::class)]), tags = ["Collection"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt index a570cfa87..3f419573c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt @@ -14,7 +14,7 @@ import jetbrains.exodus.database.TransientEntityStore abstract class AbstractDownloadHandler(protected val store: TransientEntityStore) : AccessManagedRestHandler { /** The version of the API this [AbstractDownloadHandler] belongs to. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** The roles permitted to access the [AbstractDownloadHandler]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index 0e931829a..3f55fff12 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -25,7 +25,7 @@ class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadH @OpenApi( summary = "Provides a JSON download of the entire evaluation structure.", - path = "/api/v1/download/evaluation/{evaluationId}", + path = "/api/v2/download/evaluation/{evaluationId}", tags = ["Download"], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID.", required = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index 16505c9b2..934553660 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -26,7 +26,7 @@ class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractD @OpenApi( summary = "Provides a JSON download of the entire evaluation template structure.", - path = "/api/v1/download/template/{templateId}", + path = "/api/v2/download/template/{templateId}", tags = ["Download"], pathParams = [ OpenApiParam("templateId", String::class, "The evaluation template ID", required = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index 6ff5d435c..02ac2d648 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -22,7 +22,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle @OpenApi( summary = "Provides a CSV download with the scores for a given evaluation.", - path = "/api/v1/download/evaluation/{evaluationId}/scores", + path = "/api/v2/download/evaluation/{evaluationId}/scores", tags = ["Download"], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID.", required = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt index be5ec601d..e029d7ec6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt @@ -25,7 +25,7 @@ abstract class AbstractEvaluationAdminHandler(protected val store: TransientEnti override val permittedRoles: Set = setOf(ApiRole.ADMIN) /** All [AbstractEvaluationAdminHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** * Obtains the [InteractiveRunManager] for the given [EvaluationId]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 05f0124e2..659270a33 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -27,7 +27,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi @OpenApi( summary = "Adjusts the duration of a running task. This is a method for admins.", - path = "/api/v1/run/admin/{runId}/adjust/{duration}", + path = "/api/v2/run/admin/{runId}/adjust/{duration}", methods = [HttpMethod.POST], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index f2bc0cc5c..138ec72fa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -26,7 +26,7 @@ class AdjustPropertiesHandler(store: TransientEntityStore): AbstractEvaluationAd @OpenApi( summary = "Changes the properties of an evaluation.", - path = "/api/v1/evaluation/admin/{evaluationId}/properties", + path = "/api/v2/evaluation/admin/{evaluationId}/properties", methods = [HttpMethod.PATCH], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index f1e3b8420..bbecd5ecf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -48,7 +48,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs @OpenApi( summary = "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - path = "/api/v1/evaluation/admin/create", + path = "/api/v2/evaluation/admin/create", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(ApiCompetitionStartMessage::class)]), tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt index 4e12f7359..5c9730a94 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -18,7 +18,7 @@ class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluation override val route = "run/admin/{runId}/overview" @OpenApi( summary = "Provides a complete overview of a run.", - path = "/api/v1/run/admin/{runId}/overview", + path = "/api/v2/run/admin/{runId}/overview", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index 76ffbb7d3..5728ae19b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -23,7 +23,7 @@ class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHa @OpenApi( summary = "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - path = "/api/v1/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force", + path = "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force", methods = [HttpMethod.POST], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index b55b87f49..c09709bc2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -21,7 +21,7 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH @OpenApi( summary = "Lists all past tasks for a given evaluation.", - path = "/api/v1/evaluation/admin/{evaluationId}/task/past/list", + path = "/api/v2/evaluation/admin/{evaluationId}/task/past/list", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index a632b8332..9be59d013 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -20,7 +20,7 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm @OpenApi( summary = "Lists all submissions for a given evaluation and task.", - path = "/api/v1/evaluation/admin/{evaluationId}/submission/list/{templateId}", + path = "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}", methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index 838ab0c1d..442d866d1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -19,7 +19,7 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa @OpenApi( summary = "Lists all registered viewers for a evaluation. This is a method for admins.", - path = "/api/v1/evaluation/admin/{evaluationId}/viewer/list", + path = "/api/v2/evaluation/admin/{evaluationId}/viewer/list", methods = [HttpMethod.GET], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index 7b367c90d..5392316e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -33,7 +33,7 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl @OpenApi( summary = "Moves to and selects the next task within the evaluation. This is a method for admins.", - path = "/api/v1/evaluation/admin/{runId}/task/next", + path = "/api/v2/evaluation/admin/{runId}/task/next", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 9a35051f2..64d2d7820 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -30,7 +30,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation @OpenApi( summary = "Override the submission status for a given submission.", - path = "/api/v1/evaluation/admin/{evaluationId}/submission/override", + path = "/api/v2/evaluation/admin/{evaluationId}/submission/override", methods = [HttpMethod.PATCH], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index 768dc1127..f50b33d1c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -24,7 +24,7 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH @OpenApi( summary = "Moves to and selects the previous task. This is a method for admins.", - path = "/api/v1/evaluation/admin/{evaluationId}/task/previous", + path = "/api/v2/evaluation/admin/{evaluationId}/task/previous", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index e6784448b..c919f07a8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -26,7 +26,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd @OpenApi( summary = "Starts a evaluation. This is a method for administrators.", - path = "/api/v1/evaluation/admin/{evaluationId}/start", + path = "/api/v2/evaluation/admin/{evaluationId}/start", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 0840161fc..657d8ee5e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -33,7 +33,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand @OpenApi( summary = "Starts the currently active task as a new task run. This is a method for admins.", - path = "/api/v1/evaluation/admin/{evaluationId}/task/start", + path = "/api/v2/evaluation/admin/{evaluationId}/task/start", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evalation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index 5d7128613..205bb3b93 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -27,7 +27,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi @OpenApi( summary = "Terminates an evaluation. This is a method for administrators.", - path = "/api/v1/evaluation/admin/{runId}/terminate", + path = "/api/v2/evaluation/admin/{runId}/terminate", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Competition Run Admin"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 17bb5a79e..6ecf01811 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -27,7 +27,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl @OpenApi( summary = "Aborts the currently running task run. This is a method for admins.", - path = "/api/v1/evaluation/admin/{evaluationId}/task/abort", + path = "/api/v2/evaluation/admin/{evaluationId}/task/abort", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administration"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index fcde7b8a3..f694b50e0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -23,7 +23,7 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan @OpenApi( summary = "Moves to and selects the specified task. This is a method for admins.", - path = "/api/v1/evaluation/admin/{evaluationId}/task/switch/{idx}", + path = "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}", methods = [HttpMethod.POST], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt index da685d513..15d2a4ee9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt @@ -22,7 +22,7 @@ abstract class AbstractEvaluationClientHandler(protected val store: TransientEnt override val permittedRoles: Set = setOf(ApiRole.VIEWER) /** All [AbstractEvaluationClientHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** * Returns the [RunManager] associated with the current [Context]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt index 192ced091..3efd29041 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -28,7 +28,7 @@ class ClientListEvaluationsHandler(store: TransientEntityStore): AbstractEvaluat @OpenApi( summary = "Lists an overview of all evaluation runs visible to the current client.", - path = "/api/v1/client/evaluation/list", + path = "/api/v2/client/evaluation/list", tags = ["Evaluation Client"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index bfad34e99..740280c1e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -24,7 +24,7 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie @OpenApi( summary = "Returns an overview of the currently active task for a run.", - path = "/api/v1/client/evaluation/currentTask/{evaluationId}", + path = "/api/v2/client/evaluation/currentTask/{evaluationId}", tags = ["Client Run Info"], queryParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt index e13bd2c05..7f4047cd4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt @@ -19,5 +19,5 @@ abstract class AbstractScoreHandler(protected val store: TransientEntityStore) : override val permittedRoles: Set = setOf(ApiRole.VIEWER) /** All [AbstractScoreHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index f521ea990..0a0f36049 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -32,7 +32,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle @OpenApi( summary = "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", - path = "/api/v1/score/evaluation/{evaluationId}/current", + path = "/api/v2/score/evaluation/{evaluationId}/current", tags = ["Evaluation Scores"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index 2b0c82ea9..ddc18074e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -33,7 +33,7 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle @OpenApi( summary = "Returns the overviews of all score boards for the specified task.", - path = "/api/v1/score/evaluation/{evaluationId}/history/{taskId}", + path = "/api/v2/score/evaluation/{evaluationId}/history/{taskId}", tags = ["Evaluation Scores"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt index 2180bd47f..012d8f64a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt @@ -23,7 +23,7 @@ class ListCompetitionScoreHandler(store: TransientEntityStore) : AbstractScoreHa @OpenApi( summary = "Returns the score overviews of a specific evaluation run.", - path = "/api/v1/score/evaluation/{evaluationId}", + path = "/api/v2/score/evaluation/{evaluationId}", tags = ["Evaluation Scores"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt index cd7a0b3ab..b5fbcd765 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt @@ -20,7 +20,7 @@ class ListScoreSeriesHandler(store: TransientEntityStore) : AbstractScoreHandler @OpenApi( summary = "Returns a time series for a given run and scoreboard.", - path = "/api/v1/score/evaluation/{evaluationId}/series/{scoreboard}", + path = "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}", tags = ["Evaluation Scores"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt index bd0ade4fc..968074fce 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt @@ -15,7 +15,7 @@ class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler @OpenApi( summary = "Returns a list of available scoreboard names for the given evaluation.", - path = "/api/v1/evaluation/{evaluationId}/scoreboard/list", + path = "/api/v2/evaluation/{evaluationId}/scoreboard/list", tags = ["Evaluation Scores"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index 64d915101..fa3e2fccd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -22,7 +22,7 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( @OpenApi( summary = "Returns team group aggregated values of the current task.", - path = "/api/v1/score/evaluation/{evaluationId}/teamGroup/list", + path = "/api/v2/score/evaluation/{evaluationId}/teamGroup/list", tags = ["Competition Run Scores"], pathParams = [ OpenApiParam("runId", String::class, "ID of the competition run.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt index 273066a4b..296f11909 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt @@ -26,7 +26,7 @@ abstract class AbstractEvaluationViewerHandler(protected val store: TransientEnt override val permittedRoles: Set = setOf(ApiRole.VIEWER) /** All [AbstractEvaluationClientHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** * Returns the [RunManager] associated with the current [Context]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index 4ef344c4d..8e2573794 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -34,7 +34,7 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView @OpenApi( summary = "Returns the information for the currently active task template (i.e., the one that is currently selected).", - path = "/api/v1/evaluation/{evaluationId}/task", + path = "/api/v2/evaluation/{evaluationId}/task", tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt index 5b393c98c..3ba6e1d40 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -19,7 +19,7 @@ class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluation @OpenApi( summary = "Returns basic information about a specific evaluation.", - path = "/api/v1/evaluation/{evaluationId}/info", + path = "/api/v2/evaluation/{evaluationId}/info", tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index d86681a28..65af365fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -22,7 +22,7 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation @OpenApi( summary = "Returns the state of a specific evaluation.", - path = "/api/v1/evaluation/{evaluationId}/state", + path = "/api/v2/evaluation/{evaluationId}/state", tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index d9387150a..f72191ed4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -23,7 +23,7 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua @OpenApi( summary = "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - path = "/api/v1/evaluation/{evaluationId}/submission/list/after/{timestamp}", + path = "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}", tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class,"The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index 9d2097048..af7f21bf3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -22,7 +22,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval @OpenApi( summary = "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - path = "/api/v1/evaluation/{evaluationId}/task/{taskRunId}/submission/list", + path = "/api/v2/evaluation/{evaluationId}/task/{taskRunId}/submission/list", tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index e7932ed46..f702288be 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -24,7 +24,7 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV @OpenApi( summary = "Returns all submissions for the current task run, if one is either running or has just ended.", - path = "/api/v1/evaluation/{evaluationId}/submission/list/{timestamp}", + path = "/api/v2/evaluation/{evaluationId}/submission/list/{timestamp}", tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 2f9c97d8f..5cd07ec14 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -36,7 +36,7 @@ class GetTaskHintHandler(store: TransientEntityStore, private val config: Config @OpenApi( summary = "Returns the task hint for the specified task.", - path = "/api/v1/run/{evaluationId}/hint/{taskId}", + path = "/api/v2/run/{evaluationId}/hint/{taskId}", tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 4cc2c1599..880166b3f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -26,7 +26,7 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf @OpenApi( summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", - path = "/api/v1/evaluation/{evaluationId}/target/{taskId}", + path = "/api/v2/evaluation/{evaluationId}/target/{taskId}", tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt index 807cb702f..3e7a0a074 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt @@ -19,7 +19,7 @@ class ListEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluatio @OpenApi( summary = "Lists an overview of all evaluations visible to the current user.", - path = "/api/v1/evaluation/info/list", + path = "/api/v2/evaluation/info/list", tags = ["Evaluation"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt index b611db4fa..d7f0bd591 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt @@ -20,7 +20,7 @@ class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluati @OpenApi( summary = "Lists an overview of all evaluation visible to the current user.", - path = "/api/v1/evaluation/state/list", + path = "/api/v2/evaluation/state/list", tags = ["Evaluation"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt index 9c23b2749..ea95d8172 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt @@ -24,7 +24,7 @@ abstract class AbstractJudgementHandler(protected val store: TransientEntityStor override val permittedRoles: Set = setOf(ApiRole.JUDGE) /** */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** * Checks if [RunManager] can actually be judged by the user defined in the current [Context]. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 2f26ae3ce..a7878ca02 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -23,7 +23,7 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa @OpenApi( summary = "Gets the next open submission that is waiting to be judged.", - path = "/api/v1/evaluation/{evaluationId}/judge/next", + path = "/api/v2/evaluation/{evaluationId}/judge/next", pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], tags = ["Judgement"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 8541ae75f..e7dfe9517 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -23,7 +23,7 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( @OpenApi( summary = "Gets the next open submission that is waiting to be voted on.", - path = "/api/v1/evaluation/{evaluationId}/vote/next", + path = "/api/v2/evaluation/{evaluationId}/vote/next", pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], tags = ["Judgement"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt index bc3960033..c664ad086 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -19,11 +19,11 @@ import jetbrains.exodus.database.TransientEntityStore class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHandler(store), GetRestHandler> { override val permittedRoles = setOf(ApiRole.VIEWER) override val route = "evaluation/{evaluationId}/judge/status" - override val apiVersion = "v1" + override val apiVersion = "v2" @OpenApi( summary = "Retrieves the status of all judgement validators.", - path = "/api/v1/evaluation/{evaluationId}/judge/status", + path = "/api/v2/evaluation/{evaluationId}/judge/status", pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], tags = ["Judgement"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 355358bda..db84953b0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -27,7 +27,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle @OpenApi( summary = "Endpoint to post a judgement for a previously detached judgement request.", - path = "/api/v1/run/{runId}/judge", methods = [HttpMethod.POST], + path = "/api/v2/run/{runId}/judge", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(ApiJudgement::class)]), tags = ["Judgement"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index 516d3620a..1521a4976 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -21,12 +21,12 @@ import jetbrains.exodus.database.TransientEntityStore */ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(store), PostRestHandler { override val route = "evaluation/{evaluationId}/judge/vote" - override val apiVersion = "v1" + override val apiVersion = "v2" @OpenApi( summary = "Returns a Vote.", - path = "/api/v1/evaluation/{evaluationId}/judge/vote", methods = [HttpMethod.POST], + path = "/api/v2/evaluation/{evaluationId}/judge/vote", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(ApiVote::class)]), tags = ["Judgement"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt index 6a166bda9..fdd979149 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/AbstractLogHandler.kt @@ -13,7 +13,7 @@ import io.javalin.security.RouteRole */ abstract class AbstractLogHandler: PostRestHandler, AccessManagedRestHandler { /** All [AbstractLogHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** All [AbstractLogHandler]s require [ApiRole.ADMIN] or [ApiRole.PARTICIPANT]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN, ApiRole.PARTICIPANT) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt index 92944b116..445c7b740 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt @@ -25,7 +25,7 @@ class QueryLogHandler : AbstractLogHandler() { override val route = "log/query" @OpenApi(summary = "Accepts query logs from participants", - path = "/api/v1/log/query", + path = "/api/v2/log/query", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(QueryEventLog::class)]), tags = ["Log"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt index a865c81f8..8b6e5f6cb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt @@ -25,7 +25,7 @@ class ResultLogHandler: AbstractLogHandler() { override val route = "log/result" @OpenApi(summary = "Accepts result logs from participants.", - path = "/api/v1/log/result", + path = "/api/v2/log/result", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(QueryResultLog::class)]), tags = ["Log"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index 85b94b55d..1fe49ee5b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -44,7 +44,7 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, override val permittedRoles = setOf(ApiRole.VIEWER) /** All [AbstractPreviewHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** The [Path] to the pre-calculated previews.*/ private val cacheLocation = Paths.get(config.cachePath + "/previews") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt index fb997fbbf..88f8bb74b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt @@ -30,13 +30,13 @@ class GetMediaHandler(private val store: TransientEntityStore) : GetRestHandler< override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.ADMIN) /** [GetMediaHandler] is part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" //not used override fun doGet(ctx: Context): Any = "" @OpenApi(summary = "Returns a collection item", - path = "/api/v1/media/{itemId}", + path = "/api/v2/media/{itemId}", pathParams = [ OpenApiParam("itemId", String::class, "The media item ID.") ], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt index ca2be6387..3b83eb2c3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt @@ -20,7 +20,7 @@ class MediaPreviewHandler(store: TransientEntityStore, config: Config) : Abstrac override val route: String = "preview/item/{collection}/{item}/{time}" @OpenApi( summary = "Returns a preview image from a collection item", - path = "/api/v1/preview/item/{collection}/{item}/{time}", + path = "/api/v2/preview/item/{collection}/{item}/{time}", pathParams = [ OpenApiParam("collectionId", String::class, "Unique ID of the media collection."), OpenApiParam("item", String::class, "Name of the media item-"), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index 1392c93e5..3342db0c9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -24,7 +24,7 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab @OpenApi( summary = "Returns a preview image for a specific submission.", - path = "/api/v1/preview/submission/{evaluationId}/{submissionId}", + path = "/api/v2/preview/submission/{evaluationId}/{submissionId}", pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID."), OpenApiParam("submissionId", String::class, "The submission ID") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index 7ff09ef05..b2f824fe0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -41,12 +41,12 @@ class BatchSubmissionHandler(private val store: TransientEntityStore, private va override val permittedRoles = setOf(ApiRole.PARTICIPANT) /** All [BatchSubmissionHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" override val route: String = "submit/{evaluationId}" @OpenApi(summary = "Endpoint to accept batch submissions in JSON format", - path = "/api/v1/submit/{evaluationId}", + path = "/api/v2/submit/{evaluationId}", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(RunResult::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 5396040fa..fbc53dba0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -52,7 +52,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con override val permittedRoles = setOf(ApiRole.PARTICIPANT) /** All [SubmissionHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" override val route = "submit" @@ -68,7 +68,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } @OpenApi(summary = "Endpoint to accept submissions", - path = "/api/v1/submit", + path = "/api/v2/submit", queryParams = [ OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt index e9ac829f5..3a8cb68bf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt @@ -17,10 +17,10 @@ import io.javalin.openapi.OpenApiResponse class CurrentTimeHandler : GetRestHandler { override val route = "status/time" - override val apiVersion = "v1" + override val apiVersion = "v2" @OpenApi(summary = "Returns the current time on the server.", - path = "/api/v1/status/time", + path = "/api/v2/status/time", methods = [HttpMethod.GET], tags = ["Status"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt index 09ecf5cde..f26573ab1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt @@ -26,11 +26,11 @@ class InfoHandler : GetRestHandler { override val route = "status/info" - override val apiVersion = "v1" + override val apiVersion = "v2" @OpenApi(summary = "Returns an overview of the server properties.", - path = "/api/v1/status/info", + path = "/api/v2/status/info", methods = [HttpMethod.GET], tags = ["Status"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index 47e33201a..5027db8fe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -24,12 +24,12 @@ import io.javalin.openapi.* */ class LoginHandler : RestHandler, PostRestHandler { - override val apiVersion = "v1" + override val apiVersion = "v2" data class LoginRequest(var username: String, var password: String) @OpenApi(summary = "Sets roles for session based on user account and returns a session cookie.", - path = "/api/v1/login", + path = "/api/v2/login", methods = [HttpMethod.POST], tags = ["User"], requestBody = OpenApiRequestBody([OpenApiContent(LoginRequest::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index bfe969ed8..0c286f47a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -19,10 +19,10 @@ import io.javalin.openapi.* * @author Luca Rossetto */ class LogoutHandler : RestHandler, GetRestHandler { - override val apiVersion = "v1" + override val apiVersion = "v2" @OpenApi(summary = "Clears all user roles of the current session.", - path = "/api/v1/logout", + path = "/api/v2/logout", tags = ["User"], queryParams = [ OpenApiParam("session", String::class, "Session Token") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt index 9dcccd373..2b86a3207 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt @@ -28,7 +28,7 @@ abstract class AbstractCompetitionDescriptionHandler(protected val store: Transi override val permittedRoles: Set = setOf(ApiRole.ADMIN) /** All [AbstractCollectionHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** Convenience method to extract [EvaluationTemplate]'s ID from [Context]. */ private fun competitionId(ctx: Context): TemplateId = diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index 9d8afc7ef..719c0fd83 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -27,7 +27,7 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCom @OpenApi( summary = "Creates a new evaluation template.", - path = "/api/v1/template", + path = "/api/v2/template", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(ApiCreateCompetition::class)]), tags = ["Template"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index 8dba0e951..4e80cddeb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -21,7 +21,7 @@ class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCom @OpenApi( summary = "Deletes the evaluation template with the given template ID.", - path = "/api/v1/template/{templateId}", + path = "/api/v2/template/{templateId}", methods = [HttpMethod.DELETE], pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index 80d6e6536..e28617f5f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -24,14 +24,14 @@ import kotlinx.dnq.query.query class GetTeamLogoHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { override val route = "template/logo/{logoId}" - override val apiVersion = "v1" + override val apiVersion = "v2" //not used override fun doGet(ctx: Context): Any = "" @OpenApi( summary = "Returns the logo for the given team ID.", - path = "/api/v1/template/logo/{teamId}", + path = "/api/v2/template/logo/{teamId}", tags = ["Evaluation", "Media"], pathParams = [OpenApiParam("teamId", String::class, "The ID of the team to list load the logo for.")], responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index df84af7d7..3279c90f6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -26,7 +26,7 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractComp @OpenApi( summary = "Lists an overview of all available competitions with basic information about their content.", - path = "/api/v1/template/list", + path = "/api/v2/template/list", tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 628d2ee13..7a6c1f953 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -23,7 +23,7 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescrip @OpenApi( summary = "Lists the task templates contained in a specific evaluation template.", - path = "/api/v1/competition/{templateId}/task/list", + path = "/api/v2/competition/{templateId}/task/list", pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index ed304f276..8bcf565d4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -23,7 +23,7 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractCompetitionDescript @OpenApi( summary = "Lists all the teams of a specific competition.", - path = "/api/v1/competition/{templateId}/team/list", + path = "/api/v2/competition/{templateId}/team/list", pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index f31fa2d9c..c42ea797d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -21,7 +21,7 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompe @OpenApi( summary = "Loads the detailed definition of a specific evaluation template.", - path = "/api/v1/template/{templateId}", + path = "/api/v2/template/{templateId}", pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt index 8731181ec..0de8353eb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt @@ -37,7 +37,7 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) @OpenApi( summary = "Updates an existing evaluation template.", - path = "/api/v1/template/{templateId}", + path = "/api/v2/template/{templateId}", pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(ApiEvaluationTemplate::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt index fcadc743a..8d7018b89 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt @@ -19,7 +19,7 @@ typealias SessionId = String */ abstract class AbstractUserHandler: RestHandler, AccessManagedRestHandler { /** All [AbstractUserHandler]s are part of the v1 API. */ - override val apiVersion = "v1" + override val apiVersion = "v2" /** Convenience method to extract [User] from current session. */ protected fun userFromSession(ctx: Context): User { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index 8a4e3cca9..24dd1799e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -28,7 +28,7 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, Acce @OpenApi( summary = "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - path = "/api/v1/user", methods = [HttpMethod.POST], + path = "/api/v2/user", methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index 7af9d2e9f..0abfed92b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -25,7 +25,7 @@ class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, Ac @OpenApi( summary = "Deletes the specified user. Requires ADMIN privileges", - path = "/api/v1/user/{userId}", methods = [HttpMethod.DELETE], + path = "/api/v2/user/{userId}", methods = [HttpMethod.DELETE], pathParams = [OpenApiParam("userId", Long::class, "User ID")], tags = ["User"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index 72abe6334..587d545bf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -26,11 +26,11 @@ class ListActiveUsersHandler : GetRestHandler>, AccessManagedRestH /** All [UserDetailsHandler] requires [ApiRole.ADMIN]. */ override val route = "user/session/active/list" - override val apiVersion = "v1" + override val apiVersion = "v2" @OpenApi( summary = "Get details of all current user sessions", - path = "/api/v1/user/session/active/list", + path = "/api/v2/user/session/active/list", tags = ["User"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt index 32a0cedcf..902ba1538 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -27,7 +27,7 @@ class ListUsersHandler: AbstractUserHandler(), GetRestHandler>, Ac @OpenApi( summary = "Lists all available users.", - path = "/api/v1/user/list", + path = "/api/v2/user/list", tags = ["User"], responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], methods = [HttpMethod.GET] diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt index 207d2cf91..a9b477ff7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt @@ -22,7 +22,7 @@ class ShowCurrentSessionHandler : AbstractUserHandler(), GetRestHandler, A @OpenApi( summary = "Get information about the current user.", - path = "/api/v1/user", + path = "/api/v2/user", tags = ["User"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt index 8333eccef..a1dd95f6e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -29,7 +29,7 @@ class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, Acc @OpenApi( summary = "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - path = "/api/v1/user/{userId}", methods = [HttpMethod.PATCH], + path = "/api/v2/user/{userId}", methods = [HttpMethod.PATCH], pathParams = [OpenApiParam("userId", String::class, "User ID")], requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt index c3b28ef35..57fc79207 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -23,7 +23,7 @@ class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, Acces @OpenApi( summary = "Gets details of the user with the given id.", - path = "/api/v1/user/{userId}", + path = "/api/v2/user/{userId}", pathParams = [ OpenApiParam("userId", String::class, "User's UID") ], From 6a93406bb09f5d6247dc0c24229476c5fc759e65 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 1 Feb 2023 16:03:07 +0100 Subject: [PATCH 039/498] Added missing file. --- .../dev/dres/data/model/run/EvaluationType.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt new file mode 100644 index 000000000..6af1892ed --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt @@ -0,0 +1,31 @@ +package dev.dres.data.model.run + +import dev.dres.api.rest.types.evaluation.ApiRunType +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** + * Enumeration of the type of [Evaluation]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class EvaluationType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val INTERACTIVE_SYNCHRONOUS by enumField { description = "INTERACTIVE_SYNCHRONOUS" } + val INTERACTIVE_ASYNCHRONOUS by enumField { description = "INTERACTIVE_ASYNCHRONOUS" } + val NON_INTERACTIVE by enumField { description = "NON_INTERACTIVE" } + } + + var description by xdRequiredStringProp(unique = true) + private set + + /** + * Converts this [EvaluationType] to a RESTful API representation [ApiRunType]. + * + * @return [ApiRunType] + */ + fun toApi() = ApiRunType.values().find { it.type == this } ?: throw IllegalStateException("Run type ${this.description} is not supported.") +} \ No newline at end of file From a71c3e9197fbc911a4907e011d221f66de4e080a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 16:19:32 +0100 Subject: [PATCH 040/498] Post-merge session cleanup --- .../api/rest/handler/ContextExtensions.kt | 104 ------------------ .../admin/AbstractEvaluationAdminHandler.kt | 4 +- .../evaluation/admin/AdjustDurationHandler.kt | 6 +- .../evaluation/admin/NextTaskHandler.kt | 6 +- .../admin/OverrideSubmissionHandler.kt | 6 +- .../admin/StartEvaluationHandler.kt | 6 +- .../evaluation/admin/StartTaskHandler.kt | 6 +- .../evaluation/admin/StopEvaluationHandler.kt | 6 +- .../evaluation/admin/StopTaskHandler.kt | 6 +- .../judgement/AbstractJudgementHandler.kt | 6 +- .../judgement/DequeueJudgementHandler.kt | 6 +- .../handler/judgement/PostJudgementHandler.kt | 6 +- .../api/rest/handler/log/QueryLogHandler.kt | 8 +- .../api/rest/handler/log/ResultLogHandler.kt | 8 +- .../submission/BatchSubmissionHandler.kt | 7 +- .../handler/submission/SubmissionHandler.kt | 6 +- .../api/rest/handler/system/InfoHandler.kt | 4 +- .../api/rest/handler/system/LoginHandler.kt | 4 +- .../api/rest/handler/system/LogoutHandler.kt | 10 +- .../rest/handler/users/AbstractUserHandler.kt | 6 +- .../users/ShowCurrentSessionHandler.kt | 8 +- .../handler/users/ShowCurrentUserHandler.kt | 4 +- .../utilities/extensions/ContextExtensions.kt | 100 ++++++++++++++++- 23 files changed, 163 insertions(+), 170 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt deleted file mode 100644 index a51127c34..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/ContextExtensions.kt +++ /dev/null @@ -1,104 +0,0 @@ -package dev.dres.api.rest.handler - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.admin.UserId -import dev.dres.data.model.run.EvaluationId -import dev.dres.run.RunExecutor -import dev.dres.run.RunManager -import dev.dres.run.RunManagerStatus -import dev.dres.utilities.extensions.sessionId -import io.javalin.http.Context -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.flatMapDistinct -import kotlinx.dnq.query.isEmpty - - -/** - * Returns the [UserId] for the current [Context]. - * - * @return [UserId] - */ -fun Context.userId(): UserId = AccessManager.userIdForSession(this.sessionId()) - ?: throw ErrorStatusException(401, "No user registered for session ${this.sessionId()}.", this) - -/** - * Returns the [EvaluationId] associated with the current [Context] - * - * @return [EvaluationId] - */ -fun Context.evaluationId(): EvaluationId - = this.pathParamMap()["evaluationId"] ?: throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", this) - - -/** - * Returns the active [RunManager] the used defined in the current [Context] can access. - * - * @return [RunManager] - */ -fun Context.activeManagerForUser(): RunManager { - val userId = this.userId() - val managers = AccessManager.getRunManagerForUser(userId).filter { - it.status != RunManagerStatus.CREATED && it.status != RunManagerStatus.TERMINATED - } - if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", this) - if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", this) - return managers.first() -} - -/** - * Returns the active [RunManager] the [EvaluationId] defined in the current [Context]. - * - * @return [RunManager] - */ -fun Context.eligibleManagerForId(): RunManager { - val userId = this.userId() - val evaluationId = this.evaluationId() - val manager = RunExecutor.managerForId(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) - if (this.isJudge()) { - if (manager.template.judges.filter { it.userId eq userId }.isEmpty) { - throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as judge.", this) - } - } - if (this.isParticipant()) { - if (manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isEmpty) { - throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) - } - } - - if (this.isAdmin()) { - return manager - } - - throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) -} -/** - * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. - * - * @return True if current user has [ApiRole.PARTICIPANT] - */ -fun Context.isAdmin(): Boolean { - val roles = AccessManager.rolesOfSession(this.sessionId()) - return roles.contains(ApiRole.ADMIN) -} - -/** - * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. - * - * @return True if current user has [ApiRole.PARTICIPANT] - */ -fun Context.isParticipant(): Boolean { - val roles = AccessManager.rolesOfSession(this.sessionId()) - return roles.contains(ApiRole.PARTICIPANT) && !roles.contains(ApiRole.ADMIN) -} - -/** - * Checks uf user associated with current [Context] has [ApiRole.JUDGE]. - * - * @return True if current user has [ApiRole.JUDGE] - */ -fun Context.isJudge(): Boolean { - val roles = AccessManager.rolesOfSession(this.sessionId()) - return roles.contains(ApiRole.JUDGE) && !roles.contains(ApiRole.ADMIN) -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt index e029d7ec6..a473c13da 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt @@ -9,7 +9,7 @@ import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore @@ -46,7 +46,7 @@ abstract class AbstractEvaluationAdminHandler(protected val store: TransientEnti */ fun synchronousAdminCheck(manager: RunManager, ctx: Context) { if (manager is InteractiveAsynchronousRunManager) return - if (!AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { + if (!AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN)) { throw ErrorStatusException(403, "Access Denied.", ctx); } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 659270a33..7890059a0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -9,7 +8,8 @@ import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.Task import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -49,7 +49,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.adjustDuration(rac, duration) - AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionId()) + AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Duration for run $evaluationId was successfully adjusted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index 5392316e6..f0a9940dc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -2,7 +2,6 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -11,7 +10,8 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.TaskStatus -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import io.javalin.security.RouteRole @@ -54,7 +54,7 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl return this.store.transactional { val rac = RunActionContext.runActionContext(ctx, evaluationManager) if (evaluationManager is InteractiveAsynchronousRunManager - && !AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN) + && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) && evaluationManager.currentTask(rac)?.status != TaskStatus.ENDED) { throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 64d2d7820..c23895d29 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus @@ -10,7 +9,8 @@ import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -71,7 +71,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation } if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.status)) { val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.id } - AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionId()) + AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionToken()) submission.toApi() } else { throw ErrorStatusException(500, "Could not update the submission. Please see the backend's log.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index c919f07a8..1ea38e780 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -1,14 +1,14 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -44,7 +44,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.start(rac) - AuditLogger.competitionStart(evaluationManager.id, evaluationManager.template, AuditLogSource.REST, ctx.sessionId()) + AuditLogger.competitionStart(evaluationManager.id, evaluationManager.template, AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 657d8ee5e..804a9adcb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -10,7 +9,8 @@ import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.Evaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import io.javalin.security.RouteRole @@ -54,7 +54,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.startTask(rac) - AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), AuditLogSource.REST, ctx.sessionId()) + AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index 205bb3b93..7cc59e3e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -1,14 +1,14 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -45,7 +45,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.end(rac) - AuditLogger.competitionEnd(evaluationManager.id, AuditLogSource.REST, ctx.sessionId()) + AuditLogger.competitionEnd(evaluationManager.id, AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully stopped.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 6ecf01811..36511f878 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -9,7 +8,8 @@ import dev.dres.data.model.audit.AuditLogSource import dev.dres.data.model.run.Evaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -45,7 +45,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl try { val task = evaluationManager.currentTaskTemplate(rac) evaluationManager.abortTask(rac) - AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionId()) + AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt index ea95d8172..bccb59edd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt @@ -3,11 +3,11 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler -import dev.dres.api.rest.handler.userId import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole import dev.dres.run.RunManager -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken +import dev.dres.utilities.extensions.userId import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore @@ -35,7 +35,7 @@ abstract class AbstractJudgementHandler(protected val store: TransientEntityStor */ protected fun checkEligibility(ctx: Context, runManager: RunManager) { val userId = ctx.userId() - if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { + if (AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN)) { return //Admins require no further check } if (runManager.template.judges.filter { it.userId eq userId }.isEmpty) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index a7878ca02..573e6826f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -1,13 +1,13 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.submissions.VerdictType -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -45,7 +45,7 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa checkEligibility(ctx, evaluationManager) do { val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break - val next = validator.next(ctx.sessionId()) ?: break + val next = validator.next(ctx.sessionToken()!!) ?: break val taskDescription = next.second.task.template.textualDescription() when (next.second.type) { VerdictType.TEXT -> { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index db84953b0..69ce3788c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId import dev.dres.api.rest.types.judgement.ApiJudgement import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus @@ -10,7 +9,8 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.AuditLogSource import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -59,7 +59,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } - AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.status, AuditLogSource.REST, ctx.sessionId()) + AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.status, AuditLogSource.REST, ctx.sessionToken()) } return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt index 445c7b740..aa1c611e2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt @@ -1,6 +1,5 @@ package dev.dres.api.rest.handler.log -import dev.dres.api.rest.handler.activeManagerForUser import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -8,7 +7,8 @@ import dev.dres.data.model.log.QueryEventLog import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.InvalidRequestEvent import dev.dres.run.eventstream.QueryEventLogEvent -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.activeManagerForUser +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -42,10 +42,10 @@ class QueryLogHandler : AbstractLogHandler() { val queryEventLog = try { ctx.bodyAsClass() } catch (e: BadRequestResponse){ - EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), evaluationManager.id, ctx.body())) + EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionToken(), evaluationManager.id, ctx.body())) throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) }.copy(serverTimeStamp = System.currentTimeMillis()) - EventStreamProcessor.event(QueryEventLogEvent(ctx.sessionId(), evaluationManager.id, queryEventLog)) + EventStreamProcessor.event(QueryEventLogEvent(ctx.sessionToken(), evaluationManager.id, queryEventLog)) return SuccessStatus("Log received") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt index 8b6e5f6cb..3a15a69b8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.handler.log import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.activeManagerForUser import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -9,7 +8,8 @@ import dev.dres.data.model.log.QueryResultLog import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.InvalidRequestEvent import dev.dres.run.eventstream.QueryResultLogEvent -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.activeManagerForUser +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -42,10 +42,10 @@ class ResultLogHandler: AbstractLogHandler() { val queryResultLog = try { ctx.bodyAsClass(QueryResultLog::class.java) } catch (e: BadRequestResponse){ - EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionId(), evaluationManager.id, ctx.body())) + EventStreamProcessor.event(InvalidRequestEvent(ctx.sessionToken(), evaluationManager.id, ctx.body())) throw ErrorStatusException(400, "Invalid parameters: ${e.localizedMessage}", ctx) }.copy(serverTimeStamp = System.currentTimeMillis()) - EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionId(), evaluationManager.id, queryResultLog)) + EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionToken(), evaluationManager.id, queryResultLog)) return SuccessStatus("Log entry received.") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index b2f824fe0..77c499caa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -4,8 +4,6 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.preview.AbstractPreviewHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -18,7 +16,8 @@ import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.Submission import dev.dres.run.InteractiveRunManager import dev.dres.run.NonInteractiveRunManager -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* @@ -66,7 +65,7 @@ class BatchSubmissionHandler(private val store: TransientEntityStore, private va } /* Obtain basic information required for submission processing. */ - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val runManager = this.getEligibleRunManager(userId, ctx) val time = System.currentTimeMillis() this.store.transactional { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index fbc53dba0..fcd5948e9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -27,7 +27,7 @@ import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.TimeUtil -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -91,7 +91,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con ) override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { val (s,r) = this.store.transactional { - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) val run = getEligibleRunManager(userId, ctx) val time = System.currentTimeMillis() val submission = toSubmission(userId, run, time, ctx) @@ -109,7 +109,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionId(), ctx.req().remoteAddr) + AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS)) { //pre-generate preview generatePreview(submission.verdicts.first()) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt index f26573ab1..b8c3ab28a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.RestApi import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.system.DresInfo -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi @@ -38,7 +38,7 @@ class InfoHandler : GetRestHandler { OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ]) override fun doGet(ctx: Context): DresInfo { - return if (AccessManager.rolesOfSession(ctx.sessionId()).contains(ApiRole.ADMIN)) { + return if (AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN)) { DresInfo ( DRES.VERSION, ManagementFactory.getRuntimeMXBean().startTime, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index c220bcc85..533894575 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -55,13 +55,13 @@ class LoginHandler : RestHandler, PostRestHandler { val sessionToken = ctx.getOrCreateSessionToken() AccessManager.registerUserForSession(sessionToken, user) - AuditLogger.login(loginRequest.username, sessionToken, LogEventSource.REST) + AuditLogger.login(loginRequest.username, AuditLogSource.REST, sessionToken) //explicitly set cookie on login ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) val ret = UserManager.get(username)!!.toApi() - ret.sessionId = ctx.sessionId() + ret.sessionId = sessionToken return ret } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index 0c286f47a..1ca49b866 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.AuditLogSource import dev.dres.run.audit.AuditLogger -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* @@ -34,10 +34,10 @@ class LogoutHandler : RestHandler, GetRestHandler { methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessStatus { - val username = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) - val userId = AccessManager.userIdForSession(ctx.sessionId()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) - AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionId()) - AccessManager.deregisterUserSession(ctx.sessionId()) + val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) + AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionToken()!!) + AccessManager.deregisterUserSession(ctx.sessionToken()!!) return SuccessStatus("User '${username}' logged out successfully.") } override val route = "logout" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt index 8d7018b89..d09d0084c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt @@ -6,10 +6,10 @@ import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.admin.User import dev.dres.mgmt.admin.UserManager -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context -typealias SessionId = String +typealias SessionToken = String /** * An abstract [RestHandler] to manage [User]s @@ -23,7 +23,7 @@ abstract class AbstractUserHandler: RestHandler, AccessManagedRestHandler { /** Convenience method to extract [User] from current session. */ protected fun userFromSession(ctx: Context): User { - return UserManager.get(id = AccessManager.userIdForSession(ctx.sessionId())!!) + return UserManager.get(id = AccessManager.userIdForSession(ctx.sessionToken())!!) ?: throw ErrorStatusException(404, "User could not be found!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt index a9b477ff7..21543c15f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.utilities.extensions.sessionId +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* @@ -13,7 +13,7 @@ import io.javalin.openapi.* * @author Ralph Gasser * @version 1.0 */ -class ShowCurrentSessionHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class ShowCurrentSessionHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user/session" /** [ShowCurrentUserHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ @@ -28,10 +28,10 @@ class ShowCurrentSessionHandler : AbstractUserHandler(), GetRestHandler, A ) override fun doGet(ctx: Context): ApiUser { val user = userFromSession(ctx).toApi() - user.sessionId = ctx.sessionId() + user.sessionId = ctx.sessionToken() return user } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index dc637cb1f..7860376e9 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -3,8 +3,18 @@ package dev.dres.utilities.extensions import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.util.MimeTypeHelper +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.EvaluationId +import dev.dres.run.RunExecutor +import dev.dres.run.RunManager +import dev.dres.run.RunManagerStatus import io.javalin.http.Context +import kotlinx.dnq.query.FilteringContext.eq +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct +import kotlinx.dnq.query.isEmpty import java.io.File import java.nio.file.Files import java.nio.file.Path @@ -76,4 +86,92 @@ fun Context.getOrCreateSessionToken(): String { return id -} \ No newline at end of file +} + +/** + * Returns the [UserId] for the current [Context]. + * + * @return [UserId] + */ +fun Context.userId(): UserId = AccessManager.userIdForSession(this.sessionToken()) + ?: throw ErrorStatusException(401, "No user registered for session ${this.sessionToken()}.", this) + +/** + * Returns the [EvaluationId] associated with the current [Context] + * + * @return [EvaluationId] + */ +fun Context.evaluationId(): EvaluationId + = this.pathParamMap()["evaluationId"] ?: throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", this) + + +/** + * Returns the active [RunManager] the used defined in the current [Context] can access. + * + * @return [RunManager] + */ +fun Context.activeManagerForUser(): RunManager { + val userId = this.userId() + val managers = AccessManager.getRunManagerForUser(userId).filter { + it.status != RunManagerStatus.CREATED && it.status != RunManagerStatus.TERMINATED + } + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", this) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", this) + return managers.first() +} + +/** + * Returns the active [RunManager] the [EvaluationId] defined in the current [Context]. + * + * @return [RunManager] + */ +fun Context.eligibleManagerForId(): RunManager { + val userId = this.userId() + val evaluationId = this.evaluationId() + val manager = RunExecutor.managerForId(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) + if (this.isJudge()) { + if (manager.template.judges.filter { it.userId eq userId }.isEmpty) { + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as judge.", this) + } + } + if (this.isParticipant()) { + if (manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isEmpty) { + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) + } + } + + if (this.isAdmin()) { + return manager + } + + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) +} +/** + * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. + * + * @return True if current user has [ApiRole.PARTICIPANT] + */ +fun Context.isAdmin(): Boolean { + val roles = AccessManager.rolesOfSession(this.sessionToken()) + return roles.contains(ApiRole.ADMIN) +} + +/** + * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. + * + * @return True if current user has [ApiRole.PARTICIPANT] + */ +fun Context.isParticipant(): Boolean { + val roles = AccessManager.rolesOfSession(this.sessionToken()) + return roles.contains(ApiRole.PARTICIPANT) && !roles.contains(ApiRole.ADMIN) +} + +/** + * Checks uf user associated with current [Context] has [ApiRole.JUDGE]. + * + * @return True if current user has [ApiRole.JUDGE] + */ +fun Context.isJudge(): Boolean { + val roles = AccessManager.rolesOfSession(this.sessionToken()) + return roles.contains(ApiRole.JUDGE) && !roles.contains(ApiRole.ADMIN) +} From 45bc00f053c2d396b193309051f1efe9d945d027 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 17:29:23 +0100 Subject: [PATCH 041/498] More post-merge cleanup --- .../kotlin/dev/dres/api/rest/AccessManager.kt | 30 +++---- .../handler/download/ScoreDownloadHandler.kt | 2 +- .../admin/AdjustPropertiesHandler.kt | 2 +- .../admin/EvaluationOverviewHandler.kt | 2 +- .../evaluation/admin/ForceViewerHandler.kt | 2 +- .../evaluation/admin/ListPastTaskHandler.kt | 2 +- .../admin/ListSubmissionsHandler.kt | 2 +- .../evaluation/admin/ListViewersHandler.kt | 2 +- .../evaluation/admin/PreviousTaskHandler.kt | 2 +- .../evaluation/admin/SwitchTaskHandler.kt | 2 +- .../client/AbstractEvaluationClientHandler.kt | 3 +- .../client/ClientTaskInfoHandler.kt | 2 +- .../scores/CurrentTaskScoreHandler.kt | 6 +- .../scores/HistoryTaskScoreHandler.kt | 6 +- .../scores/ListCompetitionScoreHandler.kt | 2 +- .../scores/ListScoreSeriesHandler.kt | 4 +- .../scores/ListScoreboardsHandler.kt | 2 +- .../scores/TeamGroupScoreHandler.kt | 6 +- .../viewer/AbstractEvaluationViewerHandler.kt | 3 + .../viewer/GetCurrentTaskHandler.kt | 6 +- .../viewer/GetEvaluationInfoHandler.kt | 4 +- .../viewer/GetEvaluationStateHandler.kt | 6 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 6 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 6 +- .../viewer/GetSubmissionInfoHandler.kt | 6 +- .../evaluation/viewer/GetTaskHintHandler.kt | 6 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 6 +- .../handler/judgement/DequeueVoteHandler.kt | 2 +- .../judgement/JudgementStatusHandler.kt | 2 +- .../rest/handler/judgement/PostVoteHandler.kt | 2 +- .../handler/preview/AbstractPreviewHandler.kt | 41 ++++----- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 42 ++++----- .../run/filter/CorrectPerTeamItemFilter.kt | 17 +++- .../run/filter/CorrectPerTeamMemberFilter.kt | 1 - .../dres/run/score/scorer/NewAvsTaskScorer.kt | 88 +++++++++++-------- 35 files changed, 170 insertions(+), 153 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 9e696b948..7dd69da7e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest -import dev.dres.api.rest.handler.users.SessionId +import dev.dres.api.rest.handler.users.SessionToken import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User @@ -39,26 +39,26 @@ object AccessManager { } } - /** An internal [ConcurrentHashMap] that maps [SessionId]s to [ApiRole]s. */ - private val sessionRoleMap = HashMap>() + /** An internal [ConcurrentHashMap] that maps [SessionToken]s to [ApiRole]s. */ + private val sessionRoleMap = HashMap>() - /** An internal [ConcurrentHashMap] that maps [SessionId]s to [UserId]s. */ - private val sessionUserMap = HashMap() + /** An internal [ConcurrentHashMap] that maps [SessionToken]s to [UserId]s. */ + private val sessionUserMap = HashMap() /** Map keeping track of all [RunManager]s a specific user is eligible for. */ private val usersToRunMap = HashMap>() - /** A [Set] of all [SessionId]s. */ - val currentSessions: Set + /** A [Set] of all [SessionToken]s. */ + val currentSessions: Set get() = this.locks.read { Collections.unmodifiableSet(this.sessionUserMap.keys) } /** A [ReentrantReadWriteLock] that mediates access to maps. */ private val locks = ReentrantReadWriteLock() /** - * Registers a [User] for a given [SessionId]. Usually happens upon login. + * Registers a [User] for a given [SessionToken]. Usually happens upon login. * - * @param sessionId The [SessionId] to register the [User] for. + * @param sessionId The [SessionToken] to register the [User] for. * @param user The [User] to register. */ fun registerUserForSession(sessionId: String, user: User) = this.locks.write { @@ -79,9 +79,9 @@ object AccessManager { } /** - * Deregisters a [User] for a given [SessionId]. Usually happens upon logout. + * Deregisters a [User] for a given [SessionToken]. Usually happens upon logout. * - * @param sessionId The [SessionId] to register the [User] for. + * @param sessionId The [SessionToken] to register the [User] for. */ fun deregisterUserSession(sessionId: String) = this.locks.write { this.sessionRoleMap.remove(sessionId) @@ -89,9 +89,9 @@ object AccessManager { } /** - * Queries and returns the [UserId] for the given [SessionId]. + * Queries and returns the [UserId] for the given [SessionToken]. * - * @param sessionId The [SessionId] to query. + * @param sessionId The [SessionToken] to query. * @return [UserId] or null if no [User] is logged in. */ fun userIdForSession(sessionId: String?): UserId? = this.locks.read { @@ -101,9 +101,9 @@ object AccessManager { /** - * Queries and returns the [ApiRole]s for the given [SessionId]. + * Queries and returns the [ApiRole]s for the given [SessionToken]. * - * @param sessionId The [SessionId] to query. + * @param sessionId The [SessionToken] to query. * @return Set of [ApiRole] or empty set if no user is logged in. */ fun rolesOfSession(sessionId: String?): Set = this.locks.read { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index 02ac2d648..9d9fb71d6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.download import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.utilities.extensions.eligibleManagerForId import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index 138ec72fa..7dc69f421 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -1,12 +1,12 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.handler.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.run.Evaluation import dev.dres.data.model.run.RunProperties +import dev.dres.utilities.extensions.evaluationId import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt index 5c9730a94..06f01890e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiEvaluationOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index 5728ae19b..118162b5c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index c09709bc2..f806e0416 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index 9be59d013..a2c7aac74 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiSubmissionInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index 442d866d1..852e7b45e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiViewerInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index f50b33d1c..c34b7f7b1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index f694b50e0..8a3cacfb5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt index 15d2a4ee9..a2c5b57c1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt @@ -2,8 +2,7 @@ package dev.dres.api.rest.handler.evaluation.client import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.userId +import dev.dres.utilities.extensions.userId import dev.dres.api.rest.types.users.ApiRole import dev.dres.run.RunExecutor import dev.dres.run.RunManager diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index 740280c1e..56e6456fe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.client import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 0a0f36049..08fca465c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.scores.ApiScore import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index ddc18074e..3e051dcc2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isAdmin +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isAdmin import dev.dres.api.rest.types.evaluation.scores.ApiScore import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt index 012d8f64a..f02928683 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.scores import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.handler.evaluation.scores.AbstractScoreHandler import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt index b5fbcd765..f7a51e3db 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt @@ -1,8 +1,8 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.scores.ApiScoreSeries import dev.dres.api.rest.types.evaluation.scores.ApiScoreSeriesPoint import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt index 968074fce..998a9b0c2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.status.ErrorStatus import io.javalin.http.Context import io.javalin.openapi.* diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index fa3e2fccd..6be410614 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.scores.ApiTeamGroupValue import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt index 296f11909..894a273cc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt @@ -7,6 +7,9 @@ import dev.dres.data.model.run.EvaluationId import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager +import dev.dres.utilities.extensions.isJudge +import dev.dres.utilities.extensions.isParticipant +import dev.dres.utilities.extensions.userId import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index 8e2573794..9afa8e45b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt index 3ba6e1d40..8f8c000cb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -1,8 +1,8 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index 65af365fd..8be8468f0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index f72191ed4..6bcfa59f5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index af7f21bf3..08c3e7455 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index f702288be..ab6b4589d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 5cd07ec14..229ce497c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -1,8 +1,8 @@ import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.handler.evaluation.viewer.AbstractEvaluationViewerHandler -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.competition.tasks.ApiHintContent import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 880166b3f..2ba4a872a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId -import dev.dres.api.rest.handler.evaluationId -import dev.dres.api.rest.handler.isParticipant +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.evaluationId +import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.competition.tasks.ApiTargetContent import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index e7dfe9517..5220c8f8b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt index c664ad086..fbe5e07fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.judgement.ApiJudgementValidatorStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index 1521a4976..6332a1d9a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.handler.eligibleManagerForId +import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.judgement.ApiVote import dev.dres.api.rest.types.status.ErrorStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index 1fe49ee5b..6dc33de62 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -85,42 +85,31 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, ctx.header("Cache-Control", "max-age=31622400") ctx.streamFile(basePath.resolve(item.location)) return - } else if (item.type == MediaType.VIDEO) { + } else if (item.type == MediaType.VIDEO) { + /* Prepare cache directory for item. */ - val cacheDir = this.cacheLocation.resolve("${item.collection}/${item.name}") + val cacheDir = cacheLocation.resolve("${item.collection}/${item.name}") if (!Files.exists(cacheDir)) { Files.createDirectories(cacheDir) } /* check timestamp. */ - if (time == null) throw ErrorStatusException(400, "Timestamp unspecified or invalid.", ctx) + if (time == null) { + throw ErrorStatusException(400, "Timestamp unspecified or invalid.", ctx) + } + val imgPath = cacheDir.resolve("${time}.jpg") + if (Files.exists(imgPath)) { //if file is available, send contents immediately ctx.header("Cache-Control", "max-age=31622400") - ctx.sendFile(imgPath) - } else { - val future = FFmpegUtil.executeFFmpegAsync(basePath.resolve(item.location), time, imgPath) - val waitTime = if (RestApi.readyThreadCount > 500) { - 3L - } else { - 1L - } - - try { - val path = future.get(waitTime, TimeUnit.SECONDS) ?: throw FileNotFoundException() - ctx.sendFile(path.toFile()) - } catch (e: TimeoutException) { - ctx.status(408) - ctx.header("Cache-Control", "max-age=30") - ctx.contentType("image/png") - ctx.result(WAITING_IMAGE) - } catch (t: Throwable) { - ctx.status(429) - ctx.header("Cache-Control", "max-age=600") - ctx.contentType("image/png") - ctx.result(MISSING_IMAGE) - } + ctx.sendFile(imgPath.toFile()) + } else { //if not, schedule and return error + + FFmpegUtil.extractFrame(basePath.resolve(item.location), time, imgPath) + ctx.status(408) + ctx.header("Cache-Control", "max-age=30") + } } } diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index fbbd84547..6aee27aae 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -1,6 +1,6 @@ package dev.dres.run.audit -import dev.dres.api.rest.handler.users.SessionId +import dev.dres.api.rest.handler.users.SessionToken import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.AuditLogEntry import dev.dres.data.model.audit.AuditLogSource @@ -40,7 +40,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionStart(evaluationId: EvaluationId, description: EvaluationTemplate, api: AuditLogSource, session: SessionId?) { + fun competitionStart(evaluationId: EvaluationId, description: EvaluationTemplate, api: AuditLogSource, session: SessionToken?) { AuditLogEntry.new { this.type = AuditLogType.COMPETITION_START this.source = api @@ -58,7 +58,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionEnd(evaluationId: EvaluationId, api: AuditLogSource, session: SessionId?) { + fun competitionEnd(evaluationId: EvaluationId, api: AuditLogSource, session: SessionToken?) { AuditLogEntry.new { this.type = AuditLogType.COMPETITION_END this.source = api @@ -78,7 +78,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: TaskTemplate, api: AuditLogSource, session: SessionId?) { + fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: TaskTemplate, api: AuditLogSource, session: SessionToken?) { AuditLogEntry.new { this.type = AuditLogType.TASK_START this.source = api @@ -119,7 +119,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionId?) { + fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionToken?) { AuditLogEntry.new { this.type = AuditLogType.TASK_END this.source = api @@ -136,10 +136,10 @@ object AuditLogger { * * @param submission The [Submission] that was registered. * @param api The [AuditLogSource] - * @param sessionId The identifier of the user session. + * @param sessionToken The identifier of the user session. * @param address The IP address of the submitter. */ - fun submission(submission: Submission, api: AuditLogSource, sessionId: SessionId?, address: String) { + fun submission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?, address: String) { AuditLogEntry.new { this.type = AuditLogType.SUBMISSION this.source = api @@ -147,10 +147,10 @@ object AuditLogger { this.submissionId = submission.id this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ - this.session = sessionId + this.session = sessionToken this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionId ?: "na", submission.verdicts.first().task.evaluation.evaluationId, submission.verdicts.first().task.id, submission)) + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.verdicts.first().task.evaluation.evaluationId, submission.verdicts.first().task.id, submission)) } /** @@ -178,9 +178,9 @@ object AuditLogger { * * @param submission The [Submission] that was overriden (new snapshot). * @param api The [AuditLogSource] - * @param sessionId The identifier of the user session. + * @param sessionToken The identifier of the user session. */ - fun overrideSubmission(submission: Submission, api: AuditLogSource, sessionId: SessionId?) { + fun overrideSubmission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?) { this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.SUBMISSION_STATUS_OVERWRITE @@ -190,7 +190,7 @@ object AuditLogger { this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ this.description = "Verdict: ${submission.verdicts.first().status.description}" - this.session = sessionId + this.session = sessionToken } } } @@ -222,16 +222,16 @@ object AuditLogger { * @param token The token generated by the judgement sub-system * @param verdict The [VerdictStatus] submitted by the judge. * @param api The [AuditLogSource] - * @param sessionId The identifier of the user session. + * @param sessionToken The identifier of the user session. */ - fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionId: SessionId?) { + fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionToken: SessionToken?) { AuditLogEntry.new { this.type = AuditLogType.JUDGEMENT this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.description}" - this.session = sessionId + this.session = sessionToken } } @@ -240,15 +240,15 @@ object AuditLogger { * * @param userId [EvaluationId] of the user who logged out. * @param api The [AuditLogSource] - * @param sessionId The [SessionId] + * @param sessionToken The [SessionToken] */ - fun login(userId: UserId, api: AuditLogSource, sessionId: SessionId) { + fun login(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { AuditLogEntry.new { this.type = AuditLogType.LOGIN this.source = api this.timestamp = DateTime.now() this.userId = userId - this.session = sessionId + this.session = sessionToken } } @@ -257,15 +257,15 @@ object AuditLogger { * * @param userId [EvaluationId] of the user who logged out. * @param api The [AuditLogSource] - * @param sessionId The [SessionId] + * @param sessionToken The [SessionToken] */ - fun logout(userId: UserId, api: AuditLogSource, sessionId: SessionId) { + fun logout(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { AuditLogEntry.new { this.type = AuditLogType.LOGOUT this.source = api this.timestamp = DateTime.now() this.userId = userId - this.session = sessionId + this.session = sessionToken } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index 7fd686b5e..f7cc1ba53 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -1,8 +1,11 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect +import dev.dres.data.model.submissions.VerdictStatus +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.size + class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { @@ -10,6 +13,12 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { override val reason: String = "Maximum number of correct submissions ($limit) exceeded for this item." - override fun test(t: Submission): Boolean = - t is ItemAspect && t.task!!.submissions.count { it is ItemAspect && it.item.id == t.item.id && it.status == SubmissionStatus.CORRECT && it.teamId == t.teamId } < limit + override fun test(submission: Submission): Boolean { + return submission.verdicts.asSequence().all { verdict -> + verdict.task.submissions.filter { + (it.status eq VerdictStatus.CORRECT).and(it.submission.team eq submission.team) + }.size() < this.limit + } + } + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index 264f33cdc..0a6c4047e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -2,7 +2,6 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus -import kotlinx.dnq.query.FilteringContext.eq import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.size diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt index d33225d02..2e463eab9 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt @@ -1,13 +1,14 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.competition.TeamId + import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionStatus -import dev.dres.data.model.submissions.aspects.ItemAspect -import dev.dres.run.score.ScoreEntry +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer +import dev.dres.run.score.interfaces.ScoreEntry import dev.dres.run.score.interfaces.TeamTaskScorer +import kotlinx.dnq.query.asSequence import java.lang.Double.max import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -60,14 +61,11 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint context: TaskContext ): Map { - val distinctCorrectVideos = - submissions.mapNotNullTo(mutableSetOf()) {//map directly to set and filter in one step - if (it !is ItemAspect || it.status != SubmissionStatus.CORRECT) { - null//filter all incorrect submissions - } else { - it.item.id - } - }.size + val distinctCorrectVideos = submissions.flatMap { submission -> + submission.verdicts.asSequence().filter { it.status == VerdictStatus.CORRECT && it.item != null } + }.mapNotNullTo(mutableSetOf()) {it.item } + .size + //no correct submissions yet if (distinctCorrectVideos == 0) { @@ -80,30 +78,50 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } lastScores = this.lastScoresLock.write { - teamScoreMapSanitised( - submissions.filter { - it is ItemAspect && - (it.status == SubmissionStatus.CORRECT || it.status == SubmissionStatus.WRONG) - }.groupBy { it.teamId } - .map { submissionsPerTeam -> - submissionsPerTeam.key to - max(0.0, //prevent negative total scores - submissionsPerTeam.value.groupBy { submission -> - submission as ItemAspect - submission.item.id - }.map { - val firstCorrectIdx = it.value.sortedBy { s -> s.timestamp } - .indexOfFirst { s -> s.status == SubmissionStatus.CORRECT } - if (firstCorrectIdx < 0) { //no correct submissions, only penalty - it.value.size * -penaltyConstant - } else { //apply penalty for everything before correct submission - 1.0 - firstCorrectIdx * penaltyConstant - } - }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize - ) - }.toMap(), context.teamIds - ) + teamScoreMapSanitised(submissions.groupBy { it.team }.map { + submissionsPerTeam -> + val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { it.verdicts.asSequence().filter { v -> v.item != null && (v.status == VerdictStatus.CORRECT || v.status == VerdictStatus.WRONG) } } + submissionsPerTeam.key.teamId to + max(0.0, //prevent negative total scores + verdicts.groupBy { it.item!! }.map { + val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == VerdictStatus.CORRECT } + if (firstCorrectIdx < 0) { //no correct submissions, only penalty + it.value.size * -penaltyConstant + } else { //apply penalty for everything before correct submission + 1.0 - firstCorrectIdx * penaltyConstant + } + }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize + ) + }.toMap(), context.teamIds) } + +// lastScores = this.lastScoresLock.write { +// teamScoreMapSanitised( +// submissions.filter { +// it.verdicts.asSequence().any { it.type == VerdictType.TEMPORAL && (it.status == VerdictStatus.CORRECT || it.status == VerdictStatus.WRONG) } +// } +// +// .groupBy { it.teamId } +// .map { submissionsPerTeam -> +// submissionsPerTeam.key to +// max(0.0, //prevent negative total scores +// submissionsPerTeam.value.groupBy { submission -> +// submission as ItemAspect +// submission.item.id +// }.map { +// val firstCorrectIdx = it.value.sortedBy { s -> s.timestamp } +// .indexOfFirst { s -> s.status == SubmissionStatus.CORRECT } +// if (firstCorrectIdx < 0) { //no correct submissions, only penalty +// it.value.size * -penaltyConstant +// } else { //apply penalty for everything before correct submission +// 1.0 - firstCorrectIdx * penaltyConstant +// } +// }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize +// ) +// }.toMap(), context.teamIds +// ) +// } + this.lastScoresLock.read { return lastScores } From 8e5005cec724f94fbdc86bfeafda7f721303720b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 17:36:13 +0100 Subject: [PATCH 042/498] Re-generated OpenAPI specs --- doc/oas.json | 4081 ++++++++++++++++++++++++-------------------------- 1 file changed, 1957 insertions(+), 2124 deletions(-) diff --git a/doc/oas.json b/doc/oas.json index 0288e5c13..db61a4e4a 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -3,48 +3,56 @@ "info" : { "title" : "DRES API", "summary" : null, - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", "termsOfService" : null, "contact" : null, "license" : null, - "version" : "1.0" + "version" : "2.0.0" }, "paths" : { - "/api/v1/run/admin/{runId}/task/abort" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", + "/api/v2/audit/log/list/limit/{limit}/{page}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query.", "parameters" : [ { - "name" : "runId", + "name" : "limit", "in" : "path", - "description" : "Competition Run ID", + "description" : "The maximum number of results. Default: 500", "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "string" + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "page", + "in" : "path", + "description" : "The page index offset, relative to the limit.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" } } ], "responses" : { "200" : { + "description" : "The audit logs", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } } } } }, - "401" : { + "403" : { + "description" : "Whenever a non-admin user starts the call", "content" : { "application/json" : { "schema" : { @@ -58,25 +66,49 @@ "security" : [ ] } }, - "/api/v1/user/session/active/list" : { + "/api/v2/audit/log/list/since/{since}/{upto}" : { "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "parameters" : [ ], + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", + "parameters" : [ { + "name" : "since", + "in" : "path", + "description" : "Timestamp of the earliest audit log to include", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "number", + "format" : "int64" + } + }, { + "name" : "upto", + "in" : "path", + "description" : "Timestamp of the latest audit log to include.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "number", + "format" : "int64" + } + } ], "responses" : { "200" : { + "description" : "The audit logs", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/UserDetails" + "$ref" : "#/components/schemas/ApiAuditLogEntry" } } } } }, - "500" : { + "403" : { + "description" : "Whenever a non-admin user starts the call", "content" : { "application/json" : { "schema" : { @@ -90,10 +122,10 @@ "security" : [ ] } }, - "/api/v1/collection" : { + "/api/v2/collection" : { "post" : { "tags" : [ "Collection" ], - "summary" : "Adds a new media collection", + "summary" : "Adds a new media collection.", "parameters" : [ ], "requestBody" : { "content" : { @@ -184,53 +216,16 @@ "security" : [ ] } }, - "/api/v1/mediaItem" : { + "/api/v2/mediaItem" : { "post" : { "tags" : [ "Collection" ], - "summary" : "Adds a Media Item to the specified Media Collection.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", + "summary" : "Adds a media item to the specified media collection.", "parameters" : [ ], "requestBody" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/ApiMediaItem" } } }, @@ -260,31 +255,20 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/adjust/{duration}" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Adjusts the duration of a running task run. This is a method for admins.", + "/api/v2/collection/{collectionId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", "parameters" : [ { - "name" : "runId", + "name" : "collectionId", "in" : "path", - "description" : "Competition Run ID", + "description" : "Collection ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } } ], "responses" : { "200" : { @@ -313,50 +297,19 @@ } } } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/status/info/admin" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an extensive overview of the server properties.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresAdminInfo" - } - } - } } }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v1/client/run/info/currentTask/{runId}" : { + }, "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Returns an overview of the currently active task for a run", + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -368,7 +321,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ClientTaskInfo" + "$ref" : "#/components/schemas/RestFullMediaCollection" } } } @@ -396,21 +349,21 @@ "security" : [ ] } }, - "/api/v1/competition" : { - "post" : { - "tags" : [ "Competition" ], - "summary" : "Creates a new competition.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CompetitionCreate" - } - } - }, - "required" : false - }, + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "content" : { @@ -452,26 +405,26 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { - "tags" : [ "Competition" ], - "summary" : "Updates an existing competition.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RestCompetitionDescription" - } - } - }, - "required" : false - }, + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiMediaItem" } } } @@ -508,36 +461,20 @@ "security" : [ ] } }, - "/api/v1/run/admin/create" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Creates a new competition run from an existing competition", + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CompetitionStartMessage" - } - } - }, - "required" : false - }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/RestMediaCollection" + } } } } @@ -556,33 +493,35 @@ "security" : [ ] } }, - "/api/v1/user" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, + "/api/v2/external/" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "type" : "array", + "items" : { + "type" : "string" + } } } } }, "400" : { - "description" : "If the username is already taken", "content" : { "application/json" : { "schema" : { @@ -591,7 +530,7 @@ } } }, - "500" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -599,26 +538,8 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserDetails" - } - } - } }, - "500" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -632,14 +553,14 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/hint/{taskId}" : { + "/api/v2/collection/{collectionId}/{startsWith}" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the task hint for the current task run (i.e. the one that is currently selected).", + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", "parameters" : [ { - "name" : "runId", + "name" : "collectionId", "in" : "path", - "description" : "Competition Run ID", + "description" : "Collection ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -647,9 +568,9 @@ "type" : "string" } }, { - "name" : "taskId", + "name" : "startsWith", "in" : "path", - "description" : "Task Description ID", + "description" : "Name the item(s) should start with.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -662,12 +583,15 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/TaskHint" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } } } } }, - "401" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -676,7 +600,7 @@ } } }, - "403" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -699,14 +623,14 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/task" : { + "/api/v2/collection/{collectionId}/random" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the information for the current task (i.e. the one that is currently selected).", + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", "parameters" : [ { - "name" : "runId", + "name" : "collectionId", "in" : "path", - "description" : "Competition Run ID", + "description" : "Collection ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -719,12 +643,12 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/TaskInfo" + "$ref" : "#/components/schemas/ApiMediaItem" } } } }, - "401" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -733,7 +657,7 @@ } } }, - "403" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -756,14 +680,14 @@ "security" : [ ] } }, - "/api/v1/score/run/{runId}/current" : { - "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns the overviews of all score boards for the current task run, if it is either running or has just ended.", + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", "parameters" : [ { - "name" : "runId", + "name" : "collectionId", "in" : "path", - "description" : "Competition run ID", + "description" : "Collection ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -771,12 +695,28 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ScoreOverview" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } } } } @@ -813,25 +753,54 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/target/{taskId}" : { + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task Description ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -841,14 +810,14 @@ "responses" : { "200" : { "content" : { - "application/json" : { + "text/plain" : { "schema" : { - "$ref" : "#/components/schemas/TaskTarget" + "type" : "string" } } } }, - "401" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -857,7 +826,7 @@ } } }, - "403" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -880,35 +849,15 @@ "security" : [ ] } }, - "/api/v1/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/user/session" : { + "/api/v2/download/template/{templateId}" : { "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -918,14 +867,14 @@ "responses" : { "200" : { "content" : { - "application/json" : { + "text/plain" : { "schema" : { - "$ref" : "#/components/schemas/SessionId" + "type" : "string" } } } }, - "500" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -933,28 +882,8 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/collection/{collectionId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -963,7 +892,7 @@ } } }, - "401" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -975,15 +904,17 @@ }, "deprecated" : false, "security" : [ ] - }, + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", "parameters" : [ { - "name" : "collectionId", + "name" : "runId", "in" : "path", - "description" : "Collection ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -992,10 +923,19 @@ } ], "responses" : { "200" : { + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestFullMediaCollection" + "$ref" : "#/components/schemas/ErrorStatus" } } } @@ -1023,20 +963,31 @@ "security" : [ ] } }, - "/api/v1/competition/{competitionId}" : { - "delete" : { - "tags" : [ "Competition" ], - "summary" : "Deletes the competition with the given competition ID.", + "/api/v2/run/admin/{runId}/adjust/{duration}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", "parameters" : [ { - "name" : "competitionId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } } ], "responses" : { "200" : { @@ -1078,27 +1029,39 @@ }, "deprecated" : false, "security" : [ ] - }, - "get" : { - "tags" : [ "Competition" ], - "summary" : "Loads the detailed definition of a specific competition.", + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Changes the properties of an evaluation.", "parameters" : [ { - "name" : "competitionId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition ID", - "required" : false, + "description" : "The evaluation ID", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestCompetitionDescription" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -1135,21 +1098,21 @@ "security" : [ ] } }, - "/api/v1/mediaItem/{mediaId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCompetitionStartMessage" + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { @@ -1177,28 +1140,21 @@ } } } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] - }, + } + }, + "/api/v2/run/admin/{runId}/overview" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", + "tags" : [ "Competition Run Admin" ], + "summary" : "Provides a complete overview of a run.", "parameters" : [ { - "name" : "mediaId", + "name" : "runId", "in" : "path", - "description" : "Media item ID", - "required" : false, + "description" : "The evaluation ID", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1210,7 +1166,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/ApiEvaluationOverview" } } } @@ -1247,20 +1203,29 @@ "security" : [ ] } }, - "/api/v1/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", "parameters" : [ { - "name" : "userId", + "name" : "evaluationId", "in" : "path", - "description" : "User ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "number", - "format" : "int64" + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } } ], "responses" : { @@ -1268,13 +1233,12 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "404" : { - "description" : "If the user could not be found", + "400" : { "content" : { "application/json" : { "schema" : { @@ -1283,7 +1247,16 @@ } } }, - "500" : { + "401" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -1295,37 +1268,32 @@ }, "deprecated" : false, "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone", + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", "parameters" : [ { - "name" : "userId", + "name" : "evaluationId", "in" : "path", - "description" : "User ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } } } } @@ -1339,7 +1307,7 @@ } } }, - "404" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -1348,7 +1316,7 @@ } } }, - "500" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -1360,15 +1328,27 @@ }, "deprecated" : false, "security" : [ ] - }, + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id", + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", "parameters" : [ { - "name" : "userId", + "name" : "runId", "in" : "path", - "description" : "User's UID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1380,13 +1360,24 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserDetails" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "404" : { - "description" : "If the user could not be found", + "401" : { "content" : { "application/json" : { "schema" : { @@ -1395,7 +1386,7 @@ } } }, - "500" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -1409,15 +1400,15 @@ "security" : [ ] } }, - "/api/v1/download/run/{runId}" : { + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire competition run structure.", + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1427,9 +1418,12 @@ "responses" : { "200" : { "content" : { - "text/plain" : { + "application/json" : { "schema" : { - "type" : "string" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } } } } @@ -1466,15 +1460,15 @@ "security" : [ ] } }, - "/api/v1/download/run/{runId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given competition run.", + "/api/v2/evaluation/admin/{runId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1484,9 +1478,9 @@ "responses" : { "200" : { "content" : { - "text/plain" : { + "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -1508,42 +1502,43 @@ } } } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/download/competition/{competitionId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire competition description structure.", + "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the submission status for a given submission.", "parameters" : [ { - "name" : "competitionId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { - "text/plain" : { + "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/ApiSubmission" } } } @@ -1580,25 +1575,15 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/viewer/list/{viewerId}/force" : { + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", + "name" : "evaluationId", "in" : "path", - "description" : "Viewer ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1632,39 +1617,47 @@ } } } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/audit/info" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Gives information about the audit log. Namely size and latest timestamp of known audit logs", - "parameters" : [ ], + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { - "description" : "The audit log info", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/AuditLogInfo" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "403" : { - "description" : "Whenever a non-admin user starts the call", + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { "content" : { "application/json" : { "schema" : { @@ -1678,15 +1671,15 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/info" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns a specific competition run.", + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "description" : "The evalation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1698,12 +1691,12 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RunInfo" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -1712,16 +1705,7 @@ } } }, - "403" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -1735,15 +1719,15 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/state" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the state of a specific competition run.", + "/api/v2/evaluation/admin/{runId}/terminate" : { + "post" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1755,21 +1739,12 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RunState" - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "403" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -1778,7 +1753,7 @@ } } }, - "404" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -1792,25 +1767,15 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/task/{taskRunId}/submission/list" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administration" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskRunId", + "name" : "evaluationId", "in" : "path", - "description" : "Task run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1822,24 +1787,12 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "403" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -1848,7 +1801,7 @@ } } }, - "404" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -1862,29 +1815,30 @@ "security" : [ ] } }, - "/api/v1/score/run/{runId}/history/{taskId}" : { - "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task run.", + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administration" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { - "name" : "taskId", + "name" : "idx", "in" : "path", - "description" : "Task run ID", - "required" : false, + "description" : "Index of the task to switch to.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "string" + "type" : "integer", + "format" : "int32" } } ], "responses" : { @@ -1892,7 +1846,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ScoreOverview" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -1914,32 +1868,26 @@ } } } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/status/info" : { + "/api/v2/client/evaluation/list" : { "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", "parameters" : [ ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/DresInfo" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } } } } @@ -1958,46 +1906,27 @@ "security" : [ ] } }, - "/api/v1/submit/{runId}" : { - "post" : { - "tags" : [ "Batch Submission" ], - "summary" : "Endpoint to accept batch submissions in JSON format", + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Client Run Info" ], + "summary" : "Returns an overview of the currently active task for a run.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "name" : "evaluationId", + "in" : "query", + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunResult" - } - } - }, - "required" : false - }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } } @@ -2025,15 +1954,15 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/judge/status" : { + "/api/v2/score/evaluation/{evaluationId}/current" : { "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the status of all judgement validators.", + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2045,10 +1974,7 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/JudgementValidatorStatus" - } + "$ref" : "#/components/schemas/ApiScoreOverview" } } } @@ -2062,7 +1988,7 @@ } } }, - "403" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2085,37 +2011,37 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/JudgementVote" - } - } - }, - "required" : false - }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiScoreOverview" } } } @@ -2152,49 +2078,53 @@ "security" : [ ] } }, - "/api/v1/audit/log/list/limit/{limit}/{page}" : { + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query.", + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", "parameters" : [ { - "name" : "limit", - "in" : "path", - "description" : "The maximum number of results. Default: 500", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "page", + "name" : "evaluationId", "in" : "path", - "description" : "The page index offset, relative to the limit.", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "integer", - "format" : "int32" + "type" : "string" } } ], "responses" : { "200" : { - "description" : "The audit logs", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestAuditLogEntry" + "type" : "string" } } } } }, - "403" : { - "description" : "Whenever a non-admin user starts the call", + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { "content" : { "application/json" : { "schema" : { @@ -2208,49 +2138,45 @@ "security" : [ ] } }, - "/api/v1/audit/log/list/since/{since}/{upto}" : { + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query", + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", "parameters" : [ { - "name" : "since", + "name" : "evaluationId", "in" : "path", - "description" : "Timestamp of the earliest audit log to include", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "number", - "format" : "int64" + "type" : "string" } }, { - "name" : "upto", + "name" : "scoreboard", "in" : "path", - "description" : "Timestamp of the latest audit log to include.", - "required" : false, + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "number", - "format" : "int64" + "type" : "string" } } ], "responses" : { "200" : { - "description" : "The audit logs", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestAuditLogEntry" + "$ref" : "#/components/schemas/ApiScoreSeries" } } } } }, - "403" : { - "description" : "Whenever a non-admin user starts the call", + "400" : { "content" : { "application/json" : { "schema" : { @@ -2258,31 +2184,17 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "parameters" : [ ], - "responses" : { - "200" : { + }, + "401" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestMediaCollection" - } + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "401" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -2296,11 +2208,21 @@ "security" : [ ] } }, - "/api/v1/competition/list" : { + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "parameters" : [ ], + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "ID of the competition run.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "content" : { @@ -2308,13 +2230,13 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/CompetitionOverview" + "$ref" : "#/components/schemas/ApiTeamGroupValue" } } } } }, - "401" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -2322,38 +2244,17 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/client/run/info/list" : { - "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Lists an overview of all competition runs visible to the current client", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { + }, + "403" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ClientRunInfoList" + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "401" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -2367,20 +2268,27 @@ "security" : [ ] } }, - "/api/v1/run/info/list" : { + "/api/v2/evaluation/{evaluationId}/task" : { "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Lists an overview of all competition runs visible to the current user", - "parameters" : [ ], + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RunInfo" - } + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } } @@ -2393,31 +2301,17 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/run/state/list" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Lists an overview of all competition runs visible to the current user", - "parameters" : [ ], - "responses" : { - "200" : { + }, + "403" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RunState" - } + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "401" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -2431,15 +2325,15 @@ "security" : [ ] } }, - "/api/v1/score/run/{runId}" : { + "/api/v2/evaluation/{evaluationId}/info" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns the score overviews of a specific competition run.", + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2451,10 +2345,7 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ScoreOverview" - } + "$ref" : "#/components/schemas/ApiEvaluationInfo" } } } @@ -2468,6 +2359,15 @@ } } }, + "403" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { "content" : { "application/json" : { @@ -2482,15 +2382,15 @@ "security" : [ ] } }, - "/api/v1/competition/{competitionId}/team/list/details" : { + "/api/v2/evaluation/{evaluationId}/state" : { "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists the teams with their user details", + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", "parameters" : [ { - "name" : "competitionId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2502,15 +2402,12 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestDetailedTeam" - } + "$ref" : "#/components/schemas/ApiEvaluationState" } } } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2519,7 +2416,7 @@ } } }, - "401" : { + "403" : { "content" : { "application/json" : { "schema" : { @@ -2542,20 +2439,31 @@ "security" : [ ] } }, - "/api/v1/external/" : { + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", "parameters" : [ { - "name" : "startsWith", + "name" : "evaluationId", "in" : "path", - "description" : "Name starts with.", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "number", + "format" : "int64" + } } ], "responses" : { "200" : { @@ -2564,13 +2472,13 @@ "schema" : { "type" : "array", "items" : { - "type" : "string" + "$ref" : "#/components/schemas/ApiSubmission" } } } } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2579,7 +2487,7 @@ } } }, - "401" : { + "403" : { "content" : { "application/json" : { "schema" : { @@ -2602,25 +2510,25 @@ "security" : [ ] } }, - "/api/v1/collection/{collectionId}/{startsWith}" : { + "/api/v2/evaluation/{evaluationId}/task/{taskRunId}/submission/list" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", "parameters" : [ { - "name" : "collectionId", + "name" : "evaluationId", "in" : "path", - "description" : "Collection ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { - "name" : "startsWith", + "name" : "taskId", "in" : "path", - "description" : "Name starts with", - "required" : false, + "description" : "Task ID", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2634,13 +2542,13 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/ApiSubmission" } } } } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2649,7 +2557,7 @@ } } }, - "401" : { + "403" : { "content" : { "application/json" : { "schema" : { @@ -2672,15 +2580,15 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/past/list" : { + "/api/v2/evaluation/{evaluationId}/submission/list/{timestamp}" : { "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all past tasks for a given run", + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2694,21 +2602,12 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/PastTaskInfo" + "$ref" : "#/components/schemas/ApiSubmission" } } } } }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "401" : { "content" : { "application/json" : { @@ -2732,14 +2631,24 @@ "security" : [ ] } }, - "/api/v1/score/run/{runId}/scoreboard/list" : { + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns a list of available scoreboard names for the given run.", + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "ID of the competition run.", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -2752,15 +2661,12 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } + "$ref" : "#/components/schemas/ApiTargetContent" } } } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2769,7 +2675,7 @@ } } }, - "401" : { + "403" : { "content" : { "application/json" : { "schema" : { @@ -2792,31 +2698,11 @@ "security" : [ ] } }, - "/api/v1/score/run/{runId}/series/{scoreboard}" : { + "/api/v2/evaluation/info/list" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "ID of the competition run.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "parameters" : [ ], "responses" : { "200" : { "content" : { @@ -2824,13 +2710,13 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ScoreSeries" + "$ref" : "#/components/schemas/ApiEvaluationInfo" } } } } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2838,17 +2724,31 @@ } } } - }, - "401" : { + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "parameters" : [ ], + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } } } } }, - "404" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -2862,24 +2762,14 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/submission/list/{taskId}" : { + "/api/v2/evaluation/{evaluationId}/judge/next" : { "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all submissions for a given task and run.", + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", + "name" : "evaluationId", "in" : "path", - "description" : "Task ID", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -2892,10 +2782,16 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskRunSubmissionInfo" - } + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } @@ -2918,6 +2814,15 @@ } } }, + "403" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { "content" : { "application/json" : { @@ -2932,14 +2837,14 @@ "security" : [ ] } }, - "/api/v1/competition/{competitionId}/task/list" : { + "/api/v2/evaluation/{evaluationId}/vote/next" : { "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists the Tasks of a specific competition.", + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", "parameters" : [ { - "name" : "competitionId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition ID", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -2952,10 +2857,16 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTaskDescription" - } + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } @@ -2992,14 +2903,14 @@ "security" : [ ] } }, - "/api/v1/competition/{competitionId}/team/list" : { + "/api/v2/evaluation/{evaluationId}/judge/status" : { "get" : { - "tags" : [ "Competition" ], - "summary" : "Lists the Teams of a specific competition.", + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", "parameters" : [ { - "name" : "competitionId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition ID", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3014,7 +2925,7 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestTeam" + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" } } } @@ -3029,7 +2940,7 @@ } } }, - "401" : { + "403" : { "content" : { "application/json" : { "schema" : { @@ -3052,37 +2963,14 @@ "security" : [ ] } }, - "/api/v1/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/UserDetails" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/run/admin/{runId}/viewer/list" : { - "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all registered viewers for a competition run. This is a method for admins.", + "/api/v2/run/{runId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3090,15 +2978,22 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ViewerInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -3121,7 +3016,7 @@ } } }, - "404" : { + "403" : { "content" : { "application/json" : { "schema" : { @@ -3129,38 +3024,9 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserDetails" - } - } - } }, - "400" : { + "408" : { + "description" : "On timeout: Judgement took too long", "content" : { "application/json" : { "schema" : { @@ -3169,7 +3035,7 @@ } } }, - "401" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -3183,14 +3049,14 @@ "security" : [ ] } }, - "/api/v1/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3198,60 +3064,22 @@ "type" : "string" } } ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" } } }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } + "required" : false }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v1/run/{runId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open Submission to be judged.", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/JudgementRequest" - } - } - } - }, - "202" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -3274,15 +3102,6 @@ } } }, - "403" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { "content" : { "application/json" : { @@ -3297,36 +3116,37 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open Submission to voted on.", + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Run ID", - "required" : false, + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/JudgementRequest" - } + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" } } }, - "202" : { + "required" : false + }, + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -3348,36 +3168,37 @@ } } } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/next" : { + "/api/v2/log/result" : { "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Moves to and selects the next task. This is a method for admins.", + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { @@ -3411,15 +3232,15 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/overview" : { + "/api/v2/score/evaluation/{evaluationId}" : { "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Provides a complete overview of a run.", + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", - "required" : false, + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3431,16 +3252,10 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/AdminRunOverview" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } } } } @@ -3468,14 +3283,14 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/submission/override" : { - "patch" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Lists all submissions for a given task and run", + "/api/v2/submit/{evaluationId}" : { + "post" : { + "tags" : [ "Batch Submission" ], + "summary" : "Endpoint to accept batch submissions in JSON format", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "Competition Run ID", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3487,7 +3302,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SubmissionInfo" + "$ref" : "#/components/schemas/RunResult" } } }, @@ -3498,7 +3313,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SubmissionInfo" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -3535,60 +3350,103 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Judgement.", + "/api/v2/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Run ID", + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/Judgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } }, - "401" : { + "202" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } }, - "403" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -3597,8 +3455,7 @@ } } }, - "408" : { - "description" : "On timeout: Judgement took too long", + "401" : { "content" : { "application/json" : { "schema" : { @@ -3615,42 +3472,52 @@ } } } + }, + "412" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/previous" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "parameters" : [ ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/CurrentTime" } } } - }, - "400" : { + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "parameters" : [ ], + "responses" : { + "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/DresInfo" } } } @@ -3669,26 +3536,16 @@ "security" : [ ] } }, - "/api/v1/log/query" : { + "/api/v2/login" : { "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "parameters" : [ ], "requestBody" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" + "$ref" : "#/components/schemas/LoginRequest" } } }, @@ -3699,7 +3556,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiUser" } } } @@ -3727,14 +3584,14 @@ "security" : [ ] } }, - "/api/v1/collection/{collectionId}/random" : { + "/api/v2/logout" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", + "name" : "session", + "in" : "query", + "description" : "Session Token", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3747,7 +3604,46 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateCompetition" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -3784,46 +3680,32 @@ "security" : [ ] } }, - "/api/v1/run/{runId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the submissions for the current task that are newer than an indicated time, if it is either running or has just ended.", + "/api/v2/template/{templateId}" : { + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", "parameters" : [ { - "name" : "runId", + "name" : "templateId", "in" : "path", - "description" : "Competition Run ID", + "description" : "The evaluation template ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Minimum Timestamp of returned submissions.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "number", - "format" : "int64" - } } ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { + "400" : { "content" : { "application/json" : { "schema" : { @@ -3832,7 +3714,7 @@ } } }, - "403" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -3853,16 +3735,14 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v1/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", + }, + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", "parameters" : [ { - "name" : "collectionId", + "name" : "templateId", "in" : "path", - "description" : "Collection ID", + "description" : "The evaluation template ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3870,28 +3750,12 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestMediaItem" - } + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } @@ -3926,17 +3790,15 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v1/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants", + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3947,7 +3809,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } }, @@ -3980,42 +3842,35 @@ } } } + }, + "404" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/run/admin/{runId}/start" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Starts a competition run.", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "parameters" : [ ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiCompetitionOverview" + } } } } @@ -4034,14 +3889,14 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/start" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "/api/v2/competition/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", "parameters" : [ { - "name" : "runId", + "name" : "templateId", "in" : "path", - "description" : "Competition Run ID", + "description" : "The evaluation template ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -4054,7 +3909,10 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } } } } @@ -4076,82 +3934,29 @@ } } } + }, + "404" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v1/submit" : { + "/api/v2/competition/{templateId}/team/list" : { "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific competition.", "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -4164,21 +3969,24 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } } } } }, - "202" : { + "400" : { "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "400" : { + "401" : { "content" : { "application/json" : { "schema" : { @@ -4187,7 +3995,7 @@ } } }, - "401" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -4195,8 +4003,39 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } }, - "404" : { + "required" : false + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", "content" : { "application/json" : { "schema" : { @@ -4205,7 +4044,7 @@ } } }, - "412" : { + "500" : { "content" : { "application/json" : { "schema" : { @@ -4217,46 +4056,22 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v1/run/{runId}/submission/list" : { + }, "get" : { - "tags" : [ "Competition Run" ], - "summary" : "Returns the submissions for the current task run, if it is either running or has just ended.", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "parameters" : [ ], "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/ApiUser" } } } }, - "404" : { + "500" : { "content" : { "application/json" : { "schema" : { @@ -4270,30 +4085,20 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", + "name" : "userId", "in" : "path", - "description" : "Index of the task to switch to.", + "description" : "User ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "integer", - "format" : "int32" + "type" : "number", + "format" : "int64" } } ], "responses" : { @@ -4301,12 +4106,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiUser" } } } }, - "400" : { + "404" : { + "description" : "If the user could not be found", "content" : { "application/json" : { "schema" : { @@ -4315,7 +4121,7 @@ } } }, - "401" : { + "500" : { "content" : { "application/json" : { "schema" : { @@ -4327,32 +4133,37 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v1/score/run/{runId}/teamGroup/list" : { - "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns team group aggregated values of the current task.", + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", "parameters" : [ { - "name" : "runId", + "name" : "userId", "in" : "path", - "description" : "ID of the competition run.", - "required" : true, + "description" : "User ID", + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ScoreSeries" - } + "$ref" : "#/components/schemas/ApiUser" } } } @@ -4366,7 +4177,7 @@ } } }, - "403" : { + "404" : { "content" : { "application/json" : { "schema" : { @@ -4375,7 +4186,7 @@ } } }, - "404" : { + "500" : { "content" : { "application/json" : { "schema" : { @@ -4387,16 +4198,14 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v1/run/admin/{runId}/terminate" : { - "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Terminates a competition run. This is a method for admins.", + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", "parameters" : [ { - "name" : "runId", + "name" : "userId", "in" : "path", - "description" : "Competition Run ID", + "description" : "User's UID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -4409,12 +4218,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiUser" } } } }, - "400" : { + "404" : { + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -4423,7 +4233,7 @@ } } }, - "401" : { + "500" : { "content" : { "application/json" : { "schema" : { @@ -4437,42 +4247,118 @@ "security" : [ ] } }, - "/api/v1/run/admin/{runId}/properties" : { - "patch" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Changes the properties of a run", - "parameters" : [ { - "name" : "runId", - "in" : "path", - "description" : "Competition Run ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "parameters" : [ ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" + "responses" : { + "200" : { + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } } } }, - "required" : false + "500" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/info" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", + "parameters" : [ ], "responses" : { "200" : { + "description" : "The audit log info.", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/AuditLogInfo" } } } }, - "400" : { + "403" : { + "description" : "Whenever a non-admin user executes the call.", "content" : { "application/json" : { "schema" : { @@ -4480,6 +4366,46 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/run/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } }, "401" : { "content" : { @@ -4490,6 +4416,15 @@ } } }, + "403" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { "content" : { "application/json" : { @@ -4507,14 +4442,44 @@ }, "components" : { "schemas" : { - "SuccessStatus" : { + "ApiAuditLogEntry" : { "type" : "object", "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE", "type" ] + }, + "source" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL", "source" ] + }, + "timestamp" : { + "type" : "number", + "format" : "int64" + }, + "competitionId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "submissionId" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, "description" : { "type" : "string" } }, - "required" : [ "description" ] + "required" : [ "id", "type", "source", "timestamp" ] }, "ErrorStatus" : { "type" : "object", @@ -4525,43 +4490,34 @@ }, "required" : [ "description" ] }, - "UserDetails" : { + "RestMediaCollection" : { "type" : "object", "properties" : { "id" : { "type" : "string" }, - "username" : { + "name" : { "type" : "string" }, - "role" : { - "$ref" : "#/components/schemas/Role" + "description" : { + "type" : "string" }, - "sessionId" : { + "basePath" : { "type" : "string" } }, - "required" : [ "id", "username", "role" ] + "required" : [ "id", "name" ] }, - "RestMediaCollection" : { + "SuccessStatus" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, "description" : { "type" : "string" - }, - "basePath" : { - "type" : "string" } }, - "required" : [ "id", "name" ] + "required" : [ "description" ] }, - "RestMediaItem" : { + "ApiMediaItem" : { "type" : "object", "properties" : { "id" : { @@ -4571,7 +4527,8 @@ "type" : "string" }, "type" : { - "$ref" : "#/components/schemas/RestMediaItemType" + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] }, "collectionId" : { "type" : "string" @@ -4590,141 +4547,79 @@ }, "required" : [ "id", "name", "type", "collectionId", "location" ] }, - "DresAdminInfo" : { + "RestFullMediaCollection" : { "type" : "object", "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "number", - "format" : "int64" - }, - "uptime" : { - "type" : "number", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "number", - "format" : "int64" - }, - "totalMemory" : { - "type" : "number", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" + "collection" : { + "$ref" : "#/components/schemas/RestMediaCollection" }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } } }, - "required" : [ "version", "startTime", "uptime", "os", "jvm", "args", "cores", "freeMemory", "totalMemory", "load", "availableSeverThreads" ] + "required" : [ "collection", "items" ] }, - "ClientTaskInfo" : { + "RunProperties" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" + "participantCanView" : { + "type" : "boolean" }, - "remainingTime" : { - "type" : "number", - "format" : "int64" + "shuffleTasks" : { + "type" : "boolean" }, - "running" : { + "allowRepeatedTasks" : { "type" : "boolean" - } - }, - "required" : [ "id", "name", "taskGroup", "remainingTime", "running" ] - }, - "CompetitionCreate" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" }, - "description" : { - "type" : "string" + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "name", "description" ] + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, - "CompetitionStartMessage" : { + "ApiCompetitionStartMessage" : { "type" : "object", "properties" : { - "competitionId" : { + "templateId" : { "type" : "string" }, "name" : { "type" : "string" }, "type" : { - "$ref" : "#/components/schemas/RunType" + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE", "type" ] }, "properties" : { "$ref" : "#/components/schemas/RunProperties" } }, - "required" : [ "competitionId", "name", "type", "properties" ] + "required" : [ "templateId", "name", "type", "properties" ] }, - "UserRequest" : { + "ApiEvaluationOverview" : { "type" : "object", "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/Role" - } - }, - "required" : [ "username" ] - }, - "TaskHint" : { - "type" : "object", - "properties" : { - "taskId" : { - "type" : "string" + "state" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] }, - "sequence" : { + "teamOverviews" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ContentElement" + "$ref" : "#/components/schemas/ApiTeamTaskOverview" } - }, - "loop" : { - "type" : "boolean" } }, - "required" : [ "taskId", "sequence", "loop" ] + "required" : [ "state", "teamOverviews" ] }, - "TaskInfo" : { + "ApiTaskTemplateInfo" : { "type" : "object", "properties" : { - "id" : { + "templateId" : { "type" : "string" }, "name" : { @@ -4741,79 +4636,76 @@ "format" : "int64" } }, - "required" : [ "id", "name", "taskGroup", "taskType", "duration" ] + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] }, - "ScoreOverview" : { + "ApiSubmissionInfo" : { "type" : "object", "properties" : { - "name" : { + "evaluationId" : { "type" : "string" }, - "taskGroup" : { + "taskId" : { "type" : "string" }, - "scores" : { + "submissions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/Score" + "$ref" : "#/components/schemas/ApiSubmission" } } }, - "required" : [ "name", "scores" ] + "required" : [ "evaluationId", "taskId", "submissions" ] }, - "TaskTarget" : { + "ApiViewerInfo" : { "type" : "object", "properties" : { - "taskId" : { + "viewersId" : { "type" : "string" }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "CurrentTime" : { - "type" : "object", - "properties" : { - "timeStamp" : { - "type" : "number", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "SessionId" : { - "type" : "object", - "properties" : { - "sessionId" : { + "username" : { + "type" : "string" + }, + "host" : { "type" : "string" + }, + "ready" : { + "type" : "boolean" } }, - "required" : [ "sessionId" ] + "required" : [ "viewersId", "username", "host", "ready" ] }, - "AuditLogInfo" : { + "ApiSubmission" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "number", - "format" : "int64" + "id" : { + "type" : "string" }, - "size" : { - "type" : "integer", - "format" : "int32" + "teamId" : { + "type" : "string" }, - "latest" : { + "teamName" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { "type" : "number", "format" : "int64" + }, + "verdicts" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiVerdict" + } } }, - "required" : [ "timestamp", "size", "latest" ] + "required" : [ "id", "teamId", "teamName", "memberId", "memberName", "timestamp", "verdicts" ] }, - "RestCompetitionDescription" : { + "ApiEvaluationInfo" : { "type" : "object", "properties" : { "id" : { @@ -4822,98 +4714,99 @@ "name" : { "type" : "string" }, - "description" : { + "templateId" : { "type" : "string" }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskType" - } + "templateDescription" : { + "type" : "string" }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskGroup" - } + "type" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE", "type" ] }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTaskDescription" - } + "properties" : { + "$ref" : "#/components/schemas/RunProperties" }, "teams" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTeamGroup" + "$ref" : "#/components/schemas/ApiTeamInfo" } }, - "judges" : { + "tasks" : { "type" : "array", "items" : { - "type" : "string" + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] }, - "RunInfo" : { + "ApiScoreOverview" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, "name" : { "type" : "string" }, - "description" : { + "taskGroup" : { "type" : "string" }, - "teams" : { + "scores" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/TeamInfo" + "$ref" : "#/components/schemas/ApiScore" } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "properties" : { + "team" : { + "type" : "string" }, - "tasks" : { + "name" : { + "type" : "string" + }, + "points" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/TaskInfo" + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" } - }, - "competitionId" : { + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "properties" : { + "name" : { "type" : "string" }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "type" : { - "$ref" : "#/components/schemas/RunType" + "value" : { + "type" : "number", + "format" : "double" } }, - "required" : [ "id", "name", "teams", "tasks", "competitionId", "properties", "type" ] + "required" : [ "name", "value" ] }, - "RunState" : { + "ApiEvaluationState" : { "type" : "object", "properties" : { "id" : { "type" : "string" }, "runStatus" : { - "$ref" : "#/components/schemas/RunManagerStatus" + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] }, "taskRunStatus" : { - "$ref" : "#/components/schemas/RestTaskRunStatus" + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "status" ] }, "currentTask" : { - "$ref" : "#/components/schemas/TaskInfo" + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" }, "timeLeft" : { "type" : "number", @@ -4926,64 +4819,147 @@ }, "required" : [ "id", "runStatus", "taskRunStatus", "timeLeft", "timeElapsed" ] }, - "SubmissionInfo" : { + "ApiTargetContent" : { "type" : "object", "properties" : { - "id" : { + "taskId" : { "type" : "string" }, - "teamId" : { + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "properties" : { + "token" : { "type" : "string" }, - "teamName" : { + "mediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "validator" : { "type" : "string" }, - "memberId" : { + "collection" : { "type" : "string" }, - "memberName" : { + "item" : { "type" : "string" }, - "status" : { - "$ref" : "#/components/schemas/SubmissionStatus" + "taskDescription" : { + "type" : "string" }, - "timestamp" : { + "startTime" : { "type" : "number", "format" : "int64" }, - "item" : { - "$ref" : "#/components/schemas/RestMediaItem" + "endTime" : { + "type" : "number", + "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "properties" : { + "validatorName" : { + "type" : "string" }, - "text" : { + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiJudgement" : { + "type" : "object", + "properties" : { + "token" : { "type" : "string" }, - "start" : { + "validator" : { + "type" : "string" + }, + "verdict" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE", "status" ] + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiVote" : { + "type" : "object", + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/VerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "QueryEventLog" : { + "type" : "object", + "properties" : { + "timestamp" : { "type" : "number", "format" : "int64" }, - "end" : { + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { "type" : "number", "format" : "int64" } }, - "required" : [ "teamId", "memberId", "status", "timestamp" ] + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] }, - "DresInfo" : { + "QueryResultLog" : { "type" : "object", "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { + "timestamp" : { "type" : "number", "format" : "int64" }, - "uptime" : { + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { "type" : "number", "format" : "int64" } }, - "required" : [ "version", "startTime", "uptime" ] + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, "RunResult" : { "type" : "object", @@ -5005,160 +4981,141 @@ }, "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] }, - "JudgementValidatorStatus" : { + "SuccessfulSubmissionsStatus" : { "type" : "object", "properties" : { - "validator" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" + "submission" : { + "$ref" : "#/components/schemas/VerdictStatus" }, - "open" : { - "type" : "integer", - "format" : "int32" + "description" : { + "type" : "string" } }, - "required" : [ "validator", "pending", "open" ] + "required" : [ "submission", "description" ] }, - "JudgementVote" : { + "CurrentTime" : { "type" : "object", "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/SubmissionStatus" + "timeStamp" : { + "type" : "number", + "format" : "int64" } }, - "required" : [ "verdict" ] + "required" : [ "timeStamp" ] }, - "RestAuditLogEntry" : { + "DresInfo" : { "type" : "object", "properties" : { - "type" : { - "$ref" : "#/components/schemas/AuditLogEntryType" + "version" : { + "type" : "string" }, - "timestamp" : { + "startTime" : { "type" : "number", "format" : "int64" - } - }, - "required" : [ "type", "timestamp" ] - }, - "CompetitionOverview" : { - "type" : "object", - "properties" : { - "id" : { + }, + "uptime" : { + "type" : "number", + "format" : "int64" + }, + "os" : { "type" : "string" }, - "name" : { + "jvm" : { "type" : "string" }, - "description" : { + "args" : { "type" : "string" }, - "taskCount" : { + "cores" : { "type" : "integer", "format" : "int32" }, - "teamCount" : { + "freeMemory" : { + "type" : "number", + "format" : "int64" + }, + "totalMemory" : { + "type" : "number", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { "type" : "integer", "format" : "int32" } }, - "required" : [ "id", "name", "description", "taskCount", "teamCount" ] - }, - "ClientRunInfoList" : { - "type" : "object", - "properties" : { - "runs" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ClientRunInfo" - } - } - }, - "required" : [ "runs" ] + "required" : [ "version", "startTime", "uptime" ] }, - "RestDetailedTeam" : { + "LoginRequest" : { "type" : "object", "properties" : { - "name" : { - "type" : "string" - }, - "color" : { + "username" : { "type" : "string" }, - "logoId" : { + "password" : { "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/UserDetails" - } } }, - "required" : [ "name", "color", "logoId", "users" ] + "required" : [ "username", "password" ] }, - "PastTaskInfo" : { + "ApiUser" : { "type" : "object", "properties" : { - "taskId" : { - "type" : "string" - }, - "descriptionId" : { - "type" : "string" - }, - "name" : { + "id" : { "type" : "string" }, - "taskGroup" : { + "username" : { "type" : "string" }, - "taskType" : { - "type" : "string" + "role" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] }, - "numberOfSubmissions" : { - "type" : "integer", - "format" : "int32" + "sessionId" : { + "type" : "string" } }, - "required" : [ "taskId", "descriptionId", "name", "taskGroup", "taskType", "numberOfSubmissions" ] + "required" : [ "id", "username", "role" ] }, - "ScoreSeries" : { + "ApiCreateCompetition" : { "type" : "object", "properties" : { - "team" : { - "type" : "string" - }, "name" : { "type" : "string" }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ScoreSeriesPoint" - } + "description" : { + "type" : "string" } }, - "required" : [ "team", "name", "points" ] + "required" : [ "name", "description" ] }, - "TaskRunSubmissionInfo" : { + "ApiCompetitionOverview" : { "type" : "object", "properties" : { - "taskRunId" : { + "id" : { "type" : "string" }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/SubmissionInfo" - } + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "taskRunId", "submissions" ] + "required" : [ "id", "name", "taskCount", "teamCount" ] }, - "RestTaskDescription" : { + "ApiTaskTemplate" : { "type" : "object", "properties" : { "id" : { @@ -5177,25 +5134,28 @@ "type" : "number", "format" : "int64" }, - "mediaCollectionId" : { + "collectionId" : { "type" : "string" }, - "target" : { - "$ref" : "#/components/schemas/RestTaskDescriptionTarget" + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } }, - "components" : { + "hints" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestTaskDescriptionComponent" + "$ref" : "#/components/schemas/ApiHint" } } }, - "required" : [ "id", "name", "taskGroup", "taskType", "duration", "mediaCollectionId", "target", "components" ] + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] }, - "RestTeam" : { + "ApiTeam" : { "type" : "object", "properties" : { - "uid" : { + "teamId" : { "type" : "string" }, "name" : { @@ -5204,238 +5164,180 @@ "color" : { "type" : "string" }, - "logoData" : { - "type" : "string" - }, - "logoId" : { - "type" : "string" - }, "users" : { "type" : "array", "items" : { - "type" : "string" + "$ref" : "#/components/schemas/ApiUser" } - } - }, - "required" : [ "name", "color", "users" ] - }, - "ViewerInfo" : { - "type" : "object", - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" }, - "host" : { + "logoData" : { "type" : "string" - }, - "ready" : { - "type" : "boolean" } }, - "required" : [ "viewersId", "username", "host", "ready" ] + "required" : [ "teamId", "name", "color", "users" ] }, - "LoginRequest" : { + "ApiEvaluationTemplate" : { "type" : "object", "properties" : { - "username" : { + "id" : { "type" : "string" }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "JudgementRequest" : { - "type" : "object", - "properties" : { - "token" : { + "name" : { "type" : "string" }, - "mediaType" : { - "$ref" : "#/components/schemas/JudgementRequestMediaType" - }, - "validator" : { + "description" : { "type" : "string" }, - "collection" : { - "type" : "string" + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } }, - "item" : { - "type" : "string" + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } }, - "taskDescription" : { - "type" : "string" + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } }, - "startTime" : { - "type" : "string" + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } }, - "endTime" : { - "type" : "string" - } - }, - "required" : [ "token", "mediaType", "validator", "collection", "item", "taskDescription" ] - }, - "AdminRunOverview" : { - "type" : "object", - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } }, - "teamOverviews" : { + "judges" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/TeamTaskOverview" + "type" : "string" } } }, - "required" : [ "state", "teamOverviews" ] + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] }, - "Judgement" : { + "UserRequest" : { "type" : "object", "properties" : { - "token" : { + "username" : { "type" : "string" }, - "validator" : { + "password" : { "type" : "string" }, - "verdict" : { - "$ref" : "#/components/schemas/SubmissionStatus" + "role" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] } }, - "required" : [ "token", "validator", "verdict" ] + "required" : [ "username" ] }, - "QueryEventLog" : { + "AuditLogInfo" : { "type" : "object", "properties" : { "timestamp" : { "type" : "number", "format" : "int64" }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } + "size" : { + "type" : "integer", + "format" : "int32" }, - "serverTimeStamp$backend" : { + "latest" : { "type" : "number", "format" : "int64" } }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + "required" : [ "timestamp", "size" ] }, - "QueryResultLog" : { + "ApiHintContent" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "number", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { + "taskId" : { "type" : "string" }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { + "sequence" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/QueryEvent" + "$ref" : "#/components/schemas/ApiContentElement" } }, - "serverTimeStamp$backend" : { - "type" : "number", - "format" : "int64" + "loop" : { + "type" : "boolean" } }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + "required" : [ "taskId", "sequence", "loop" ] }, - "RestFullMediaCollection" : { + "ApiTeamTaskOverview" : { "type" : "object", "properties" : { - "collection" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "teamId" : { + "type" : "string" }, - "items" : { + "tasks" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestMediaItem" + "$ref" : "#/components/schemas/ApiTaskOverview" } } }, - "required" : [ "collection", "items" ] + "required" : [ "teamId", "tasks" ] }, - "SuccessfulSubmissionsStatus" : { + "ApiVerdict" : { "type" : "object", "properties" : { - "submission" : { - "$ref" : "#/components/schemas/SubmissionStatus" + "type" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT", "type" ] }, - "description" : { - "type" : "string" - } - }, - "required" : [ "submission", "description" ] - }, - "RunProperties" : { - "type" : "object", - "properties" : { - "participantCanView" : { - "type" : "boolean" + "status" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE", "status" ] }, - "shuffleTasks" : { - "type" : "boolean" + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" }, - "allowRepeatedTasks" : { - "type" : "boolean" + "text" : { + "type" : "string" }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" + "start" : { + "type" : "number", + "format" : "int64" + }, + "end" : { + "type" : "number", + "format" : "int64" } }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + "required" : [ "type", "status" ] }, - "Role" : { - "type" : "object", - "properties" : { } - }, - "RestMediaItemType" : { - "type" : "object", - "properties" : { } - }, - "RunType" : { - "type" : "object", - "properties" : { } - }, - "ContentElement" : { + "ApiTeamInfo" : { "type" : "object", "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ContentType" + "id" : { + "type" : "string" }, - "content" : { + "name" : { "type" : "string" }, - "offset" : { - "type" : "number", - "format" : "int64" + "color" : { + "type" : "string" } }, - "required" : [ "contentType", "offset" ] + "required" : [ "id", "name", "color" ] }, - "Score" : { + "ApiScore" : { "type" : "object", "properties" : { "teamId" : { @@ -5448,105 +5350,90 @@ }, "required" : [ "teamId", "score" ] }, - "TaskType" : { + "ApiScoreSeriesPoint" : { "type" : "object", "properties" : { - "name" : { - "type" : "string" + "score" : { + "type" : "number", + "format" : "double" }, - "taskDuration" : { + "timestamp" : { "type" : "number", "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiContentElement" : { + "type" : "object", + "properties" : { + "contentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE", "mimeType", "base64" ] }, - "targetType" : { - "$ref" : "#/components/schemas/ConfiguredOption" - }, - "components" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ConfiguredOption" - } - }, - "score" : { - "$ref" : "#/components/schemas/ConfiguredOption" - }, - "filter" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ConfiguredOption" - } + "content" : { + "type" : "string" }, - "options" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ConfiguredOption" - } + "offset" : { + "type" : "number", + "format" : "int64" } }, - "required" : [ "name", "taskDuration", "targetType", "components", "score", "filter", "options" ] + "required" : [ "contentType", "offset" ] }, - "TaskGroup" : { + "VerdictStatus" : { "type" : "object", "properties" : { - "name" : { - "type" : "string" - }, - "type" : { + "description" : { "type" : "string" } }, - "required" : [ "name", "type" ] + "required" : [ "description" ] }, - "RestTeamGroup" : { + "QueryEvent" : { "type" : "object", "properties" : { - "uid" : { - "type" : "string" + "timestamp" : { + "type" : "number", + "format" : "int64" }, - "name" : { - "type" : "string" + "category" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] }, - "teams" : { - "type" : "array", - "items" : { - "type" : "string" - } + "type" : { + "type" : "string" }, - "aggregation" : { + "value" : { "type" : "string" } }, - "required" : [ "name", "teams", "aggregation" ] + "required" : [ "timestamp", "category", "type", "value" ] }, - "TeamInfo" : { + "QueryResult" : { "type" : "object", "properties" : { - "uid" : { + "item" : { "type" : "string" }, - "name" : { - "type" : "string" + "segment" : { + "type" : "integer", + "format" : "int32" }, - "color" : { - "type" : "string" + "frame" : { + "type" : "integer", + "format" : "int32" }, - "logoId" : { - "type" : "string" + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "uid", "name", "color", "logoId" ] - }, - "RunManagerStatus" : { - "type" : "object", - "properties" : { } - }, - "RestTaskRunStatus" : { - "type" : "object", - "properties" : { } - }, - "SubmissionStatus" : { - "type" : "object", - "properties" : { } + "required" : [ "item" ] }, "TaskResult" : { "type" : "object", @@ -5569,62 +5456,28 @@ }, "required" : [ "task", "resultName", "results" ] }, - "AuditLogEntryType" : { - "type" : "object", - "properties" : { } - }, - "ClientRunInfo" : { + "ApiTarget" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" + "type" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT", "type" ] }, - "description" : { + "target" : { "type" : "string" }, - "status" : { - "$ref" : "#/components/schemas/RunManagerStatus" - } - }, - "required" : [ "id", "name", "status" ] - }, - "ScoreSeriesPoint" : { - "type" : "object", - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "number", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "RestTaskDescriptionTarget" : { - "type" : "object", - "properties" : { - "type" : { - "$ref" : "#/components/schemas/TargetOption" - }, - "mediaItems" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/RestTaskDescriptionTargetItem" - } + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" } }, - "required" : [ "type", "mediaItems" ] + "required" : [ "type" ] }, - "RestTaskDescriptionComponent" : { + "ApiHint" : { "type" : "object", "properties" : { "type" : { - "$ref" : "#/components/schemas/QueryComponentOption" + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE", "type" ] }, "start" : { "type" : "number", @@ -5647,122 +5500,125 @@ "type" : "string" }, "range" : { - "$ref" : "#/components/schemas/RestTemporalRange" + "$ref" : "#/components/schemas/ApiTemporalRange" } }, "required" : [ "type" ] }, - "JudgementRequestMediaType" : { - "type" : "object", - "properties" : { } - }, - "TeamTaskOverview" : { + "ApiTaskType" : { "type" : "object", "properties" : { - "teamId" : { + "name" : { "type" : "string" }, - "tasks" : { + "duration" : { + "type" : "number", + "format" : "int64" + }, + "targetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT", "option" ] + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiComponentOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/TaskRunOverview" + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS", "option" ] + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" } } }, - "required" : [ "teamId", "tasks" ] + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] }, - "QueryEvent" : { + "ApiTaskGroup" : { "type" : "object", "properties" : { - "timestamp" : { - "type" : "number", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { + "name" : { "type" : "string" }, - "value" : { + "type" : { "type" : "string" } }, - "required" : [ "timestamp", "category", "type", "value" ] + "required" : [ "name", "type" ] }, - "QueryResult" : { + "ApiTeamGroup" : { "type" : "object", "properties" : { - "item" : { + "id" : { "type" : "string" }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" + "name" : { + "type" : "string" }, - "score" : { - "type" : "number", - "format" : "double" + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "ContentType" : { - "type" : "object", - "properties" : { - "mimeType" : { + "aggregation" : { "type" : "string" - }, - "base64" : { - "type" : "boolean" } }, - "required" : [ "mimeType", "base64" ] + "required" : [ "id", "name", "teams", "aggregation" ] }, - "ConfiguredOption" : { + "ApiTaskOverview" : { "type" : "object", "properties" : { - "option" : { - "$ref" : "#/components/schemas/Option" - }, - "parameters" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } + "id" : { + "type" : "string" }, - "" : { + "name" : { "type" : "string" }, - "asBool" : { - "type" : "boolean" + "type" : { + "type" : "string" }, - "asInt" : { - "type" : "integer", - "format" : "int32" + "group" : { + "type" : "string" }, - "asLong" : { + "duration" : { "type" : "number", "format" : "int64" }, - "asFloat" : { + "taskId" : { + "type" : "string" + }, + "status" : { + "type" : "string", + "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] + }, + "started" : { "type" : "number", - "format" : "float" + "format" : "int64" }, - "asDouble" : { + "ended" : { "type" : "number", - "format" : "double" + "format" : "int64" } }, - "required" : [ "option", "parameters" ] + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] }, "ResultElement" : { "type" : "object", @@ -5793,107 +5649,84 @@ } } }, - "TargetOption" : { - "type" : "object", - "properties" : { } - }, - "RestTaskDescriptionTargetItem" : { - "type" : "object", - "properties" : { - "mediaItem" : { - "type" : "string" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/RestTemporalRange" - } - }, - "required" : [ "mediaItem" ] - }, - "QueryComponentOption" : { - "type" : "object", - "properties" : { } - }, - "RestTemporalRange" : { + "ApiTemporalRange" : { "type" : "object", "properties" : { "start" : { - "$ref" : "#/components/schemas/RestTemporalPoint" + "$ref" : "#/components/schemas/ApiTemporalPoint" }, "end" : { - "$ref" : "#/components/schemas/RestTemporalPoint" + "$ref" : "#/components/schemas/ApiTemporalPoint" } }, "required" : [ "start", "end" ] }, - "TaskRunOverview" : { + "ApiComponentOption" : { "type" : "object", "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "number", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/TaskRunStatus" - }, - "started" : { - "type" : "number", - "format" : "int64" - }, - "ended" : { - "type" : "number", - "format" : "int64" + "option" : { + "$ref" : "#/components/schemas/HintOption" } }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + "required" : [ "option" ] }, - "QueryEventCategory" : { + "ApiSubmissionOption" : { "type" : "object", - "properties" : { } + "properties" : { + "option" : { + "$ref" : "#/components/schemas/SubmissionOption" + } + }, + "required" : [ "option" ] }, - "Option" : { + "ApiTaskOption" : { "type" : "object", "properties" : { - "ordinal" : { - "type" : "integer", - "format" : "int32" + "option" : { + "$ref" : "#/components/schemas/TaskOption" } }, - "required" : [ "ordinal" ] + "required" : [ "option" ] }, - "RestTemporalPoint" : { + "ApiTemporalPoint" : { "type" : "object", "properties" : { "value" : { "type" : "string" }, "unit" : { - "$ref" : "#/components/schemas/RestTemporalUnit" + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] } }, "required" : [ "value", "unit" ] }, - "TaskRunStatus" : { + "HintOption" : { + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ] + }, + "SubmissionOption" : { "type" : "object", - "properties" : { } + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ] }, - "RestTemporalUnit" : { + "TaskOption" : { "type" : "object", - "properties" : { } + "properties" : { + "description" : { + "type" : "string" + } + }, + "required" : [ "description" ] } }, "securitySchemes" : { } From 39f1bab9532142d80ab07c4280c7256dd9921680 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 17:38:43 +0100 Subject: [PATCH 043/498] OpenAPI generator now uses proper version information --- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 5e6c770d2..ac44e195f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -2,6 +2,7 @@ package dev.dres.api.rest import GetAuditLogInfoHandler import GetTaskHintHandler +import dev.dres.DRES import dev.dres.api.rest.handler.* import dev.dres.api.rest.handler.audit.ListAuditLogsHandler import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler @@ -202,8 +203,8 @@ object RestApi { OpenApiPlugin( OpenApiConfiguration().apply { this.info.title = "DRES API" - this.info.version = "1.0" - this.info.description = "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0" + this.info.version = DRES.VERSION + this.info.description = "API for DRES (Distributed Retrieval Evaluation Server), Version ${DRES.VERSION}" this.documentationPath = "/swagger-docs" } ) From e587604089b3050cd6d936069f98de65015457e8 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 18:30:49 +0100 Subject: [PATCH 044/498] Started frontend api adaption --- frontend/src/app/app-routing.module.ts | 4 +- frontend/src/app/app.component.ts | 6 +- .../admin-auditlog-overview.component.ts | 22 +++--- .../auditlog.datasource.ts | 12 ++-- .../media-item-builder-dialog.component.ts | 14 ++-- .../collection-list.component.ts | 8 +-- .../collection-viewer.component.ts | 16 ++--- ...mpetition-builder-task-dialog.component.ts | 48 ++++++------- .../competition-form.builder.ts | 20 +++--- ...mpetition-builder-team-dialog.component.ts | 6 +- .../competition-builder.component.ts | 68 +++++++++---------- .../competition-start-dialog.component.ts | 6 +- .../judgement/judgement-viewer.component.ts | 16 ++--- frontend/src/app/model/user-group.model.ts | 4 +- .../app/run/abstract-run-list.component.ts | 12 ++-- .../run-async-admin-view.component.ts | 12 ++-- frontend/src/app/run/run-list.component.ts | 4 +- .../services/session/access-role.service.ts | 4 +- .../services/session/authentication.sevice.ts | 23 +++---- ...in-user-create-or-edit-dialog.component.ts | 10 +-- .../admin-user-list.component.ts | 22 +++--- .../src/app/user/profile/profile.component.ts | 10 +-- .../src/app/viewer/run-viewer.component.ts | 18 ++--- .../src/app/viewer/teams-viewer.component.ts | 6 +- 24 files changed, 185 insertions(+), 186 deletions(-) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 94dfb1e62..6e3d79c15 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -4,7 +4,7 @@ import { CompetitionBuilderComponent } from './competition/competition-builder/c import { CompetitionListComponent } from './competition/competition-list/competition-list.component'; import { LoginComponent } from './user/login/login.component'; import { AuthenticationGuard } from './services/session/authentication.guard'; -import { UserDetails } from '../../openapi'; +import { ApiUser } from '../../openapi'; import { RunViewerComponent } from './viewer/run-viewer.component'; import { ProfileComponent } from './user/profile/profile.component'; import { AdminUserListComponent } from './user/admin-user-list/admin-user-list.component'; @@ -18,7 +18,7 @@ import { CanDeactivateGuard } from './services/can-deactivate.guard'; import { RunAdminSubmissionsListComponent } from './run/run-admin-submissions-list/run-admin-submissions-list.component'; import { RunScoreHistoryComponent } from './run/score-history/run-score-history.component'; import { JudgementVotingViewerComponent } from './judgement/judgement-voting-viewer.component'; -import RoleEnum = UserDetails.RoleEnum; +import RoleEnum = ApiUser.RoleEnum; import { RunAsyncAdminViewComponent } from './run/run-async-admin-view/run-async-admin-view.component'; import { NonescapingUrlserializerClass } from './nonescaping-urlserializer.class'; diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 63a948fc2..7e4215593 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,12 +1,12 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { AuthenticationService } from './services/session/authentication.sevice'; -import { UserDetails } from '../../openapi'; +import { ApiUser } from '../../openapi'; import { MatSnackBar } from '@angular/material/snack-bar'; import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { AppConfig } from './app.config'; -import RoleEnum = UserDetails.RoleEnum; +import RoleEnum = ApiUser.RoleEnum; @Component({ selector: 'app-root', @@ -16,7 +16,7 @@ import RoleEnum = UserDetails.RoleEnum; export class AppComponent { title = 'dres-frontend'; - user: Observable; + user: Observable; isAdmin: Observable; loggedIn: Observable; canJudge: Observable; diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts index 95e6c3d05..8f027fa10 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts +++ b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts @@ -1,15 +1,15 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; import { AuditService, - RestAuditLogEntry, - RestCompetitionEndAuditLogEntry, - RestJudgementAuditLogEntry, - RestLoginAuditLogEntry, - RestLogoutAuditLogEntry, - RestSubmissionAuditLogEntry, - RestTaskEndAuditLogEntry, - RestTaskModifiedAuditLogEntry, - RestTaskStartAuditLogEntry, + ApiAuditLogEntry, + ApiCompetitionEndAuditLogEntry, + ApiJudgementAuditLogEntry, + ApiLoginAuditLogEntry, + ApiLogoutAuditLogEntry, + ApiSubmissionAuditLogEntry, + ApiTaskEndAuditLogEntry, + ApiTaskModifiedAuditLogEntry, + ApiTaskStartAuditLogEntry, } from '../../../../openapi'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Subscription, timer } from 'rxjs'; @@ -86,11 +86,11 @@ export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy this.paginationSub = null; } - resolveAuditLogEntryById(_: number, item: RestAuditLogEntry) { + resolveAuditLogEntryById(_: number, item: ApiAuditLogEntry) { return item.id; } - public detailsOf(log: RestAuditLogEntry): string { + public detailsOf(log: ApiAuditLogEntry): string { switch (log.type) { case 'COMPETITION_START': const cs = log as unknown as RestCompetitionEndAuditLogEntry; diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts index 87ac00f73..57ea8fd35 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts +++ b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts @@ -1,14 +1,14 @@ import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { AuditService, RestAuditLogEntry } from '../../../../openapi'; +import { AuditService, ApiAuditLogEntry } from '../../../../openapi'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, finalize, first } from 'rxjs/operators'; /** - * {@link DataSource} implementation of {@link RestAuditLogEntry}. + * {@link DataSource} implementation of {@link ApiAuditLogEntry}. */ -export class AuditlogDatasource implements DataSource { - /** {@link BehaviorSubject} used to publish {@link RestAuditLogEntry} array. */ - private logsSubject = new BehaviorSubject([]); +export class AuditlogDatasource implements DataSource { + /** {@link BehaviorSubject} used to publish {@link ApiAuditLogEntry} array. */ + private logsSubject = new BehaviorSubject([]); private loadingSubject = new BehaviorSubject(false); constructor(private logService: AuditService) {} @@ -18,7 +18,7 @@ export class AuditlogDatasource implements DataSource { * * @param collectionViewer The collection viewer */ - connect(collectionViewer: CollectionViewer): Observable { + connect(collectionViewer: CollectionViewer): Observable { return this.logsSubject.asObservable(); } diff --git a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts index b0c522b37..2ca5b96b0 100644 --- a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts @@ -1,10 +1,10 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { RestMediaItem } from '../../../../../openapi'; +import { ApiMediaItem } from '../../../../../openapi'; export interface MediaItemBuilderData { - item?: RestMediaItem; + item?: ApiMediaItem; collectionId: string; } @@ -16,7 +16,7 @@ export interface MediaItemBuilderData { export class MediaItemBuilderDialogComponent implements OnInit { form: FormGroup; - types = Object.values(RestMediaItem.TypeEnum).sort((a, b) => a.localeCompare(b)); + types = Object.values(ApiMediaItem.TypeEnum).sort((a, b) => a.localeCompare(b)); constructor( public dialogRef: MatDialogRef, @@ -29,7 +29,7 @@ export class MediaItemBuilderDialogComponent implements OnInit { type: new FormControl({ value: data?.item?.type, disabled: this.isEditing() }, [Validators.required]), collectionId: new FormControl(data.collectionId), }); - if (data?.item?.type === RestMediaItem.TypeEnum.VIDEO) { + if (data?.item?.type === ApiMediaItem.TypeEnum.VIDEO) { this.form.addControl('durationMs', new FormControl(data?.item?.durationMs, [Validators.required, Validators.min(1)])); this.form.addControl( 'fps', @@ -72,19 +72,19 @@ export class MediaItemBuilderDialogComponent implements OnInit { console.log(this.asJson()); } - private fetchFormData(): RestMediaItem { + private fetchFormData(): ApiMediaItem { const item = { name: this.form.get('name').value, location: this.form.get('location').value, type: this.form.get('type').value, collectionId: this.form.get('collectionId').value, - } as RestMediaItem; + } as ApiMediaItem; /* Are we editing ? */ if (this.isEditing()) { item.id = this.form.get('id').value; } /* only relevant for video */ - if (item.type === RestMediaItem.TypeEnum.VIDEO) { + if (item.type === ApiMediaItem.TypeEnum.VIDEO) { item.durationMs = this.form.get('durationMs').value; item.fps = this.form.get('fps').value; } diff --git a/frontend/src/app/collection/collection-list/collection-list.component.ts b/frontend/src/app/collection/collection-list/collection-list.component.ts index d8e6a4ac4..e60904502 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.ts +++ b/frontend/src/app/collection/collection-list/collection-list.component.ts @@ -23,7 +23,7 @@ export class CollectionListComponent implements AfterViewInit { ) {} refresh() { - this.collectionService.getApiV1CollectionList().subscribe( + this.collectionService.apiV2CollectionListGet().subscribe( (results: RestMediaCollection[]) => { this.collections = results; }, @@ -52,9 +52,9 @@ export class CollectionListComponent implements AfterViewInit { filter((r) => r != null), flatMap((r: RestMediaCollection) => { if (id) { - return this.collectionService.patchApiV1Collection(r); + return this.collectionService.apiV2CollectionPatch(r); } else { - return this.collectionService.postApiV1Collection(r); + return this.collectionService.apiV2CollectionPost(r); } }) ) @@ -75,7 +75,7 @@ export class CollectionListComponent implements AfterViewInit { delete(id: string) { if (confirm(`Do you really want to delete collection with ID ${id}?`)) { - this.collectionService.deleteApiV1CollectionWithCollectionid(id).subscribe( + this.collectionService.apiV2CollectionCollectionIdDelete(id).subscribe( (r) => { this.refresh(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index 57795ebd9..2e4edc9a2 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -1,5 +1,5 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; -import { CollectionService, RestFullMediaCollection, RestMediaItem } from '../../../../openapi'; +import { CollectionService, RestFullMediaCollection, ApiMediaItem } from '../../../../openapi'; import { ActivatedRoute, Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; @@ -29,7 +29,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; /** Data source for Material tabl.e */ - dataSource = new MatTableDataSource(); + dataSource = new MatTableDataSource(); /** Observable containing the collection ID of the collection displayed by this component. Derived from active route. */ collectionId: Observable; @@ -72,7 +72,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { this.collection = this.refreshSubject.pipe( flatMap((s) => this.collectionId), switchMap((id) => - this.collectionService.getApiV1CollectionWithCollectionid(id).pipe( + this.collectionService.apiV2CollectionCollectionIdGet(id).pipe( retry(3), catchError((err, o) => { console.log(`[CollectionViewer.${id}] There was an error while loading the current collection ${err?.message}`); @@ -101,7 +101,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { delete(id: string) { if (confirm(`Do you really want to delete media item with ID ${id}?`)) { - this.collectionService.deleteApiV1MediaitemWithMediaid(id).subscribe( + this.collectionService.apiV2MediaItemMediaIdDelete(id).subscribe( (r) => { this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -136,11 +136,11 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { .afterClosed() .pipe( filter((r) => r != null), - flatMap((r: RestMediaItem) => { + flatMap((r: ApiMediaItem) => { if (id) { - return this.collectionService.patchApiV1Mediaitem(r); + return this.collectionService.apiV2MediaitemPatch(r); } else { - return this.collectionService.postApiV1Mediaitem(r); + return this.collectionService.apiV2MediaItemPost(r); } }) ) @@ -156,7 +156,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { }); } - resolveMediaItemById(_: number, item: RestMediaItem) { + resolveMediaItemById(_: number, item: ApiMediaItem) { return item.id; } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index af71f7308..46bb7cd46 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -5,12 +5,12 @@ import { ConfiguredOptionQueryComponentOption, ConfiguredOptionTargetOption, RestMediaCollection, - RestMediaItem, - RestTaskDescription, - RestTemporalPoint, - RestTemporalRange, - TaskGroup, - TaskType, + ApiMediaItem, + ApiTaskTemplate, + ApiTemporalPoint, + ApiTemporalRange, + ApiTaskGroup, + ApiTaskType, } from '../../../../../openapi'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -29,9 +29,9 @@ import { TimeUtilities } from '../../../utilities/time.utilities'; * even in the case this is 'edit'! */ export interface CompetitionBuilderTaskDialogData { - taskGroup: TaskGroup; - taskType: TaskType; - task?: RestTaskDescription; + taskGroup: ApiTaskGroup; + taskType: ApiTaskType; + task?: ApiTaskTemplate; } @Component({ @@ -61,7 +61,7 @@ export class CompetitionBuilderTaskDialogComponent { ) { this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.data.task); this.form = this.builder.form; - this.mediaCollectionSource = this.collectionService.getApiV1CollectionList(); + this.mediaCollectionSource = this.collectionService.apiV2CollectionListGet(); } private static randInt(min: number, max: number): number { @@ -71,7 +71,7 @@ export class CompetitionBuilderTaskDialogComponent { } uploaded = (taskData: string) => { - const task = JSON.parse(taskData) as RestTaskDescription; + const task = JSON.parse(taskData) as ApiTaskTemplate; this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, task); this.form = this.builder.form; console.log('Loaded task: ' + JSON.stringify(task)); @@ -114,7 +114,7 @@ export class CompetitionBuilderTaskDialogComponent { * * @param value MediaItem to convert */ - public mediaItemToDisplay(value: RestMediaItem) { + public mediaItemToDisplay(value: ApiMediaItem) { if (value) { return `${value.name} (${value.type})`; } else { @@ -150,7 +150,7 @@ export class CompetitionBuilderTaskDialogComponent { */ public pickRandomMediaItem(collectionId: string, target: FormControl) { this.collectionService - .getApiV1CollectionWithCollectionidRandom(collectionId) + .apiV2CollectionCollectionIdRandomGet(collectionId) .pipe(first()) .subscribe((value) => { target.setValue(value); @@ -165,7 +165,7 @@ export class CompetitionBuilderTaskDialogComponent { * @param endControl The target {@link FormControl} to apply the value to. * @param unitControl The target {@link FormControl} to apply the value to. */ - public pickRandomSegment(item: RestMediaItem, startControl: FormControl, endControl: FormControl, unitControl: FormControl) { + public pickRandomSegment(item: ApiMediaItem, startControl: FormControl, endControl: FormControl, unitControl: FormControl) { const start = CompetitionBuilderTaskDialogComponent.randInt(1, item.durationMs / 1000 / 2); // always in first half let end = 1; do { @@ -176,7 +176,7 @@ export class CompetitionBuilderTaskDialogComponent { unitControl.setValue('SECONDS'); } - toggleVideoPlayer(mediaItem: RestMediaItem, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { /* Add to toggleVideoPlayer button if [disabled]="!target.get('mediaItem').value && !target.get('segment_start').value && !target.get('segment_end').value" */ @@ -186,13 +186,13 @@ export class CompetitionBuilderTaskDialogComponent { */ let start = -1; let end = -1; - const unit = unitControl?.value ? (unitControl.value as RestTemporalPoint.UnitEnum) : RestTemporalPoint.UnitEnum.SECONDS; + const unit = unitControl?.value ? (unitControl.value as ApiTemporalPoint.UnitEnum) : ApiTemporalPoint.UnitEnum.SECONDS; if (startControl && startControl.value) { if (unitControl.value === 'TIMECODE') { start = TimeUtilities.timeCode2Milliseconds(startControl.value, mediaItem.fps) / 1000; } else { start = - TimeUtilities.point2Milliseconds({ value: startControl.value, unit } as RestTemporalPoint, mediaItem.fps) / 1000; + TimeUtilities.point2Milliseconds({ value: startControl.value, unit } as ApiTemporalPoint, mediaItem.fps) / 1000; } // start = Number.parseInt(startControl.value, 10); } @@ -200,7 +200,7 @@ export class CompetitionBuilderTaskDialogComponent { if (unitControl.value === 'TIMECODE') { end = TimeUtilities.timeCode2Milliseconds(endControl.value, mediaItem.fps) / 1000; } else { - end = TimeUtilities.point2Milliseconds({ value: endControl.value, unit } as RestTemporalPoint, mediaItem.fps) / 1000; + end = TimeUtilities.point2Milliseconds({ value: endControl.value, unit } as ApiTemporalPoint, mediaItem.fps) / 1000; } } @@ -221,14 +221,14 @@ export class CompetitionBuilderTaskDialogComponent { this.showVideo = !this.showVideo; } - onRangeChange(range: RestTemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + onRangeChange(range: ApiTemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { startControl?.setValue(range.start.value); endControl?.setValue(range.end.value); - unitControl?.setValue(RestTemporalPoint.UnitEnum.SECONDS); + unitControl?.setValue(ApiTemporalPoint.UnitEnum.SECONDS); console.log('Range updated'); } - isImageMediaItem(mi: RestMediaItem): boolean { + isImageMediaItem(mi: ApiMediaItem): boolean { if (mi) { return mi.type === 'IMAGE'; } else { @@ -253,7 +253,7 @@ export class CompetitionBuilderTaskDialogComponent { } } - getImageUrl(mi: RestMediaItem) { + getImageUrl(mi: ApiMediaItem) { if (mi && mi.type === 'IMAGE') { return this.config.resolveApiUrl(`/media/${mi.collectionId}/${mi.id}`); } @@ -280,7 +280,7 @@ export class CompetitionBuilderTaskDialogComponent { .subscribe((r: Array) => { this.builder.removeTargetForm(0); const mediaCollectionId = this.builder.form.get('mediaCollection').value; - this.collectionService.postApiV1CollectionWithCollectionidResolve(mediaCollectionId, r).subscribe((items) => { + this.collectionService.apiV2CollectionCollectionIdResolvePost(mediaCollectionId, r).subscribe((items) => { items.forEach((item) => { const form = this.builder.addTargetForm(ConfiguredOptionTargetOption.OptionEnum.MULTIPLE_MEDIA_ITEMS); console.log(`Adding new mediaItem as target ${mediaCollectionId}/${item.name}`); @@ -316,7 +316,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for 'close' button. */ - private pathForItem(item: RestMediaItem): string { + private pathForItem(item: ApiMediaItem): string { // units = ['FRAME_NUMBER', 'SECONDS', 'MILLISECONDS', 'TIMECODE']; let timeSuffix = ''; switch (this.form.get('time_unit').value) { diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 2577c3ce7..feceed6e5 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -301,7 +301,7 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.getApiV1CollectionWithCollectionidWithStartswith(this.form.get('mediaCollection').value, s) + this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) ) ) ); @@ -309,7 +309,7 @@ export class CompetitionFormBuilder { /* Load media item from API. */ if (initialize?.mediaItem && this.data?.mediaCollectionId) { this.collectionService - .getApiV1MediaitemWithMediaid(initialize.mediaItem) + .apiV2MediaItemMediaIdGet(initialize.mediaItem) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -333,7 +333,7 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.getApiV1CollectionWithCollectionidWithStartswith(this.form.get('mediaCollection').value, s) + this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) ) ) ); @@ -341,7 +341,7 @@ export class CompetitionFormBuilder { /* Load media item from API. */ if (initialize?.mediaItem && this.data.mediaCollectionId) { this.collectionService - .getApiV1MediaitemWithMediaid(initialize.mediaItem) + .apiV2MediaItemMediaIdGet(initialize.mediaItem) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -434,7 +434,7 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.getApiV1CollectionWithCollectionidWithStartswith(this.form.get('mediaCollection').value, s) + this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) ) ) ); @@ -442,7 +442,7 @@ export class CompetitionFormBuilder { /* Load media item from API. */ if (initialize?.mediaItem && this.data?.mediaCollectionId) { this.collectionService - .getApiV1MediaitemWithMediaid(initialize?.mediaItem) + .apiV2MediaItemMediaIdGet(initialize?.mediaItem) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -484,7 +484,7 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.getApiV1CollectionWithCollectionidWithStartswith(this.form.get('mediaCollection').value, s) + this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) ) ) ); @@ -492,7 +492,7 @@ export class CompetitionFormBuilder { /* Load media item from API. */ if (initialize?.mediaItem && this.data?.mediaCollectionId) { this.collectionService - .getApiV1MediaitemWithMediaid(initialize.mediaItem) + .apiV2MediaItemMediaIdGet(initialize.mediaItem) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -586,7 +586,7 @@ export class CompetitionFormBuilder { `components.${index}.path`, pathFormControl.valueChanges.pipe( filter((s) => s.length >= 1), - switchMap((s) => this.collectionService.getApiV1ExternalWithStartswith(s)) + switchMap((s) => this.collectionService.apiV2ExternalStartsWithGet(s)) ) ); @@ -618,7 +618,7 @@ export class CompetitionFormBuilder { `components.${index}.path`, pathFormControl.valueChanges.pipe( filter((s) => s.length >= 1), - switchMap((s) => this.collectionService.getApiV1ExternalWithStartswith(s)) + switchMap((s) => this.collectionService.apiV2ExternalStartsWithGet(s)) ) ); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index 37e542eb6..42ed260be 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { RestTeam, UserDetails, UserService } from '../../../../../openapi'; +import { RestTeam, ApiUser, UserService } from '../../../../../openapi'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { map, shareReplay } from 'rxjs/operators'; import { Observable } from 'rxjs'; @@ -14,7 +14,7 @@ import { AppConfig } from '../../../app.config'; export class CompetitionBuilderTeamDialogComponent { form: FormGroup; logoName = ''; - availableUsers: Observable; + availableUsers: Observable; colorPalette = [ '#BF0000', '#BF3900', @@ -116,7 +116,7 @@ export class CompetitionBuilderTeamDialogComponent { * * @param id User ID of the desired user. */ - public userForId(id: string): Observable { + public userForId(id: string): Observable { return this.availableUsers.pipe(map((users) => users.find((u) => u.id === id))); } diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 95a47b7ae..22bb1d036 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -9,12 +9,12 @@ import { ConfiguredOptionSimpleOption, ConfiguredOptionSubmissionFilterOption, ConfiguredOptionTargetOption, - RestCompetitionDescription, - RestTaskDescription, - RestTeam, - TaskGroup, - TaskType, - UserDetails, + ApiEvaluationTemplate, + ApiTaskTemplate, + ApiTeam, + ApiTaskGroup, + ApiTaskType, + ApiUser, UserService, UserRequest, } from '../../../../openapi'; @@ -59,7 +59,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat { option: ConfiguredOptionSubmissionFilterOption.OptionEnum.TEMPORAL_SUBMISSION, parameters: {} }, ], options: [{ option: ConfiguredOptionSimpleOption.OptionEnum.HIDDEN_RESULTS, parameters: {} }], - } as TaskType; + } as ApiTaskType; /** * The official VBS Visual Known Item Search task type template @@ -76,7 +76,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat { option: ConfiguredOptionSubmissionFilterOption.OptionEnum.TEMPORAL_SUBMISSION, parameters: {} }, ], options: [], - } as TaskType; + } as ApiTaskType; /** * The official VBS Ad-hoc Video Search task type template @@ -93,7 +93,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat { option: ConfiguredOptionSubmissionFilterOption.OptionEnum.LIMIT_CORRECT_PER_ITEM_AND_TEAM, parameters: {} }, ], options: [{ option: ConfiguredOptionSimpleOption.OptionEnum.MAP_TO_SEGMENT, parameters: {} }], - } as TaskType; + } as ApiTaskType; /** * The official legacy (pre 2023) VBS Ad-hoc Video Search task type template @@ -109,7 +109,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat { option: ConfiguredOptionSubmissionFilterOption.OptionEnum.TEMPORAL_SUBMISSION, parameters: {} }, ], options: [{ option: ConfiguredOptionSimpleOption.OptionEnum.MAP_TO_SEGMENT, parameters: {} }], - } as TaskType; + } as ApiTaskType; /** * The official LSC task type template @@ -125,16 +125,16 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat { option: ConfiguredOptionSubmissionFilterOption.OptionEnum.LIMIT_CORRECT_PER_TEAM, parameters: {} }, ], options: [{ option: ConfiguredOptionSimpleOption.OptionEnum.HIDDEN_RESULTS, parameters: {} }], - } as TaskType; + } as ApiTaskType; competitionId: string; - competition: RestCompetitionDescription; + competition: ApiEvaluationTemplate; @ViewChild('taskTable') taskTable: MatTable; @ViewChild('teamTable') teamTable: MatTable; @ViewChild('judgesTable') - judgesTable: MatTable; + judgesTable: MatTable; displayedColumnsTeams: string[] = ['logo', 'name', 'action']; displayedColumnsJudges: string[] = ['name', 'action']; displayedColumnsTasks: string[] = ['name', 'group', 'type', 'duration', 'action']; @@ -143,7 +143,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat routeSubscription: Subscription; changeSubscription: Subscription; - availableJudges: Observable; + availableJudges: Observable; /** * Ref to template for easy access in thml @@ -172,7 +172,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat private dialog: MatDialog, private config: AppConfig ) { - this.availableJudges = this.userService.getApiV1UserList().pipe( + this.availableJudges = this.userService.apiV2UserListGet().pipe( map((users) => users.filter((user) => user.role === RoleEnum.JUDGE)), shareReplay(1) ); @@ -220,7 +220,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat }; downloadProvider = () => { - return this.downloadService.getApiV1DownloadCompetitionWithCompetitionid(this.competitionId).pipe(take(1)); + return this.downloadService.apiV2DownloadEvaluationEvaluationIdGet(this.competitionId).pipe(take(1)); // .toPromise(); }; @@ -247,7 +247,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat } } - public addTaskType(type?: TaskType) { + public addApiTaskType(type?: ApiTaskType) { const dialogRef = this.dialog.open(CompetitionBuilderTaskTypeDialogComponent, { data: type ? type : null, width: '750px' }); dialogRef .afterClosed() @@ -261,9 +261,9 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat /** * Removes a task type and all associated task groups. * - * @param taskType The {@link TaskType} to remove. + * @param ApiTaskType The {@link ApiTaskType} to remove. */ - public removeTaskType(taskType: TaskType) { + public removeTaskType(taskType: ApiTaskType) { this.competition.taskTypes.splice(this.competition.taskTypes.indexOf(taskType), 1); this.competition.taskGroups .filter((t) => t.type === taskType.name) @@ -276,7 +276,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat /** * Opens the dialog to add a new task group. */ - public addTaskGroup(group?: TaskGroup) { + public addTaskGroup(group?: ApiTaskGroup) { const dialogRef = this.dialog.open(CompetitionBuilderTaskGroupDialogComponent, { data: { types: this.competition.taskTypes, group: group ? group : null } as CompetitionBuilderTaskGroupDialogData, width: '750px', @@ -295,7 +295,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param group The {@link TaskGroup} to remove */ - public removeTaskGroup(group: TaskGroup) { + public removeTaskGroup(group: ApiTaskGroup) { this.competition.taskGroups.splice(this.competition.taskGroups.indexOf(group), 1); this.competition.tasks .filter((t) => t.taskGroup === group.name) @@ -310,7 +310,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param group The TaskGroup to add a description to. */ - public addTask(group: TaskGroup) { + public addTask(group: ApiTaskGroup) { const type = this.competition.taskTypes.find((v) => v.name === group.type); const width = 750; const dialogRef = this.dialog.open(CompetitionBuilderTaskDialogComponent, { @@ -332,7 +332,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param task The {@link RestTaskDescription} to edit. */ - public editTask(task: RestTaskDescription) { + public editTask(task: ApiTaskTemplate) { const index = this.competition.tasks.indexOf(task); const width = 750; if (index > -1) { @@ -360,7 +360,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param task The {@link RestTaskDescription} that should be moved. */ - public moveTaskUp(task: RestTaskDescription) { + public moveTaskUp(task: ApiTaskTemplate) { const oldIndex = this.competition.tasks.indexOf(task); if (oldIndex > 0) { const buffer = this.competition.tasks[oldIndex - 1]; @@ -376,7 +376,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param task The {@link RestTaskDescription} that should be moved. */ - public moveTaskDown(task: RestTaskDescription) { + public moveTaskDown(task: ApiTaskTemplate) { const oldIndex = this.competition.tasks.indexOf(task); if (oldIndex < this.competition.tasks.length - 1) { const buffer = this.competition.tasks[oldIndex + 1]; @@ -392,7 +392,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat * * @param task The {@link RestTaskDescription} to remove. */ - public removeTask(task: RestTaskDescription) { + public removeTask(task: ApiTaskTemplate) { this.competition.tasks.splice(this.competition.tasks.indexOf(task), 1); this.dirty = true; this.taskTable.renderRows(); @@ -401,7 +401,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat /** * Generates a URL for the logo of the team. */ - public teamLogo(team: RestTeam): string { + public teamLogo(team: ApiTeam): string { if (team.logoData != null) { return team.logoData; } else { @@ -421,14 +421,14 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat }); } - public editTeam(team: RestTeam) { + public editTeam(team: ApiTeam) { const index = this.competition.teams.indexOf(team); if (index > -1) { const dialogRef = this.dialog.open(CompetitionBuilderTeamDialogComponent, { data: team, width: '500px' }); dialogRef .afterClosed() .pipe(filter((t) => t != null)) - .subscribe((t: RestTeam) => { + .subscribe((t: ApiTeam) => { this.competition.teams[index] = t; this.dirty = true; this.teamTable.renderRows(); @@ -436,13 +436,13 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat } } - public removeTeam(team: RestTeam) { + public removeTeam(team: ApiTeam) { this.competition.teams.splice(this.competition.teams.indexOf(team), 1); this.dirty = true; this.teamTable.renderRows(); } - public judgeFor(id: string): Observable { + public judgeFor(id: string): Observable { return this.availableJudges.pipe(map((users) => users.find((u) => u.id === id))); } @@ -461,16 +461,16 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat this.judgesTable.renderRows(); } - public dispJudge(user: UserDetails) { + public dispJudge(user: ApiUser) { return user.username; } /** * Summarises a task type to present detailed info as tooltip. * - * @param taskType The {@link TaskType} to summarize. + * @param taskType The {@link ApiTaskType} to summarize. */ - summariseTaskType(taskType: TaskType): string { + summariseTaskType(taskType: ApiTaskType): string { return `Consists of ${taskType.components.map((c) => c.option).join(', ')}, has filters: ${taskType.filter .map((f) => f.option) .join(', ')} and options: ${taskType.options.map((o) => o.option).join(', ')}`; diff --git a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts index dbcddeebb..2588dfd95 100644 --- a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts +++ b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { MatDialogRef } from '@angular/material/dialog'; -import { CompetitionStartMessage } from '../../../../openapi'; +import { ApiCompetitionStartMessage } from '../../../../openapi'; export interface CompetitionStartDialogResult { name: string; - type: CompetitionStartMessage.TypeEnum; + type: ApiCompetitionStartMessage.TypeEnum; participantCanView: boolean; allowRepeatedTasks: boolean; shuffleTasks: boolean; @@ -25,7 +25,7 @@ export class CompetitionStartDialogComponent { allowRepeatedTasks: new FormControl(false), limit: new FormControl(0) }); - runTypes: CompetitionStartMessage.TypeEnum[] = ['SYNCHRONOUS', 'ASYNCHRONOUS']; + runTypes: ApiCompetitionStartMessage.TypeEnum[] = ['SYNCHRONOUS', 'ASYNCHRONOUS']; typeObservable = this.form.get('type').valueChanges; diff --git a/frontend/src/app/judgement/judgement-viewer.component.ts b/frontend/src/app/judgement/judgement-viewer.component.ts index 1a93bdbc4..2fabc30f7 100644 --- a/frontend/src/app/judgement/judgement-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-viewer.component.ts @@ -1,6 +1,6 @@ import {AfterViewInit, Component, HostListener, Input, OnDestroy, ViewChild} from '@angular/core'; import {BehaviorSubject, interval, Observable, of, Subscription, timer} from 'rxjs'; -import {Judgement, JudgementRequest, JudgementService, SubmissionInfo} from '../../../openapi'; +import {ApiJudgement, ApiJudgementRequest, JudgementService} from '../../../openapi'; import {ActivatedRoute, Router} from '@angular/router'; import {catchError, filter, map, switchMap, withLatestFrom} from 'rxjs/operators'; import {JudgementMediaViewerComponent} from './judgement-media-viewer.component'; @@ -44,8 +44,8 @@ export class JudgementViewerComponent implements AfterViewInit, OnDestroy { @Input() pollingFrequency = 1000; @Input() timeout = 60; @ViewChild(JudgementMediaViewerComponent) judgePlayer: JudgementMediaViewerComponent; - observableJudgementRequest: BehaviorSubject = new BehaviorSubject(null); - judgementRequest: JudgementRequest; + observableJudgementRequest: BehaviorSubject = new BehaviorSubject(null); + judgementRequest: ApiJudgementRequest; prevDescHash: number; noJudgementMessage = ''; isJudgmentAvailable = false; @@ -122,7 +122,7 @@ export class JudgementViewerComponent implements AfterViewInit, OnDestroy { .pipe( withLatestFrom(this.runId), switchMap(([i, runId]) => { - return this.judgementService.getApiV1RunWithRunidJudgeStatus(runId).pipe( + return this.judgementService.apiV2EvaluationEvaluationIdJudgeStatusGet(runId).pipe( catchError((err) => { console.log('Error in JudgeStatus'); console.log(err); @@ -150,8 +150,8 @@ export class JudgementViewerComponent implements AfterViewInit, OnDestroy { switchMap(([i, runId]) => { /* Stop polling while judgment is ongooing */ if (this.runId && !this.isJudgmentAvailable) { - return this.judgementService.getApiV1RunWithRunidJudgeNext(runId, 'response').pipe( - map((req: HttpResponse) => { + return this.judgementService.apiV2EvaluationEvaluationIdJudgeNextGet(runId, 'response').pipe( + map((req: HttpResponse) => { if (req.status === 202) { this.noJudgementMessage = 'There is currently no submission awaiting judgement.'; /* Don't penalise if there's nothing to do*/ @@ -221,9 +221,9 @@ export class JudgementViewerComponent implements AfterViewInit, OnDestroy { token: this.judgementRequest.token, validator: this.judgementRequest.validator, verdict: status, - } as Judgement; + } as ApiJudgement; this.runId - .pipe(switchMap((runId) => this.judgementService.postApiV1RunWithRunidJudge(runId, judgement)), + .pipe(switchMap((runId) => this.judgementService.apiV2RunRunIdJudgePost(runId, judgement)), catchError((err) => { const httperr = err as HttpErrorResponse; if (httperr) { diff --git a/frontend/src/app/model/user-group.model.ts b/frontend/src/app/model/user-group.model.ts index b17401c85..dc93edc2c 100644 --- a/frontend/src/app/model/user-group.model.ts +++ b/frontend/src/app/model/user-group.model.ts @@ -1,5 +1,5 @@ -import { UserDetails } from '../../../openapi'; -import RoleEnum = UserDetails.RoleEnum; +import { ApiUser } from '../../../openapi'; +import RoleEnum = ApiUser.RoleEnum; export class UserGroup { constructor(public readonly name: string, public readonly roles: RoleEnum[]) {} diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index 1801667dc..b65b3afb6 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -5,7 +5,7 @@ import { CompetitionRunService, DownloadService, RunProperties, - RunState, + ApiEvaluationState, } from '../../../openapi'; import { flatMap, map, take } from 'rxjs/operators'; import { Router } from '@angular/router'; @@ -16,8 +16,8 @@ export interface RunInfoWithState { name: string; description?: string; teams: number; - runStatus: RunState.RunStatusEnum; - taskRunStatus: RunState.TaskRunStatusEnum; + runStatus: ApiEvaluationState.RunStatusEnum; + taskRunStatus: ApiEvaluationState.TaskRunStatusEnum; currentTask?: string; timeLeft: string; asynchronous: boolean; @@ -95,7 +95,7 @@ export class AbstractRunListComponent { } public downloadScores(runId: string) { - this.downloadService.getApiV1DownloadRunWithRunidScores(runId).subscribe((scoresCSV) => { + this.downloadService.apiV2DownloadEvaluationEvaluationIdScoresGet(runId).subscribe((scoresCSV) => { const csvBlob = new Blob([scoresCSV], { type: 'text/csv' }); const fake = document.createElement('a'); fake.href = URL.createObjectURL(csvBlob); @@ -118,7 +118,7 @@ export class AbstractRunListComponent { } public startTask(runId: string) { - this.runAdminService.postApiV1RunAdminWithRunidTaskStart(runId).subscribe( + this.runAdminService.apiV2EvaluationAdminRunIdTerminatePost(runId).subscribe( (r) => { this.update.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -140,7 +140,7 @@ export class AbstractRunListComponent { }; downloadProvider = (runId) => { - return this.downloadService.getApiV1DownloadRunWithRunid(runId).pipe(take(1)); + return this.downloadService.apiV2DownloadEvaluationEvaluationIdGet(runId).pipe(take(1)); // .toPromise(); }; diff --git a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts index b79e5e92c..4b434be87 100644 --- a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts +++ b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts @@ -4,11 +4,11 @@ import { CompetitionRunAdminService, CompetitionRunScoresService, CompetitionRunService, - CompetitionService, + EvaluationService, DownloadService, PastTaskInfo, RestDetailedTeam, - RestTeam, + ApiTeam, TeamInfo, TeamTaskOverview, } from '../../../../openapi'; @@ -34,7 +34,7 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { displayedColumnsTasks: string[] = ['name', 'group', 'type', 'duration', 'past', 'action']; displayedColumnsTeamTasks: string[] = ['name', 'state', 'group', 'type', 'duration', 'past', 'action']; - teams: Observable; + teams: Observable; pastTasks = new BehaviorSubject([]); pastTasksValue: PastTaskInfo[]; nbOpenTeamOverviews = 0; @@ -44,7 +44,7 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { private activeRoute: ActivatedRoute, private config: AppConfig, private runService: CompetitionRunService, - private competitionService: CompetitionService, + private evaluationService: EvaluationService, private runAdminService: CompetitionRunAdminService, private scoreService: CompetitionRunScoresService, private downloadService: DownloadService, @@ -81,7 +81,7 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { this.teams = this.run.pipe( switchMap((runAndOverview) => { - return this.competitionService.getApiV1CompetitionWithCompetitionidTeamList(runAndOverview.runInfo.competitionId); + return this.evaluationService.getApiV1CompetitionWithCompetitionidTeamList(runAndOverview.runInfo.competitionId); }), shareReplay({ bufferSize: 1, refCount: true }) /* Cache last successful loading. */ ); @@ -97,7 +97,7 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { return item.teamId; } - public resolveTeamById(index: number, item: RestTeam) { + public resolveTeamById(index: number, item: ApiTeam) { return item.uid; } diff --git a/frontend/src/app/run/run-list.component.ts b/frontend/src/app/run/run-list.component.ts index 9375396e5..cf138442f 100644 --- a/frontend/src/app/run/run-list.component.ts +++ b/frontend/src/app/run/run-list.component.ts @@ -1,12 +1,12 @@ import { Component } from '@angular/core'; -import { UserDetails } from '../../../openapi'; +import { ApiUser } from '../../../openapi'; import { Observable } from 'rxjs'; import { AuthenticationService } from '../services/session/authentication.sevice'; import { map } from 'rxjs/operators'; import { AccessChecking } from '../model/access-checking.interface'; import { UserGroup } from '../model/user-group.model'; import { AccessRoleService } from '../services/session/access-role.service'; -import RoleEnum = UserDetails.RoleEnum; +import RoleEnum = ApiUser.RoleEnum; @Component({ selector: 'app-run-list', diff --git a/frontend/src/app/services/session/access-role.service.ts b/frontend/src/app/services/session/access-role.service.ts index 7125c2ff1..e5cdc72a6 100644 --- a/frontend/src/app/services/session/access-role.service.ts +++ b/frontend/src/app/services/session/access-role.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { AuthenticationService } from './authentication.sevice'; -import { UserDetails } from '../../../../openapi'; +import { ApiUser } from '../../../../openapi'; import { UserGroup } from '../../model/user-group.model'; import { BehaviorSubject } from 'rxjs'; -import RoleEnum = UserDetails.RoleEnum; +import RoleEnum = ApiUser.RoleEnum; @Injectable() export class AccessRoleService { diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index 55770bfcf..8912be96c 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@angular/core'; -import { LoginRequest, UserDetails, UserRequest, UserService } from '../../../../openapi'; +import { LoginRequest, ApiUser, UserRequest, UserService } from '../../../../openapi'; import { catchError, filter, flatMap, map, tap } from 'rxjs/operators'; import { BehaviorSubject, Observable, of } from 'rxjs'; import RoleEnum = UserRequest.RoleEnum; @@ -11,19 +11,18 @@ import RoleEnum = UserRequest.RoleEnum; providedIn: 'root', }) export class AuthenticationService { - /** UserDetails created during login. */ - private userDetails: BehaviorSubject = new BehaviorSubject(null); + /** ApiUser created during login. */ + private userDetails: BehaviorSubject = new BehaviorSubject(null); /** * Constructor */ constructor(@Inject(UserService) private userService: UserService) { - this.userService - .getApiV1UserSession() + this.userService.apiV2UserSessionGet() .pipe( catchError((e) => of(null)), filter((s) => s != null), - flatMap((s) => this.userService.getApiV1User()), + flatMap((s) => this.userService.apiV2UserGet()), filter((u) => u != null) ) .subscribe((u) => { @@ -39,8 +38,8 @@ export class AuthenticationService { * @param pass The password. */ public login(user: string, pass: string) { - return this.userService.postApiV1Login({ username: user, password: pass } as LoginRequest).pipe( - flatMap(() => this.userService.getApiV1User()), + return this.userService.apiV2LoginPost({ username: user, password: pass } as LoginRequest).pipe( + flatMap(() => this.userService.apiV2UserGet()), tap((data) => { this.userDetails.next(data); console.log(`Successfully logged in as '${this.userDetails.value.username}'.`); @@ -55,8 +54,8 @@ export class AuthenticationService { */ public updateUser(user: UserRequest) { return this.user.pipe( - flatMap((u: UserDetails) => this.userService.patchApiV1UserWithUserid(u.id, user)), - tap((u: UserDetails) => this.userDetails.next(u)) + flatMap((u: ApiUser) => this.userService.apiV2UserUserIdPatch(u.id, user)), + tap((u: ApiUser) => this.userDetails.next(u)) ); } @@ -64,7 +63,7 @@ export class AuthenticationService { * Tries to logout the current user. Returns an Observable! */ public logout() { - return this.userService.getApiV1Logout().pipe( + return this.userService.apiV2LogoutGet().pipe( catchError((e) => of(null)), tap(() => { this.userDetails.next(null); @@ -86,7 +85,7 @@ export class AuthenticationService { /** * Returns the username of the current user as Observable. */ - get user(): Observable { + get user(): Observable { return this.userDetails.asObservable(); } diff --git a/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts b/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts index ceaf50383..bbca3735e 100644 --- a/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts +++ b/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts @@ -1,8 +1,8 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { UserDetails, UserRequest } from '../../../../openapi'; +import { ApiUser, UserRequest } from '../../../../openapi'; import { FormControl, FormGroup } from '@angular/forms'; -import RoleEnum = UserDetails.RoleEnum; +import RoleEnum = ApiUser.RoleEnum; @Component({ selector: 'app-admin-user-create-or-edit-dialog', @@ -22,7 +22,7 @@ export class AdminUserCreateOrEditDialogComponent { constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public usr?: UserDetails + @Inject(MAT_DIALOG_DATA) public usr?: ApiUser ) { this.init(); } @@ -46,7 +46,7 @@ export class AdminUserCreateOrEditDialogComponent { } uploaded = (userData: string) => { - this.usr = JSON.parse(userData) as UserDetails; + this.usr = JSON.parse(userData) as ApiUser; this.init(); }; @@ -66,7 +66,7 @@ export class AdminUserCreateOrEditDialogComponent { asJson(): string { const user = this.fetchData(); - return JSON.stringify({ id: this?.usr?.id, username: user.username, role: user.role } as UserDetails); + return JSON.stringify({ id: this?.usr?.id, username: user.username, role: user.role } as ApiUser); } public close(): void { diff --git a/frontend/src/app/user/admin-user-list/admin-user-list.component.ts b/frontend/src/app/user/admin-user-list/admin-user-list.component.ts index 0da8a0a6c..a50de5d4f 100644 --- a/frontend/src/app/user/admin-user-list/admin-user-list.component.ts +++ b/frontend/src/app/user/admin-user-list/admin-user-list.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, Component, ViewChild } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { UserDetails, UserRequest, UserService } from '../../../../openapi'; +import { ApiUser, UserRequest, UserService } from '../../../../openapi'; import { MatDialog } from '@angular/material/dialog'; import { AdminUserCreateOrEditDialogComponent } from '../admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component'; import { filter, flatMap } from 'rxjs/operators'; @@ -19,7 +19,7 @@ export class AdminUserListComponent implements AfterViewInit { displayColumns = ['actions', 'id', 'name', 'role']; @ViewChild(MatSort) sort: MatSort; - dataSource = new MatTableDataSource([]); + dataSource = new MatTableDataSource([]); isFilterInputActive = false; filterValue = ''; @@ -38,7 +38,7 @@ export class AdminUserListComponent implements AfterViewInit { .pipe( filter((r) => r != null), flatMap((u: UserRequest) => { - return this.userService.postApiV1User(u); + return this.userService.apiV2UserPost(u); }) ) .subscribe( @@ -52,15 +52,15 @@ export class AdminUserListComponent implements AfterViewInit { ); } - public edit(user: UserDetails) { - const dialogRef = this.dialog.open(AdminUserCreateOrEditDialogComponent, { width: '500px', data: user as UserDetails }); + public edit(user: ApiUser) { + const dialogRef = this.dialog.open(AdminUserCreateOrEditDialogComponent, { width: '500px', data: user as ApiUser }); dialogRef .afterClosed() .pipe( filter((r) => r != null), flatMap((u: UserRequest) => { console.debug(`Edit Result: ${u}`); - return this.userService.patchApiV1UserWithUserid(user.id, u); + return this.userService.apiV2UserUserIdPatch(user.id, u); }) ) .subscribe( @@ -76,8 +76,8 @@ export class AdminUserListComponent implements AfterViewInit { public delete(userId: number) { if (confirm(`Do you really want to delete user (${userId})?`)) { - this.userService.deleteApiV1UserWithUserid(userId).subscribe( - (u: UserDetails) => { + this.userService.apiV2UserUserIdDelete(userId).subscribe( + (u: ApiUser) => { this.refresh(); this.snackBar.open(`Success: ${u.username} (${u.id}) deleted`, null, { duration: 5000 }); }, @@ -89,8 +89,8 @@ export class AdminUserListComponent implements AfterViewInit { } public refresh() { - this.userService.getApiV1UserList().subscribe( - (users: UserDetails[]) => { + this.userService.apiV2UserListGet().subscribe( + (users: ApiUser[]) => { this.dataSource.data = users; this.dataSource.sort = this.sort; }, @@ -106,7 +106,7 @@ export class AdminUserListComponent implements AfterViewInit { this.refresh(); } - resolveUserById(_: number, user: UserDetails) { + resolveUserById(_: number, user: ApiUser) { return user.id; } diff --git a/frontend/src/app/user/profile/profile.component.ts b/frontend/src/app/user/profile/profile.component.ts index aa6a66db9..851dadea1 100644 --- a/frontend/src/app/user/profile/profile.component.ts +++ b/frontend/src/app/user/profile/profile.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { SessionId, UserDetails, UserRequest, UserService } from '../../../../openapi'; +import { ApiUser, UserRequest, UserService } from '../../../../openapi'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { FormControl, FormGroup } from '@angular/forms'; @@ -13,8 +13,8 @@ import { AuthenticationService } from '../../services/session/authentication.sev styleUrls: ['./profile.component.scss'], }) export class ProfileComponent implements OnInit, OnDestroy { - user: Observable; - sessionId: Observable; + user: Observable; + sessionId: Observable; loggedIn: Observable; editing = false; @@ -35,7 +35,7 @@ export class ProfileComponent implements OnInit, OnDestroy { ) { this.user = this.authenticationService.user; this.loggedIn = this.authenticationService.isLoggedIn; - this.sessionId = this.userService.getApiV1UserSession(); + this.sessionId = this.userService.apiV2UserSessionGet(); } ngOnInit(): void { @@ -60,7 +60,7 @@ export class ProfileComponent implements OnInit, OnDestroy { .updateUser(usr) .pipe(first()) .subscribe( - (r: UserDetails) => { + (r: ApiUser) => { this.snackBar.open(`Save successful!`, null, { duration: 5000 }); this.toggleEdit(); }, diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index e01628b7e..e8725cd79 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -19,7 +19,7 @@ import { import { webSocket, WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/webSocket'; import { AppConfig } from '../app.config'; import { IWsMessage } from '../model/ws/ws-message.interface'; -import { CompetitionRunService, RunInfo, RunState, TaskInfo } from '../../../openapi'; +import { EvaluationService, ApiEvaluationInfo, ApiEvaluationState, TaskInfo } from '../../../openapi'; import { IWsServerMessage } from '../model/ws/ws-server-message.interface'; import { IWsClientMessage } from '../model/ws/ws-client-message.interface'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -44,10 +44,10 @@ export class RunViewerComponent implements OnInit, OnDestroy { runId: Observable; /** Observable for information about the current run. Usually queried once when the view is loaded. */ - runInfo: Observable; + runInfo: Observable; /** Observable for information about the current run's state. Usually queried when a state change is signaled via WebSocket. */ - runState: Observable; + runState: Observable; /** Observable that fires whenever a task starts. Emits the task description of the task that just started. */ taskStarted: Observable; @@ -77,7 +77,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { private router: Router, private activeRoute: ActivatedRoute, private config: AppConfig, - private runService: CompetitionRunService, + private runService: EvaluationService, private snackBar: MatSnackBar, private titleService: Title, @Inject(DOCUMENT) private document: Document @@ -134,7 +134,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { /* Basic observable for general run info; this information is static and does not change over the course of a run. */ this.runInfo = this.runId.pipe( switchMap((runId) => - this.runService.getApiV1RunWithRunidInfo(runId).pipe( + this.runService.apiV2EvaluationEvaluationIdInfoGet(runId).pipe( catchError((err, o) => { console.log( `[RunViewerComponent] There was an error while loading information in the current run: ${err?.message}` @@ -199,7 +199,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { this.runState = merge(this.runId, wsMessages).pipe( sampleTime(500) /* State updates are triggered only once every 500ms. */, switchMap((runId) => - this.runService.getApiV1RunWithRunidState(runId).pipe( + this.runService.apiV2EvaluationEvaluationIdStateGet(runId).pipe( catchError((err, o) => { console.log( `[RunViewerComponent] There was an error while loading information in the current run state: ${err?.message}` @@ -227,7 +227,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { ); /* Basic observable that fires when a task ends. */ - this.taskEnded = merge(of(null as RunState), this.runState).pipe( + this.taskEnded = merge(of(null as ApiEvaluationState), this.runState).pipe( pairwise(), filter(([s1, s2]) => (s1 === null || s1.taskRunStatus === 'RUNNING') && s2.taskRunStatus === 'ENDED'), map(([s1, s2]) => s2.currentTask), @@ -235,14 +235,14 @@ export class RunViewerComponent implements OnInit, OnDestroy { ); /* Observable that tracks the currently active task. */ - this.taskChanged = merge(of(null as RunState), this.runState).pipe( + this.taskChanged = merge(of(null as ApiEvaluationState), this.runState).pipe( pairwise(), filter(([s1, s2]) => s1 === null || s1.currentTask.name !== s2.currentTask.name), map(([s1, s2]) => s2.currentTask), shareReplay({ bufferSize: 1, refCount: true }) ); - this.runInfo.subscribe((info: RunInfo) => { + this.runInfo.subscribe((info: ApiEvaluationInfo) => { this.titleService.setTitle(info.name + ' - DRES'); }) } diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index 9581c52fa..5d0f710c2 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -10,7 +10,7 @@ import { } from '@angular/core'; import { CompetitionRunScoresService, - CompetitionRunService, + EvaluationService, RunInfo, RunState, ScoreOverview, @@ -95,7 +95,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { submissionTrackInterval: number = 60_000; constructor( - private runService: CompetitionRunService, + private evaluationService: EvaluationService, private scoresService: CompetitionRunScoresService, private ref: ChangeDetectorRef, public config: AppConfig @@ -110,7 +110,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /* Create source observable; list of all submissions. */ this.submissions = this.state.pipe( switchMap((st) => - this.runService.getApiV1RunWithRunidSubmissionList(st.id).pipe( + this.evaluationService.getApiV1RunWithRunidSubmissionList(st.id).pipe( retry(3), catchError((err, o) => { console.log(`[TeamsViewerComponent] Error while loading submissions: ${err?.message}.`); From c8f22536fe4c264fe0a7bad4d4ad5109a922fba2 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 21:40:43 +0100 Subject: [PATCH 045/498] Minor fix in user creation command --- backend/src/main/kotlin/dev/dres/api/cli/Cli.kt | 2 +- backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt | 2 +- backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 4fe6a7cf6..2b9b1c18a 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -109,7 +109,7 @@ object Cli { is com.github.ajalt.clikt.core.PrintHelpMessage -> println(e.command.getFormattedHelp()) is com.github.ajalt.clikt.core.MissingParameter -> println(e.localizedMessage) is com.github.ajalt.clikt.core.NoSuchOption -> println(e.localizedMessage) - is com.github.ajalt.clikt.core.UsageError -> println("invalid command") + is com.github.ajalt.clikt.core.UsageError -> println("invalid command: ${e.localizedMessage}") else -> e.printStackTrace() } } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 8e5c8014b..dec75541a 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -49,7 +49,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { private val password: Password.Plain by option("-p", "--password", help = "Password of at least $MIN_LENGTH_PASSWORD characters length.") .convert { Password.Plain(it) } .required() - .validate { require(it.password.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } + .validate { require(it.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } /** The desired [Role] of the newly created user. */ private val role: Role by option("-r", "--role", help = "Role of the new user.").convert { Role.parse(it) }.required() diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt index e5798d067..68ec78c88 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt @@ -14,7 +14,7 @@ import org.mindrot.jbcrypt.BCrypt sealed class Password(val password: String) { class Plain(password: String): Password(password) { init { - require (password.length < User.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${User.MIN_LENGTH_PASSWORD}" } + require (password.length >= User.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${User.MIN_LENGTH_PASSWORD} characters long" } } fun hash(): Hashed = Hashed(BCrypt.hashpw(this.password, BCrypt.gensalt())) val length: Int From efc4e682643aca60504059c34ea636efdf7e73b0 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 1 Feb 2023 22:11:56 +0100 Subject: [PATCH 046/498] Some tinkering with UserManager --- .../main/kotlin/dev/dres/api/cli/UserCommand.kt | 6 ++++-- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index dec75541a..3e170fd3d 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -6,6 +6,7 @@ import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.parameters.options.* import com.jakewharton.picnic.table +import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.admin.Password import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User @@ -13,6 +14,7 @@ import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_PASSWORD import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_USERNAME import dev.dres.data.model.admin.UserId import dev.dres.mgmt.admin.UserManager +import java.lang.IllegalArgumentException import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOption @@ -52,10 +54,10 @@ class UserCommand : NoOpCliktCommand(name = "user") { .validate { require(it.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } /** The desired [Role] of the newly created user. */ - private val role: Role by option("-r", "--role", help = "Role of the new user.").convert { Role.parse(it) }.required() + private val apiRole: ApiRole by option("-r", "--role", help = "Role of the new user.").convert { ApiRole.valueOf(it) }.required() override fun run() { - val successful = UserManager.create(username = this.username, password = this.password, role = role) + val successful = UserManager.create(username = this.username, password = this.password.hash(), role = apiRole) if (successful) { println("New user '${UserManager.get(username = this.username)}' created.") } else { diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 3011bdc22..b3de442af 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -1,5 +1,6 @@ package dev.dres.mgmt.admin +import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.* import jetbrains.exodus.database.TransientEntityStore @@ -28,7 +29,7 @@ object UserManager { * @param role The [Role] of the new user. */ fun create(username: String, password: Password.Hashed, role: Role): Boolean { - check(::store.isInitialized) { "PUserManager requires an initialized store which is unavailable. This is a programmer's error!"} + check(::store.isInitialized) { "UserManager requires an initialized store which is unavailable. This is a programmer's error!"} try { this.store.transactional { User.new { @@ -43,6 +44,18 @@ object UserManager { return true } + /** + * Creates a [User] with the given [username], [password] and [role]. + * + * @param username The name of the [User]. Must be unique. + * @param password The [Password.Hashed] of the user. + * @param role The [ApiRole] of the new user. + */ + fun create(username: String, password: Password.Hashed, role: ApiRole): Boolean { + check(::store.isInitialized) { "UserManager requires an initialized store which is unavailable. This is a programmer's error!"} + return create(username, password, role.toRole() ?: throw IllegalArgumentException("Invalid Role")) + } + /** * Creates a [User] with the given [username], [password] and [role]. * From 464aa4dd6dc2a40fc1a285720faed9900fc647da Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 09:40:59 +0100 Subject: [PATCH 047/498] More frontend repair, still very broken --- .../app/services/pipes/resolve-team.pipe.ts | 4 +-- frontend/src/app/utilities/time.utilities.ts | 10 +++--- ...competition-scoreboard-viewer.component.ts | 10 +++--- .../video-object-preview.component.ts | 4 +-- .../src/app/viewer/task-viewer.component.ts | 30 ++++++++-------- .../src/app/viewer/teams-viewer.component.ts | 36 +++++++++---------- 6 files changed, 47 insertions(+), 47 deletions(-) diff --git a/frontend/src/app/services/pipes/resolve-team.pipe.ts b/frontend/src/app/services/pipes/resolve-team.pipe.ts index 68c83d5b7..9766fc30a 100644 --- a/frontend/src/app/services/pipes/resolve-team.pipe.ts +++ b/frontend/src/app/services/pipes/resolve-team.pipe.ts @@ -1,11 +1,11 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { RestTeam } from '../../../../openapi'; +import { ApiTeam } from '../../../../openapi'; @Pipe({ name: 'resolveTeam', }) export class ResolveTeamPipe implements PipeTransform { - transform(teamId: string, teams: RestTeam[]): RestTeam | null { + transform(teamId: string, teams: ApiTeam[]): ApiTeam | null { if (!teamId || !teams) { return null; } diff --git a/frontend/src/app/utilities/time.utilities.ts b/frontend/src/app/utilities/time.utilities.ts index c907ac255..b8332b7df 100644 --- a/frontend/src/app/utilities/time.utilities.ts +++ b/frontend/src/app/utilities/time.utilities.ts @@ -1,7 +1,7 @@ /** * A class with time related utilities. Basically a port of dev.dres.utilities.TimeUtil.kt */ -import { RestTemporalPoint, RestTemporalRange } from '../../../openapi'; +import { ApiTemporalPoint, ApiTemporalRange } from '../../../openapi'; export class TimeUtilities { /** @@ -17,7 +17,7 @@ export class TimeUtilities { */ private static timeCodeRegexParsing = /([0-9]+:)?([0-5]?\d:)?([0-5]?\d:)?([0-9]+)/; - static point2Milliseconds(point: RestTemporalPoint, fps: number): number { + static point2Milliseconds(point: ApiTemporalPoint, fps: number): number { switch (point.unit) { case 'FRAME_NUMBER': return (Number.parseInt(point.value, 10) / fps) * 1000; @@ -28,15 +28,15 @@ export class TimeUtilities { } } - static point2Milliseconds24fps(point: RestTemporalPoint): number { + static point2Milliseconds24fps(point: ApiTemporalPoint): number { return this.point2Milliseconds(point, 24); } - static range2Milliseconds(range: RestTemporalRange, fps: number): [number, number] { + static range2Milliseconds(range: ApiTemporalRange, fps: number): [number, number] { return [this.point2Milliseconds(range.start, fps), this.point2Milliseconds(range.end, fps)]; } - static range2Milliseconds24fps(range: RestTemporalRange): [number, number] { + static range2Milliseconds24fps(range: ApiTemporalRange): [number, number] { return this.range2Milliseconds(range, 24); } diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts index 4a1c657ca..a78a55fb5 100644 --- a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts +++ b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core'; -import { CompetitionRunScoresService, RunInfo, RunState, Score, ScoreOverview, TeamInfo } from '../../../../openapi'; +import { CompetitionRunScoresService, ApiEvaluationInfo, ApiEvaluationState, ApiScore, ApiScoreOverview, ApiTeamInfo } from '../../../../openapi'; import { combineLatest, concat, Observable, of } from 'rxjs'; import { ApexAxisChartSeries, @@ -29,12 +29,12 @@ export class CompetitionScoreboardViewerComponent implements OnInit { /** * The run info of the current run */ - @Input() info: Observable; + @Input() info: Observable; /** * The observable for the state, which is updated through a websocket */ - @Input() state: Observable; + @Input() state: Observable; /** * Whether or not to show competition overview scores. @@ -90,7 +90,7 @@ export class CompetitionScoreboardViewerComponent implements OnInit { series: Observable; - teams: Observable; + teams: Observable; currentTaskGroup: Observable; // TODO Make this somewhat more beautiful and configurable @@ -185,7 +185,7 @@ export class CompetitionScoreboardViewerComponent implements OnInit { */ private competitionOverviewSeries(): Observable { /* Fetch scores. */ - const score: Observable> = this.state.pipe( + const score: Observable> = this.state.pipe( switchMap((s) => { return this.scoreService.getApiV1ScoreRunWithRunid(s.id).pipe( catchError((err) => { diff --git a/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts b/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts index e15395814..a3bfa0030 100644 --- a/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts +++ b/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts @@ -2,7 +2,7 @@ import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { Observable } from 'rxjs'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { filter, map } from 'rxjs/operators'; -import { ContentElement } from '../../../../openapi'; +import { ApiContentElement } from '../../../../openapi'; import { DataUtilities } from '../../utilities/data.utilities'; @Component({ @@ -25,7 +25,7 @@ import { DataUtilities } from '../../utilities/data.utilities'; }) export class VideoObjectPreviewComponent implements OnInit { /** Observable of current {@link ContentElement} that should be displayed. Provided by user of this component. */ - @Input() queryObject: Observable; + @Input() queryObject: Observable; /** Flag indicating whether video player should be muted or not. Can be provided by a user of this component. */ @Input() muted = true; diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index ad006be9b..0010237f4 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -1,5 +1,5 @@ import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core'; -import { CompetitionRunService, ContentElement, RunState, TaskInfo, TaskTarget } from '../../../openapi'; +import { EvaluationService, ApiContentElement, ApiEvaluationState, ApiTaskTemplateInfo, TaskTarget } from '../../../openapi'; import { BehaviorSubject, combineLatest, interval, merge, Observable, of, Subscription, timer } from 'rxjs'; import { catchError, @@ -22,7 +22,7 @@ import { WebSocketSubject } from 'rxjs/webSocket'; import { AppConfig } from '../app.config'; import { AudioPlayerUtilities } from '../utilities/audio-player.utilities'; import { fromArray } from 'rxjs/internal/observable/fromArray'; -import TaskRunStatusEnum = RunState.TaskRunStatusEnum; +import TaskRunStatusEnum = ApiEvaluationState.TaskRunStatusEnum; /** * Internal enumeration used for TaskViewerComponent. @@ -43,10 +43,10 @@ enum ViewerState { }) export class TaskViewerComponent implements AfterViewInit, OnDestroy { @Input() runId: Observable; - @Input() state: Observable; - @Input() taskStarted: Observable; - @Input() taskChanged: Observable; - @Input() taskEnded: Observable; + @Input() state: Observable; + @Input() taskStarted: Observable; + @Input() taskChanged: Observable; + @Input() taskEnded: Observable; @Input() webSocketSubject: WebSocketSubject; /** Time that is still left (only when a task is running). */ @@ -62,10 +62,10 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { viewerState: BehaviorSubject = new BehaviorSubject(ViewerState.VIEWER_UNKNOWN); /** Reference to the current {@link TaskHint} {@link ContentElement}s. */ - currentTaskHint: Observable; + currentTaskHint: Observable; /** Reference to the current {@link TaskTarget} {@link ContentElement}. */ - currentTaskTarget: Observable; + currentTaskTarget: Observable; /** The subscription associated with the current viewer state. */ viewerStateSubscription: Subscription; @@ -73,7 +73,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /** Reference to the audio element used during countdown. */ @ViewChild('audio') audio: ElementRef; - constructor(protected runService: CompetitionRunService, public config: AppConfig) {} + constructor(protected runService: EvaluationService, public config: AppConfig) {} /** * Create a subscription for task changes. @@ -83,7 +83,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskHint = this.taskChanged.pipe( withLatestFrom(this.runId), switchMap(([task, runId]) => - this.runService.getApiV1RunWithRunidHintWithTaskid(runId, task.id).pipe( + this.runService.apiV2RunEvaluationIdHintTaskIdGet(runId, task.id).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current query hint due to an error.', e); return of(null); @@ -97,7 +97,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskTarget = this.state.pipe( filter(s => s.taskRunStatus == TaskRunStatusEnum.ENDED), switchMap((s) => - this.runService.getApiV1RunWithRunidTargetWithTaskid(s.id, s.currentTask.id).pipe( + this.runService.apiV2RunEvaluationIdHintTaskIdGet(s.id, s.currentTask?.id).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); @@ -119,9 +119,9 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { sampleTime(1000) /* This is again sampled to only ever emit once every second. */, switchMap((s) => { if (typeof s === 'string') { - return this.runService.getApiV1RunWithRunidState(s); /* Timer! Load run state! */ + return this.runService.apiV2EvaluationEvaluationIdStateGet(s); /* Timer! Load run state! */ } else { - return of(s as RunState); /* This is a freshly loaded run state. */ + return of(s as ApiEvaluationState); /* This is a freshly loaded run state. */ } }), catchError((err, o) => { @@ -175,7 +175,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { return fromArray([]); } return fromArray(h.sequence).pipe( - delayWhen((c: ContentElement) => interval(1000 * c.offset)), + delayWhen((c: ApiContentElement) => interval(1000 * c.offset)), repeat(-1) ); }) @@ -191,7 +191,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const actualTimeElapsed = Math.max(timeElapsed, 0); const sequence = []; if (hint) { - const largest = new Map(); + const largest = new Map(); hint.sequence.forEach((c) => { if (c.offset >= actualTimeElapsed) { sequence.push(c); diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index 5d0f710c2..d78baf857 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -11,12 +11,12 @@ import { import { CompetitionRunScoresService, EvaluationService, - RunInfo, - RunState, - ScoreOverview, - SubmissionInfo, - TaskInfo, - TeamInfo, + ApiEvaluationInfo, + ApiEvaluationState, + ApiScoreOverview, + ApiSubmissionInfo, + ApiTaskTemplateInfo, + ApiTeamInfo, } from '../../../openapi'; import {BehaviorSubject, combineLatest, merge, Observable, of, Subscription} from 'rxjs'; import {catchError, filter, flatMap, map, pairwise, retry, shareReplay, switchMap, withLatestFrom,} from 'rxjs/operators'; @@ -66,15 +66,15 @@ interface SubmissionDelta { }) export class TeamsViewerComponent implements AfterViewInit, OnDestroy { @Input() runId: Observable; - @Input() info: Observable; - @Input() state: Observable; + @Input() info: Observable; + @Input() state: Observable; @Input() taskEnded: Observable; /** Observable that tracks all the submissions. */ - submissions: Observable; + submissions: Observable; /** Observable that tracks all the submissions per team. */ - submissionsPerTeam: Observable>; + submissionsPerTeam: Observable>; /** Observable that tracks the current score per team. */ scores: Observable>; @@ -110,7 +110,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /* Create source observable; list of all submissions. */ this.submissions = this.state.pipe( switchMap((st) => - this.evaluationService.getApiV1RunWithRunidSubmissionList(st.id).pipe( + this.evaluationService.apiV2EvaluationEvaluationIdSubmissionListTimestampGet(st.id).pipe( retry(3), catchError((err, o) => { console.log(`[TeamsViewerComponent] Error while loading submissions: ${err?.message}.`); @@ -125,7 +125,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /* Observable that tracks all the submissions per team. */ this.submissionsPerTeam = combineLatest([this.submissions, this.info]).pipe( map(([submissions, info]) => { - const submissionsPerTeam = new Map(); + const submissionsPerTeam = new Map(); info.teams.forEach((t) => { submissionsPerTeam.set( t.uid, @@ -149,7 +149,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { filter((sc) => sc != null) /* Filter null responses. */ ) ), - map((sc: ScoreOverview) => { + map((sc: ApiScoreOverview) => { const scores = new Map(); sc.scores.forEach((v) => scores.set(v.teamId, v.score)); return scores; @@ -239,21 +239,21 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /** * Generates a URL for the preview image of a submission. */ - public previewForSubmission(submission: SubmissionInfo): Observable { + public previewForSubmission(submission: ApiSubmissionInfo): Observable { return this.runId.pipe(map((runId) => this.config.resolveApiUrl(`/preview/submission/${runId}/${submission.id}`))); } /** * Generates a URL for the preview image of a submission. */ - public tooltipForSubmission(submission: SubmissionInfo): string { + public tooltipForSubmission(submission: ApiSubmissionInfo): string { return submission.text == null ? '' : submission.text; } /** * Generates a URL for the logo of the team. */ - public teamLogo(team: TeamInfo): string { + public teamLogo(team: ApiTeamInfo): string { return this.config.resolveApiUrl(`/competition/logo/${team.logoId}`); } @@ -262,7 +262,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { * * @param teamId The team's uid. */ - public submissionForTeam(teamId: string): Observable { + public submissionForTeam(teamId: string): Observable { return combineLatest([this.info, this.submissionsPerTeam]).pipe( map(([i, s]) => { if (s != null) { @@ -285,7 +285,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { * @param index * @param sub */ - public trackSubmission(index: Number, sub: SubmissionInfo){ + public trackSubmission(index: Number, sub: ApiSubmissionInfo){ let timeout = 30000; //only re-render once every 30 seconds From e300d417d47bc1bad1a51e0e1e3170f1e59acfce Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 2 Feb 2023 10:16:41 +0100 Subject: [PATCH 048/498] Fixed user creation process. --- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 2 +- .../kotlin/dev/dres/api/cli/UserCommand.kt | 69 ++++++++++--------- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 6 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 2 + 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 2b9b1c18a..9eda4f797 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -42,7 +42,7 @@ object Cli { clikt = DRESBaseCommand().subcommands( EvaluationCommand(store, config), - UserCommand(), + UserCommand(store), MediaCollectionCommand(store), EvaluationRunCommand(store), OpenApiCommand(), diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 3e170fd3d..7ea1d0b38 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -14,10 +14,12 @@ import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_PASSWORD import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_USERNAME import dev.dres.data.model.admin.UserId import dev.dres.mgmt.admin.UserManager +import jetbrains.exodus.database.TransientEntityStore import java.lang.IllegalArgumentException import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOption +import java.util.* /** * A collection of [CliktCommand]s for [User] management @@ -25,9 +27,9 @@ import java.nio.file.StandardOpenOption * @author Ralph Gasser * @version 2.0.0 */ -class UserCommand : NoOpCliktCommand(name = "user") { +class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") { init { - this.subcommands(Create(), Update(), Delete(), List(), Roles(), Export(), Import()) + this.subcommands(Create(store), Update(store), Delete(store), List(store), Roles(store), Export(store), Import(store)) } override fun aliases() = mapOf( @@ -41,7 +43,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to create a new [User]. */ - inner class Create: CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { + inner class Create(private val store: TransientEntityStore): CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { /** The name of the newly created user. */ private val username: String by option("-u", "--username", help = "Username of at least $MIN_LENGTH_USERNAME characters length. Must be unique!") .required() @@ -56,13 +58,12 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** The desired [Role] of the newly created user. */ private val apiRole: ApiRole by option("-r", "--role", help = "Role of the new user.").convert { ApiRole.valueOf(it) }.required() - override fun run() { + override fun run() = this.store.transactional { val successful = UserManager.create(username = this.username, password = this.password.hash(), role = apiRole) if (successful) { println("New user '${UserManager.get(username = this.username)}' created.") } else { println("Could not create user '${this.username}' because a user with that name already exists.") - } } } @@ -70,7 +71,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to update an existing [User]. */ - inner class Update: CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { + inner class Update(private val store: TransientEntityStore): CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id") /** The new username. */ @@ -85,14 +86,14 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** The new [Role] of the updated user. Left unchanged if null! */ private val role: Role? by option("-r", "--role", help = "New user Role").convert { Role.parse(it) } - override fun run() { + override fun run() = this.store.transactional { if (this.id == null && this.username == null) { println("You must specify a valid username or user ID in order to update a user!") - return + return@transactional } if (UserManager.update(id = this.id, username = this.username, password = this.password, role = this.role)) { println("User updated successfully!") - return + return@transactional } else { println("User updated failed. It probably doesn't exist!") } @@ -102,19 +103,19 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to delete a [User]. */ - inner class Delete : CliktCommand(name = "delete", help = "Deletes an existing user.", printHelpOnEmptyArgs = true) { + inner class Delete(private val store: TransientEntityStore): CliktCommand(name = "delete", help = "Deletes an existing user.", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id", help = "ID of the user to be deleted.") private val username: String? by option("-u", "--username", help = "Username of the user to be deleted.") .validate { require(it.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } - override fun run() { + override fun run() = this.store.transactional { if (this.id == null && this.username == null) { println("You must specify a valid username or user ID in order to delete a user!") } val success = UserManager.delete(id = this.id, username = this.username) if (success) { println("User deleted successfully!") - return + return@transactional } else { println("User could not be deleted because it doesn't exist!") } @@ -124,12 +125,12 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to export a [User]. */ - inner class Export : CliktCommand(name = "export", help = "Exports one or multiple user(s) as JSON.", printHelpOnEmptyArgs = true) { + inner class Export(private val store: TransientEntityStore): CliktCommand(name = "export", help = "Exports one or multiple user(s) as JSON.", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id", help = "ID of the user to be exported.") private val username: String? by option("-u", "--username", help = "Username of the user to be exported.") .validate { require(it.length >= MIN_LENGTH_USERNAME) { "Username for DRES user must consist of at least $MIN_LENGTH_USERNAME characters." } } private val path: String by option("-o", "--output").required() - override fun run() { + override fun run() = this.store.transactional(true) { if (this.id == null && this.username == null) { val users = UserManager.list() val path = Paths.get(this.path) @@ -138,7 +139,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { mapper.writeValue(writer, users) } println("Successfully wrote ${users.size} users to $path.") - return + return@transactional } else { val user = UserManager.get(id = this.id, username = this.username) if (user != null) { @@ -158,7 +159,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * Imports a specific competition from JSON. */ - inner class Import: CliktCommand(name = "import", help = "Imports a user description from JSON. Either a single user or an array of users", printHelpOnEmptyArgs = true) { + inner class Import(private val store: TransientEntityStore): CliktCommand(name = "import", help = "Imports a user description from JSON. Either a single user or an array of users", printHelpOnEmptyArgs = true) { private val new: Boolean by option("-n", "--new", help = "Flag indicating whether users should be created anew.").flag("-u", "--update", default = true) @@ -166,7 +167,7 @@ class UserCommand : NoOpCliktCommand(name = "user") { private val destination: String by option("-i", "--in", help = "The input file for the users.").required() - override fun run() { + override fun run() = this.store.transactional { val path = Paths.get(this.destination) val mapper = ObjectMapper() @@ -193,9 +194,9 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to list all [User]s. */ - inner class List: CliktCommand(name = "list", help = "Lists all Users") { + inner class List(private val store: TransientEntityStore): CliktCommand(name = "list", help = "Lists all Users") { val plain by option("-p", "--plain", help = "Plain print: No fancy table. Might be easier if the output should be processed").flag(default = false) - override fun run() { + override fun run() = this.store.transactional(true) { val users = UserManager.list() println("Available users: ${users.size}") if (plain) { @@ -204,21 +205,21 @@ class UserCommand : NoOpCliktCommand(name = "user") { } } else { println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "username", "role") - } - body { - users.forEach { - row(it.id, it.username, it.role) - } + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "username", "role") + } + body { + users.forEach { + row(it.id, it.username, it.role) } } + } ) } } @@ -227,8 +228,8 @@ class UserCommand : NoOpCliktCommand(name = "user") { /** * [CliktCommand] to list all [Role]s. */ - inner class Roles : CliktCommand(name = "roles", help = "Lists all Roles") { - override fun run() { + inner class Roles(private val store: TransientEntityStore): CliktCommand(name = "roles", help = "Lists all Roles") { + override fun run() = this.store.transactional(true) { println("Available roles: ${Role.values().joinToString(", ")}") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index ac44e195f..0017bbaff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -229,9 +229,9 @@ object RestApi { it.http.prefer405over404 = true it.jetty.server { setupHttpServer() } it.accessManager(AccessManager::manage) - it.staticFiles.add("html", Location.CLASSPATH) - it.spaRoot.addFile("/vote", "vote/index.html") - it.spaRoot.addFile("/", "html/index.html") + //it.staticFiles.add("html", Location.CLASSPATH) + //it.spaRoot.addFile("/vote", "vote/index.html") + //it.spaRoot.addFile("/", "html/index.html") if (config.enableSsl) { val ssl = SSLPlugin { conf -> diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index b3de442af..436b8e2cd 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* +import java.util.* /** * User management class of DRES. Requires one-time initialisation @@ -33,6 +34,7 @@ object UserManager { try { this.store.transactional { User.new { + this.id = UUID.randomUUID().toString() this.username = username this.password = password.password this.role = role From fd4a78be615479d459f70c74148ff5475d03af93 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 10:28:13 +0100 Subject: [PATCH 049/498] Some more OpenAPI cleanup --- .../handler/evaluation/admin/CreateEvaluationHandler.kt | 6 +++--- .../api/rest/handler/evaluation/admin/StopTaskHandler.kt | 2 +- .../rest/handler/evaluation/admin/SwitchTaskHandler.kt | 2 +- ...titionStartMessage.kt => ApiEvaluationStartMessage.kt} | 2 +- doc/oas.json | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/{ApiCompetitionStartMessage.kt => ApiEvaluationStartMessage.kt} (69%) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index bbecd5ecf..8b60e8829 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.competition.ApiCompetitionStartMessage +import dev.dres.api.rest.types.competition.ApiEvaluationStartMessage import dev.dres.api.rest.types.evaluation.ApiRunType import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException @@ -50,7 +50,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs summary = "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", path = "/api/v2/evaluation/admin/create", methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(ApiCompetitionStartMessage::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiEvaluationStartMessage::class)]), tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -60,7 +60,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs ) override fun doPost(ctx: Context): SuccessStatus { val message = try { - ctx.bodyAsClass() + ctx.bodyAsClass() } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 36511f878..114a01034 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -30,7 +30,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl path = "/api/v2/evaluation/admin/{evaluationId}/task/abort", methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], - tags = ["Evaluation Administration"], + tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index 8a3cacfb5..0c6c7406a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -29,7 +29,7 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), OpenApiParam("idx", Int::class, "Index of the task to switch to.", required = true, allowEmptyValue = false) ], - tags = ["Evaluation Administration"], + tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt similarity index 69% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt index b979a9a8a..d020a3711 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionStartMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt @@ -10,4 +10,4 @@ import dev.dres.data.model.template.TemplateId * @author Ralph Gasser * @version 1.1.0 */ -data class ApiCompetitionStartMessage(val templateId: TemplateId, val name: String, val type: ApiRunType, val properties: RunProperties = RunProperties()) \ No newline at end of file +data class ApiEvaluationStartMessage(val templateId: TemplateId, val name: String, val type: ApiRunType, val properties: RunProperties = RunProperties()) \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index db61a4e4a..edf8d5a97 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1107,7 +1107,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiCompetitionStartMessage" + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" } } }, @@ -1769,7 +1769,7 @@ }, "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { "post" : { - "tags" : [ "Evaluation Administration" ], + "tags" : [ "Evaluation Administrator" ], "summary" : "Aborts the currently running task run. This is a method for admins.", "parameters" : [ { "name" : "evaluationId", @@ -1817,7 +1817,7 @@ }, "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { "post" : { - "tags" : [ "Evaluation Administration" ], + "tags" : [ "Evaluation Administrator" ], "summary" : "Moves to and selects the specified task. This is a method for admins.", "parameters" : [ { "name" : "evaluationId", @@ -4581,7 +4581,7 @@ }, "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, - "ApiCompetitionStartMessage" : { + "ApiEvaluationStartMessage" : { "type" : "object", "properties" : { "templateId" : { From ccf39221ec529f84d25a51a19d172f8edb764f1f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 2 Feb 2023 11:27:40 +0100 Subject: [PATCH 050/498] Started disentangling of DB enum and API enums --- .../kotlin/dev/dres/api/cli/UserCommand.kt | 2 +- .../admin/OverrideSubmissionHandler.kt | 2 +- .../handler/judgement/PostJudgementHandler.kt | 4 +-- .../template/UpdateCompetitionHandler.kt | 10 +++---- .../types/competition/tasks/ApiTargetType.kt | 21 +++++++++---- .../tasks/options/ApiScoreOption.kt | 15 ++++++++-- .../tasks/options/ApiSubmissionOption.kt | 30 ++++++++++++------- .../tasks/options/ApiTargetOption.kt | 21 +++++++++---- .../tasks/options/ApiTaskOption.kt | 17 ++++++++--- .../rest/types/evaluation/ApiVerdictStatus.kt | 19 ++++++++---- .../kotlin/dev/dres/data/model/admin/Role.kt | 2 ++ .../dev/dres/data/model/media/MediaType.kt | 2 ++ .../data/model/submissions/VerdictStatus.kt | 4 ++- .../data/model/template/task/TargetType.kt | 4 ++- .../template/task/options/ScoreOption.kt | 4 ++- .../template/task/options/SubmissionOption.kt | 2 +- .../template/task/options/TargetOption.kt | 4 ++- .../model/template/task/options/TaskOption.kt | 2 +- 18 files changed, 116 insertions(+), 49 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 7ea1d0b38..6a36fc038 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -150,7 +150,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") } println("Successfully wrote user ${user.id} to $path.") } else { - println("User with ID ${id} does not exist.") + println("User with ID $id does not exist.") } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index c23895d29..54e9908d6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -69,7 +69,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation if (evaluationManager.allSubmissions(rac).none { it.id == submissionInfo.id }) { throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) } - if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.status)) { + if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.toVerdictStatus())) { val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.id } AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionToken()) submission.toApi() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 69ce3788c..ff26d137e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -55,11 +55,11 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) try { - validator.judge(judgement.token, judgement.verdict.status) + validator.judge(judgement.token, judgement.verdict.toVerdictStatus()) } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } - AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.status, AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.toVerdictStatus(), AuditLogSource.REST, ctx.sessionToken()) } return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt index 0de8353eb..cac999de2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt @@ -73,14 +73,14 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) } t.name = type.name t.duration = type.duration - t.score = type.scoreOption.option - t.target = type.targetOption.option + t.score = type.scoreOption.toScoreOption() + t.target = type.targetOption.toTargetOption() t.hints.clear() t.hints.addAll(type.hintOptions.map { it.option }) t.submission.clear() - t.submission.addAll(type.submissionOptions.map { it.option }) + t.submission.addAll(type.submissionOptions.map { it.toSubmissionOption() }) t.options.clear() - t.options.addAll(type.taskOptions.map { it.option }) + t.options.addAll(type.taskOptions.map { it.toTaskOption() }) t.configurations.clear() t.configurations.addAll(type.configuration.entries.map { ConfiguredOption.new { @@ -121,7 +121,7 @@ class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) val item = MediaItem.query(MediaItem::id eq target.target).first() t.targets.add(TaskTemplateTarget.new { this.item = item - this.type = target.type.type + this.type = target.type.toTargetType() this.start = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() this.end = target.range?.end?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() }) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt index 6fac625e8..7489afcdb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt @@ -8,10 +8,19 @@ import dev.dres.data.model.template.task.TargetType * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -enum class ApiTargetType(val type: TargetType) { - JUDGEMENT(TargetType.JUDGEMENT), - JUDGEMENT_WITH_VOTE(TargetType.JUDGEMENT_WITH_VOTE), - MEDIA_ITEM(TargetType.MEDIA_ITEM), - MEDIA_ITEM_TEMPORAL_RANGE(TargetType.MEDIA_ITEM_TEMPORAL_RANGE), - TEXT(TargetType.TEXT) +enum class ApiTargetType { + JUDGEMENT, JUDGEMENT_WITH_VOTE, MEDIA_ITEM, MEDIA_ITEM_TEMPORAL_RANGE, TEXT; + + /** + * Converts this [ApiTargetType] to a [TargetType] representation. Requires an ongoing transaction. + * + * @return [TargetType] + */ + fun toTargetType(): TargetType = when(this) { + JUDGEMENT -> TargetType.JUDGEMENT + JUDGEMENT_WITH_VOTE -> TargetType.JUDGEMENT_WITH_VOTE + MEDIA_ITEM -> TargetType.MEDIA_ITEM + MEDIA_ITEM_TEMPORAL_RANGE -> TargetType.MEDIA_ITEM_TEMPORAL_RANGE + TEXT -> TargetType.TEXT + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt index 8fa300fc6..01b8c780f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt @@ -8,7 +8,16 @@ import dev.dres.data.model.template.task.options.ScoreOption * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiScoreOption(val option: ScoreOption) { - KIS(ScoreOption.KIS), - AVS(ScoreOption.AVS) +enum class ApiScoreOption { + KIS, AVS; + + /** + * Converts this [ApiScoreOption] to a [ScoreOption] representation. Requires an ongoing transaction. + * + * @return [ScoreOption] + */ + fun toScoreOption(): ScoreOption = when(this) { + KIS -> ScoreOption.KIS + AVS -> ScoreOption.AVS + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt index 54625a4f4..f2fbc145a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt @@ -8,14 +8,24 @@ import dev.dres.data.model.template.task.options.SubmissionOption * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiSubmissionOption(val option: SubmissionOption) { - NO_DUPLICATES(SubmissionOption.NO_DUPLICATES), - LIMIT_CORRECT_PER_TEAM(SubmissionOption.LIMIT_CORRECT_PER_TEAM), - LIMIT_WRONG_PER_TEAM(SubmissionOption.LIMIT_WRONG_PER_TEAM), - LIMIT_TOTAL_PER_TEAM(SubmissionOption.LIMIT_TOTAL_PER_TEAM), - LIMIT_CORRECT_PER_MEMBER(SubmissionOption.LIMIT_CORRECT_PER_MEMBER), - TEMPORAL_SUBMISSION(SubmissionOption.TEMPORAL_SUBMISSION), - TEXTUAL_SUBMISSION(SubmissionOption.TEXTUAL_SUBMISSION), - ITEM_SUBMISSION(SubmissionOption.ITEM_SUBMISSION), - MINIMUM_TIME_GAP(SubmissionOption.MINIMUM_TIME_GAP) +enum class ApiSubmissionOption { + NO_DUPLICATES, LIMIT_CORRECT_PER_TEAM, LIMIT_WRONG_PER_TEAM, LIMIT_TOTAL_PER_TEAM, + LIMIT_CORRECT_PER_MEMBER, TEMPORAL_SUBMISSION, TEXTUAL_SUBMISSION, ITEM_SUBMISSION, MINIMUM_TIME_GAP; + + /** + * Converts this [ApiSubmissionOption] to a [SubmissionOption] representation. Requires an ongoing transaction. + * + * @return [SubmissionOption] + */ + fun toSubmissionOption(): SubmissionOption = when(this) { + NO_DUPLICATES -> SubmissionOption.NO_DUPLICATES + LIMIT_CORRECT_PER_TEAM -> SubmissionOption.LIMIT_CORRECT_PER_TEAM + LIMIT_WRONG_PER_TEAM -> SubmissionOption.LIMIT_WRONG_PER_TEAM + LIMIT_TOTAL_PER_TEAM -> SubmissionOption.LIMIT_TOTAL_PER_TEAM + LIMIT_CORRECT_PER_MEMBER -> SubmissionOption.LIMIT_CORRECT_PER_MEMBER + TEMPORAL_SUBMISSION -> SubmissionOption.TEMPORAL_SUBMISSION + TEXTUAL_SUBMISSION -> SubmissionOption.TEXTUAL_SUBMISSION + ITEM_SUBMISSION -> SubmissionOption.ITEM_SUBMISSION + MINIMUM_TIME_GAP -> SubmissionOption.MINIMUM_TIME_GAP + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt index 0ea405310..f2eb4b263 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt @@ -8,10 +8,19 @@ import dev.dres.data.model.template.task.options.TargetOption * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiTargetOption(val option: TargetOption){ - SINGLE_MEDIA_ITEM(TargetOption.MEDIA_ITEM), - SINGLE_MEDIA_SEGMENT(TargetOption.MEDIA_SEGMENT), - JUDGEMENT(TargetOption.JUDGEMENT), - VOTE(TargetOption.VOTE), - TEXT(TargetOption.TEXT) +enum class ApiTargetOption{ + SINGLE_MEDIA_ITEM, SINGLE_MEDIA_SEGMENT, JUDGEMENT, VOTE, TEXT; + + /** + * Converts this [ApiTargetOption] to a [TargetOption] representation. Requires an ongoing transaction. + * + * @return [TargetOption] + */ + fun toTargetOption(): TargetOption = when(this) { + SINGLE_MEDIA_ITEM -> TargetOption.MEDIA_ITEM + SINGLE_MEDIA_SEGMENT -> TargetOption.MEDIA_SEGMENT + JUDGEMENT -> TargetOption.JUDGEMENT + VOTE -> TargetOption.VOTE + TEXT -> TargetOption.TEXT + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt index f2f3692bd..5e3b4f313 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt @@ -8,8 +8,17 @@ import dev.dres.data.model.template.task.options.TaskOption * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiTaskOption(val option: TaskOption) { - HIDDEN_RESULTS(TaskOption.HIDDEN_RESULTS), - MAP_TO_SEGMENT(TaskOption.MAP_TO_SEGMENT), - PROLONG_ON_SUBMISSION(TaskOption.PROLONG_ON_SUBMISSION) +enum class ApiTaskOption { + HIDDEN_RESULTS, MAP_TO_SEGMENT, PROLONG_ON_SUBMISSION; + + /** + * Converts this [ApiTaskOption] to a [TaskOption] representation. Requires an ongoing transaction. + * + * @return [TaskOption] + */ + fun toTaskOption(): TaskOption = when(this) { + HIDDEN_RESULTS -> TaskOption.HIDDEN_RESULTS + MAP_TO_SEGMENT -> TaskOption.MAP_TO_SEGMENT + PROLONG_ON_SUBMISSION -> TaskOption.PROLONG_ON_SUBMISSION + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt index 20f6212bb..e9c3e5a22 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt @@ -9,9 +9,18 @@ import dev.dres.data.model.submissions.VerdictStatus * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiVerdictStatus(val status: VerdictStatus) { - CORRECT(VerdictStatus.CORRECT), - WRONG(VerdictStatus.WRONG), - INDETERMINATE(VerdictStatus.INDETERMINATE), - UNDECIDABLE(VerdictStatus.UNDECIDABLE) +enum class ApiVerdictStatus { + CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; + + /** + * Converts this [ApiVerdictStatus] to a [VerdictStatus] representation. Requires an ongoing transaction. + * + * @return [VerdictStatus] + */ + fun toVerdictStatus(): VerdictStatus = when(this) { + CORRECT -> VerdictStatus.CORRECT + WRONG -> VerdictStatus.WRONG + INDETERMINATE -> VerdictStatus.INDETERMINATE + UNDECIDABLE -> VerdictStatus.UNDECIDABLE + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt index 11ed592dc..7560a9aa4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt @@ -48,4 +48,6 @@ class Role(entity: Entity) : XdEnumEntity(entity) { * @return [ApiRole] */ fun toApi(): ApiRole = ApiRole.values().find { it.toRole() == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") + + override fun toString() = this.description } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt index 637bc1795..1d1e00fe7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt @@ -32,4 +32,6 @@ class MediaType(entity: Entity) : XdEnumEntity(entity) { */ fun toApi(): ApiMediaType = ApiMediaType.values().find { it.toMediaType() == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") + + override fun toString(): String = this.description } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt index 3c080c45c..4f42a31e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt @@ -50,5 +50,7 @@ class VerdictStatus(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiVerdictStatus] */ - fun toApi() = ApiVerdictStatus.values().find { it.status == this } ?: throw IllegalStateException("Verdict status ${this.description} is not supported.") + fun toApi() = ApiVerdictStatus.values().find { it.toVerdictStatus() == this } ?: throw IllegalStateException("Verdict status ${this.description} is not supported.") + + override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt index b11fd1673..c186eaf85 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt @@ -29,5 +29,7 @@ class TargetType(entity: Entity): XdEnumEntity(entity) { * @return [ApiTargetType] */ fun toApi(): ApiTargetType - = ApiTargetType.values().find { it.type == this } ?: throw IllegalStateException("Target type ${this.description} is not supported.") + = ApiTargetType.values().find { it.toTargetType() == this } ?: throw IllegalStateException("Target type ${this.description} is not supported.") + + override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt index f2e42a677..6d30477f8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt @@ -41,5 +41,7 @@ class ScoreOption(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiScoreOption] */ - fun toApi() = ApiScoreOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiScoreOption.values().find { it.toScoreOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + + override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt index 190f0b25f..8bc8f4d13 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt @@ -53,5 +53,5 @@ class SubmissionOption(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiSubmissionOption] */ - fun toApi() = ApiSubmissionOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiSubmissionOption.values().find { it.toSubmissionOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt index f64d7659a..70f5ceba1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt @@ -30,5 +30,7 @@ class TargetOption(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiTargetOption] */ - fun toApi() = ApiTargetOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiTargetOption.values().find { it.toTargetOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + + override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt index 6eb596824..6a6d01a17 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt @@ -29,5 +29,5 @@ class TaskOption(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiTaskOption] */ - fun toApi() = ApiTaskOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiTaskOption.values().find { it.toTaskOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file From 73e37fc8459547e847a31625c27007e3af84ed41 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 14:25:51 +0100 Subject: [PATCH 051/498] Fixed some more naming inconsistencies --- .../dev/dres/api/cli/EvaluationCommand.kt | 32 +++++------ .../main/kotlin/dev/dres/api/rest/RestApi.kt | 12 ++--- ...ndler.kt => ListEvaluationScoreHandler.kt} | 2 +- ...t => AbstractEvaluationTemplateHandler.kt} | 2 +- .../CreateEvaluationTemplateHandler.kt | 10 ++-- .../DeleteEvaluationTemplateHandler.kt | 4 +- .../handler/template/GetTeamLogoHandler.kt | 4 +- .../ListEvaluationTemplatesHandler.kt | 8 +-- .../rest/handler/template/ListTasksHandler.kt | 4 +- .../rest/handler/template/ListTeamHandler.kt | 4 +- .../template/ShowEvaluationTemplateHandler.kt | 4 +- ...nHandler.kt => UpdateEvaluationHandler.kt} | 4 +- ...eCompetition.kt => ApiCreateEvaluation.kt} | 2 +- ...onOverview.kt => ApiEvaluationOverview.kt} | 2 +- doc/oas.json | 54 +++++++------------ .../competition-form.builder.ts | 21 +++----- .../video-player-segment-builder.component.ts | 12 ++--- ...tion-builder-task-type-dialog.component.ts | 14 ++--- .../competition-builder.component.ts | 10 +--- .../competition-create-dialog.component.ts | 4 +- .../competition-list.component.ts | 25 +++++---- .../app/run/abstract-run-list.component.ts | 12 ++--- .../src/app/run/admin-run-list.component.ts | 13 ++--- .../run-admin-submissions-list.component.ts | 16 +++--- .../run-admin-toolbar.component.ts | 8 +-- .../src/app/run/run-admin-view.component.ts | 19 +++---- .../run-async-admin-view.component.ts | 5 -- .../run-score-history.component.ts | 10 ++-- .../src/app/run/viewer-run-list.component.ts | 8 ++- .../src/app/viewer/task-viewer.component.ts | 4 +- .../src/app/viewer/teams-viewer.component.ts | 1 - 31 files changed, 132 insertions(+), 198 deletions(-) rename backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/{ListCompetitionScoreHandler.kt => ListEvaluationScoreHandler.kt} (92%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/template/{AbstractCompetitionDescriptionHandler.kt => AbstractEvaluationTemplateHandler.kt} (93%) rename backend/src/main/kotlin/dev/dres/api/rest/handler/template/{UpdateCompetitionHandler.kt => UpdateEvaluationHandler.kt} (96%) rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/{ApiCreateCompetition.kt => ApiCreateEvaluation.kt} (74%) rename backend/src/main/kotlin/dev/dres/api/rest/types/competition/{ApiCompetitionOverview.kt => ApiEvaluationOverview.kt} (59%) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index a58771445..aeab06102 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -33,17 +33,17 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) init { this.subcommands( Create(), - ListCompetitionCommand(), - ShowCompetitionCommand(), - PrepareCompetitionCommand(), - DeleteCompetitionCommand(), - CopyCompetitionCommand(), - ExportCompetitionCommand(), - ImportCompetitionCommand() + List(), + Show(), + Prepare(), + Delete(), + Copy(), + Export(), + Import() ) } - override fun aliases(): Map> { + override fun aliases(): Map> { return mapOf( "ls" to listOf("list"), "remove" to listOf("delete"), @@ -55,7 +55,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** The cache location [Paths]. */ private val cacheLocation = Paths.get(config.cachePath, "tasks") - abstract inner class AbstractCompetitionCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { + abstract inner class AbstractEvaluationCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { protected val id: String? by option("-i", "--id") protected val name: String? by option("-c", "--competition") } @@ -88,7 +88,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * [CliktCommand] to delete a [EvaluationTemplate]. */ - inner class DeleteCompetitionCommand : AbstractCompetitionCommand(name = "delete", help = "Deletes a competition") { + inner class Delete : AbstractEvaluationCommand(name = "delete", help = "Deletes a competition") { override fun run() { this@EvaluationCommand.store.transactional { val competition = EvaluationTemplate.query((EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name)).firstOrNull() @@ -105,7 +105,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * [CliktCommand] to copy a [EvaluationTemplate]. */ - inner class CopyCompetitionCommand : AbstractCompetitionCommand(name = "copy", help = "Copies a Competition") { + inner class Copy : AbstractEvaluationCommand(name = "copy", help = "Copies a Competition") { override fun run() { this@EvaluationCommand.store.transactional { val competition = EvaluationTemplate.query((EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name)).firstOrNull() @@ -123,7 +123,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * [CliktCommand] to list all [EvaluationTemplate]s. */ - inner class ListCompetitionCommand : CliktCommand(name = "list", help = "Lists an overview of all Competitions") { + inner class List : CliktCommand(name = "list", help = "Lists an overview of all Competitions") { override fun run() = this@EvaluationCommand.store.transactional(true) { var no = 0 println(table { @@ -148,7 +148,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * [CliktCommand] to show a specific [EvaluationTemplate]. */ - inner class ShowCompetitionCommand : AbstractCompetitionCommand(name = "show", help = "Shows details of a Competition") { + inner class Show : AbstractEvaluationCommand(name = "show", help = "Shows details of a Competition") { override fun run() = this@EvaluationCommand.store.transactional(true) { val competition = EvaluationTemplate.query( (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) @@ -179,7 +179,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * [CliktCommand] to prepare a specific [EvaluationTemplate]. */ - inner class PrepareCompetitionCommand : AbstractCompetitionCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { + inner class Prepare : AbstractEvaluationCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { override fun run() = this@EvaluationCommand.store.transactional(true) { val competition = EvaluationTemplate.query( @@ -210,7 +210,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * Exports a specific competition to a JSON file. */ - inner class ExportCompetitionCommand : AbstractCompetitionCommand(name = "export", help = "Exports a competition description as JSON.") { + inner class Export : AbstractEvaluationCommand(name = "export", help = "Exports a competition description as JSON.") { /** Path to the file that should be created .*/ private val path: Path by option("-o", "--out", help = "The destination file for the competition.").path().required() @@ -245,7 +245,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) /** * Imports a competition from a JSON file. */ - inner class ImportCompetitionCommand : CliktCommand(name = "import", help = "Imports a competition description from JSON.") { + inner class Import : CliktCommand(name = "import", help = "Imports a competition description from JSON.") { /** Flag indicating whether a new competition should be created.*/ private val new: Boolean by option("-n", "--new", help = "Flag indicating whether a new competition should be created.").flag("-u", "--update", default = true) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 0017bbaff..6c720e86f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -21,7 +21,7 @@ import dev.dres.api.rest.handler.template.* import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.MediaPreviewHandler import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler -import dev.dres.api.rest.handler.scores.ListCompetitionScoreHandler +import dev.dres.api.rest.handler.scores.ListEvaluationScoreHandler import dev.dres.api.rest.handler.submission.BatchSubmissionHandler import dev.dres.api.rest.handler.submission.SubmissionHandler import dev.dres.api.rest.handler.system.CurrentTimeHandler @@ -37,19 +37,13 @@ import dev.dres.utilities.NamedThreadFactory import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* import io.javalin.community.ssl.SSLPlugin -import io.javalin.http.staticfiles.Location import io.javalin.openapi.plugin.OpenApiConfiguration import io.javalin.openapi.plugin.OpenApiPlugin import jetbrains.exodus.database.TransientEntityStore -import org.eclipse.jetty.http.HttpCookie import org.eclipse.jetty.server.* -import org.eclipse.jetty.server.session.DefaultSessionCache -import org.eclipse.jetty.server.session.FileSessionDataStore -import org.eclipse.jetty.server.session.SessionHandler import org.eclipse.jetty.util.thread.QueuedThreadPool import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory -import java.io.File /** * This is a singleton instance of the RESTful API @@ -112,7 +106,7 @@ object RestApi { // Competition ListEvaluationTemplatesHandler(store), CreateEvaluationTemplateHandler(store), - UpdateCompetitionHandler(store, config), + UpdateEvaluationHandler(store, config), ShowEvaluationTemplateHandler(store), DeleteEvaluationTemplateHandler(store), ListTeamHandler(store), @@ -140,7 +134,7 @@ object RestApi { GetSubmissionHistoryInfoHandler(store), // Competition run scores - ListCompetitionScoreHandler(store), + ListEvaluationScoreHandler(store), CurrentTaskScoreHandler(store), HistoryTaskScoreHandler(store), ListScoreSeriesHandler(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt similarity index 92% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt index f02928683..817b244b7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListCompetitionScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt @@ -17,7 +17,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class ListCompetitionScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { +class ListEvaluationScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { override val route = "score/evaluation/{evaluationId}" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt similarity index 93% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt index 2b86a3207..35ab8576f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractCompetitionDescriptionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt @@ -22,7 +22,7 @@ import kotlinx.dnq.query.query * @author Loris Sauter * @version 1.0.0 */ -abstract class AbstractCompetitionDescriptionHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { +abstract class AbstractEvaluationTemplateHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index 719c0fd83..72fd288a7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.competition.ApiCreateCompetition +import dev.dres.api.rest.types.competition.ApiCreateEvaluation import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -14,14 +14,14 @@ import jetbrains.exodus.database.TransientEntityStore import java.util.* /** - * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to create a new [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), PostRestHandler { +class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), PostRestHandler { override val route: String = "template" @@ -29,7 +29,7 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCom summary = "Creates a new evaluation template.", path = "/api/v2/template", methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(ApiCreateCompetition::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiCreateEvaluation::class)]), tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -40,7 +40,7 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCom ) override fun doPost(ctx: Context): SuccessStatus { val createRequest = try { - ctx.bodyAsClass() + ctx.bodyAsClass() } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index 4e80cddeb..894fbcd50 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -9,14 +9,14 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractCompetitionDescriptionHandler] that can be used to delete an existing [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to delete an existing [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), DeleteRestHandler { +class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), DeleteRestHandler { override val route: String = "template/{templateId}" @OpenApi( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index e28617f5f..cb16bb4e7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -14,14 +14,14 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * A [AbstractCompetitionDescriptionHandler] that can be used to list all [Team] logos. + * A [AbstractEvaluationTemplateHandler] that can be used to list all [Team] logos. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 1.0.0 */ -class GetTeamLogoHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { +class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler { override val route = "template/logo/{logoId}" override val apiVersion = "v2" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index 3279c90f6..b4f273971 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.competition.ApiCompetitionOverview +import dev.dres.api.rest.types.competition.ApiEvaluationOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.template.EvaluationTemplate import io.javalin.http.Context @@ -21,7 +21,7 @@ import kotlinx.dnq.query.size * @author Loris Sauter * @version 2.0.0 */ -class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { +class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler> { override val route: String = "template/list" @OpenApi( @@ -29,14 +29,14 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractComp path = "/api/v2/template/list", tags = ["Template"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = this.store.transactional(true) { EvaluationTemplate.all().asSequence().map { - ApiCompetitionOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) + ApiEvaluationOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 7a6c1f953..78c122b49 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -10,14 +10,14 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence /** - * A [AbstractCompetitionDescriptionHandler] that can be used to list all [TaskTemplate]s. + * A [AbstractEvaluationTemplateHandler] that can be used to list all [TaskTemplate]s. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class ListTasksHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { +class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler> { override val route: String = "template/{templateId}/task/list" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index 8bcf565d4..d8ea441aa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -10,14 +10,14 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence /** - * A [AbstractCompetitionDescriptionHandler] that can be used to list all [Team]s. + * A [AbstractEvaluationTemplateHandler] that can be used to list all [Team]s. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class ListTeamHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler> { +class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler> { override val route: String = "competition/{competitionId}/team/list" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index c42ea797d..f7eb59361 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -9,14 +9,14 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractCompetitionDescriptionHandler] that can be used to show an existing [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to show an existing [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractCompetitionDescriptionHandler(store), GetRestHandler { +class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler { override val route: String = "template/{templateId}" @OpenApi( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt similarity index 96% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt index cac999de2..1431de9e9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateCompetitionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt @@ -24,14 +24,14 @@ import java.io.ByteArrayInputStream import java.util.* /** - * A [AbstractCompetitionDescriptionHandler] that can be used to create a new [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to create a new [EvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter * @version 2.0.0 */ -class UpdateCompetitionHandler(store: TransientEntityStore, val config: Config) : AbstractCompetitionDescriptionHandler(store), PatchRestHandler { +class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : AbstractEvaluationTemplateHandler(store), PatchRestHandler { override val route: String = "template" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateCompetition.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateEvaluation.kt similarity index 74% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateCompetition.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateEvaluation.kt index c37a6362c..1eb93496f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateCompetition.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCreateEvaluation.kt @@ -6,4 +6,4 @@ package dev.dres.api.rest.types.competition * @author Ralph Gasser * @version 1.0 */ -data class ApiCreateCompetition(val name: String, val description: String) \ No newline at end of file +data class ApiCreateEvaluation(val name: String, val description: String) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt similarity index 59% rename from backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt index 5a73ce704..e9c1d1b3c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiCompetitionOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt @@ -8,4 +8,4 @@ import dev.dres.data.model.template.EvaluationTemplate * @author Ralph Gasser * @version 1.0.0 */ -data class ApiCompetitionOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int) \ No newline at end of file +data class ApiEvaluationOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int) \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index edf8d5a97..916424655 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -3632,7 +3632,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiCreateCompetition" + "$ref" : "#/components/schemas/ApiCreateEvaluation" } } }, @@ -3869,7 +3869,7 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiCompetitionOverview" + "$ref" : "#/components/schemas/ApiEvaluationOverview" } } } @@ -4603,18 +4603,25 @@ "ApiEvaluationOverview" : { "type" : "object", "properties" : { - "state" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + "id" : { + "type" : "string" }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "state", "teamOverviews" ] + "required" : [ "id", "name", "taskCount", "teamCount" ] }, "ApiTaskTemplateInfo" : { "type" : "object", @@ -5080,7 +5087,7 @@ }, "required" : [ "id", "username", "role" ] }, - "ApiCreateCompetition" : { + "ApiCreateEvaluation" : { "type" : "object", "properties" : { "name" : { @@ -5092,29 +5099,6 @@ }, "required" : [ "name", "description" ] }, - "ApiCompetitionOverview" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskCount" : { - "type" : "integer", - "format" : "int32" - }, - "teamCount" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "id", "name", "taskCount", "teamCount" ] - }, "ApiTaskTemplate" : { "type" : "object", "properties" : { diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index feceed6e5..a40a57028 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -1,16 +1,7 @@ import { + ApiMediaItem, + ApiTaskGroup, ApiTaskType, CollectionService, - ConfiguredOptionQueryComponentOption, - ConfiguredOptionTargetOption, - RestMediaItem, - RestTaskDescription, - RestTaskDescriptionComponent, - RestTaskDescriptionTarget, - RestTaskDescriptionTargetItem, - RestTemporalPoint, - RestTemporalRange, - TaskGroup, - TaskType, } from '../../../../../openapi'; import { AbstractControl, FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { filter, first, switchMap } from 'rxjs/operators'; @@ -26,7 +17,7 @@ export class CompetitionFormBuilder { public form: FormGroup; /** List of data sources managed by this CompetitionFormBuilder. */ - private dataSources = new Map>(); + private dataSources = new Map>(); /** * Constructor for CompetitionFormBuilder. @@ -37,8 +28,8 @@ export class CompetitionFormBuilder { * @param data The {@link RestTaskDescription} to initialize the form with. */ constructor( - private taskGroup: TaskGroup, - private taskType: TaskType, + private taskGroup: ApiTaskGroup, + private taskType: ApiTaskType, private collectionService: CollectionService, private data?: RestTaskDescription ) { @@ -63,7 +54,7 @@ export class CompetitionFormBuilder { * * @param key Key to fetch the data source for. */ - public dataSource(key: string): Observable { + public dataSource(key: string): Observable { return this.dataSources.get(key); } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts index 074bc6c7f..cab974671 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts @@ -1,11 +1,11 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core'; import { Observable, of, Subscription } from 'rxjs'; -import { RestMediaItem, RestTemporalPoint, RestTemporalRange } from '../../../../../../openapi'; +import { ApiMediaItem, ApiTemporalPoint, ApiTemporalRange } from '../../../../../../openapi'; import { AppConfig } from '../../../../app.config'; import { Options } from '@angular-slider/ngx-slider'; export interface VideoPlayerSegmentBuilderData { - mediaItem: RestMediaItem; + mediaItem: ApiMediaItem; segmentStart: number; segmentEnd: number; } @@ -17,7 +17,7 @@ export interface VideoPlayerSegmentBuilderData { }) export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDestroy { @Input() data: VideoPlayerSegmentBuilderData; - @Output() rangeChange = new EventEmitter(); + @Output() rangeChange = new EventEmitter(); @ViewChild('videoPlayer', { static: false }) video: ElementRef; videoUrl: Observable; @@ -162,9 +162,9 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest public fetchData() { const out = { - start: { value: this.startInSeconds.toString(), unit: 'SECONDS' } as RestTemporalPoint, - end: { value: this.endInSeconds.toString(), unit: 'SECONDS' } as RestTemporalPoint, - } as RestTemporalRange; + start: { value: this.startInSeconds.toString(), unit: 'SECONDS' } as ApiTemporalPoint, + end: { value: this.endInSeconds.toString(), unit: 'SECONDS' } as ApiTemporalPoint, + } as ApiTemporalRange; console.log(`Fetched: ${out}`); return out; } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts index af6e01737..c3b7cf0e0 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts @@ -1,12 +1,8 @@ import { AfterViewInit, Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { - ConfiguredOptionQueryComponentOption, - ConfiguredOptionScoringOption, - ConfiguredOptionSimpleOption, - ConfiguredOptionSubmissionFilterOption, - ConfiguredOptionTargetOption, - TaskType, + ApiTaskType + } from '../../../../../openapi'; import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatCheckboxChange } from '@angular/material/checkbox'; @@ -52,7 +48,7 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: TaskType + @Inject(MAT_DIALOG_DATA) public data: ApiTaskType ) { this.init(); } @@ -80,7 +76,7 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV } uploaded = (data: string) => { - const parsed = JSON.parse(data) as TaskType; + const parsed = JSON.parse(data) as ApiTaskType; this.data = parsed; this.init(); console.log('Loaded task group: ' + JSON.stringify(parsed)); @@ -232,7 +228,7 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV /** * Creates the [TaskType] object from the form data and returns it. */ - private fetchFromForm(): TaskType { + private fetchFromForm(): ApiTaskType { return { name: this.form.get('name').value, taskDuration: this.form.get('defaultTaskDuration').value, diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 22bb1d036..9843a0506 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -3,12 +3,6 @@ import { ActivatedRoute, Router, RouterStateSnapshot } from '@angular/router'; import { filter, map, shareReplay, take } from 'rxjs/operators'; import { DownloadService, - CompetitionService, - ConfiguredOptionQueryComponentOption, - ConfiguredOptionScoringOption, - ConfiguredOptionSimpleOption, - ConfiguredOptionSubmissionFilterOption, - ConfiguredOptionTargetOption, ApiEvaluationTemplate, ApiTaskTemplate, ApiTeam, @@ -16,7 +10,7 @@ import { ApiTaskType, ApiUser, UserService, - UserRequest, + UserRequest, EvaluationService, } from '../../../../openapi'; import { MatSnackBar } from '@angular/material/snack-bar'; import { FormControl, FormGroup } from '@angular/forms'; @@ -163,7 +157,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat lscTemplate = CompetitionBuilderComponent.LSC_TEMPLATE; constructor( - private competitionService: CompetitionService, + private competitionService: EvaluationService, private userService: UserService, private downloadService: DownloadService, private route: ActivatedRoute, diff --git a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts index 8bbeaaef2..aa8c27a33 100644 --- a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts +++ b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; import { FormControl, FormGroup } from '@angular/forms'; -import { CompetitionCreate } from '../../../../openapi'; +import { ApiCreateEvaluation } from '../../../../openapi'; @Component({ selector: 'app-competition-create-dialog', @@ -22,7 +22,7 @@ export class CompetitionCreateDialogComponent { this.dialogRef.close({ name: this.form.get('name').value, description: this.form.get('description').value, - } as CompetitionCreate); + } as ApiCreateEvaluation); } } diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts index 0675eb9f1..5747a84f5 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ b/frontend/src/app/competition/competition-list/competition-list.component.ts @@ -1,10 +1,9 @@ import { AfterViewInit, Component } from '@angular/core'; import { - CompetitionCreate, - CompetitionOverview, + ApiCreateEvaluation, + ApiEvaluationOverview, CompetitionRunAdminService, - CompetitionService, - CompetitionStartMessage, + EvaluationService, DownloadService, RunProperties, } from '../../../../openapi'; @@ -27,11 +26,11 @@ import { export class CompetitionListComponent implements AfterViewInit { /** */ displayedColumns = ['actions', 'id', 'name', 'description', 'taskCount', 'teamCount']; - competitions: CompetitionOverview[] = []; + competitions: ApiEvaluationOverview[] = []; waitingForRun = false; constructor( - private competitionService: CompetitionService, + private evaluationService: EvaluationService, private runAdminService: CompetitionRunAdminService, private downloadService: DownloadService, private routerService: Router, @@ -45,8 +44,8 @@ export class CompetitionListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - flatMap((r: CompetitionCreate) => { - return this.competitionService.postApiV1Competition(r); + flatMap((r: ApiCreateEvaluation) => { + return this.evaluationService.postApiV1Competition(r); }) ) .subscribe( @@ -79,7 +78,7 @@ export class CompetitionListComponent implements AfterViewInit { name: r.name, type: r.type, properties: properties, - } as CompetitionStartMessage); + } as ApiCreateEvaluation); }) ) .subscribe( @@ -107,7 +106,7 @@ export class CompetitionListComponent implements AfterViewInit { }); dialogRef.afterClosed().subscribe((result) => { if (result) { - this.competitionService.deleteApiV1CompetitionWithCompetitionid(competitionId).subscribe( + this.evaluationService.deleteApiV1CompetitionWithCompetitionid(competitionId).subscribe( (r) => { this.refresh(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -121,8 +120,8 @@ export class CompetitionListComponent implements AfterViewInit { } public refresh() { - this.competitionService.getApiV1CompetitionList().subscribe( - (results: CompetitionOverview[]) => { + this.evaluationService.getApiV1CompetitionList().subscribe( + (results: ApiEvaluationOverview[]) => { this.competitions = results; }, (r) => { @@ -145,7 +144,7 @@ export class CompetitionListComponent implements AfterViewInit { return () => name; }; - resolveCompetitionOverviewById(_: number, item: CompetitionOverview) { + resolveCompetitionOverviewById(_: number, item: ApiEvaluationOverview) { return item.id; } } diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index b65b3afb6..50d9806ef 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -1,11 +1,9 @@ import { combineLatest, merge, Observable, Subject, timer } from 'rxjs'; import { - CompetitionRunAdminService, CompetitionRunScoresService, - CompetitionRunService, DownloadService, RunProperties, - ApiEvaluationState, + ApiEvaluationState, EvaluationAdministratorService, EvaluationService, } from '../../../openapi'; import { flatMap, map, take } from 'rxjs/operators'; import { Router } from '@angular/router'; @@ -31,8 +29,8 @@ export class AbstractRunListComponent { update = new Subject(); constructor( - protected runService: CompetitionRunService, - protected runAdminService: CompetitionRunAdminService, + protected runService: EvaluationService, + protected runAdminService: EvaluationAdministratorService, protected scoreService: CompetitionRunScoresService, protected downloadService: DownloadService, protected router: Router, @@ -106,7 +104,7 @@ export class AbstractRunListComponent { } public nextTask(runId: string) { - this.runAdminService.postApiV1RunAdminWithRunidTaskNext(runId).subscribe( + this.runAdminService.apiV2EvaluationAdminRunIdTaskNextPost(runId).subscribe( (r) => { this.update.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -157,7 +155,7 @@ export class AbstractRunListComponent { * Creates a combined observable that updates the state in a regular interval and the info + * state whenever a manual update is triggered. */ - const query = combineLatest([this.runService.getApiV1RunInfoList(), this.runService.getApiV1RunStateList()]); + const query = combineLatest([this.runService.apiV2EvaluationInfoListGet(), this.runService.apiV2EvaluationStateListGet()]); this.runs = merge(timer(0, this.updateInterval), this.update).pipe( flatMap((t) => query), map(([info, state]) => { diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index 776825c86..7d42cf404 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -1,13 +1,10 @@ import { Component } from '@angular/core'; import { AbstractRunListComponent, RunInfoWithState } from './abstract-run-list.component'; import { - AdminRunOverview, + ApiEvaluationInfo, ApiEvaluationState, CompetitionRunAdminService, CompetitionRunScoresService, - CompetitionRunService, - DownloadService, - RunInfo, - RunState, + DownloadService, EvaluationService, } from '../../../openapi'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; @@ -18,10 +15,10 @@ import { } from '../shared/confirmation-dialog/confirmation-dialog.component'; import { forkJoin, merge, timer } from 'rxjs'; import { flatMap, map, switchMap } from 'rxjs/operators'; -import RunStatusEnum = RunState.RunStatusEnum; +import RunStatusEnum = ApiEvaluationState.RunStatusEnum; export interface RunInfoOverviewTuple { - runInfo: RunInfo; + runInfo: ApiEvaluationInfo; overview: AdminRunOverview; } @@ -31,7 +28,7 @@ export interface RunInfoOverviewTuple { }) export class AdminRunListComponent extends AbstractRunListComponent { constructor( - runService: CompetitionRunService, + runService: EvaluationService, runAdminService: CompetitionRunAdminService, scoreService: CompetitionRunScoresService, downloadService: DownloadService, diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts index f0afd4317..9c97f8051 100644 --- a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts +++ b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts @@ -1,5 +1,5 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; -import { CompetitionRunAdminService, SubmissionInfo, TaskRunSubmissionInfo } from '../../../../openapi'; +import {ApiSubmissionInfo, CompetitionRunAdminService} from '../../../../openapi'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog } from '@angular/material/dialog'; import { MatTable, MatTableDataSource } from '@angular/material/table'; @@ -18,7 +18,7 @@ import { AppConfig } from '../../app.config'; export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestroy { @ViewChild('group', { static: true }) group: MatButtonToggleGroup; - @ViewChild('table', { static: true }) table: MatTable; + @ViewChild('table', { static: true }) table: MatTable; @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; @@ -55,10 +55,10 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro public refreshSubject: Subject = new Subject(); /** The data source for the table. */ - public dataSource: MatTableDataSource = new MatTableDataSource(); + public dataSource: MatTableDataSource = new MatTableDataSource(); /** The data sources mapped by the taskRunId */ - public dataSources: Map> = new Map(); + public dataSources: Map> = new Map(); /** The list of taskRunIds there are submissions for, for convenience in the template */ public taskRunIds: string[] = []; @@ -101,7 +101,7 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro this.taskRunIds = []; /* Repopulate */ s.forEach((trsi) => { - const ds = new MatTableDataSource(); + const ds = new MatTableDataSource(); ds.data = trsi.submissions; this.dataSources.set(trsi.taskRunId, ds); this.taskRunIds.push(trsi.taskRunId); @@ -123,7 +123,7 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro * @param submission The {@link SubmissionInfo} to update. * @param newStatus The new status. */ - public update(submission: SubmissionInfo, newStatus: SubmissionInfo.StatusEnum) { + public update(submission: ApiSubmissionInfo, newStatus: SubmissionInfo.StatusEnum) { submission.status = newStatus; console.log(submission); this.runId @@ -136,7 +136,7 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro /** * Generates a URL for the preview image of a submission. */ - public previewForSubmission(submission: SubmissionInfo): Observable { + public previewForSubmission(submission: ApiSubmissionInfo): Observable { return this.runId.pipe(map((runId) => this.config.resolveApiUrl(`/preview/submission/${runId}/${submission.id}`))); } @@ -144,7 +144,7 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro return item; } - resolveSubmissionById(_: number, item: SubmissionInfo) { + resolveSubmissionById(_: number, item: ApiSubmissionInfo) { return item.id; } } diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index d331f04f9..bff0ee61a 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -6,11 +6,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { AppConfig } from '../../app.config'; import { CompetitionRunAdminService, - CompetitionRunScoresService, - CompetitionRunService, - CompetitionService, - DownloadService, - RunInfo, + CompetitionRunScoresService, DownloadService, EvaluationService, } from '../../../../openapi'; import { MatDialog } from '@angular/material/dialog'; import { take } from 'rxjs/operators'; @@ -34,7 +30,7 @@ export class RunAdminToolbarComponent implements OnInit { private router: Router, private activeRoute: ActivatedRoute, private config: AppConfig, - private runService: CompetitionRunService, + private runService: EvaluationService, private competitionService: CompetitionService, private runAdminService: CompetitionRunAdminService, private scoreService: CompetitionRunScoresService, diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index 8e1be23f0..99a7470b2 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -2,16 +2,9 @@ import { AfterViewInit, Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AppConfig } from '../app.config'; import { - CompetitionRunAdminService, - CompetitionRunService, - CompetitionService, - PastTaskInfo, - RestDetailedTeam, - RunInfo, - RunState, - TaskInfo, - TaskRunSubmissionInfo, - ViewerInfo, + ApiEvaluationInfo, + ApiEvaluationState, ApiViewerInfo, + CompetitionRunAdminService, } from '../../../openapi'; import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, timer } from 'rxjs'; import { catchError, filter, flatMap, map, shareReplay, switchMap } from 'rxjs/operators'; @@ -24,8 +17,8 @@ import { import { RunInfoOverviewTuple } from './admin-run-list.component'; export interface CombinedRun { - info: RunInfo; - state: RunState; + info: ApiEvaluationInfo; + state: ApiEvaluationState; } @Component({ @@ -38,7 +31,7 @@ export class RunAdminViewComponent { runIdAsSubject: BehaviorSubject = new BehaviorSubject(''); run: Observable; runOverview: Observable; - viewers: Observable; + viewers: Observable; update = new Subject(); displayedColumnsTasks: string[] = ['name', 'group', 'type', 'duration', 'past', 'action']; teams: Observable; diff --git a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts index 4b434be87..7ebdc5538 100644 --- a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts +++ b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts @@ -3,14 +3,9 @@ import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, timer } import { CompetitionRunAdminService, CompetitionRunScoresService, - CompetitionRunService, EvaluationService, DownloadService, - PastTaskInfo, - RestDetailedTeam, ApiTeam, - TeamInfo, - TeamTaskOverview, } from '../../../../openapi'; import { ActivatedRoute, Router } from '@angular/router'; import { AppConfig } from '../../app.config'; diff --git a/frontend/src/app/run/score-history/run-score-history.component.ts b/frontend/src/app/run/score-history/run-score-history.component.ts index 2d0720d12..4abd14f20 100644 --- a/frontend/src/app/run/score-history/run-score-history.component.ts +++ b/frontend/src/app/run/score-history/run-score-history.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { CompetitionRunScoresService, CompetitionRunService, RunInfo } from '../../../../openapi'; +import { CompetitionRunScoresService, EvaluationService, ApiEvaluationInfo } from '../../../../openapi'; import { catchError, filter, flatMap, map, shareReplay, switchMap, tap } from 'rxjs/operators'; import { combineLatest, concat, interval, Observable, of } from 'rxjs'; import { @@ -26,7 +26,7 @@ export class RunScoreHistoryComponent { /** Title for the time series graph. */ public title: Observable; /** Run information for the current run ID. */ - public runInfo: Observable; + public runInfo: Observable; /** Time series data. */ public series: Observable; /** The currently selected scoreboard. */ @@ -113,14 +113,14 @@ export class RunScoreHistoryComponent { constructor( private router: Router, private activeRoute: ActivatedRoute, - private runService: CompetitionRunService, + private runService: EvaluationService, private scoreService: CompetitionRunScoresService ) { /* Information about current run. */ this.runId = this.activeRoute.params.pipe(map((a) => a.runId)); this.runInfo = this.runId.pipe( switchMap((runId) => - this.runService.getApiV1RunWithRunidInfo(runId).pipe( + this.runService.apiV2EvaluationEvaluationIdInfoGet(runId).pipe( catchError((err, o) => { console.log( `[ScoreHistoryComponent] There was an error while loading information in the current run: ${err?.message}` @@ -151,7 +151,7 @@ export class RunScoreHistoryComponent { /* List of scoreboard for the current run ID. */ this.scoreboards = this.runId.pipe( switchMap((runId) => - this.scoreService.getApiV1ScoreRunWithRunidScoreboardList(runId).pipe( + this.scoreService.apiV2ScoreEvaluationEvaluationIdTeamGroupListGet(runId).pipe( catchError((err, o) => { console.log( `[ScoreHistoryComponent] There was an error while loading information in the current run: ${err?.message}` diff --git a/frontend/src/app/run/viewer-run-list.component.ts b/frontend/src/app/run/viewer-run-list.component.ts index f86bb500b..d97cf7e85 100644 --- a/frontend/src/app/run/viewer-run-list.component.ts +++ b/frontend/src/app/run/viewer-run-list.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; import { AbstractRunListComponent, RunInfoWithState } from './abstract-run-list.component'; import { - CompetitionRunAdminService, - DownloadService, CompetitionRunScoresService, - CompetitionRunService, + DownloadService, EvaluationAdministratorService, EvaluationService, } from '../../../openapi'; import { Router } from '@angular/router'; import { AccessChecking } from '../model/access-checking.interface'; @@ -22,8 +20,8 @@ export class ViewerRunListComponent extends AbstractRunListComponent implements participantGroup = AccessRoleService.PARTICIPANT_GROUP; constructor( - runService: CompetitionRunService, - runAdminService: CompetitionRunAdminService, + runService: EvaluationService, + runAdminService: EvaluationAdministratorService, scoreService: CompetitionRunScoresService, downloadService: DownloadService, router: Router, diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index 0010237f4..6580d62f3 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -1,5 +1,5 @@ import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core'; -import { EvaluationService, ApiContentElement, ApiEvaluationState, ApiTaskTemplateInfo, TaskTarget } from '../../../openapi'; +import {EvaluationService, ApiContentElement, ApiEvaluationState, ApiTaskTemplateInfo, ApiTarget} from '../../../openapi'; import { BehaviorSubject, combineLatest, interval, merge, Observable, of, Subscription, timer } from 'rxjs'; import { catchError, @@ -170,7 +170,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /** Map task target to representation used by viewer. */ this.currentTaskTarget = currentTaskTarget.pipe( - flatMap((h: TaskTarget) => { + flatMap((h: ApiTarget) => { if (!h) { return fromArray([]); } diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index d78baf857..a787d91d4 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -15,7 +15,6 @@ import { ApiEvaluationState, ApiScoreOverview, ApiSubmissionInfo, - ApiTaskTemplateInfo, ApiTeamInfo, } from '../../../openapi'; import {BehaviorSubject, combineLatest, merge, Observable, of, Subscription} from 'rxjs'; From dbf69ced67136dd1148b165a61d551c9d80cc42b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 18:12:19 +0100 Subject: [PATCH 052/498] Fixed issue in AccessManager --- .../kotlin/dev/dres/api/rest/AccessManager.kt | 6 +-- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 44 ++++++++++--------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 7dd69da7e..b877553e2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -33,7 +33,7 @@ object AccessManager { fun manage(handler: Handler, ctx: Context, permittedRoles: Set) { when { permittedRoles.isEmpty() -> handler.handle(ctx) //fallback in case no roles are set, none are required - permittedRoles.contains(RestApiRole.ANYONE) -> handler.handle(ctx) + permittedRoles.contains(ApiRole.ANYONE) -> handler.handle(ctx) rolesOfSession(ctx.sessionToken()).any { it in permittedRoles } -> handler.handle(ctx) else -> ctx.status(401) } @@ -155,6 +155,4 @@ object AccessManager { fun getRunManagerForUser(userId: UserId): Set = this.locks.read { return this.usersToRunMap[userId] ?: emptySet() } -} - -enum class RestApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN } \ No newline at end of file +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 6c720e86f..f88a46479 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -36,9 +36,12 @@ import dev.dres.run.RunExecutor import dev.dres.utilities.NamedThreadFactory import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* +import io.javalin.http.staticfiles.Location import io.javalin.community.ssl.SSLPlugin import io.javalin.openapi.plugin.OpenApiConfiguration import io.javalin.openapi.plugin.OpenApiPlugin +import io.javalin.openapi.plugin.swagger.SwaggerConfiguration +import io.javalin.openapi.plugin.swagger.SwaggerPlugin import jetbrains.exodus.database.TransientEntityStore import org.eclipse.jetty.server.* import org.eclipse.jetty.util.thread.QueuedThreadPool @@ -208,16 +211,16 @@ object RestApi { ClientOpenApiPlugin() ) -// it.plugins.register( -// SwaggerPlugin( -// SwaggerConfiguration().apply { -// this.documentationPath = "/swagger-docs" -// this.uiPath = "/swagger-ui" -// } -// ) -// ) + it.plugins.register( + SwaggerPlugin( + SwaggerConfiguration().apply { + this.documentationPath = "/swagger-docs" + this.uiPath = "/swagger-ui" + } + ) + ) - it.plugins.register(ClientSwaggerPlugin()) +// it.plugins.register(ClientSwaggerPlugin()) it.http.defaultContentType = "application/json" it.http.prefer405over404 = true @@ -231,7 +234,7 @@ object RestApi { val ssl = SSLPlugin { conf -> conf.keystoreFromPath(config.keystorePath, config.keystorePassword) conf.http2 = true - conf.secure = false + conf.secure = true conf.insecurePort = config.httpPort conf.securePort = config.httpsPort conf.sniHostCheck = false @@ -259,6 +262,17 @@ object RestApi { ctx.attribute("session", paramId) } + //logging + logger.info( + logMarker, + "${ctx.req().method} request to ${ctx.path()} with params (${ + ctx.queryParamMap().map { e -> "${e.key}=${e.value}" }.joinToString() + }) from ${ctx.req().remoteAddr}" + ) + if (ctx.path().startsWith("/api/")) { //do not cache api requests + ctx.header("Cache-Control", "no-store") + } + }.routes { path("api") { apiRestHandlers.groupBy { it.apiVersion }.forEach { apiGroup -> @@ -293,16 +307,6 @@ object RestApi { } ws("ws/run", runExecutor) } - }.before { - logger.info( - logMarker, - "${it.req().method} request to ${it.path()} with params (${ - it.queryParamMap().map { e -> "${e.key}=${e.value}" }.joinToString() - }) from ${it.req().remoteAddr}" - ) - if (it.path().startsWith("/api/")) { //do not cache api requests - it.header("Cache-Control", "no-store") - } }.error(401) { it.json(ErrorStatus("Unauthorized request!")) }.exception(Exception::class.java) { e, ctx -> From cb02eeaf57d716f909325e57879e1186054f3912 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 20:43:34 +0100 Subject: [PATCH 053/498] Added default initialization mechanism for id for persistent entities --- .../dres/data/model/XdIdNaturalEntityType.kt | 18 ++++++++++++++++++ .../kotlin/dev/dres/data/model/admin/User.kt | 3 ++- .../dev/dres/data/model/audit/AuditLogEntry.kt | 4 ++-- .../dres/data/model/media/MediaCollection.kt | 3 ++- .../dev/dres/data/model/media/MediaItem.kt | 3 ++- .../dev/dres/data/model/media/MediaSegment.kt | 3 ++- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 1 - 7 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt b/backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt new file mode 100644 index 000000000..a99f0f474 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt @@ -0,0 +1,18 @@ +package dev.dres.data.model + +import kotlinx.dnq.XdNaturalEntityType +import java.util.UUID + +open class XdIdNaturalEntityType : XdNaturalEntityType() { + + override fun new(init: (T.() -> Unit)): T { + val transaction = (entityStore.threadSession + ?: throw IllegalStateException("New entities can be created only in transactional block")) + return wrap(transaction.newEntity(entityType)).apply { + constructor() + this.id = UUID.randomUUID().toString() //default initializer + init() + } + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index 3aea2d7e6..d86b6507a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -2,6 +2,7 @@ package dev.dres.data.model.admin import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.simple.length @@ -17,7 +18,7 @@ typealias UserId = String * @version 2.0.0 */ class User(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() { + companion object : XdIdNaturalEntityType() { /** The minimum length of a password. */ const val MIN_LENGTH_PASSWORD = 6 diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt index 24048e055..b6c99fa56 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt @@ -2,8 +2,8 @@ package dev.dres.data.model.audit import dev.dres.api.rest.types.audit.* import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.xdLink1 import kotlinx.dnq.xdRequiredDateTimeProp import kotlinx.dnq.xdStringProp @@ -12,7 +12,7 @@ import kotlinx.dnq.xdStringProp * */ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() + companion object : XdIdNaturalEntityType() /** The type of [AuditLogEntry]. */ var type by xdLink1(AuditLogType) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt index f65a7c970..b08d3e3cb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -13,7 +14,7 @@ typealias CollectionId = String * @version 2.0.0 */ class MediaCollection(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() + companion object : XdIdNaturalEntityType() /** The name of this [MediaItem]. */ var name: String by xdRequiredStringProp(unique = true, trimmed = false) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index 9437e0538..5032dfdfd 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -2,6 +2,7 @@ package dev.dres.data.model.media import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.simple.requireIf @@ -17,7 +18,7 @@ typealias MediaId = String * @version 2.0.0 */ class MediaItem(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() { + companion object : XdIdNaturalEntityType() { /** Combination of [MediaItem] name / competition must be unique. */ override val compositeIndices = listOf( listOf(MediaItem::name, MediaItem::collection) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt index 6764bf19d..3f60dd613 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.XdIdNaturalEntityType import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity @@ -14,7 +15,7 @@ import kotlinx.dnq.simple.min * @version 2.0.0 */ class MediaSegment(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() { + companion object : XdIdNaturalEntityType() { /** Combination of [MediaSegment] name / item must be unique. */ override val compositeIndices = listOf( listOf(MediaSegment::name, MediaSegment::item) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 436b8e2cd..de9bc051c 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -34,7 +34,6 @@ object UserManager { try { this.store.transactional { User.new { - this.id = UUID.randomUUID().toString() this.username = username this.password = password.password this.role = role From 9b1ad2feeef3d5794b3895972354b5d4c21da837 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 20:44:02 +0100 Subject: [PATCH 054/498] Minor changes in UserManager --- .../kotlin/dev/dres/api/rest/AccessManager.kt | 11 +++++---- .../api/rest/handler/system/LoginHandler.kt | 8 +++---- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 5 ++-- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 24 +++++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index b877553e2..7aa4a6309 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -2,6 +2,7 @@ package dev.dres.api.rest import dev.dres.api.rest.handler.users.SessionToken import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Role import dev.dres.data.model.admin.User import dev.dres.data.model.admin.UserId @@ -61,7 +62,7 @@ object AccessManager { * @param sessionId The [SessionToken] to register the [User] for. * @param user The [User] to register. */ - fun registerUserForSession(sessionId: String, user: User) = this.locks.write { + fun registerUserForSession(sessionId: String, user: ApiUser) = this.locks.write { if (!this.sessionRoleMap.containsKey(sessionId)){ this.sessionRoleMap[sessionId] = mutableSetOf() } @@ -69,10 +70,10 @@ object AccessManager { this.sessionUserMap[sessionId] = user.id this.sessionRoleMap[sessionId]!!.addAll( when(user.role) { - Role.ADMIN -> arrayOf(ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.JUDGE, ApiRole.ADMIN) - Role.JUDGE -> arrayOf(ApiRole.VIEWER, ApiRole.JUDGE) - Role.PARTICIPANT -> arrayOf(ApiRole.VIEWER, ApiRole.PARTICIPANT) - Role.VIEWER -> arrayOf(ApiRole.VIEWER) + ApiRole.ADMIN -> arrayOf(ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.JUDGE, ApiRole.ADMIN) + ApiRole.JUDGE -> arrayOf(ApiRole.VIEWER, ApiRole.JUDGE) + ApiRole.PARTICIPANT -> arrayOf(ApiRole.VIEWER, ApiRole.PARTICIPANT) + ApiRole.VIEWER -> arrayOf(ApiRole.VIEWER) else -> throw IllegalStateException("Role held by user is unknown!") } ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index 533894575..29642e578 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -9,7 +9,6 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Password import dev.dres.data.model.audit.AuditLogSource import dev.dres.mgmt.admin.UserManager -import dev.dres.mgmt.admin.UserManager.getMatchingUser import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.getOrCreateSessionToken import io.javalin.http.BadRequestResponse @@ -50,7 +49,7 @@ class LoginHandler : RestHandler, PostRestHandler { /* Validate login request. */ val username = loginRequest.username val password = Password.Plain(loginRequest.password) - val user = getMatchingUser(username, password) ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) + val user = UserManager.getMatchingApiUser(username, password) ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) val sessionToken = ctx.getOrCreateSessionToken() @@ -60,9 +59,8 @@ class LoginHandler : RestHandler, PostRestHandler { //explicitly set cookie on login ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) - val ret = UserManager.get(username)!!.toApi() - ret.sessionId = sessionToken - return ret + user.sessionId = sessionToken + return user } override val route = "login" diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index de9bc051c..54029cc65 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -1,6 +1,7 @@ package dev.dres.mgmt.admin import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.users.ApiUser import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.* import jetbrains.exodus.database.TransientEntityStore @@ -179,8 +180,8 @@ object UserManager { /** * Either returns a user for this username/password tuple or null */ - fun getMatchingUser(username: String, password: Password.Plain) : User? { + fun getMatchingApiUser(username: String, password: Password.Plain) : ApiUser? = this.store.transactional(readonly = true) { val user = get(null, username) - return if (user?.hashedPassword()?.check(password) == true) user else null + return@transactional if (user?.hashedPassword()?.check(password) == true) user.toApi() else null } } diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 6aee27aae..b4c470143 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -40,7 +40,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionStart(evaluationId: EvaluationId, description: EvaluationTemplate, api: AuditLogSource, session: SessionToken?) { + fun competitionStart(evaluationId: EvaluationId, description: EvaluationTemplate, api: AuditLogSource, session: SessionToken?) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.COMPETITION_START this.source = api @@ -58,7 +58,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun competitionEnd(evaluationId: EvaluationId, api: AuditLogSource, session: SessionToken?) { + fun competitionEnd(evaluationId: EvaluationId, api: AuditLogSource, session: SessionToken?) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.COMPETITION_END this.source = api @@ -78,7 +78,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: TaskTemplate, api: AuditLogSource, session: SessionToken?) { + fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: TaskTemplate, api: AuditLogSource, session: SessionToken?) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.TASK_START this.source = api @@ -99,7 +99,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: AuditLogSource, session: String?) { + fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: AuditLogSource, session: String?) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.TASK_MODIFIED this.source = api @@ -119,7 +119,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionToken?) { + fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionToken?) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.TASK_END this.source = api @@ -139,7 +139,7 @@ object AuditLogger { * @param sessionToken The identifier of the user session. * @param address The IP address of the submitter. */ - fun submission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?, address: String) { + fun submission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?, address: String) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.SUBMISSION this.source = api @@ -159,7 +159,7 @@ object AuditLogger { * @param submission The [Submission] the submission that was validated * @param validator The [SubmissionValidator] instance. */ - fun validateSubmission(submission: Submission, validator: SubmissionValidator) { + fun validateSubmission(submission: Submission, validator: SubmissionValidator) = this.store.transactional { this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.SUBMISSION_VALIDATION @@ -180,7 +180,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param sessionToken The identifier of the user session. */ - fun overrideSubmission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?) { + fun overrideSubmission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?) = this.store.transactional { this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.SUBMISSION_STATUS_OVERWRITE @@ -202,7 +202,7 @@ object AuditLogger { * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system */ - fun prepareJudgement(verdict: Verdict, validator: JudgementValidator, token: String) { + fun prepareJudgement(verdict: Verdict, validator: JudgementValidator, token: String) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.PREPARE_JUDGEMENT this.source = AuditLogSource.INTERNAL @@ -224,7 +224,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param sessionToken The identifier of the user session. */ - fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionToken: SessionToken?) { + fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionToken: SessionToken?) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.JUDGEMENT this.source = api @@ -242,7 +242,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param sessionToken The [SessionToken] */ - fun login(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { + fun login(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.LOGIN this.source = api @@ -259,7 +259,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param sessionToken The [SessionToken] */ - fun logout(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { + fun logout(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) = this.store.transactional { AuditLogEntry.new { this.type = AuditLogType.LOGOUT this.source = api From f59c79bbe6471dc2551e893c5a1b5a5efd1f77f9 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 2 Feb 2023 22:52:44 +0100 Subject: [PATCH 055/498] Simplified id generation of persistent entities --- .../dev/dres/api/cli/EvaluationCommand.kt | 1 - .../dev/dres/api/cli/MediaCollectionCommand.kt | 6 ------ .../handler/collection/AddCollectionHandler.kt | 1 - .../handler/collection/AddMediaItemHandler.kt | 1 - .../admin/CreateEvaluationHandler.kt | 1 - .../submission/BatchSubmissionHandler.kt | 1 - .../handler/submission/SubmissionHandler.kt | 1 - .../template/UpdateEvaluationHandler.kt | 2 +- .../dev/dres/data/model/PersistentEntity.kt | 5 +++++ .../dres/data/model/XdIdNaturalEntityType.kt | 18 ------------------ .../kotlin/dev/dres/data/model/admin/User.kt | 3 +-- .../dev/dres/data/model/audit/AuditLogEntry.kt | 4 ++-- .../dres/data/model/media/MediaCollection.kt | 3 +-- .../dev/dres/data/model/media/MediaItem.kt | 3 +-- .../dev/dres/data/model/media/MediaSegment.kt | 3 +-- .../run/InteractiveAsynchronousEvaluation.kt | 2 -- .../run/InteractiveSynchronousEvaluation.kt | 2 -- 17 files changed, 12 insertions(+), 45 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index aeab06102..7d108f516 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -76,7 +76,6 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) override fun run() { val newCompetition = this@EvaluationCommand.store.transactional { EvaluationTemplate.new { - this.id = UUID.randomUUID().toString() this.name = this@Create.name this.description = this@Create.description } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index cc4077206..31fa77b1c 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -99,7 +99,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } this@MediaCollectionCommand.store.transactional { MediaCollection.new { - this.id = UUID.randomUUID().toString() this.name = this@Create.name this.description = this@Create.description this.path = this@Create.basePath @@ -329,7 +328,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik in this.imageTypes -> { println("Found image $it; analyzing...") collection.items.add(MediaItem.new { - this.id = UUID.randomUUID().toString() this.type = MediaType.IMAGE this.name = it.fileName.nameWithoutExtension this.location = relativePath.toString() @@ -353,7 +351,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik println("Found frame rate to be $fps frames per seconds and duration $duration ms") collection.items.add(MediaItem.new { - this.id = UUID.randomUUID().toString() this.type = MediaType.VIDEO this.name = it.fileName.nameWithoutExtension this.location = relativePath.toString() @@ -461,7 +458,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /* Add new media item. */ collection.items.add(MediaItem.new { - this.id = UUID.randomUUID().toString() this.type = this@AddItem.type.toMediaType() this.name = Paths.get(this@AddItem.path).nameWithoutExtension this.location = this@AddItem.path @@ -535,7 +531,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik for (row in rows) { inserted += 1 collection.items.add(MediaItem.new { - this.id = UUID.randomUUID().toString() this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).toMediaType() this.name = row.getValue("name") this.location = row.getValue("location") @@ -593,7 +588,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inserted += 1 videoItem.segments.add( MediaSegment.new { - this.id = UUID.randomUUID().toString() this.name = videoName this.start = start this.end = end diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index ec17e0502..5d4047840 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -47,7 +47,6 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand val collection = this.store.transactional { MediaCollection.new { - this.id = UUID.randomUUID().toString() this.name = restCollection.name this.description = restCollection.description this.path = restCollection.basePath.cleanPathString() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 584252b86..84b3aa8b4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -56,7 +56,6 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl } val item = MediaItem.new { - this.id = UUID.randomUUID().toString() this.type = mediaItem.type.toMediaType() this.name = mediaItem.name this.location = mediaItem.location diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 8b60e8829..417984b40 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -98,7 +98,6 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs /* Prepare evaluation. */ val evaluation = Evaluation.new { - this.id = UUID.randomUUID().toString() this.name = message.name this.template = template /* TODO: Create copy. */ this.type = message.type.type diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index 77c499caa..713423dbb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -96,7 +96,6 @@ class BatchSubmissionHandler(private val store: TransientEntityStore, private va /* Create new submission. */ val new = Submission.new { - this.id = UUID.randomUUID().toString() this.user = user this.team = team this.timestamp = submissionTime diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index fcd5948e9..e88c06f37 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -165,7 +165,6 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con /* Create new submission. */ val submission = Submission.new { - this.id = UUID.randomUUID().toString() this.user = user this.team = team this.timestamp = submissionTime diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt index 1431de9e9..7ccdeaf6b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt @@ -108,7 +108,7 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : val t = if (task.id != null) { existing.tasks.filter { it.id eq task.id }.first() } else { - val desc = TaskTemplate.new { this.id = UUID.randomUUID().toString() } + val desc = TaskTemplate.new { } existing.tasks.add(desc) desc } diff --git a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt index 7eb87f75b..da98dca11 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt @@ -5,6 +5,7 @@ import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEntity import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.xdRequiredStringProp +import java.util.* /** * The root class for all DRES entities that are persisted via Xodus DNQ. @@ -15,4 +16,8 @@ import kotlinx.dnq.xdRequiredStringProp abstract class PersistentEntity(entity: Entity): XdEntity(entity) { companion object: XdNaturalEntityType() var id: String by xdRequiredStringProp(unique = true, trimmed = false) + + override fun constructor() { + this.id = UUID.randomUUID().toString() + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt b/backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt deleted file mode 100644 index a99f0f474..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/XdIdNaturalEntityType.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.data.model - -import kotlinx.dnq.XdNaturalEntityType -import java.util.UUID - -open class XdIdNaturalEntityType : XdNaturalEntityType() { - - override fun new(init: (T.() -> Unit)): T { - val transaction = (entityStore.threadSession - ?: throw IllegalStateException("New entities can be created only in transactional block")) - return wrap(transaction.newEntity(entityType)).apply { - constructor() - this.id = UUID.randomUUID().toString() //default initializer - init() - } - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt index d86b6507a..3aea2d7e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt @@ -2,7 +2,6 @@ package dev.dres.data.model.admin import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.simple.length @@ -18,7 +17,7 @@ typealias UserId = String * @version 2.0.0 */ class User(entity: Entity): PersistentEntity(entity) { - companion object : XdIdNaturalEntityType() { + companion object : XdNaturalEntityType() { /** The minimum length of a password. */ const val MIN_LENGTH_PASSWORD = 6 diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt index b6c99fa56..24048e055 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt @@ -2,8 +2,8 @@ package dev.dres.data.model.audit import dev.dres.api.rest.types.audit.* import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.xdLink1 import kotlinx.dnq.xdRequiredDateTimeProp import kotlinx.dnq.xdStringProp @@ -12,7 +12,7 @@ import kotlinx.dnq.xdStringProp * */ class AuditLogEntry(entity: Entity): PersistentEntity(entity) { - companion object : XdIdNaturalEntityType() + companion object : XdNaturalEntityType() /** The type of [AuditLogEntry]. */ var type by xdLink1(AuditLogType) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt index b08d3e3cb..f65a7c970 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -14,7 +13,7 @@ typealias CollectionId = String * @version 2.0.0 */ class MediaCollection(entity: Entity): PersistentEntity(entity) { - companion object : XdIdNaturalEntityType() + companion object : XdNaturalEntityType() /** The name of this [MediaItem]. */ var name: String by xdRequiredStringProp(unique = true, trimmed = false) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index 5032dfdfd..9437e0538 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -2,7 +2,6 @@ package dev.dres.data.model.media import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.XdIdNaturalEntityType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.simple.requireIf @@ -18,7 +17,7 @@ typealias MediaId = String * @version 2.0.0 */ class MediaItem(entity: Entity) : PersistentEntity(entity) { - companion object : XdIdNaturalEntityType() { + companion object : XdNaturalEntityType() { /** Combination of [MediaItem] name / competition must be unique. */ override val compositeIndices = listOf( listOf(MediaItem::name, MediaItem::collection) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt index 3f60dd613..6764bf19d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.media import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.XdIdNaturalEntityType import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity @@ -15,7 +14,7 @@ import kotlinx.dnq.simple.min * @version 2.0.0 */ class MediaSegment(entity: Entity) : PersistentEntity(entity) { - companion object : XdIdNaturalEntityType() { + companion object : XdNaturalEntityType() { /** Combination of [MediaSegment] name / item must be unique. */ override val compositeIndices = listOf( listOf(MediaSegment::name, MediaSegment::item) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 1508bdfab..33638f601 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -89,7 +89,6 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm * @param template The [EvaluationTemplate] */ constructor(name: String, shuffle: Boolean, template: EvaluationTemplate) : this(Evaluation.new { - this.id = UUID.randomUUID().toString() this.type = EvaluationType.INTERACTIVE_ASYNCHRONOUS this.name = name this.template = template @@ -165,7 +164,6 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm * @param teamId The [TeamId] this [IATaskRun] is created for. */ internal constructor(template: TaskTemplate, teamId: TeamId) : this(Task.new { - this.id = UUID.randomUUID().toString() this.evaluation = this@InteractiveAsynchronousEvaluation.evaluation this.template = template this.team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId }.singleOrNull() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index c657d2e0b..ab0822e31 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -36,7 +36,6 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat * @param template The [EvaluationTemplate] */ constructor(name: String, template: EvaluationTemplate) : this(Evaluation.new { - this.id = UUID.randomUUID().toString() this.type = EvaluationType.INTERACTIVE_SYNCHRONOUS this.template = template this.name = name @@ -78,7 +77,6 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat * @param template [TaskTemplate] to generate [ISTaskRun] from. */ constructor(template: TaskTemplate) : this(Task.new { - this.id = UUID.randomUUID().toString() this.evaluation = this@InteractiveSynchronousEvaluation.evaluation this.template = template }) From b82806bba12ebc0bcaeafc482fc3b2a7e32bbdcd Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 3 Feb 2023 10:03:34 +0100 Subject: [PATCH 056/498] Refactoring: - All DB object are now prefixed with Db. - Continued disentangling of DB and API enums. --- backend/src/main/kotlin/dev/dres/DRES.kt | 107 ++++++----- .../dev/dres/api/cli/EvaluationCommand.kt | 37 ++-- .../dev/dres/api/cli/EvaluationRunCommand.kt | 50 +++--- .../dres/api/cli/MediaCollectionCommand.kt | 105 ++++++----- .../kotlin/dev/dres/api/cli/UserCommand.kt | 35 ++-- .../kotlin/dev/dres/api/rest/AccessManager.kt | 17 +- .../handler/audit/GetAuditLogInfoHandler.kt | 4 +- .../handler/audit/ListAuditLogsHandler.kt | 6 +- .../audit/ListAuditLogsInRangeHandler.kt | 6 +- .../collection/AbstractCollectionHandler.kt | 8 +- .../collection/AddCollectionHandler.kt | 5 +- .../handler/collection/AddMediaItemHandler.kt | 12 +- .../collection/DeleteMediaItemHandler.kt | 4 +- .../collection/ListCollectionHandler.kt | 4 +- .../collection/ListMediaItemHandler.kt | 4 +- .../collection/ShowCollectionHandler.kt | 5 +- .../collection/ShowMediaItemHandler.kt | 4 +- .../collection/UpdateCollectionHandler.kt | 4 +- .../collection/UpdateMediaItemHandler.kt | 6 +- .../download/EvaluationDownloadHandler.kt | 6 +- .../EvaluationTemplateDownloadHandler.kt | 7 +- .../evaluation/admin/AdjustDurationHandler.kt | 8 +- .../admin/AdjustPropertiesHandler.kt | 4 +- .../admin/CreateEvaluationHandler.kt | 23 ++- .../admin/OverrideSubmissionHandler.kt | 10 +- .../admin/StartEvaluationHandler.kt | 4 +- .../evaluation/admin/StartTaskHandler.kt | 8 +- .../evaluation/admin/StopEvaluationHandler.kt | 4 +- .../evaluation/admin/StopTaskHandler.kt | 8 +- .../client/ClientListEvaluationsHandler.kt | 10 +- .../evaluation/scores/AbstractScoreHandler.kt | 4 +- .../scores/CurrentTaskScoreHandler.kt | 4 +- .../scores/HistoryTaskScoreHandler.kt | 4 +- .../viewer/GetCurrentTaskHandler.kt | 10 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 4 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 4 +- .../viewer/GetSubmissionInfoHandler.kt | 16 +- .../evaluation/viewer/GetTaskHintHandler.kt | 10 +- .../judgement/DequeueJudgementHandler.kt | 8 +- .../handler/judgement/DequeueVoteHandler.kt | 8 +- .../handler/judgement/PostJudgementHandler.kt | 6 +- .../handler/preview/AbstractPreviewHandler.kt | 32 ++-- .../rest/handler/preview/GetMediaHandler.kt | 6 +- .../handler/preview/MediaPreviewHandler.kt | 4 +- .../preview/SubmissionPreviewHandler.kt | 4 +- .../submission/BatchSubmissionHandler.kt | 17 +- .../handler/submission/SubmissionHandler.kt | 91 +++++----- .../api/rest/handler/system/LoginHandler.kt | 4 +- .../api/rest/handler/system/LogoutHandler.kt | 4 +- .../AbstractEvaluationTemplateHandler.kt | 16 +- .../CreateEvaluationTemplateHandler.kt | 6 +- .../DeleteEvaluationTemplateHandler.kt | 4 +- .../handler/template/GetTeamLogoHandler.kt | 6 +- .../ListEvaluationTemplatesHandler.kt | 6 +- .../rest/handler/template/ListTasksHandler.kt | 4 +- .../rest/handler/template/ListTeamHandler.kt | 4 +- .../template/ShowEvaluationTemplateHandler.kt | 4 +- .../template/UpdateEvaluationHandler.kt | 75 ++++---- .../rest/handler/users/AbstractUserHandler.kt | 12 +- .../rest/handler/users/CreateUsersHandler.kt | 14 +- .../rest/handler/users/DeleteUsersHandler.kt | 4 +- .../handler/users/ListActiveUsersHandler.kt | 4 +- .../rest/handler/users/ListUsersHandler.kt | 4 +- .../rest/handler/users/UpdateUsersHandler.kt | 8 +- .../rest/handler/users/UserDetailsHandler.kt | 4 +- .../api/rest/types/audit/ApiAuditLogEntry.kt | 4 +- .../api/rest/types/audit/ApiAuditLogSource.kt | 19 +- .../api/rest/types/audit/ApiAuditLogType.kt | 39 ++-- .../api/rest/types/collection/ApiMediaItem.kt | 6 +- .../api/rest/types/collection/ApiMediaType.kt | 14 +- .../types/collection/RestMediaCollection.kt | 14 +- .../competition/ApiEvaluationOverview.kt | 4 +- .../competition/ApiEvaluationStartMessage.kt | 4 +- .../competition/ApiEvaluationTemplate.kt | 6 +- .../types/competition/tasks/ApiHintType.kt | 23 ++- .../rest/types/competition/tasks/ApiTarget.kt | 4 +- .../types/competition/tasks/ApiTargetType.kt | 20 +-- .../types/competition/tasks/ApiTaskGroup.kt | 4 +- .../competition/tasks/ApiTaskTemplate.kt | 6 +- .../types/competition/tasks/ApiTaskType.kt | 6 +- .../tasks/options/ApiComponentOption.kt | 17 -- .../tasks/options/ApiHintOption.kt | 26 +++ .../tasks/options/ApiScoreOption.kt | 14 +- .../tasks/options/ApiSubmissionOption.kt | 28 +-- .../tasks/options/ApiTargetOption.kt | 20 +-- .../tasks/options/ApiTaskOption.kt | 16 +- .../rest/types/competition/team/ApiTeam.kt | 4 +- .../types/competition/team/ApiTeamGroup.kt | 4 +- .../rest/types/evaluation/ApiAnswerType.kt | 25 +++ .../rest/types/evaluation/ApiEvaluation.kt | 6 +- .../types/evaluation/ApiEvaluationInfo.kt | 8 +- .../types/evaluation/ApiEvaluationType.kt | 20 +++ .../api/rest/types/evaluation/ApiRunType.kt | 12 -- .../rest/types/evaluation/ApiSubmission.kt | 4 +- .../dres/api/rest/types/evaluation/ApiTask.kt | 4 +- .../types/evaluation/ApiTaskTemplateInfo.kt | 7 +- .../api/rest/types/evaluation/ApiTeamInfo.kt | 6 +- .../api/rest/types/evaluation/ApiVerdict.kt | 2 +- .../rest/types/evaluation/ApiVerdictStatus.kt | 18 +- .../rest/types/evaluation/ApiVerdictType.kt | 17 -- .../dres/api/rest/types/judgement/ApiVote.kt | 4 +- .../status/SuccessfulSubmissionsStatus.kt | 4 +- .../dev/dres/api/rest/types/users/ApiRole.kt | 18 +- .../dev/dres/api/rest/types/users/ApiUser.kt | 4 +- .../dev/dres/data/model/PersistentEntity.kt | 1 - .../data/model/admin/{Role.kt => DbRole.kt} | 18 +- .../data/model/admin/{User.kt => DbUser.kt} | 20 +-- .../dev/dres/data/model/admin/Password.kt | 2 +- .../dres/data/model/audit/AuditLogEntry.kt | 55 ------ .../dres/data/model/audit/DbAuditLogEntry.kt | 55 ++++++ ...{AuditLogSource.kt => DbAuditLogSource.kt} | 12 +- .../{AuditLogType.kt => DbAuditLogType.kt} | 14 +- ...ediaCollection.kt => DbMediaCollection.kt} | 14 +- .../media/{MediaItem.kt => DbMediaItem.kt} | 38 ++-- .../dres/data/model/media/DbMediaSegment.kt | 39 ++++ .../media/{MediaType.kt => DbMediaType.kt} | 10 +- .../dev/dres/data/model/media/MediaSegment.kt | 39 ---- .../data/model/media/PlayableMediaItem.kt | 2 +- .../data/model/media/time/TemporalPoint.kt | 4 +- .../dres/data/model/run/AbstractEvaluation.kt | 16 +- .../data/model/run/AbstractInteractiveTask.kt | 24 +-- .../model/run/AbstractNonInteractiveTask.kt | 14 +- .../dev/dres/data/model/run/AbstractTask.kt | 32 ++-- .../run/{Evaluation.kt => DbEvaluation.kt} | 40 ++--- ...{EvaluationType.kt => DbEvaluationType.kt} | 14 +- .../kotlin/dev/dres/data/model/run/DbTask.kt | 61 +++++++ .../run/InteractiveAsynchronousEvaluation.kt | 44 ++--- .../run/InteractiveSynchronousEvaluation.kt | 47 +++-- .../model/run/NonInteractiveEvaluation.kt | 14 +- .../dres/data/model/run/RunActionContext.kt | 18 +- .../kotlin/dev/dres/data/model/run/Task.kt | 61 ------- .../model/run/interfaces/EvaluationRun.kt | 12 +- .../dres/data/model/run/interfaces/TaskRun.kt | 14 +- .../data/model/submissions/DbAnswerSet.kt | 70 ++++++++ .../{VerdictType.kt => DbAnswerType.kt} | 24 +-- .../{Submission.kt => DbSubmission.kt} | 32 ++-- .../{VerdictStatus.kt => DbVerdictStatus.kt} | 22 ++- .../dres/data/model/submissions/Verdict.kt | 70 -------- ...ionTemplate.kt => DbEvaluationTemplate.kt} | 68 +++---- .../template/task/{Hint.kt => DbHint.kt} | 59 +++--- .../task/{HintType.kt => DbHintType.kt} | 20 +-- .../task/{TargetType.kt => DbTargetType.kt} | 12 +- .../data/model/template/task/DbTaskGroup.kt | 40 +++++ .../{TaskTemplate.kt => DbTaskTemplate.kt} | 52 +++--- ...plateTarget.kt => DbTaskTemplateTarget.kt} | 65 ++++--- .../task/{TaskType.kt => DbTaskType.kt} | 56 +++--- .../data/model/template/task/TaskGroup.kt | 41 ----- ...figuredOption.kt => DbConfiguredOption.kt} | 18 +- .../{HintOption.kt => DbHintOption.kt} | 18 +- .../{ScoreOption.kt => DbScoreOption.kt} | 10 +- ...missionOption.kt => DbSubmissionOption.kt} | 10 +- .../{TargetOption.kt => DbTargetOption.kt} | 10 +- .../{TaskOption.kt => DbTaskOption.kt} | 14 +- .../dres/data/model/template/team/DbTeam.kt | 61 +++++++ ...{TeamAggregator.kt => DbTeamAggregator.kt} | 16 +- .../team/{TeamGroup.kt => DbTeamGroup.kt} | 32 ++-- .../dev/dres/data/model/template/team/Team.kt | 61 ------- .../model/template/team/TeamAggregatorImpl.kt | 12 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 84 ++++----- .../run/InteractiveAsynchronousRunManager.kt | 100 +++++------ .../dev/dres/run/InteractiveRunManager.kt | 50 +++--- .../run/InteractiveSynchronousRunManager.kt | 98 +++++----- .../dev/dres/run/NonInteractiveRunManager.kt | 12 +- .../main/kotlin/dev/dres/run/RunManager.kt | 32 ++-- .../main/kotlin/dev/dres/run/TaskStatus.kt | 14 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 136 +++++++------- .../dev/dres/run/eventstream/StreamEvent.kt | 12 +- .../handlers/ResultLogStatisticsHandler.kt | 8 +- .../handlers/SubmissionStatisticsHandler.kt | 12 +- .../handlers/TeamCombinationScoreHandler.kt | 8 +- .../dres/run/filter/AllSubmissionFilter.kt | 6 +- .../dres/run/filter/CorrectPerTeamFilter.kt | 11 +- .../run/filter/CorrectPerTeamItemFilter.kt | 8 +- .../run/filter/CorrectPerTeamMemberFilter.kt | 10 +- .../run/filter/DuplicateSubmissionFilter.kt | 6 +- .../dres/run/filter/ItemSubmissionFilter.kt | 8 +- .../run/filter/MaximumTotalPerTeamFilter.kt | 4 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 10 +- .../dev/dres/run/filter/SubmissionFilter.kt | 14 +- .../run/filter/SubmissionFilterAggregator.kt | 6 +- .../dres/run/filter/SubmissionRateFilter.kt | 4 +- .../run/filter/SubmissionRejectedException.kt | 7 +- .../run/filter/TemporalSubmissionFilter.kt | 10 +- .../run/filter/TextualSubmissionFilter.kt | 10 +- .../IncrementalSubmissionTaskScorer.kt | 9 +- .../RecalculatingSubmissionTaskScorer.kt | 10 +- .../dres/run/score/interfaces/TaskScorer.kt | 4 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 10 +- .../dres/run/score/scoreboard/Scoreboard.kt | 10 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 20 +-- .../dres/run/score/scorer/KisTaskScorer.kt | 10 +- .../dres/run/score/scorer/NewAvsTaskScorer.kt | 12 +- .../dres/run/updatables/EndTaskUpdatable.kt | 12 +- .../dres/run/updatables/ScoresUpdatable.kt | 14 +- .../validation/ChainedSubmissionValidator.kt | 14 +- .../MediaItemsSubmissionValidator.kt | 18 +- .../TemporalContainmentSubmissionValidator.kt | 30 ++-- .../TemporalOverlapSubmissionValidator.kt | 34 ++-- .../dev/dres/run/validation/TextValidator.kt | 22 +-- .../interfaces/JudgementValidator.kt | 26 +-- .../interfaces/SubmissionValidator.kt | 12 +- .../validation/interfaces/VoteValidator.kt | 8 +- .../judged/BasicJudgementValidator.kt | 56 +++--- .../validation/judged/BasicVoteValidator.kt | 24 +-- .../dres/run/validation/judged/ItemRange.kt | 21 ++- .../kotlin/dev/dres/utilities/FFmpegUtil.kt | 13 +- .../kotlin/dev/dres/utilities/TimeUtil.kt | 10 +- .../run/score/scorer/AvsTaskScorerTest.kt | 168 +++++++++--------- .../run/score/scorer/KisTaskScorerTest.kt | 38 ++-- .../run/score/scorer/NewAvsTaskScorerTest.kt | 52 +++--- 210 files changed, 2188 insertions(+), 2167 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiHintOption.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt rename backend/src/main/kotlin/dev/dres/data/model/admin/{Role.kt => DbRole.kt} (66%) rename backend/src/main/kotlin/dev/dres/data/model/admin/{User.kt => DbUser.kt} (71%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt rename backend/src/main/kotlin/dev/dres/data/model/audit/{AuditLogSource.kt => DbAuditLogSource.kt} (65%) rename backend/src/main/kotlin/dev/dres/data/model/audit/{AuditLogType.kt => DbAuditLogType.kt} (72%) rename backend/src/main/kotlin/dev/dres/data/model/media/{MediaCollection.kt => DbMediaCollection.kt} (51%) rename backend/src/main/kotlin/dev/dres/data/model/media/{MediaItem.kt => DbMediaItem.kt} (53%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt rename backend/src/main/kotlin/dev/dres/data/model/media/{MediaType.kt => DbMediaType.kt} (69%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt rename backend/src/main/kotlin/dev/dres/data/model/run/{Evaluation.kt => DbEvaluation.kt} (58%) rename backend/src/main/kotlin/dev/dres/data/model/run/{EvaluationType.kt => DbEvaluationType.kt} (54%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/Task.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt rename backend/src/main/kotlin/dev/dres/data/model/submissions/{VerdictType.kt => DbAnswerType.kt} (55%) rename backend/src/main/kotlin/dev/dres/data/model/submissions/{Submission.kt => DbSubmission.kt} (53%) rename backend/src/main/kotlin/dev/dres/data/model/submissions/{VerdictStatus.kt => DbVerdictStatus.kt} (63%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt rename backend/src/main/kotlin/dev/dres/data/model/template/{EvaluationTemplate.kt => DbEvaluationTemplate.kt} (52%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/{Hint.kt => DbHint.kt} (71%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/{HintType.kt => DbHintType.kt} (56%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/{TargetType.kt => DbTargetType.kt} (66%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt rename backend/src/main/kotlin/dev/dres/data/model/template/task/{TaskTemplate.kt => DbTaskTemplate.kt} (72%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/{TaskTemplateTarget.kt => DbTaskTemplateTarget.kt} (59%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/{TaskType.kt => DbTaskType.kt} (55%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt rename backend/src/main/kotlin/dev/dres/data/model/template/task/options/{ConfiguredOption.kt => DbConfiguredOption.kt} (73%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/options/{HintOption.kt => DbHintOption.kt} (60%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/options/{ScoreOption.kt => DbScoreOption.kt} (75%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/options/{SubmissionOption.kt => DbSubmissionOption.kt} (84%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/options/{TargetOption.kt => DbTargetOption.kt} (68%) rename backend/src/main/kotlin/dev/dres/data/model/template/task/options/{TaskOption.kt => DbTaskOption.kt} (64%) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt rename backend/src/main/kotlin/dev/dres/data/model/template/team/{TeamAggregator.kt => DbTeamAggregator.kt} (69%) rename backend/src/main/kotlin/dev/dres/data/model/template/team/{TeamGroup.kt => DbTeamGroup.kt} (50%) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index b4e4aac25..bdfaf0c50 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -3,30 +3,29 @@ package dev.dres import dev.dres.api.cli.Cli import dev.dres.api.cli.OpenApiCommand import dev.dres.api.rest.RestApi -import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.Config -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User -import dev.dres.data.model.audit.AuditLogEntry -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.audit.AuditLogType -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.admin.DbRole +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.audit.DbAuditLogEntry +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.audit.DbAuditLogType +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.* import dev.dres.data.model.template.task.options.* -import dev.dres.data.model.template.team.Team -import dev.dres.data.model.template.team.TeamAggregator -import dev.dres.data.model.template.team.TeamGroup -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaSegment -import dev.dres.data.model.media.MediaType -import dev.dres.data.model.run.Evaluation -import dev.dres.data.model.run.EvaluationType -import dev.dres.data.model.run.Task -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.template.team.DbTeam +import dev.dres.data.model.template.team.DbTeamAggregator +import dev.dres.data.model.template.team.DbTeamGroup +import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.DbMediaSegment +import dev.dres.data.model.media.DbMediaType +import dev.dres.data.model.run.DbEvaluation +import dev.dres.data.model.run.DbEvaluationType +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.mgmt.admin.UserManager import dev.dres.run.RunExecutor import dev.dres.run.audit.AuditLogger @@ -107,39 +106,39 @@ object DRES { */ private fun prepareDatabase(config: Config): TransientEntityStore { XdModel.registerNodes( - AuditLogEntry, - AuditLogSource, - AuditLogType, - ConfiguredOption, - Evaluation, - EvaluationType, - EvaluationTemplate, - Hint, - HintOption, - HintType, - MediaType, - MediaCollection, - MediaItem, - MediaSegment, - Role, - ScoreOption, - Submission, - SubmissionOption, - Task, - TaskGroup, - TaskType, - TaskOption, - TaskTemplate, - TaskTemplateTarget, - TargetType, - TargetOption, - Team, - TeamAggregator, - TeamGroup, - User, - Verdict, - VerdictType, - VerdictStatus, + DbAuditLogEntry, + DbAuditLogSource, + DbAuditLogType, + DbConfiguredOption, + DbEvaluation, + DbEvaluationType, + DbEvaluationTemplate, + DbHint, + DbHintOption, + DbHintType, + DbMediaType, + DbMediaCollection, + DbMediaItem, + DbMediaSegment, + DbRole, + DbScoreOption, + DbSubmission, + DbSubmissionOption, + DbTask, + DbTaskGroup, + DbTaskType, + DbTaskOption, + DbTaskTemplate, + DbTaskTemplateTarget, + DbTargetType, + DbTargetOption, + DbTeam, + DbTeamAggregator, + DbTeamGroup, + DbUser, + DbAnswerSet, + DbAnswerType, + DbVerdictStatus, ) val store = StaticStoreContainer.init(dbFolder = File(config.dataPath), entityStoreName = "dres-db") initMetaData(XdModel.hierarchy, store) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index 7d108f516..b5fc73e24 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -11,7 +11,7 @@ import com.github.ajalt.clikt.parameters.options.validate import com.github.ajalt.clikt.parameters.types.path import com.jakewharton.picnic.table import dev.dres.data.model.Config -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.utilities.FFmpegUtil import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* @@ -19,10 +19,9 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.nio.file.StandardOpenOption -import java.util.* /** - * A collection of [CliktCommand]s for [EvaluationTemplate] management. + * A collection of [CliktCommand]s for [DbEvaluationTemplate] management. * * @author Luca Rossetto * @author Ralph Gasser @@ -61,7 +60,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) } /** - * [CliktCommand] to create a new [EvaluationTemplate]. + * [CliktCommand] to create a new [DbEvaluationTemplate]. */ inner class Create : CliktCommand(name = "create", help = "Creates a new Competition") { @@ -75,7 +74,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) override fun run() { val newCompetition = this@EvaluationCommand.store.transactional { - EvaluationTemplate.new { + DbEvaluationTemplate.new { this.name = this@Create.name this.description = this@Create.description } @@ -85,12 +84,12 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) } /** - * [CliktCommand] to delete a [EvaluationTemplate]. + * [CliktCommand] to delete a [DbEvaluationTemplate]. */ inner class Delete : AbstractEvaluationCommand(name = "delete", help = "Deletes a competition") { override fun run() { this@EvaluationCommand.store.transactional { - val competition = EvaluationTemplate.query((EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name)).firstOrNull() + val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() if (competition == null) { println("Could not find competition to delete.") return@transactional @@ -102,12 +101,12 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) } /** - * [CliktCommand] to copy a [EvaluationTemplate]. + * [CliktCommand] to copy a [DbEvaluationTemplate]. */ inner class Copy : AbstractEvaluationCommand(name = "copy", help = "Copies a Competition") { override fun run() { this@EvaluationCommand.store.transactional { - val competition = EvaluationTemplate.query((EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name)).firstOrNull() + val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() if (competition == null) { println("Could not find competition to copy.") return@transactional @@ -120,7 +119,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) } /** - * [CliktCommand] to list all [EvaluationTemplate]s. + * [CliktCommand] to list all [DbEvaluationTemplate]s. */ inner class List : CliktCommand(name = "list", help = "Lists an overview of all Competitions") { override fun run() = this@EvaluationCommand.store.transactional(true) { @@ -135,7 +134,7 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) row("name", "id", "# teams", "# tasks", "description", ) } body { - EvaluationTemplate.all().asSequence().forEach { c -> + DbEvaluationTemplate.all().asSequence().forEach { c -> row(c.name, c.id, c.teams.size(), c.tasks.size(), c.description).also { no++ } } } @@ -145,12 +144,12 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) } /** - * [CliktCommand] to show a specific [EvaluationTemplate]. + * [CliktCommand] to show a specific [DbEvaluationTemplate]. */ inner class Show : AbstractEvaluationCommand(name = "show", help = "Shows details of a Competition") { override fun run() = this@EvaluationCommand.store.transactional(true) { - val competition = EvaluationTemplate.query( - (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) + val competition = DbEvaluationTemplate.query( + (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) ).firstOrNull() if (competition == null) { @@ -176,13 +175,13 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) } /** - * [CliktCommand] to prepare a specific [EvaluationTemplate]. + * [CliktCommand] to prepare a specific [DbEvaluationTemplate]. */ inner class Prepare : AbstractEvaluationCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { override fun run() = this@EvaluationCommand.store.transactional(true) { - val competition = EvaluationTemplate.query( - (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) + val competition = DbEvaluationTemplate.query( + (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) ).firstOrNull() if (competition == null) { @@ -218,8 +217,8 @@ class EvaluationCommand(private val store: TransientEntityStore, config: Config) private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) override fun run() = this@EvaluationCommand.store.transactional(true) { - val competition = EvaluationTemplate.query( - (EvaluationTemplate::id eq this.id).or(EvaluationTemplate::name eq this.name) + val competition = DbEvaluationTemplate.query( + (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) ).firstOrNull() if (competition == null) { diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt index 46e93f1dd..dc01e1d13 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt @@ -8,12 +8,12 @@ import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.path import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter import com.jakewharton.picnic.table -import dev.dres.data.model.template.task.TargetType +import dev.dres.data.model.template.task.DbTargetType import dev.dres.data.model.run.* import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.* import dev.dres.utilities.extensions.toDateString import jetbrains.exodus.database.TransientEntityStore @@ -120,7 +120,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt inner class List : CliktCommand(name = "list", help = "Lists all (ongoing and past) competition runs.") { private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val query = Evaluation.all().sortedBy(Evaluation::started) + val query = DbEvaluation.all().sortedBy(DbEvaluation::started) if (this.plain) { query.asSequence().forEach { println( @@ -129,7 +129,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt it.id, it.name, it.template.description, - if (it.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { + if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" @@ -155,7 +155,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt it.id, it.name, it.template.description, - if (it.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { + if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" @@ -178,7 +178,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt // TODO fancification with table override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val query = Evaluation.query(Evaluation::ended ne null).sortedBy(Evaluation::started) + val query = DbEvaluation.query(DbEvaluation::ended ne null).sortedBy(DbEvaluation::started) query.asSequence().forEach { println(it.name) @@ -197,7 +197,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt println("Evaluated Tasks:") it.tasks.asSequence().forEach { t -> println(t.template) - if (t.evaluation.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { + if (t.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { println("Submissions") t.submissions.asSequence().forEach { s -> println(s) } } @@ -208,7 +208,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt } /** - * Deletes a selected [Evaluation] for the current DRES instance. + * Deletes a selected [DbEvaluation] for the current DRES instance. */ inner class Delete : CliktCommand(name = "delete", help = "Deletes an existing competition run.", printHelpOnEmptyArgs = true) { private val id: EvaluationId by option("-r", "--run").required() @@ -220,7 +220,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt } this@EvaluationRunCommand.store.transactional { - val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() if (evaluation == null) { println("Evaluation with ID ${this.id} could not be deleted because it doesn't exist!") return@transactional @@ -235,7 +235,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt */ inner class Export : CliktCommand(name = "export", help = "Exports the selected competition run to a JSON file.", printHelpOnEmptyArgs = true) { - /** [EvaluationId] of the [Evaluation] that should be exported. .*/ + /** [EvaluationId] of the [DbEvaluation] that should be exported. .*/ private val id: EvaluationId by option("-i", "--id").required() /** Path to the file that should be created .*/ @@ -245,7 +245,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() if (evaluation == null) { println("Evaluation ${this.id} does not seem to exist.") return@transactional @@ -266,15 +266,15 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt } /** - * [CliktCommand] to reactivate an [Evaluation] that has previously ended. + * [CliktCommand] to reactivate an [DbEvaluation] that has previously ended. */ inner class Reactivate : CliktCommand(name = "reactivate", help = "Reactivates a previously ended competition run", printHelpOnEmptyArgs = true) { - /** [EvaluationId] of the [Evaluation] that should be reactivated. .*/ + /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ private val id: EvaluationId by option("-i", "--id").required() override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() if (evaluation == null) { println("Evaluation ${this.id} does not seem to exist.") return@transactional @@ -299,31 +299,31 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt } /** - * [CliktCommand] to reset the status of [Submission]s. + * [CliktCommand] to reset the status of [DbSubmission]s. */ inner class ResetSubmission : CliktCommand(name = "resetSubmission", help = "Resets submission status to INDETERMINATE for selected submissions.", printHelpOnEmptyArgs = true) { - /** [EvaluationId] of the [Evaluation] that should be reactivated. .*/ + /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ private val id: EvaluationId by option("-i", "--id").required() /** The [EvaluationId]s to reset. */ private val submissionIds: kotlin.collections.List by option("-s", "--submissions", help = "IDs of the submissions to reset.").multiple() - /** The [EvaluationId]s to reset [Submission]s for. */ + /** The [EvaluationId]s to reset [DbSubmission]s for. */ private val taskIds: kotlin.collections.List by option("-t", "--tasks", help = "IDs of the tasks to resetsubmissions for.").multiple() - /** The names of the task groups to reset [Submission]s for. */ + /** The names of the task groups to reset [DbSubmission]s for. */ private val taskGroups: kotlin.collections.List by option("-g", "--groups", help = "Names of the task groups to reset submissions for.").multiple() override fun run() = this@EvaluationRunCommand.store.transactional { /* Fetch competition run. */ - val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() if (evaluation == null) { println("Evaluation ${this.id} does not seem to exist.") return@transactional } - if (evaluation.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { + if (evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { /* Prepare query. */ var query = if (this.taskIds.isNotEmpty()) { evaluation.tasks.filter { it.id.isIn(this@ResetSubmission.taskIds) }.flatMapDistinct { it.submissions } @@ -340,7 +340,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt var affected = 0 query.asSequence().forEach { affected += 1 - it.status = VerdictStatus.INDETERMINATE + it.status = DbVerdictStatus.INDETERMINATE } println("Successfully reset $affected} submissions.") @@ -354,7 +354,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt * [CliktCommand] to export judgements made for relevant tasks as CSVs. */ inner class ExportJudgements : CliktCommand(name = "exportJudgements", help = "Exports all judgements made for all relevant tasks of an evaluation run as CSV", printHelpOnEmptyArgs = true) { - /** [EvaluationId] of the [Evaluation] for which judgements should be exported.*/ + /** [EvaluationId] of the [DbEvaluation] for which judgements should be exported.*/ private val id: EvaluationId by option("-r", "--run", help = "Id of the run").required() /** The [Path] to the output file. */ @@ -364,14 +364,14 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt override fun run() = this@EvaluationRunCommand.store.transactional(true) { /* Fetch competition run. */ - val evaluation = Evaluation.query(Evaluation::id eq this.id).firstOrNull() + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() if (evaluation == null) { println("Evaluation ${this.id} does not seem to exist.") return@transactional } val tasks = evaluation.tasks.filter { - it.template.targets.filter { it.type.isIn(listOf(TargetType.JUDGEMENT,TargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() + it.template.targets.filter { it.type.isIn(listOf(DbTargetType.JUDGEMENT,DbTargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() } if (tasks.isEmpty) { diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 31fa77b1c..90b8e278b 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -23,14 +23,13 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.nio.file.StandardOpenOption -import java.util.* import java.util.concurrent.TimeUnit import kotlin.io.path.extension import kotlin.io.path.nameWithoutExtension import kotlin.io.path.relativeTo /** - * A collection of [CliktCommand]s for [MediaItem], [MediaCollection] and [MediaSegment] management. + * A collection of [CliktCommand]s for [DbMediaItem], [DbMediaCollection] and [DbMediaSegment] management. * * @author Luca Rossetto * @author Ralph Gasser @@ -63,34 +62,34 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik */ abstract inner class AbstractCollectionCommand(name: String, help: String) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = true) { - /** The [CollectionId] of the [MediaCollection] affected by this [AbstractCollectionCommand]. */ + /** The [CollectionId] of the [DbMediaCollection] affected by this [AbstractCollectionCommand]. */ protected val id: CollectionId? by option("-i", "--id", help = "ID of a media collection.") - /** The name of the [MediaCollection] affected by this [AbstractCollectionCommand]. */ + /** The name of the [DbMediaCollection] affected by this [AbstractCollectionCommand]. */ protected val name: String? by option("-c", "--collection", help = "Name of a media collection.") /** - * Loads and returns the [MediaCollection] for the specified parameters. + * Loads and returns the [DbMediaCollection] for the specified parameters. * This is a convenience method and requires a transaction context. * - * @return [MediaCollection] or null + * @return [DbMediaCollection] or null */ - protected fun getCollection(): MediaCollection? - = MediaCollection.query((MediaCollection::id eq this.id).or(MediaCollection::name eq this.name)).firstOrNull() + protected fun getCollection(): DbMediaCollection? + = DbMediaCollection.query((DbMediaCollection::id eq this.id).or(DbMediaCollection::name eq this.name)).firstOrNull() } /** - * [CliktCommand] to create a new [MediaCollection]. + * [CliktCommand] to create a new [DbMediaCollection]. */ inner class Create: CliktCommand(name = "create", help = "Creates a new media collection.", printHelpOnEmptyArgs = true) { - /** The name of the new [MediaCollection]. */ + /** The name of the new [DbMediaCollection]. */ private val name: String by option("-n", "--name", help = "Name of the Collection to be created").required() - /** A description of the new [MediaCollection]. */ + /** A description of the new [DbMediaCollection]. */ private val description: String by option("-d", "--description", help = "Description of the Collection to be created").default("") - /** The base path to the new [MediaCollection]. */ + /** The base path to the new [DbMediaCollection]. */ private val basePath: String by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to").required() override fun run() { @@ -98,7 +97,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik this@MediaCollectionCommand.logger.warn("Collection base path ${this.basePath} does not exist!") } this@MediaCollectionCommand.store.transactional { - MediaCollection.new { + DbMediaCollection.new { this.name = this@Create.name this.description = this@Create.description this.path = this@Create.basePath @@ -109,7 +108,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to create a new [MediaCollection]. + * [CliktCommand] to create a new [DbMediaCollection]. */ inner class Delete: AbstractCollectionCommand("delete", help = "Deletes a media collection.") { override fun run() { @@ -126,17 +125,17 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to create a new [MediaCollection]. + * [CliktCommand] to create a new [DbMediaCollection]. */ inner class Update : AbstractCollectionCommand(name = "update", help = "Updates an existing Collection") { - /** The new name for the [MediaCollection]. */ + /** The new name for the [DbMediaCollection]. */ private val newName: String? by option("-n", "--name", help = "The new name of the collection") - /** The new description for the [MediaCollection]. */ + /** The new description for the [DbMediaCollection]. */ private val newDescription: String? by option("-d", "--description", help = "Description of the Collection to be created") - /** The new path for the [MediaCollection]. */ + /** The new path for the [DbMediaCollection]. */ private val newPath: String? by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to") override fun run() { @@ -158,15 +157,15 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to list all [MediaCollection]s. + * [CliktCommand] to list all [DbMediaCollection]s. */ inner class List : CliktCommand(name = "list", help = "Lists all media collections.") { val plain by option("-p", "--plain", help = "Plain print: No fancy table presentation for machine readable output").flag(default = false) override fun run() = this@MediaCollectionCommand.store.transactional(true) { - println("Available media collections ${MediaCollection.all().size()}") + println("Available media collections ${DbMediaCollection.all().size()}") if (this.plain) { - MediaCollection.all().asSequence().forEach { println(it) } + DbMediaCollection.all().asSequence().forEach { println(it) } } else { println( table { @@ -179,7 +178,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik row("id", "name", "description", "basePath", "# items") } body { - MediaCollection.all().asSequence().forEach { c -> + DbMediaCollection.all().asSequence().forEach { c -> row(c.id, c.name, c.description ?: "", c.path, c.items.size()) } } @@ -190,11 +189,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to show a [MediaCollection]'s [MediaItem]s in detail. + * [CliktCommand] to show a [DbMediaCollection]'s [DbMediaItem]s in detail. */ inner class Show : AbstractCollectionCommand("show", help = "Lists the content of a media collection.") { - /** The property of the [MediaItem]s to sort by. */ + /** The property of the [DbMediaItem]s to sort by. */ private val sort by option("-s", "--sort", help = "Chose which sorting to use").enum(ignoreCase = true).defaultLazy { SortField.NAME } private val plain by option("-p", "--plain", help = "Plain formatting. No fancy tables").flag(default = false) @@ -210,11 +209,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /* Query for items.. */ val query = collection.items.sortedBy( when (this.sort) { - SortField.ID -> MediaItem::id - SortField.NAME -> MediaItem::name - SortField.LOCATION -> MediaItem::location - SortField.DURATION -> MediaItem::durationMs - SortField.FPS -> MediaItem::fps + SortField.ID -> DbMediaItem::id + SortField.NAME -> DbMediaItem::name + SortField.LOCATION -> DbMediaItem::location + SortField.DURATION -> DbMediaItem::durationMs + SortField.FPS -> DbMediaItem::fps } ) @@ -244,7 +243,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to validate a [MediaCollection]'s [MediaItem]s. + * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ inner class Check : AbstractCollectionCommand("check", help = "Checks if all the files in a media collection are present and accessible.") { override fun run() = this@MediaCollectionCommand.store.transactional(true) { @@ -274,7 +273,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to validate a [MediaCollection]'s [MediaItem]s. + * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ inner class Scan : AbstractCollectionCommand("scan", help = "Scans a collection directory and adds found items") { @@ -321,14 +320,14 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) }.forEach { val relativePath = it.relativeTo(base) - val exists = MediaItem.query((MediaItem::collection eq collection) and (MediaItem::location eq relativePath.toString())).isNotEmpty + val exists = DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty if (!exists) { try { when (it.extension.lowercase()) { in this.imageTypes -> { println("Found image $it; analyzing...") - collection.items.add(MediaItem.new { - this.type = MediaType.IMAGE + collection.items.add(DbMediaItem.new { + this.type = DbMediaType.IMAGE this.name = it.fileName.nameWithoutExtension this.location = relativePath.toString() }) @@ -350,8 +349,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } println("Found frame rate to be $fps frames per seconds and duration $duration ms") - collection.items.add(MediaItem.new { - this.type = MediaType.VIDEO + collection.items.add(DbMediaItem.new { + this.type = DbMediaType.VIDEO this.name = it.fileName.nameWithoutExtension this.location = relativePath.toString() this.durationMs = duration @@ -379,16 +378,16 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to delete [MediaItem]s. + * [CliktCommand] to delete [DbMediaItem]s. */ inner class DeleteItem : AbstractCollectionCommand("deleteItem", help = "Deletes media item(s).") { - /** The item ID matching the name of the [MediaItem] to delete. */ + /** The item ID matching the name of the [DbMediaItem] to delete. */ private val itemId: MediaId? by option("-ii", "--itemId", help = "ID of the media item.") - /** The name of the [MediaItem] to delete. */ + /** The name of the [DbMediaItem] to delete. */ private val itemName: String? by option("-in", "--itemName", help = "The exact name of the media item.") - /** A RegEx matching the name of the [MediaItem] to delete. */ + /** A RegEx matching the name of the [DbMediaItem] to delete. */ private val itemNameRegex: Regex? by option("-e", "--regex", help="Regex for item names").convert { it.toRegex() } @@ -426,20 +425,20 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to delete [MediaItem]s. + * [CliktCommand] to delete [DbMediaItem]s. */ inner class AddItem : AbstractCollectionCommand(name = "add", help = "Adds a media item to a media collection.") { - /** The [ApiMediaType] of the new [MediaItem]. */ + /** The [ApiMediaType] of the new [DbMediaItem]. */ private val type: ApiMediaType by option("-t", "--type", help = "Type of the new media item.").enum().required() - /** The relative path of the new [MediaItem]. */ + /** The relative path of the new [DbMediaItem]. */ private val path: String by option("-p", "--path", help = "Path of the new media item. relative to the collection base path").required() - /** The duration of the new [MediaItem]. */ + /** The duration of the new [DbMediaItem]. */ private val duration: Long? by option("-d", "--duration", help = "video duration in seconds").long() - /** The fps rate of the new [MediaItem]. */ + /** The fps rate of the new [DbMediaItem]. */ private val fps: Float? by option("-f", "--fps").float().required() override fun run() = this@MediaCollectionCommand.store.transactional { @@ -457,8 +456,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /* Add new media item. */ - collection.items.add(MediaItem.new { - this.type = this@AddItem.type.toMediaType() + collection.items.add(DbMediaItem.new { + this.type = this@AddItem.type.toDb() this.name = Paths.get(this@AddItem.path).nameWithoutExtension this.location = this@AddItem.path this.durationMs = this@AddItem.duration @@ -470,7 +469,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to export a [MediaCollection]. + * [CliktCommand] to export a [DbMediaCollection]. */ inner class Export : AbstractCollectionCommand("export", help = "Exports a media collection into a CSV file.") { @@ -500,7 +499,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } /** - * [CliktCommand] to import a [MediaCollection]. + * [CliktCommand] to import a [DbMediaCollection]. */ inner class Import : AbstractCollectionCommand("import", help = "Imports a media collection from a CSV file.") { @@ -530,8 +529,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik val rows: kotlin.collections.List> = csvReader().readAllWithHeader(ips) for (row in rows) { inserted += 1 - collection.items.add(MediaItem.new { - this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).toMediaType() + collection.items.add(DbMediaItem.new { + this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).toDb() this.name = row.getValue("name") this.location = row.getValue("location") this.durationMs = row["duration"]?.toLongOrNull() @@ -547,7 +546,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /** - * [CliktCommand] to import a [MediaSegment]s. + * [CliktCommand] to import a [DbMediaSegment]s. * * Uses the VBS format. */ @@ -587,7 +586,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik if (videoItem != null) { inserted += 1 videoItem.segments.add( - MediaSegment.new { + DbMediaSegment.new { this.name = videoName this.start = start this.end = end diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 6a36fc038..6b2ce61b3 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -8,21 +8,20 @@ import com.github.ajalt.clikt.parameters.options.* import com.jakewharton.picnic.table import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.admin.Password -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User -import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_PASSWORD -import dev.dres.data.model.admin.User.Companion.MIN_LENGTH_USERNAME +import dev.dres.data.model.admin.DbRole +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.admin.DbUser.Companion.MIN_LENGTH_PASSWORD +import dev.dres.data.model.admin.DbUser.Companion.MIN_LENGTH_USERNAME import dev.dres.data.model.admin.UserId import dev.dres.mgmt.admin.UserManager import jetbrains.exodus.database.TransientEntityStore -import java.lang.IllegalArgumentException import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOption import java.util.* /** - * A collection of [CliktCommand]s for [User] management + * A collection of [CliktCommand]s for [DbUser] management * * @author Ralph Gasser * @version 2.0.0 @@ -41,7 +40,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") ) /** - * [CliktCommand] to create a new [User]. + * [CliktCommand] to create a new [DbUser]. */ inner class Create(private val store: TransientEntityStore): CliktCommand(name = "create", help = "Creates a new User", printHelpOnEmptyArgs = true) { /** The name of the newly created user. */ @@ -55,7 +54,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") .required() .validate { require(it.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } - /** The desired [Role] of the newly created user. */ + /** The desired [DbRole] of the newly created user. */ private val apiRole: ApiRole by option("-r", "--role", help = "Role of the new user.").convert { ApiRole.valueOf(it) }.required() override fun run() = this.store.transactional { @@ -69,7 +68,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") } /** - * [CliktCommand] to update an existing [User]. + * [CliktCommand] to update an existing [DbUser]. */ inner class Update(private val store: TransientEntityStore): CliktCommand(name = "update", help = "Updates Password or Role of an existing User", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id") @@ -83,8 +82,8 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") .convert { Password.Plain(it) } .validate { require(it.password.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } - /** The new [Role] of the updated user. Left unchanged if null! */ - private val role: Role? by option("-r", "--role", help = "New user Role").convert { Role.parse(it) } + /** The new [DbRole] of the updated user. Left unchanged if null! */ + private val role: DbRole? by option("-r", "--role", help = "New user Role").convert { DbRole.parse(it) } override fun run() = this.store.transactional { if (this.id == null && this.username == null) { @@ -101,7 +100,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") } /** - * [CliktCommand] to delete a [User]. + * [CliktCommand] to delete a [DbUser]. */ inner class Delete(private val store: TransientEntityStore): CliktCommand(name = "delete", help = "Deletes an existing user.", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id", help = "ID of the user to be deleted.") @@ -123,7 +122,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") } /** - * [CliktCommand] to export a [User]. + * [CliktCommand] to export a [DbUser]. */ inner class Export(private val store: TransientEntityStore): CliktCommand(name = "export", help = "Exports one or multiple user(s) as JSON.", printHelpOnEmptyArgs = true) { private val id: UserId? by option("-i", "--id", help = "ID of the user to be exported.") @@ -173,9 +172,9 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") val import = Files.newBufferedReader(path).use { if (this.multiple) { - mapper.readValue(it, Array::class.java) + mapper.readValue(it, Array::class.java) } else { - arrayOf(mapper.readValue(it, User::class.java)) + arrayOf(mapper.readValue(it, DbUser::class.java)) } } @@ -192,7 +191,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") /** - * [CliktCommand] to list all [User]s. + * [CliktCommand] to list all [DbUser]s. */ inner class List(private val store: TransientEntityStore): CliktCommand(name = "list", help = "Lists all Users") { val plain by option("-p", "--plain", help = "Plain print: No fancy table. Might be easier if the output should be processed").flag(default = false) @@ -226,11 +225,11 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") } /** - * [CliktCommand] to list all [Role]s. + * [CliktCommand] to list all [DbRole]s. */ inner class Roles(private val store: TransientEntityStore): CliktCommand(name = "roles", help = "Lists all Roles") { override fun run() = this.store.transactional(true) { - println("Available roles: ${Role.values().joinToString(", ")}") + println("Available roles: ${DbRole.values().joinToString(", ")}") } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 7aa4a6309..22d08d19a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -3,8 +3,7 @@ package dev.dres.api.rest import dev.dres.api.rest.handler.users.SessionToken import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionToken @@ -57,10 +56,10 @@ object AccessManager { private val locks = ReentrantReadWriteLock() /** - * Registers a [User] for a given [SessionToken]. Usually happens upon login. + * Registers a [DbUser] for a given [SessionToken]. Usually happens upon login. * - * @param sessionId The [SessionToken] to register the [User] for. - * @param user The [User] to register. + * @param sessionId The [SessionToken] to register the [DbUser] for. + * @param user The [DbUser] to register. */ fun registerUserForSession(sessionId: String, user: ApiUser) = this.locks.write { if (!this.sessionRoleMap.containsKey(sessionId)){ @@ -80,9 +79,9 @@ object AccessManager { } /** - * Deregisters a [User] for a given [SessionToken]. Usually happens upon logout. + * Deregisters a [DbUser] for a given [SessionToken]. Usually happens upon logout. * - * @param sessionId The [SessionToken] to register the [User] for. + * @param sessionId The [SessionToken] to register the [DbUser] for. */ fun deregisterUserSession(sessionId: String) = this.locks.write { this.sessionRoleMap.remove(sessionId) @@ -93,7 +92,7 @@ object AccessManager { * Queries and returns the [UserId] for the given [SessionToken]. * * @param sessionId The [SessionToken] to query. - * @return [UserId] or null if no [User] is logged in. + * @return [UserId] or null if no [DbUser] is logged in. */ fun userIdForSession(sessionId: String?): UserId? = this.locks.read { this.sessionUserMap[sessionId] @@ -151,7 +150,7 @@ object AccessManager { /** * Returns all registered [RunManager]s for the given [userId]. * - * @param userId The [UserId] of the [User] to return [RunManager]s for. + * @param userId The [UserId] of the [DbUser] to return [RunManager]s for. */ fun getRunManagerForUser(userId: UserId): Set = this.locks.read { return this.usersToRunMap[userId] ?: emptySet() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt index ad23711e9..191bef569 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt @@ -3,7 +3,7 @@ import dev.dres.api.rest.handler.audit.AbstractAuditLogHandler import jetbrains.exodus.database.TransientEntityStore import dev.dres.api.rest.types.audit.AuditLogInfo import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.data.model.audit.DbAuditLogEntry import io.javalin.http.Context import io.javalin.openapi.* import kotlinx.dnq.query.lastOrNull @@ -25,6 +25,6 @@ class GetAuditLogInfoHandler(store: TransientEntityStore) : AbstractAuditLogHand methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): AuditLogInfo = this.store.transactional(true) { - AuditLogInfo(size = AuditLogEntry.all().size(), latest = AuditLogEntry.all().sortedBy(AuditLogEntry::timestamp, true).lastOrNull()?.timestamp?.millis) + AuditLogInfo(size = DbAuditLogEntry.all().size(), latest = DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, true).lastOrNull()?.timestamp?.millis) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index 534a5d627..f877851fc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.audit import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.audit.ApiAuditLogEntry import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.data.model.audit.DbAuditLogEntry import dev.dres.utilities.extensions.toPathParamKey import io.javalin.http.Context import io.javalin.openapi.* @@ -13,7 +13,7 @@ import kotlinx.dnq.query.drop import kotlinx.dnq.query.take /** - * [AbstractAuditLogHandler] to list all [AuditLogEntry]. Allows for pagination. + * [AbstractAuditLogHandler] to list all [DbAuditLogEntry]. Allows for pagination. * * @author Loris Sauter * @version 1.0.0 @@ -49,6 +49,6 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle override fun doGet(ctx: Context): List = this.store.transactional(true) { val limit = (ctx.pathParamMap()[LIMIT_PARAM]?.toIntOrNull() ?: DEFAULT_LIMIT).coerceAtLeast(0) val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) - AuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() + DbAuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt index b7a0287b5..6a1859f0a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.audit.ApiAuditLogEntry import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.data.model.audit.DbAuditLogEntry import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -12,7 +12,7 @@ import kotlinx.dnq.query.* import org.joda.time.DateTime /** - * [AbstractAuditLogHandler] to list all [AuditLogEntry] between two points in time. + * [AbstractAuditLogHandler] to list all [DbAuditLogEntry] between two points in time. * * @author Loris Sauter * @version 1.0.0 @@ -42,7 +42,7 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog if (since < upto) throw ErrorStatusException(400, "Since must be smaller or equal to upto.", ctx) return this.store.transactional(true) { - AuditLogEntry.query((AuditLogEntry::timestamp gt since) and (AuditLogEntry::timestamp lt upto)).asSequence().map { + DbAuditLogEntry.query((DbAuditLogEntry::timestamp gt since) and (DbAuditLogEntry::timestamp lt upto)).asSequence().map { it.toApi() }.toList() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt index 987b0055d..dcc1a01f8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.DbMediaCollection import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore @@ -25,9 +25,9 @@ abstract class AbstractCollectionHandler(protected val store: TransientEntitySto /** All [AbstractCollectionHandler]s are part of the v1 API. */ override val apiVersion = "v2" - /** Convenience method to extract [MediaCollection] from [Context]. */ - protected fun collectionFromContext(ctx: Context): MediaCollection { + /** Convenience method to extract [DbMediaCollection] from [Context]. */ + protected fun collectionFromContext(ctx: Context): DbMediaCollection { val id = ctx.pathParamMap()["collectionId"] ?: throw ErrorStatusException(404, "Parameter 'collectionId' is missing!'", ctx) - return MediaCollection.query(MediaCollection::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Collection with ID $id not found.'", ctx) + return DbMediaCollection.query(DbMediaCollection::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Collection with ID $id not found.'", ctx) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index 5d4047840..7cfea592a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -5,13 +5,12 @@ import dev.dres.api.rest.types.collection.RestMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.DbMediaCollection import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -import java.util.UUID /** * @@ -46,7 +45,7 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand } val collection = this.store.transactional { - MediaCollection.new { + DbMediaCollection.new { this.name = restCollection.name this.description = restCollection.description this.path = restCollection.basePath.cleanPathString() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 84b3aa8b4..fb27bc54d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -5,15 +5,13 @@ import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType +import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* -import java.util.* /** * @@ -49,14 +47,14 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl /* Try to persist media item. */ val collectionId = mediaItem.collectionId return this.store.transactional { - val collection = MediaCollection.query(MediaCollection::id eq collectionId).firstOrNull() + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() ?: throw ErrorStatusException(400, "Invalid parameters, collection with ID $collectionId does not exist.", ctx) if (collection.items.filter { it.name eq mediaItem.name }.isNotEmpty) { throw ErrorStatusException(400, "Media item with name '${mediaItem.name}' already exists in collection ${collection.name}.", ctx) } - val item = MediaItem.new { - this.type = mediaItem.type.toMediaType() + val item = DbMediaItem.new { + this.type = mediaItem.type.toDb() this.name = mediaItem.name this.location = mediaItem.location this.fps = mediaItem.fps diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt index 651670649..dba78f263 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -42,7 +42,7 @@ class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa } return this.store.transactional { - val item = MediaItem.query(MediaItem::id eq mediaId).firstOrNull() + val item = DbMediaItem.query(DbMediaItem::id eq mediaId).firstOrNull() ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) item.delete() SuccessStatus("Media item ${item.id} deleted successfully.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt index 162cab3e4..c9cf6e9b9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.RestMediaCollection import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.DbMediaCollection import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi @@ -33,7 +33,7 @@ class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan ) override fun doGet(ctx: Context): List { return this.store.transactional(true) { - MediaCollection.all().asSequence().map { RestMediaCollection.fromMediaCollection(it) }.toList() + DbMediaCollection.all().asSequence().map { RestMediaCollection.fromMediaCollection(it) }.toList() } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt index 2b28ae8c7..e66c68248 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -40,7 +40,7 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand val collection = collectionFromContext(ctx) val start = ctx.pathParamMap()["startsWith"] val query = if (!start.isNullOrBlank()) { - collection.items.query(MediaItem::name startsWith start) + collection.items.query(DbMediaItem::name startsWith start) } else { collection.items } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index 434765a82..214595f0c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -3,9 +3,8 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.RestFullMediaCollection import dev.dres.api.rest.types.collection.RestMediaCollection -import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -36,7 +35,7 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan ) override fun doGet(ctx: Context): RestFullMediaCollection = this.store.transactional(true) { val collection = collectionFromContext(ctx) //also checks if collection exists - val items = MediaItem.query(MediaItem::collection eq collection).asSequence().map { it.toApi() }.toList() + val items = DbMediaItem.query(DbMediaItem::collection eq collection).asSequence().map { it.toApi() }.toList() RestFullMediaCollection(RestMediaCollection.fromMediaCollection(collection), items) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt index 977c6a96a..ef77332ee 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -41,7 +41,7 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand } return this.store.transactional(true) { - val item = MediaItem.query(MediaItem::id eq mediaId).firstOrNull() + val item = DbMediaItem.query(DbMediaItem::id eq mediaId).firstOrNull() ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) item.toApi() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt index 9c09462f6..d9e255937 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.collection.RestMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.media.DbMediaCollection import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -45,7 +45,7 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH } val status = this.store.transactional { - val collection = MediaCollection.query(MediaCollection::id eq restCollection.id).firstOrNull() + val collection = DbMediaCollection.query(DbMediaCollection::id eq restCollection.id).firstOrNull() ?: throw ErrorStatusException(400, "Invalid parameters, collection with ID ${restCollection.id} does not exist.", ctx) collection.name = restCollection.name.trim() collection.description = restCollection.description?.trim() ?: collection.description diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 28a949b50..9ca1edeb5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -45,10 +45,10 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa } return this.store.transactional { - val item = MediaItem.query(MediaItem::id eq mediaItem.id).firstOrNull() + val item = DbMediaItem.query(DbMediaItem::id eq mediaItem.id).firstOrNull() ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.id} not found.", ctx) - item.type = mediaItem.type.toMediaType() + item.type = mediaItem.type.toDb() item.name = item.name item.location = item.location item.fps = item.fps diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index 3f55fff12..a271aa9ef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.DbEvaluation import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -13,7 +13,7 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * A [GetRestHandler] that allows for downloading the entire [Evaluation] structure as JSON file. + * A [GetRestHandler] that allows for downloading the entire [DbEvaluation] structure as JSON file. * * @author Ralph Gasser * @version 1.0.0 @@ -42,7 +42,7 @@ class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadH /* Obtain run id and run. */ val evaluationId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", ctx) } val evaluation = this.store.transactional(true) { - Evaluation.query(Evaluation::id eq evaluationId).firstOrNull()?.toApi() + DbEvaluation.query(DbEvaluation::id eq evaluationId).firstOrNull()?.toApi() ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index 934553660..cc3cf1ce8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -4,8 +4,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.InteractiveAsynchronousEvaluation -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -14,7 +13,7 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * A [GetRestHandler] that allow for downloading the entire [EvaluationTemplate] structure as JSON file. + * A [GetRestHandler] that allow for downloading the entire [DbEvaluationTemplate] structure as JSON file. * * @author Ralph Gasser * @version 1.0.0 @@ -43,7 +42,7 @@ class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractD /* Obtain run id and run. */ val templateId = ctx.pathParamMap()["competitionId"] ?: throw ErrorStatusException(400, "Parameter 'templateId' is missing!'", ctx) val template = this.store.transactional(true) { - EvaluationTemplate.query(EvaluationTemplate::id eq templateId).firstOrNull()?.toApi() + DbEvaluationTemplate.query(DbEvaluationTemplate::id eq templateId).firstOrNull()?.toApi() ?: throw ErrorStatusException(404, "Competition $templateId not found", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 7890059a0..071910774 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -4,9 +4,9 @@ import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Task +import dev.dres.data.model.run.DbTask import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -15,7 +15,7 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [PatchRestHandler] handler to adjust an ongoing [Task]'s duration. + * A [PatchRestHandler] handler to adjust an ongoing [DbTask]'s duration. * * @author Ralph Gasser * @author Luca Rossetto @@ -49,7 +49,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.adjustDuration(rac, duration) - AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Duration for run $evaluationId was successfully adjusted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index 7dc69f421..09851052c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunProperties import dev.dres.utilities.extensions.evaluationId import io.javalin.http.BadRequestResponse @@ -14,7 +14,7 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [PatchRestHandler] handler to adjust an ongoing [Evaluation]'s [RunProperties]. + * A [PatchRestHandler] handler to adjust an ongoing [DbEvaluation]'s [RunProperties]. * * @author Ralph Gasser * @author Luca Rossetto diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 417984b40..bd5d0fac3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -2,14 +2,14 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.competition.ApiEvaluationStartMessage -import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.evaluation.ApiEvaluationType import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.Config -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.InteractiveAsynchronousEvaluation -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.run.InteractiveSynchronousRunManager import dev.dres.run.RunExecutor @@ -27,10 +27,9 @@ import org.slf4j.LoggerFactory import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import java.util.* /** - * [PostRestHandler] to create an [Evaluation]. + * [PostRestHandler] to create an [DbEvaluation]. * * @author Ralph Gasser * @author Luca Rossetto @@ -67,11 +66,11 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs /* Prepare run manager. */ val evaluation = this.store.transactional { tx -> - val template = EvaluationTemplate.query(EvaluationTemplate::id eq message.templateId).firstOrNull() + val template = DbEvaluationTemplate.query(DbEvaluationTemplate::id eq message.templateId).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID ${message.templateId} not found.'", ctx) /* ensure that only one synchronous run of a competition is happening at any given time */ - if (message.type == ApiRunType.SYNCHRONOUS && RunExecutor.managers().any { + if (message.type == ApiEvaluationType.SYNCHRONOUS && RunExecutor.managers().any { it is InteractiveSynchronousRunManager && it.template == template && it.status != RunManagerStatus.TERMINATED } ) { @@ -97,10 +96,10 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs } /* Prepare evaluation. */ - val evaluation = Evaluation.new { + val evaluation = DbEvaluation.new { this.name = message.name this.template = template /* TODO: Create copy. */ - this.type = message.type.type + this.type = message.type.toDb() this.allowRepeatedTasks = message.properties.allowRepeatedTasks this.participantCanView = message.properties.participantCanView this.shuffleTasks = message.properties.shuffleTasks @@ -112,9 +111,9 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs throw ErrorStatusException(500, "Failed to store new evaluation.", ctx) } RunExecutor.schedule(when (message.type) { - ApiRunType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ - ApiRunType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) - ApiRunType.NON_INTERACTIVE -> TODO() + ApiEvaluationType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ + ApiEvaluationType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) + ApiEvaluationType.NON_INTERACTIVE -> TODO() }, this.store) evaluation } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 54e9908d6..9998a5d8c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -5,9 +5,9 @@ import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -18,7 +18,7 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [PatchRestHandler] used to overwrite [VerdictStatus] information. + * A [PatchRestHandler] used to overwrite [DbVerdictStatus] information. * * @author Ralph Gasser * @author Luca Rossetto @@ -69,9 +69,9 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation if (evaluationManager.allSubmissions(rac).none { it.id == submissionInfo.id }) { throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) } - if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.toVerdictStatus())) { + if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.verdicts.first().status.toDb())) { val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.id } - AuditLogger.overrideSubmission(submission, AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.overrideSubmission(submission, DbAuditLogSource.REST, ctx.sessionToken()) submission.toApi() } else { throw ErrorStatusException(500, "Could not update the submission. Please see the backend's log.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index 1ea38e780..e5bacae75 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId @@ -44,7 +44,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.start(rac) - AuditLogger.competitionStart(evaluationManager.id, evaluationManager.template, AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.competitionStart(evaluationManager.id, evaluationManager.template, DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 804a9adcb..845b954ad 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -5,8 +5,8 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId @@ -17,7 +17,7 @@ import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore /** - * [PostRestHandler] to start the current task within an [Evaluation]. + * [PostRestHandler] to start the current task within an [DbEvaluation]. * @author Ralph Gasser * @author Luca Rossetto @@ -54,7 +54,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.startTask(rac) - AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index 7cc59e3e6..45096af7f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId @@ -45,7 +45,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.end(rac) - AuditLogger.competitionEnd(evaluationManager.id, AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.competitionEnd(evaluationManager.id, DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully stopped.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 114a01034..4c3dda288 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -4,8 +4,8 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId @@ -15,7 +15,7 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * [PostRestHandler] to abort the current task within an [Evaluation]. + * [PostRestHandler] to abort the current task within an [DbEvaluation]. * @author Ralph Gasser * @author Luca Rossetto @@ -45,7 +45,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl try { val task = evaluationManager.currentTaskTemplate(rac) evaluationManager.abortTask(rac) - AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.taskEnd(evaluationManager.id, task.id, DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt index 3efd29041..e390b465c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.handler.evaluation.client import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.evaluation.ApiEvaluationType import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.DbEvaluation import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import io.javalin.http.Context @@ -15,7 +15,7 @@ import io.javalin.openapi.OpenApiResponse import jetbrains.exodus.database.TransientEntityStore /** - * A [GetRestHandler] used to list all ongoing [Evaluation]s available to the current user. + * A [GetRestHandler] used to list all ongoing [DbEvaluation]s available to the current user. * * @author Ralph Gasser * @author Luca Rossetto @@ -43,8 +43,8 @@ class ClientListEvaluationsHandler(store: TransientEntityStore): AbstractEvaluat templateId = it.template.id, templateDescription = it.template.description, when (it) { - is InteractiveAsynchronousRunManager -> ApiRunType.ASYNCHRONOUS - is InteractiveSynchronousRunManager -> ApiRunType.SYNCHRONOUS + is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS + is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS else -> TODO() }, properties = it.runProperties, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt index 7f4047cd4..d9200470a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt @@ -3,13 +3,13 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.DbEvaluation import dev.dres.run.score.scoreboard.ScoreOverview import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore /** - * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing [Evaluation]s. + * A collection of [RestHandler]s that deal with [ScoreOverview]s for ongoing [DbEvaluation]s. * * @author Ralph Gasser * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 08fca465c..d8281bcf4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -9,7 +9,7 @@ import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Task +import dev.dres.data.model.run.DbTask import dev.dres.run.InteractiveRunManager import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.score.scoreboard.ScoreOverview @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence /** - * Generates and lists the [ScoreOverview] for the currently active [Task]. + * Generates and lists the [ScoreOverview] for the currently active [DbTask]. * * Only valid for [InteractiveRunManager]s. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index 3e051dcc2..71200489f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -9,7 +9,7 @@ import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Task +import dev.dres.data.model.run.DbTask import dev.dres.run.InteractiveRunManager import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.score.scoreboard.ScoreOverview @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence /** - * Generates and lists the [ScoreOverview] for the specified [Task]. + * Generates and lists the [ScoreOverview] for the specified [DbTask]. * * * Only valid for [InteractiveRunManager]s.Can only be invoked by admins. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index 9afa8e45b..f6f9d9fe5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -8,18 +8,18 @@ import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Task -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractEvaluationViewerHandler] that returns the currently active [TaskTemplate]. + * A [AbstractEvaluationViewerHandler] that returns the currently active [DbTaskTemplate]. * - * If a [Task] is being executed, the method returns the [TaskTemplate] for that [Task]. - * Otherwise, the selected [TaskTemplate] is returned (active task vs. task template navigation). + * If a [DbTask] is being executed, the method returns the [DbTaskTemplate] for that [DbTask]. + * Otherwise, the selected [DbTaskTemplate] is returned (active task vs. task template navigation). * * Only eligible for [InteractiveRunManager]s. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index 6bcfa59f5..c145d4e20 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* @@ -47,7 +47,7 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) - val blind = currentTask.template.taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS) && currentTask.isRunning + val blind = currentTask.template.taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS) && currentTask.isRunning manager.currentSubmissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index 08c3e7455..aee637400 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* @@ -47,7 +47,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval val task = manager.currentTask(rac) if (task?.template?.id == taskId && task.isRunning) { if (task.isRunning) { - val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq TaskOption.HIDDEN_RESULTS }.any() + val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq DbTaskOption.HIDDEN_RESULTS }.any() manager.currentSubmissions(rac).map { it.toApi(hidden) } } else { manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() } ?: emptyList() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index ab6b4589d..530281f76 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -8,8 +8,8 @@ import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* @@ -48,7 +48,7 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV val limit = manager.runProperties.limitSubmissionPreviews val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) - val blind = currentTask.template.taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS) && currentTask.isRunning + val blind = currentTask.template.taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS) && currentTask.isRunning /* Obtain current task run and check status. */ if (limit > 0) { @@ -60,16 +60,16 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV } /** - * Implements a manual limit on the provided list of [Submission]s. + * Implements a manual limit on the provided list of [DbSubmission]s. * * TODO: Delegate to database? * - * @param submissions The [List] of [Submission]s to limit. + * @param submissions The [List] of [DbSubmission]s to limit. * @param limit The number of items to limit to. - * @param blind If [Submission] should be anonymised. - * @return Limited [List] of [Submission] + * @param blind If [DbSubmission] should be anonymised. + * @return Limited [List] of [DbSubmission] */ - private fun limitSubmissions(submissions: List, limit: Int, blind: Boolean = false): List + private fun limitSubmissions(submissions: List, limit: Int, blind: Boolean = false): List = submissions.groupBy { it.team.id }.values.map { it.sortedBy { s -> s.timestamp }.take(limit) }.flatMap { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 229ce497c..e3a14cb4e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -8,8 +8,8 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Task -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* @@ -18,10 +18,10 @@ import java.io.FileNotFoundException import java.io.IOException /** - * A [AbstractEvaluationViewerHandler] that returns the currently active [TaskTemplate]. + * A [AbstractEvaluationViewerHandler] that returns the currently active [DbTaskTemplate]. * - * If a [Task] is being executed, the method returns the [TaskTemplate] for that [Task]. - * Otherwise, the selected [TaskTemplate] is returned (active task vs. task template navigation). + * If a [DbTask] is being executed, the method returns the [DbTaskTemplate] for that [DbTask]. + * Otherwise, the selected [DbTaskTemplate] is returned (active task vs. task template navigation). * * Only eligible for [InteractiveRunManager]s. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 573e6826f..12b86e246 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -48,15 +48,15 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa val next = validator.next(ctx.sessionToken()!!) ?: break val taskDescription = next.second.task.template.textualDescription() when (next.second.type) { - VerdictType.TEXT -> { + DbAnswerType.TEXT -> { val text = next.second.text ?: continue return@transactional ApiJudgementRequest(next.first, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } - VerdictType.ITEM -> { + DbAnswerType.ITEM -> { val item = next.second.item ?: continue return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) } - VerdictType.TEMPORAL -> { + DbAnswerType.TEMPORAL -> { val item = next.second.item ?: continue val start = next.second.start ?: continue val end = next.second.end ?: continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 5220c8f8b..39dd1a895 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -6,7 +6,7 @@ import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.Context import io.javalin.openapi.* @@ -46,15 +46,15 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( val next = validator.nextSubmissionToVoteOn() ?: break val taskDescription = next.task.template.textualDescription() when (next.type) { - VerdictType.TEXT -> { + DbAnswerType.TEXT -> { val text = next.text ?: continue return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } - VerdictType.ITEM -> { + DbAnswerType.ITEM -> { val item = next.item ?: continue return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) } - VerdictType.TEMPORAL -> { + DbAnswerType.TEMPORAL -> { val item = next.item ?: continue val start = next.start ?: continue val end = next.end ?: continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index ff26d137e..05994feac 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -6,7 +6,7 @@ import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.utilities.extensions.eligibleManagerForId @@ -55,11 +55,11 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) try { - validator.judge(judgement.token, judgement.verdict.toVerdictStatus()) + validator.judge(judgement.token, judgement.verdict.toDb()) } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } - AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.toVerdictStatus(), AuditLogSource.REST, ctx.sessionToken()) + AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.toDb(), DbAuditLogSource.REST, ctx.sessionToken()) } return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index 6dc33de62..c88c68c4e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -1,6 +1,5 @@ package dev.dres.api.rest.handler.preview -import dev.dres.api.rest.RestApi import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler @@ -8,21 +7,18 @@ import dev.dres.api.rest.handler.collection.AbstractCollectionHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config import dev.dres.data.model.media.CollectionId -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType +import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.DbMediaType import dev.dres.utilities.FFmpegUtil import dev.dres.utilities.extensions.sendFile import dev.dres.utilities.extensions.streamFile import io.javalin.http.Context import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* -import java.io.FileNotFoundException import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException /** * An abstract [GetRestHandler] used to access preview images. @@ -54,38 +50,38 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, } /** - * Handles a request for a preview based on an [CollectionId] and a [MediaItem]'s name. Fetching of the [MediaItem] takes + * Handles a request for a preview based on an [CollectionId] and a [DbMediaItem]'s name. Fetching of the [DbMediaItem] takes * place in a transaction context. However, the (potentially) long running media processing is executed outside. * - * @param collectionId [CollectionId] of the [MediaCollection]. - * @param itemName Name of the [MediaItem] - * @param time The exact timepoint of the [MediaItem] in ms. Only works for [MediaType.VIDEO]. + * @param collectionId [CollectionId] of the [DbMediaCollection]. + * @param itemName Name of the [DbMediaItem] + * @param time The exact timepoint of the [DbMediaItem] in ms. Only works for [DbMediaType.VIDEO]. * @param ctx The request [Context] */ protected fun handlePreviewRequest(collectionId: CollectionId, itemName: String, time: Long?, ctx: Context) { val item = this.store.transactional(true) { - MediaItem.query((MediaItem::name eq itemName) and (MediaItem::collection.matches(MediaCollection::id eq collectionId))).firstOrNull() + DbMediaItem.query((DbMediaItem::name eq itemName) and (DbMediaItem::collection.matches(DbMediaCollection::id eq collectionId))).firstOrNull() ?: throw ErrorStatusException(404, "Media item $itemName (collection = $collectionId) not found!", ctx) } handlePreviewRequest(item, time, ctx) } /** - * Handles a request for a preview based on an [MediaItem] and an optional timepoint. + * Handles a request for a preview based on an [DbMediaItem] and an optional timepoint. * - * @param item The [MediaItem] - * @param time The exact timepoint of the [MediaItem] in ms. Only works for [MediaType.VIDEO]. + * @param item The [DbMediaItem] + * @param time The exact timepoint of the [DbMediaItem] in ms. Only works for [DbMediaType.VIDEO]. * @param ctx The request [Context] */ - protected fun handlePreviewRequest(item: MediaItem, time: Long?, ctx: Context) { + protected fun handlePreviewRequest(item: DbMediaItem, time: Long?, ctx: Context) { val basePath = Paths.get(item.collection.path) - if (item.type == MediaType.IMAGE) { + if (item.type == DbMediaType.IMAGE) { //TODO scale down image if too large ctx.header("Cache-Control", "max-age=31622400") ctx.streamFile(basePath.resolve(item.location)) return - } else if (item.type == MediaType.VIDEO) { + } else if (item.type == DbMediaType.VIDEO) { /* Prepare cache directory for item. */ val cacheDir = cacheLocation.resolve("${item.collection}/${item.name}") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt index 88f8bb74b..84f6412e2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.preview import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.utilities.extensions.errorResponse import dev.dres.utilities.extensions.streamFile import io.javalin.http.Context @@ -18,7 +18,7 @@ import kotlinx.dnq.query.query import java.nio.file.Paths /** - * An [GetRestHandler] used to access the files that belong to a specific [MediaItem]. + * An [GetRestHandler] used to access the files that belong to a specific [DbMediaItem]. * * @author Luca Rossetto * @version 2.0.0 @@ -55,7 +55,7 @@ class GetMediaHandler(private val store: TransientEntityStore) : GetRestHandler< /* Lookup item by ID. */ val item = this.store.transactional(true) { - MediaItem.query(MediaItem::id eq itemId).firstOrNull() + DbMediaItem.query(DbMediaItem::id eq itemId).firstOrNull() } if (item == null) { ctx.errorResponse(404, "item with name $itemId found") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt index 3b83eb2c3..368d2c209 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt @@ -2,14 +2,14 @@ package dev.dres.api.rest.handler.preview import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.utilities.extensions.errorResponse import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * An [AbstractPreviewHandler] used to access previews of specific [MediaItem]s. + * An [AbstractPreviewHandler] used to access previews of specific [DbMediaItem]s. * * @author Luca Rossetto * @author Loris Sauter diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index 3342db0c9..ec183045e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler.preview import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor @@ -13,7 +13,7 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.firstOrNull /** - * An [AbstractPreviewHandler] used to access previews of [MediaItem]s based on a specific [Submission]. + * An [AbstractPreviewHandler] used to access previews of [DbMediaItem]s based on a specific [Submission]. * * @author Luca Rossetto * @author Loris Sauter diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index 713423dbb..087a9dfa2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -10,10 +10,10 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.submission.RunResult import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.Config -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.InteractiveRunManager import dev.dres.run.NonInteractiveRunManager import dev.dres.utilities.extensions.evaluationId @@ -26,10 +26,9 @@ import kotlinx.dnq.query.eq import kotlinx.dnq.query.filter import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query -import java.util.* /** - * An [GetRestHandler] used to process batched [Submission]s. + * An [GetRestHandler] used to process batched [DbSubmission]s. * * @author Luca Rossetto * @author Loris Sauter @@ -77,25 +76,25 @@ class BatchSubmissionHandler(private val store: TransientEntityStore, private va } /** - * Converts the user request tu a [Submission]. + * Converts the user request tu a [DbSubmission]. * * Creates the associated database entry. Requires an ongoing transaction. * - * @param userId The [UserId] of the user who triggered the [Submission]. + * @param userId The [UserId] of the user who triggered the [DbSubmission]. * @param runManager The [InteractiveRunManager] * @param submission The submitted [RunResult]s. * @param submissionTime Time of the submission. * @param ctx The HTTP [Context] */ - private fun toSubmission(userId: UserId, runManager: NonInteractiveRunManager, submission: RunResult, submissionTime: Long, ctx: Context): Submission { + private fun toSubmission(userId: UserId, runManager: NonInteractiveRunManager, submission: RunResult, submissionTime: Long, ctx: Context): DbSubmission { /* Find team that the user belongs to. */ - val user = User.query(User::id eq userId).firstOrNull() + val user = DbUser.query(DbUser::id eq userId).firstOrNull() ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) val team = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull() ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) /* Create new submission. */ - val new = Submission.new { + val new = DbSubmission.new { this.user = user this.team = team this.timestamp = submissionTime diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index e88c06f37..7d5dec096 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -8,18 +8,18 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus import dev.dres.data.model.Config -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.media.* import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.Task -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.InteractiveRunManager import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -35,12 +35,11 @@ import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.nio.file.Files import java.nio.file.Paths -import java.util.* /** - * An [GetRestHandler] used to process [Submission]s. + * An [GetRestHandler] used to process [DbSubmission]s. * - * This endpoint strictly considers [Submission]s to contain single [Verdict]s. + * This endpoint strictly considers [DbSubmission]s to contain single [DbAnswerSet]s. * * @author Luca Rossetto * @author Loris Sauter @@ -109,8 +108,8 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - AuditLogger.submission(submission, AuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) - if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(TaskOption.HIDDEN_RESULTS)) { //pre-generate preview + AuditLogger.submission(submission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) + if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS)) { //pre-generate preview generatePreview(submission.verdicts.first()) } submission to result @@ -119,13 +118,13 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con logger.info("Submission ${s.id} received status $r.") return when (r) { - VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(VerdictStatus.CORRECT, "Submission correct!") - VerdictStatus.WRONG -> SuccessfulSubmissionsStatus(VerdictStatus.WRONG, "Submission incorrect! Try again") - VerdictStatus.INDETERMINATE -> { + DbVerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(DbVerdictStatus.CORRECT, "Submission correct!") + DbVerdictStatus.WRONG -> SuccessfulSubmissionsStatus(DbVerdictStatus.WRONG, "Submission incorrect! Try again") + DbVerdictStatus.INDETERMINATE -> { ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(VerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") + SuccessfulSubmissionsStatus(DbVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") } - VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(VerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") + DbVerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(DbVerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } } @@ -144,27 +143,27 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } /** - * Converts the user request tu a [Submission]. + * Converts the user request tu a [DbSubmission]. * * Creates the associated database entry. Requires an ongoing transaction. * - * @param userId The [UserId] of the user who triggered the [Submission]. + * @param userId The [UserId] of the user who triggered the [DbSubmission]. * @param runManager The [InteractiveRunManager] * @param submissionTime Time of the submission. * @param ctx The HTTP [Context] */ - private fun toSubmission(userId: UserId, runManager: InteractiveRunManager, submissionTime: Long, ctx: Context): Submission { + private fun toSubmission(userId: UserId, runManager: InteractiveRunManager, submissionTime: Long, ctx: Context): DbSubmission { val map = ctx.queryParamMap() /* Find team that the user belongs to. */ - val user = User.query(User::id eq userId).firstOrNull() + val user = DbUser.query(DbUser::id eq userId).firstOrNull() ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) val team = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull() ?: throw ErrorStatusException(404, "No team for user '$userId' could be found.", ctx) val rac = RunActionContext.runActionContext(ctx, runManager) /* Create new submission. */ - val submission = Submission.new { + val submission = DbSubmission.new { this.user = user this.team = team this.timestamp = submissionTime @@ -174,31 +173,31 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con val textParam = map[PARAMETER_NAME_TEXT]?.first() val itemParam = map[PARAMETER_NAME_ITEM]?.first() val currentTaskId = runManager.currentTask(rac)?.id - val task = Task.query(Task::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException(404, "No active task for ID '$currentTaskId' could be found.", ctx) + val task = DbTask.query(DbTask::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException(404, "No active task for ID '$currentTaskId' could be found.", ctx) /* Create Verdict. */ - val verdict = Verdict.new { - this.status = VerdictStatus.INDETERMINATE + val answerSet = DbAnswerSet.new { + this.status = DbVerdictStatus.INDETERMINATE this.task = task } - submission.verdicts.add(verdict) + submission.verdicts.add(answerSet) if (textParam != null) { - verdict.type = VerdictType.TEXT - verdict.text = textParam + answerSet.type = DbAnswerType.TEXT + answerSet.text = textParam return submission } else if (itemParam != null) { val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ - val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(TaskOption.MAP_TO_SEGMENT) - val item = MediaItem.query((MediaItem::name eq itemParam) and (MediaItem::collection eq collection)).firstOrNull() + val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) + val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)).firstOrNull() ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) val range: Pair? = when { - map.containsKey(PARAMETER_NAME_SHOT) && item.type == MediaType.VIDEO -> { + map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { val time = TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) time.first to time.second } - map.containsKey(PARAMETER_NAME_FRAME) && item.type == MediaType.VIDEO -> { + map.containsKey(PARAMETER_NAME_FRAME) && item.type == DbMediaType.VIDEO -> { val fps = item.fps ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") val time = TemporalPoint.Frame.toMilliseconds( @@ -231,13 +230,13 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con /* Assign information to submission. */ if (range != null) { - verdict.item = item - verdict.type = VerdictType.TEMPORAL - verdict.start = range.first - verdict.end = range.second + answerSet.item = item + answerSet.type = DbAnswerType.TEMPORAL + answerSet.start = range.first + answerSet.end = range.second } else { - verdict.item = item - verdict.type = VerdictType.ITEM + answerSet.item = item + answerSet.type = DbAnswerType.ITEM } } else { throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) @@ -247,17 +246,17 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } /** - * Triggers generation of a preview image for the provided [Submission]. + * Triggers generation of a preview image for the provided [DbSubmission]. * - * @param verdict The [Verdict] to generate preview for. + * @param answerSet The [DbAnswerSet] to generate preview for. */ - private fun generatePreview(verdict: Verdict) { - if (verdict.type != VerdictType.TEMPORAL) return - if (verdict.item == null) return - val destinationPath = Paths.get(this.config.cachePath, "previews", verdict.item!!.collection.name, verdict.item!!.name, "${verdict.start}.jpg") + private fun generatePreview(answerSet: DbAnswerSet) { + if (answerSet.type != DbAnswerType.TEMPORAL) return + if (answerSet.item == null) return + val destinationPath = Paths.get(this.config.cachePath, "previews", answerSet.item!!.collection.name, answerSet.item!!.name, "${answerSet.start}.jpg") if (Files.exists(destinationPath)){ return } - FFmpegUtil.extractFrame(verdict.item!!.pathToOriginal(), verdict.start!!, destinationPath) + FFmpegUtil.extractFrame(answerSet.item!!.pathToOriginal(), answerSet.start!!, destinationPath) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index 29642e578..73f1e4565 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Password -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.mgmt.admin.UserManager import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.getOrCreateSessionToken @@ -54,7 +54,7 @@ class LoginHandler : RestHandler, PostRestHandler { val sessionToken = ctx.getOrCreateSessionToken() AccessManager.registerUserForSession(sessionToken, user) - AuditLogger.login(loginRequest.username, AuditLogSource.REST, sessionToken) + AuditLogger.login(loginRequest.username, DbAuditLogSource.REST, sessionToken) //explicitly set cookie on login ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index 1ca49b866..ca5a10a5e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -6,7 +6,7 @@ import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -36,7 +36,7 @@ class LogoutHandler : RestHandler, GetRestHandler { override fun doGet(ctx: Context): SuccessStatus { val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) - AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionToken()!!) + AuditLogger.logout(userId, DbAuditLogSource.REST, ctx.sessionToken()!!) AccessManager.deregisterUserSession(ctx.sessionToken()!!) return SuccessStatus("User '${username}' logged out successfully.") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt index 35ab8576f..dc4219adb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.handler.collection.AbstractCollectionHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.TemplateId import io.javalin.http.Context import io.javalin.security.RouteRole @@ -15,7 +15,7 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * An abstract [RestHandler] used to access and manipulate [EvaluationTemplate]s. + * An abstract [RestHandler] used to access and manipulate [DbEvaluationTemplate]s. * * @author Luca Rossetto * @author Ralph Gasser @@ -30,16 +30,16 @@ abstract class AbstractEvaluationTemplateHandler(protected val store: TransientE /** All [AbstractCollectionHandler]s are part of the v1 API. */ override val apiVersion = "v2" - /** Convenience method to extract [EvaluationTemplate]'s ID from [Context]. */ + /** Convenience method to extract [DbEvaluationTemplate]'s ID from [Context]. */ private fun competitionId(ctx: Context): TemplateId = ctx.pathParamMap().getOrElse("competitionId") { throw ErrorStatusException(404, "Parameter 'competitionId' is missing!'", ctx) } - /** Convenience method to extract [EvaluationTemplate] from [Context]. */ - protected fun competitionFromContext(ctx: Context): EvaluationTemplate = competitionById(competitionId(ctx), ctx) + /** Convenience method to extract [DbEvaluationTemplate] from [Context]. */ + protected fun competitionFromContext(ctx: Context): DbEvaluationTemplate = competitionById(competitionId(ctx), ctx) - /** Convenience method to extract [EvaluationTemplate] by ID. */ - protected fun competitionById(id: TemplateId, ctx: Context): EvaluationTemplate - = EvaluationTemplate.query(EvaluationTemplate::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) + /** Convenience method to extract [DbEvaluationTemplate] by ID. */ + protected fun competitionById(id: TemplateId, ctx: Context): DbEvaluationTemplate + = DbEvaluationTemplate.query(DbEvaluationTemplate::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index 72fd288a7..e32604bf0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.competition.ApiCreateEvaluation import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -14,7 +14,7 @@ import jetbrains.exodus.database.TransientEntityStore import java.util.* /** - * A [AbstractEvaluationTemplateHandler] that can be used to create a new [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to create a new [DbEvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto @@ -47,7 +47,7 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva val newId = UUID.randomUUID().toString() this.store.transactional { - EvaluationTemplate.new { + DbEvaluationTemplate.new { id = newId name = createRequest.name description = createRequest.description diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index 894fbcd50..c1ee8f708 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -3,13 +3,13 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractEvaluationTemplateHandler] that can be used to delete an existing [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to delete an existing [DbEvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index cb16bb4e7..3c652c3a0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi @@ -14,7 +14,7 @@ import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query /** - * A [AbstractEvaluationTemplateHandler] that can be used to list all [Team] logos. + * A [AbstractEvaluationTemplateHandler] that can be used to list all [DbTeam] logos. * * @author Ralph Gasser * @author Luca Rossetto @@ -43,7 +43,7 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla val teamId = ctx.pathParamMap()["teamId"] ?: throw ErrorStatusException(400, "Parameter 'teamId' is missing!'", ctx) this.store.transactional(true) { - val logo = Team.query(Team::id eq teamId).firstOrNull()?.logo + val logo = DbTeam.query(DbTeam::id eq teamId).firstOrNull()?.logo if (logo != null) { ctx.contentType("image/png") ctx.result(logo) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index b4f273971..cb42bf409 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.ApiEvaluationOverview import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi @@ -14,7 +14,7 @@ import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.size /** - * A [GetRestHandler] that can be used to list all [EvaluationTemplate]s. + * A [GetRestHandler] that can be used to list all [DbEvaluationTemplate]s. * * @author Ralph Gasser * @author Luca Rossetto @@ -35,7 +35,7 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEval methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = this.store.transactional(true) { - EvaluationTemplate.all().asSequence().map { + DbEvaluationTemplate.all().asSequence().map { ApiEvaluationOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) }.toList() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 78c122b49..b34150fc4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -3,14 +3,14 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.tasks.ApiTaskTemplate import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence /** - * A [AbstractEvaluationTemplateHandler] that can be used to list all [TaskTemplate]s. + * A [AbstractEvaluationTemplateHandler] that can be used to list all [DbTaskTemplate]s. * * @author Ralph Gasser * @author Luca Rossetto diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index d8ea441aa..ce6db1239 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -3,14 +3,14 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence /** - * A [AbstractEvaluationTemplateHandler] that can be used to list all [Team]s. + * A [AbstractEvaluationTemplateHandler] that can be used to list all [DbTeam]s. * * @author Ralph Gasser * @author Luca Rossetto diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index f7eb59361..ad4e421c2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -3,13 +3,13 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** - * A [AbstractEvaluationTemplateHandler] that can be used to show an existing [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to show an existing [DbEvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt index 7ccdeaf6b..c6d64584a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt @@ -7,13 +7,13 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.Config -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.* -import dev.dres.data.model.template.task.options.ConfiguredOption -import dev.dres.data.model.template.team.Team -import dev.dres.data.model.template.team.TeamGroup -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.template.task.options.DbConfiguredOption +import dev.dres.data.model.template.team.DbTeam +import dev.dres.data.model.template.team.DbTeamGroup +import dev.dres.data.model.media.DbMediaItem import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -21,10 +21,9 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.query.* import java.io.ByteArrayInputStream -import java.util.* /** - * A [AbstractEvaluationTemplateHandler] that can be used to create a new [EvaluationTemplate]. + * A [AbstractEvaluationTemplateHandler] that can be used to create a new [DbEvaluationTemplate]. * * @author Ralph Gasser * @author Luca Rossetto @@ -66,24 +65,24 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : /* Update task type information. */ val taskTypes = apiValue.taskTypes.map { it.name }.toTypedArray() - existing.taskTypes.removeAll(TaskType.query(TaskType::evaluation eq existing and not(TaskType::name.containsIn(*taskTypes)))) + existing.taskTypes.removeAll(DbTaskType.query(DbTaskType::evaluation eq existing and not(DbTaskType::name.containsIn(*taskTypes)))) for (type in apiValue.taskTypes) { - val t = TaskType.findOrNew { - (TaskType::name eq type.name) and (TaskType::evaluation eq existing) + val t = DbTaskType.findOrNew { + (DbTaskType::name eq type.name) and (DbTaskType::evaluation eq existing) } t.name = type.name t.duration = type.duration - t.score = type.scoreOption.toScoreOption() - t.target = type.targetOption.toTargetOption() + t.score = type.scoreOption.toDb() + t.target = type.targetOption.toDb() t.hints.clear() - t.hints.addAll(type.hintOptions.map { it.option }) + t.hints.addAll(type.hintOptions.map { it.toDb() }) t.submission.clear() - t.submission.addAll(type.submissionOptions.map { it.toSubmissionOption() }) + t.submission.addAll(type.submissionOptions.map { it.toDb() }) t.options.clear() - t.options.addAll(type.taskOptions.map { it.toTaskOption() }) + t.options.addAll(type.taskOptions.map { it.toDb() }) t.configurations.clear() t.configurations.addAll(type.configuration.entries.map { - ConfiguredOption.new { + DbConfiguredOption.new { this.key = it.key this.value = it.value } @@ -92,23 +91,23 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : /* Update task group information. */ val taskGroups = apiValue.taskGroups.map { it.name }.toTypedArray() - existing.taskGroups.removeAll(TaskGroup.query(TaskGroup::evaluation eq existing and not(TaskGroup::name.containsIn(*taskGroups)))) + existing.taskGroups.removeAll(DbTaskGroup.query(DbTaskGroup::evaluation eq existing and not(DbTaskGroup::name.containsIn(*taskGroups)))) for (group in apiValue.taskGroups) { - val g = TaskGroup.findOrNew { - (TaskGroup::name eq type.name) and (TaskGroup::evaluation eq existing) + val g = DbTaskGroup.findOrNew { + (DbTaskGroup::name eq type.name) and (DbTaskGroup::evaluation eq existing) } g.name = group.name - g.type = TaskType.query((TaskType::name eq group.name) and (TaskGroup::evaluation eq existing)).first() + g.type = DbTaskType.query((DbTaskType::name eq group.name) and (DbTaskGroup::evaluation eq existing)).first() } /* Update task information. */ val taskIds = apiValue.tasks.mapNotNull { it.id }.toTypedArray() - existing.tasks.removeAll(TaskTemplate.query(TaskTemplate::evaluation eq existing and not(TaskTemplate::id.containsIn(*taskIds)))) + existing.tasks.removeAll(DbTaskTemplate.query(DbTaskTemplate::evaluation eq existing and not(DbTaskTemplate::id.containsIn(*taskIds)))) for (task in apiValue.tasks) { val t = if (task.id != null) { existing.tasks.filter { it.id eq task.id }.first() } else { - val desc = TaskTemplate.new { } + val desc = DbTaskTemplate.new { } existing.tasks.add(desc) desc } @@ -118,10 +117,10 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : /* Update task targets. */ t.targets.clear() for (target in task.targets) { - val item = MediaItem.query(MediaItem::id eq target.target).first() - t.targets.add(TaskTemplateTarget.new { + val item = DbMediaItem.query(DbMediaItem::id eq target.target).first() + t.targets.add(DbTaskTemplateTarget.new { this.item = item - this.type = target.type.toTargetType() + this.type = target.type.toDb() this.start = target.range?.start?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() this.end = target.range?.end?.toTemporalPoint(item.fps ?: 0.0f)?.toMilliseconds() }) @@ -130,9 +129,9 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : /* Update task hints. */ t.hints.clear() for (hint in task.hints) { - val item = MediaItem.query(MediaItem::id eq hint.mediaItem).firstOrNull() - t.hints.add(Hint.new { - this.type = hint.type.type + val item = DbMediaItem.query(DbMediaItem::id eq hint.mediaItem).firstOrNull() + t.hints.add(DbHint.new { + this.type = hint.type.toDb() this.item = item this.text = hint.description this.path = hint.path @@ -146,10 +145,10 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : /* Update team information. */ val teamIds = apiValue.teams.map { it.teamId }.toTypedArray() - existing.teams.removeAll(Team.query(Team::evaluation eq existing and not(Team::id.containsIn(*teamIds)))) + existing.teams.removeAll(DbTeam.query(DbTeam::evaluation eq existing and not(DbTeam::id.containsIn(*teamIds)))) for (team in apiValue.teams) { - val t = Team.findOrNew { - (Team::name eq team.name) and (Team::evaluation eq existing) + val t = DbTeam.findOrNew { + (DbTeam::name eq team.name) and (DbTeam::evaluation eq existing) } t.name = team.name t.color = team.color @@ -157,24 +156,24 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : t.logo = ByteArrayInputStream(team.logoData!!.decodeBase64()) } t.users.clear() - t.users.addAll(User.query(User::id.containsIn(*team.users.map { it.id }.toTypedArray()))) + t.users.addAll(DbUser.query(DbUser::id.containsIn(*team.users.map { it.id }.toTypedArray()))) } /* Update teamGroup information */ val teamGroupIds = apiValue.teamGroups.map { it.id }.toTypedArray() - existing.teamsGroups.removeAll(TeamGroup.query(TeamGroup::evaluation eq existing and not(TeamGroup::id.containsIn(*teamGroupIds)))) + existing.teamsGroups.removeAll(DbTeamGroup.query(DbTeamGroup::evaluation eq existing and not(DbTeamGroup::id.containsIn(*teamGroupIds)))) for (teamGroup in apiValue.teamGroups) { - val t = TeamGroup.findOrNew { - (Team::name eq teamGroup.name) and (Team::evaluation eq existing) + val t = DbTeamGroup.findOrNew { + (DbTeam::name eq teamGroup.name) and (DbTeam::evaluation eq existing) } t.name = teamGroup.name t.teams.clear() - t.teams.addAll(Team.query(Team::id.containsIn(*teamGroup.teams.map { it.teamId }.toTypedArray()))) + t.teams.addAll(DbTeam.query(DbTeam::id.containsIn(*teamGroup.teams.map { it.teamId }.toTypedArray()))) } /* Update judge information */ existing.judges.clear() - existing.judges.addAll(User.query(User::id.containsIn(*apiValue.judges.toTypedArray()))) + existing.judges.addAll(DbUser.query(DbUser::id.containsIn(*apiValue.judges.toTypedArray()))) } return SuccessStatus("Competition with ID ${apiValue.id} was updated successfully.") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt index d09d0084c..209caee84 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -12,7 +12,7 @@ import io.javalin.http.Context typealias SessionToken = String /** - * An abstract [RestHandler] to manage [User]s + * An abstract [RestHandler] to manage [DbUser]s * * @author Loris Sauter * @version 2.0.0 @@ -21,14 +21,14 @@ abstract class AbstractUserHandler: RestHandler, AccessManagedRestHandler { /** All [AbstractUserHandler]s are part of the v1 API. */ override val apiVersion = "v2" - /** Convenience method to extract [User] from current session. */ - protected fun userFromSession(ctx: Context): User { + /** Convenience method to extract [DbUser] from current session. */ + protected fun userFromSession(ctx: Context): DbUser { return UserManager.get(id = AccessManager.userIdForSession(ctx.sessionToken())!!) ?: throw ErrorStatusException(404, "User could not be found!", ctx) } - /** Convenience method to extract [User] from [Context] (userId parameter). */ - protected fun userFromContext(ctx: Context): User { + /** Convenience method to extract [DbUser] from [Context] (userId parameter). */ + protected fun userFromContext(ctx: Context): DbUser { val id = ctx.pathParamMap()["userId"] ?: throw ErrorStatusException(404, "Parameter 'userId' is missing!'", ctx) return UserManager.get(id = id) ?: throw ErrorStatusException(404, "User ($id) not found!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index 24dd1799e..cba8cc784 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -8,14 +8,14 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.Password -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* /** - * An [AbstractUserHandler] to create new [User]s. + * An [AbstractUserHandler] to create new [DbUser]s. * * @author Loris Sauter * @version 2.0.0 @@ -44,14 +44,14 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, Acce throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - if (req.password == null || req.password.length < User.MIN_LENGTH_PASSWORD) - throw ErrorStatusException(400, "Invalid parameters. Password must consist of at least ${User.MIN_LENGTH_PASSWORD} characters.", ctx) - if (req.username.length < User.MIN_LENGTH_USERNAME) - throw ErrorStatusException(400, "Invalid parameters. Username must consist of at least ${User.MIN_LENGTH_USERNAME} characters.", ctx) + if (req.password == null || req.password.length < DbUser.MIN_LENGTH_PASSWORD) + throw ErrorStatusException(400, "Invalid parameters. Password must consist of at least ${DbUser.MIN_LENGTH_PASSWORD} characters.", ctx) + if (req.username.length < DbUser.MIN_LENGTH_USERNAME) + throw ErrorStatusException(400, "Invalid parameters. Username must consist of at least ${DbUser.MIN_LENGTH_USERNAME} characters.", ctx) if (req.role == null) throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) - val success = UserManager.create(req.username, Password.Plain(req.password), req.role.toRole() ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) + val success = UserManager.create(req.username, Password.Plain(req.password), req.role.toDb() ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) if (success) { return UserManager.get(username = req.username)!!.toApi() } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index 0abfed92b..ea69493a6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -6,13 +6,13 @@ import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.* /** - * An [AbstractUserHandler] to delete [User]s. + * An [AbstractUserHandler] to delete [DbUser]s. * * @author Loris Sauter * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index 587d545bf..2ad62ad2c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -6,7 +6,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.HttpMethod @@ -15,7 +15,7 @@ import io.javalin.openapi.OpenApiContent import io.javalin.openapi.OpenApiResponse /** - * An [AbstractUserHandler] to list all [User]s that are currently logged in. + * An [AbstractUserHandler] to list all [DbUser]s that are currently logged in. * * @author Loris Sauter * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt index 902ba1538..48a555e77 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.HttpMethod @@ -13,7 +13,7 @@ import io.javalin.openapi.OpenApiContent import io.javalin.openapi.OpenApiResponse /** - * An [AbstractUserHandler] to list all [User]s. + * An [AbstractUserHandler] to list all [DbUser]s. * * @author Loris Sauter * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt index a1dd95f6e..45cf30c72 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -7,15 +7,15 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser import dev.dres.api.rest.types.users.UserRequest -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbRole +import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* /** - * An [AbstractUserHandler] to update an existing [User]s. + * An [AbstractUserHandler] to update an existing [DbUser]s. * * @author Loris Sauter * @version 2.0.0 @@ -51,7 +51,7 @@ class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, Acc val user = userFromContext(ctx) val caller = userFromSession(ctx) - if (caller.role == Role.ADMIN || user.id == caller.id) { + if (caller.role == DbRole.ADMIN || user.id == caller.id) { val success = UserManager.update(id = user.id, request = request) if (success) { return UserManager.get(id = user.id)!!.toApi() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt index 57fc79207..86cb1eab1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -5,12 +5,12 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import io.javalin.http.Context import io.javalin.openapi.* /** - * An [AbstractUserHandler] to show [User] details. + * An [AbstractUserHandler] to show [DbUser] details. * * @author Loris Sauter * @version 2.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt index 3bdb0f600..d5f35bc6e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.audit -import dev.dres.data.model.audit.AuditLogEntry +import dev.dres.data.model.audit.DbAuditLogEntry /** - * A RESTful API representation of a [AuditLogEntry] + * A RESTful API representation of a [DbAuditLogEntry] * * @author Loris Sauter * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt index f5ab09973..5055fc31f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt @@ -1,13 +1,22 @@ package dev.dres.api.rest.types.audit -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource /** * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiAuditLogSource(val source: AuditLogSource) { - REST(AuditLogSource.REST), - CLI(AuditLogSource.CLI), - INTERNAL(AuditLogSource.INTERNAL) +enum class ApiAuditLogSource { + REST, CLI, INTERNAL; + + /** + * Converts this [ApiAuditLogSource] to a RESTful API representation [DbAuditLogSource]. + * + * @return [DbAuditLogSource] + */ + fun toDb(): DbAuditLogSource = when(this) { + REST -> DbAuditLogSource.REST + CLI -> DbAuditLogSource.CLI + INTERNAL -> DbAuditLogSource.INTERNAL + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt index 52a1d1b56..24b71c6a5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt @@ -1,22 +1,33 @@ package dev.dres.api.rest.types.audit -import dev.dres.data.model.audit.AuditLogType +import dev.dres.data.model.audit.DbAuditLogType /** * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiAuditLogType(val type: AuditLogType) { - COMPETITION_START(AuditLogType.COMPETITION_START), - COMPETITION_END(AuditLogType.COMPETITION_END), - TASK_START(AuditLogType.TASK_START), - TASK_MODIFIED(AuditLogType.TASK_MODIFIED), - TASK_END(AuditLogType.TASK_END), - SUBMISSION(AuditLogType.SUBMISSION), - PREPARE_JUDGEMENT(AuditLogType.PREPARE_JUDGEMENT), - JUDGEMENT(AuditLogType.JUDGEMENT), - LOGIN(AuditLogType.LOGIN), - LOGOUT(AuditLogType.LOGOUT), - SUBMISSION_VALIDATION(AuditLogType.SUBMISSION_VALIDATION), - SUBMISSION_STATUS_OVERWRITE(AuditLogType.SUBMISSION_STATUS_OVERWRITE) +enum class ApiAuditLogType { + COMPETITION_START, COMPETITION_END, TASK_START, TASK_MODIFIED, TASK_END, SUBMISSION, + PREPARE_JUDGEMENT, JUDGEMENT, LOGIN, LOGOUT, SUBMISSION_VALIDATION, SUBMISSION_STATUS_OVERWRITE; + + + /** + * Converts this [ApiAuditLogType] to a RESTful API representation [DbAuditLogType]. + * + * @return [DbAuditLogType] + */ + fun toDb(): DbAuditLogType = when(this) { + COMPETITION_START -> DbAuditLogType.COMPETITION_START + COMPETITION_END -> DbAuditLogType.COMPETITION_END + TASK_START -> DbAuditLogType.TASK_START + TASK_MODIFIED -> DbAuditLogType.TASK_MODIFIED + TASK_END -> DbAuditLogType.TASK_END + SUBMISSION -> DbAuditLogType.SUBMISSION + PREPARE_JUDGEMENT -> DbAuditLogType.PREPARE_JUDGEMENT + JUDGEMENT -> DbAuditLogType.JUDGEMENT + LOGIN -> DbAuditLogType.LOGIN + LOGOUT -> DbAuditLogType.LOGOUT + SUBMISSION_VALIDATION -> DbAuditLogType.SUBMISSION_VALIDATION + SUBMISSION_STATUS_OVERWRITE -> DbAuditLogType.SUBMISSION_STATUS_OVERWRITE + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 9e50df6a9..5da844454 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.collection -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem /** - * The RESTful API equivalent for [MediaItem]. + * The RESTful API equivalent for [DbMediaItem]. * - * @see MediaItem + * @see DbMediaItem * @author Ralph Gasser * @version 1.1.0 */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt index 584086520..f6434506f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.collection -import dev.dres.data.model.media.MediaType +import dev.dres.data.model.media.DbMediaType /** * The RESTful API equivalent for the type of a [ApiMediaItem] @@ -13,13 +13,13 @@ enum class ApiMediaType { IMAGE, VIDEO, TEXT; /** - * Converts this [ApiMediaType] to a [MediaType] representation. Requires an ongoing transaction! + * Converts this [ApiMediaType] to a [DbMediaType] representation. Requires an ongoing transaction! * - * @return [MediaType] + * @return [DbMediaType] */ - fun toMediaType(): MediaType = when(this) { - IMAGE -> MediaType.IMAGE - VIDEO -> MediaType.VIDEO - TEXT -> MediaType.TEXT + fun toDb(): DbMediaType = when(this) { + IMAGE -> DbMediaType.IMAGE + VIDEO -> DbMediaType.VIDEO + TEXT -> DbMediaType.TEXT } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt index e30df632c..8ec716699 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt @@ -1,23 +1,23 @@ package dev.dres.api.rest.types.collection import dev.dres.data.model.media.CollectionId -import dev.dres.data.model.media.MediaCollection -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.template.task.DbTaskTemplate /** - * The RESTful API equivalent for [MediaCollection]. + * The RESTful API equivalent for [DbMediaCollection]. * - * @see MediaCollection + * @see DbMediaCollection * @author Ralph Gasser * @version 1.0 */ data class RestMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { companion object { /** - * Generates a [ApiMediaItem] from a [TaskTemplate] and returns it. + * Generates a [ApiMediaItem] from a [DbTaskTemplate] and returns it. * - * @param task The [TaskTemplate] to convert. + * @param task The [DbTaskTemplate] to convert. */ - fun fromMediaCollection(item: MediaCollection) = RestMediaCollection(item.id, item.name, item.description, item.path) + fun fromMediaCollection(item: DbMediaCollection) = RestMediaCollection(item.id, item.name, item.description, item.path) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt index e9c1d1b3c..034757e2e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate /** - * An overview over a [EvaluationTemplate]. + * An overview over a [DbEvaluationTemplate]. * * @author Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt index d020a3711..5441d558f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationStartMessage.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.competition -import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.evaluation.ApiEvaluationType import dev.dres.data.model.run.RunProperties import dev.dres.data.model.template.TemplateId @@ -10,4 +10,4 @@ import dev.dres.data.model.template.TemplateId * @author Ralph Gasser * @version 1.1.0 */ -data class ApiEvaluationStartMessage(val templateId: TemplateId, val name: String, val type: ApiRunType, val properties: RunProperties = RunProperties()) \ No newline at end of file +data class ApiEvaluationStartMessage(val templateId: TemplateId, val name: String, val type: ApiEvaluationType, val properties: RunProperties = RunProperties()) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt index 690556800..8475c9961 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt @@ -5,13 +5,13 @@ import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup import dev.dres.api.rest.types.competition.tasks.ApiTaskType import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.competition.team.ApiTeamGroup -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.TemplateId /** - * The RESTful API equivalent for [EvaluationTemplate]. + * The RESTful API equivalent for [DbEvaluationTemplate]. * - * @see EvaluationTemplate + * @see DbEvaluationTemplate * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt index 6d455600a..26998288e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiHintType.kt @@ -1,16 +1,25 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.template.task.HintType +import dev.dres.data.model.template.task.DbHintType /** - * The RESTful API equivalent for [HintType]. + * The RESTful API equivalent for [DbHintType]. * * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiHintType(val type: HintType) { - EMPTY(HintType.EMPTY), - TEXT(HintType.TEXT), - VIDEO(HintType.VIDEO), - IMAGE(HintType.IMAGE) +enum class ApiHintType { + EMPTY, TEXT, VIDEO, IMAGE; + + /** + * Converts this [ApiHintType] to a [DbHintType] representation. Requires an ongoing transaction. + * + * @return [DbHintType] + */ + fun toDb(): DbHintType = when(this) { + EMPTY -> DbHintType.EMPTY + TEXT -> DbHintType.TEXT + VIDEO -> DbHintType.VIDEO + IMAGE -> DbHintType.IMAGE + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt index 9dda47de1..979742adb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTarget.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.api.rest.types.collection.time.ApiTemporalRange -import dev.dres.data.model.template.task.TaskTemplateTarget +import dev.dres.data.model.template.task.DbTaskTemplateTarget /** - * The RESTful API equivalent for [TaskTemplateTarget]. + * The RESTful API equivalent for [DbTaskTemplateTarget]. * * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt index 7489afcdb..6f00b874a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTargetType.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.template.task.TargetType +import dev.dres.data.model.template.task.DbTargetType /** - * The RESTful API equivalent for [TargetType]. + * The RESTful API equivalent for [DbTargetType]. * * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 @@ -12,15 +12,15 @@ enum class ApiTargetType { JUDGEMENT, JUDGEMENT_WITH_VOTE, MEDIA_ITEM, MEDIA_ITEM_TEMPORAL_RANGE, TEXT; /** - * Converts this [ApiTargetType] to a [TargetType] representation. Requires an ongoing transaction. + * Converts this [ApiTargetType] to a [DbTargetType] representation. Requires an ongoing transaction. * - * @return [TargetType] + * @return [DbTargetType] */ - fun toTargetType(): TargetType = when(this) { - JUDGEMENT -> TargetType.JUDGEMENT - JUDGEMENT_WITH_VOTE -> TargetType.JUDGEMENT_WITH_VOTE - MEDIA_ITEM -> TargetType.MEDIA_ITEM - MEDIA_ITEM_TEMPORAL_RANGE -> TargetType.MEDIA_ITEM_TEMPORAL_RANGE - TEXT -> TargetType.TEXT + fun toDb(): DbTargetType = when(this) { + JUDGEMENT -> DbTargetType.JUDGEMENT + JUDGEMENT_WITH_VOTE -> DbTargetType.JUDGEMENT_WITH_VOTE + MEDIA_ITEM -> DbTargetType.MEDIA_ITEM + MEDIA_ITEM_TEMPORAL_RANGE -> DbTargetType.MEDIA_ITEM_TEMPORAL_RANGE + TEXT -> DbTargetType.TEXT } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt index b666f032f..7d33ada1f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskGroup.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.template.task.TaskGroup +import dev.dres.data.model.template.task.DbTaskGroup /** - * The RESTful API equivalent of a [TaskGroup]. + * The RESTful API equivalent of a [DbTaskGroup]. * * @author Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt index 59e8eac52..13c2c5ca1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt @@ -1,13 +1,13 @@ package dev.dres.api.rest.types.competition.tasks -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.CollectionId import dev.dres.data.model.template.TemplateId /** - * The RESTful API equivalent for [TaskTemplate]. + * The RESTful API equivalent for [DbTaskTemplate]. * - * @see TaskTemplate + * @see DbTaskTemplate * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt index e6a52ee29..41274b233 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskType.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.competition.tasks import dev.dres.api.rest.types.competition.tasks.options.* -import dev.dres.data.model.template.task.TaskType +import dev.dres.data.model.template.task.DbTaskType /** - * The RESTful API equivalent of a [TaskType]. + * The RESTful API equivalent of a [DbTaskType]. * * @author Ralph Gasser * @version 1.0.0 @@ -13,7 +13,7 @@ data class ApiTaskType( val name: String, val duration: Long, val targetOption: ApiTargetOption, - val hintOptions: List, + val hintOptions: List, val submissionOptions: List, val taskOptions: List, val scoreOption: ApiScoreOption, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt deleted file mode 100644 index acec5525d..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiComponentOption.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dev.dres.api.rest.types.competition.tasks.options - -import dev.dres.data.model.template.task.options.HintOption - -/** - * A RESTful API representation of [HintOption]. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -enum class ApiComponentOption(val option: HintOption) { - IMAGE_ITEM(HintOption.IMAGE_ITEM), - VIDEO_ITEM_SEGMENT(HintOption.VIDEO_ITEM_SEGMENT), - TEXT(HintOption.TEXT), - EXTERNAL_IMAGE(HintOption.EXTERNAL_IMAGE), - EXTERNAL_VIDEO(HintOption.EXTERNAL_VIDEO) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiHintOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiHintOption.kt new file mode 100644 index 000000000..d8c9b8cd3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiHintOption.kt @@ -0,0 +1,26 @@ +package dev.dres.api.rest.types.competition.tasks.options + +import dev.dres.data.model.template.task.options.DbHintOption + +/** + * A RESTful API representation of [DbHintOption]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiHintOption { + IMAGE_ITEM, VIDEO_ITEM_SEGMENT, TEXT, EXTERNAL_IMAGE, EXTERNAL_VIDEO; + + /** + * Converts this [ApiHintOption] to a RESTful API representation [DbHintOption]. + * + * @return [DbHintOption] + */ + fun toDb() = when(this) { + IMAGE_ITEM -> DbHintOption.IMAGE_ITEM + VIDEO_ITEM_SEGMENT -> DbHintOption.VIDEO_ITEM_SEGMENT + TEXT -> DbHintOption.TEXT + EXTERNAL_IMAGE -> DbHintOption.EXTERNAL_IMAGE + EXTERNAL_VIDEO -> DbHintOption.EXTERNAL_VIDEO + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt index 01b8c780f..9adbd79c4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiScoreOption.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.template.task.options.ScoreOption +import dev.dres.data.model.template.task.options.DbScoreOption /** - * A RESTful API representation of [ScoreOption]. + * A RESTful API representation of [DbScoreOption]. * * @author Ralph Gasser * @version 1.0.0 @@ -12,12 +12,12 @@ enum class ApiScoreOption { KIS, AVS; /** - * Converts this [ApiScoreOption] to a [ScoreOption] representation. Requires an ongoing transaction. + * Converts this [ApiScoreOption] to a [DbScoreOption] representation. Requires an ongoing transaction. * - * @return [ScoreOption] + * @return [DbScoreOption] */ - fun toScoreOption(): ScoreOption = when(this) { - KIS -> ScoreOption.KIS - AVS -> ScoreOption.AVS + fun toDb(): DbScoreOption = when(this) { + KIS -> DbScoreOption.KIS + AVS -> DbScoreOption.AVS } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt index f2fbc145a..51d6cf4c5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiSubmissionOption.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.template.task.options.SubmissionOption +import dev.dres.data.model.template.task.options.DbSubmissionOption /** - * A RESTful API representation of [SubmissionOption]. + * A RESTful API representation of [DbSubmissionOption]. * * @author Ralph Gasser * @version 1.0.0 @@ -13,19 +13,19 @@ enum class ApiSubmissionOption { LIMIT_CORRECT_PER_MEMBER, TEMPORAL_SUBMISSION, TEXTUAL_SUBMISSION, ITEM_SUBMISSION, MINIMUM_TIME_GAP; /** - * Converts this [ApiSubmissionOption] to a [SubmissionOption] representation. Requires an ongoing transaction. + * Converts this [ApiSubmissionOption] to a [DbSubmissionOption] representation. Requires an ongoing transaction. * - * @return [SubmissionOption] + * @return [DbSubmissionOption] */ - fun toSubmissionOption(): SubmissionOption = when(this) { - NO_DUPLICATES -> SubmissionOption.NO_DUPLICATES - LIMIT_CORRECT_PER_TEAM -> SubmissionOption.LIMIT_CORRECT_PER_TEAM - LIMIT_WRONG_PER_TEAM -> SubmissionOption.LIMIT_WRONG_PER_TEAM - LIMIT_TOTAL_PER_TEAM -> SubmissionOption.LIMIT_TOTAL_PER_TEAM - LIMIT_CORRECT_PER_MEMBER -> SubmissionOption.LIMIT_CORRECT_PER_MEMBER - TEMPORAL_SUBMISSION -> SubmissionOption.TEMPORAL_SUBMISSION - TEXTUAL_SUBMISSION -> SubmissionOption.TEXTUAL_SUBMISSION - ITEM_SUBMISSION -> SubmissionOption.ITEM_SUBMISSION - MINIMUM_TIME_GAP -> SubmissionOption.MINIMUM_TIME_GAP + fun toDb(): DbSubmissionOption = when(this) { + NO_DUPLICATES -> DbSubmissionOption.NO_DUPLICATES + LIMIT_CORRECT_PER_TEAM -> DbSubmissionOption.LIMIT_CORRECT_PER_TEAM + LIMIT_WRONG_PER_TEAM -> DbSubmissionOption.LIMIT_WRONG_PER_TEAM + LIMIT_TOTAL_PER_TEAM -> DbSubmissionOption.LIMIT_TOTAL_PER_TEAM + LIMIT_CORRECT_PER_MEMBER -> DbSubmissionOption.LIMIT_CORRECT_PER_MEMBER + TEMPORAL_SUBMISSION -> DbSubmissionOption.TEMPORAL_SUBMISSION + TEXTUAL_SUBMISSION -> DbSubmissionOption.TEXTUAL_SUBMISSION + ITEM_SUBMISSION -> DbSubmissionOption.ITEM_SUBMISSION + MINIMUM_TIME_GAP -> DbSubmissionOption.MINIMUM_TIME_GAP } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt index f2eb4b263..671e2072c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTargetOption.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.template.task.options.TargetOption +import dev.dres.data.model.template.task.options.DbTargetOption /** - * A RESTful API representation of [TargetOption]. + * A RESTful API representation of [DbTargetOption]. * * @author Ralph Gasser * @version 1.0.0 @@ -12,15 +12,15 @@ enum class ApiTargetOption{ SINGLE_MEDIA_ITEM, SINGLE_MEDIA_SEGMENT, JUDGEMENT, VOTE, TEXT; /** - * Converts this [ApiTargetOption] to a [TargetOption] representation. Requires an ongoing transaction. + * Converts this [ApiTargetOption] to a [DbTargetOption] representation. Requires an ongoing transaction. * - * @return [TargetOption] + * @return [DbTargetOption] */ - fun toTargetOption(): TargetOption = when(this) { - SINGLE_MEDIA_ITEM -> TargetOption.MEDIA_ITEM - SINGLE_MEDIA_SEGMENT -> TargetOption.MEDIA_SEGMENT - JUDGEMENT -> TargetOption.JUDGEMENT - VOTE -> TargetOption.VOTE - TEXT -> TargetOption.TEXT + fun toDb(): DbTargetOption = when(this) { + SINGLE_MEDIA_ITEM -> DbTargetOption.MEDIA_ITEM + SINGLE_MEDIA_SEGMENT -> DbTargetOption.MEDIA_SEGMENT + JUDGEMENT -> DbTargetOption.JUDGEMENT + VOTE -> DbTargetOption.VOTE + TEXT -> DbTargetOption.TEXT } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt index 5e3b4f313..88b87136c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/options/ApiTaskOption.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.competition.tasks.options -import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.data.model.template.task.options.DbTaskOption /** - * A RESTful API representation of [TaskOption]. + * A RESTful API representation of [DbTaskOption]. * * @author Ralph Gasser * @version 1.0.0 @@ -12,13 +12,13 @@ enum class ApiTaskOption { HIDDEN_RESULTS, MAP_TO_SEGMENT, PROLONG_ON_SUBMISSION; /** - * Converts this [ApiTaskOption] to a [TaskOption] representation. Requires an ongoing transaction. + * Converts this [ApiTaskOption] to a [DbTaskOption] representation. Requires an ongoing transaction. * - * @return [TaskOption] + * @return [DbTaskOption] */ - fun toTaskOption(): TaskOption = when(this) { - HIDDEN_RESULTS -> TaskOption.HIDDEN_RESULTS - MAP_TO_SEGMENT -> TaskOption.MAP_TO_SEGMENT - PROLONG_ON_SUBMISSION -> TaskOption.PROLONG_ON_SUBMISSION + fun toDb(): DbTaskOption = when(this) { + HIDDEN_RESULTS -> DbTaskOption.HIDDEN_RESULTS + MAP_TO_SEGMENT -> DbTaskOption.MAP_TO_SEGMENT + PROLONG_ON_SUBMISSION -> DbTaskOption.PROLONG_ON_SUBMISSION } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt index a6de9417c..d8087fcbc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.competition.team import dev.dres.api.rest.types.users.ApiUser -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId /** - * A RESTful API representation of a [Team] + * A RESTful API representation of a [DbTeam] * * @author Loris Sauter * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt index 4d06f14d1..a78790454 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.competition.team -import dev.dres.data.model.template.team.TeamGroup +import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.template.team.TeamGroupId /** - * A RESTful API representation of a [TeamGroup] + * A RESTful API representation of a [DbTeamGroup] * * @author Loris Sauter * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt new file mode 100644 index 000000000..a3671e633 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt @@ -0,0 +1,25 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.submissions.DbAnswerType + +/** + * The RESTful API equivalent for the type of a [DbAnswerType] + * + * @see ApiVerdict + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiAnswerType { + ITEM, TEMPORAL, TEXT; + + /** + * Converts this [ApiAnswerType] to a [DbAnswerType] representation. Requires an ongoing transaction. + * + * @return [DbAnswerType] + */ + fun toDb(): DbAnswerType = when(this) { + ITEM -> DbAnswerType.ITEM + TEMPORAL -> DbAnswerType.TEMPORAL + TEXT -> DbAnswerType.TEXT + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt index 665093d60..a20380f5b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.competition.ApiEvaluationTemplate -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.EvaluationId /** - * The RESTful API equivalent of a [Evaluation]. + * The RESTful API equivalent of a [DbEvaluation]. * * @author Luca Rossetto * @author Ralph Gasser @@ -14,7 +14,7 @@ import dev.dres.data.model.run.EvaluationId data class ApiEvaluation( val evaluationId: EvaluationId, val name: String, - val type: ApiRunType, + val type: ApiEvaluationType, val template: ApiEvaluationTemplate, val started: Long, val ended: Long?, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt index 899d5552c..adff6c8e8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -21,7 +21,7 @@ data class ApiEvaluationInfo( val name: String, val templateId: String, val templateDescription: String?, - val type: ApiRunType, + val type: ApiEvaluationType, val properties: RunProperties, val teams: List, val tasks: List, @@ -32,9 +32,9 @@ data class ApiEvaluationInfo( manager.template.id, manager.template.name, when(manager) { - is InteractiveSynchronousRunManager -> ApiRunType.SYNCHRONOUS - is InteractiveAsynchronousRunManager -> ApiRunType.ASYNCHRONOUS - is NonInteractiveRunManager -> ApiRunType.NON_INTERACTIVE + is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS + is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS + is NonInteractiveRunManager -> ApiEvaluationType.NON_INTERACTIVE else -> throw IllegalStateException("Incompatible type of run manager.") }, manager.runProperties, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationType.kt new file mode 100644 index 000000000..cdf74539a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationType.kt @@ -0,0 +1,20 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.run.DbEvaluationType +/** + * + */ +enum class ApiEvaluationType { + SYNCHRONOUS, ASYNCHRONOUS, NON_INTERACTIVE; + + /** + * Converts this [ApiEvaluationType] to a [DbEvaluationType] representation. Requires an ongoing transaction. + * + * @return [DbEvaluationType] + */ + fun toDb(): DbEvaluationType = when(this) { + SYNCHRONOUS -> DbEvaluationType.INTERACTIVE_SYNCHRONOUS + ASYNCHRONOUS -> DbEvaluationType.INTERACTIVE_ASYNCHRONOUS + NON_INTERACTIVE -> DbEvaluationType.NON_INTERACTIVE + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt deleted file mode 100644 index 52008a285..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiRunType.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.data.model.run.EvaluationType - -/** - * - */ -enum class ApiRunType(val type: EvaluationType) { - SYNCHRONOUS(EvaluationType.INTERACTIVE_SYNCHRONOUS), - ASYNCHRONOUS(EvaluationType.INTERACTIVE_ASYNCHRONOUS), - NON_INTERACTIVE(EvaluationType.NON_INTERACTIVE) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index a316719f5..708b96cce 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.SubmissionId /** - * The RESTful API equivalent of a [Submission]. + * The RESTful API equivalent of a [DbSubmission]. * * @author Luca Rossetto * @author Ralph Gasser diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index ae7d82202..a064757b7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.run.Task +import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.TemplateId /** - * The RESTful API equivalent of a [Task]. + * The RESTful API equivalent of a [DbTask]. * * @author Luca Rossetto * @author Ralph Gasser diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt index 1f869abce..76637349d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt @@ -1,10 +1,9 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.run.InteractiveSynchronousEvaluation +import dev.dres.data.model.template.task.DbTaskTemplate /** - * Basic and most importantly static information about a [TaskTemplate]. + * Basic and most importantly static information about a [DbTaskTemplate]. * * Since this information usually doesn't change in the course of a run, it allows for local caching and other optimizations. * @@ -14,7 +13,7 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation */ data class ApiTaskTemplateInfo(val templateId: String, val name: String, val taskGroup: String, val taskType: String, val duration: Long) { - constructor(task: TaskTemplate) : this(task.id, task.name, task.taskGroup.name, task.taskGroup.type.name, task.duration) + constructor(task: DbTaskTemplate) : this(task.id, task.name, task.taskGroup.name, task.taskGroup.type.name, task.duration) companion object { val EMPTY_INFO = ApiTaskTemplateInfo("", "N/A", "N/A", "N/A", 0) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt index 6c4ad5fc1..6c391e1b6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt @@ -1,11 +1,11 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.template.team.TeamId /** - * Basic and most importantly static information about the [Team] partaking in a [InteractiveSynchronousEvaluation]. + * Basic and most importantly static information about the [DbTeam] partaking in a [InteractiveSynchronousEvaluation]. * Since this information usually doesn't change in the course of a run,t allows for local caching * and other optimizations. * @@ -13,5 +13,5 @@ import dev.dres.data.model.template.team.TeamId * @version 1.1.0 */ data class ApiTeamInfo(val id: TeamId, val name: String, val color: String) { - constructor(team: Team) : this(team.id, team.name, team.color) + constructor(team: DbTeam) : this(team.id, team.name, team.color) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt index 54c803d8b..01150fbd1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt @@ -10,7 +10,7 @@ import dev.dres.api.rest.types.collection.ApiMediaItem * @version 1.0.0 */ data class ApiVerdict( - val type: ApiVerdictType, + val type: ApiAnswerType, val status: ApiVerdictStatus, val item: ApiMediaItem?, val text: String?, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt index e9c3e5a22..75ea25043 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt @@ -1,9 +1,9 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbVerdictStatus /** - * The RESTful API equivalent for the type of a [VerdictStatus] + * The RESTful API equivalent for the type of a [DbVerdictStatus] * * @see ApiVerdict * @author Ralph Gasser @@ -13,14 +13,14 @@ enum class ApiVerdictStatus { CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; /** - * Converts this [ApiVerdictStatus] to a [VerdictStatus] representation. Requires an ongoing transaction. + * Converts this [ApiVerdictStatus] to a [DbVerdictStatus] representation. Requires an ongoing transaction. * - * @return [VerdictStatus] + * @return [DbVerdictStatus] */ - fun toVerdictStatus(): VerdictStatus = when(this) { - CORRECT -> VerdictStatus.CORRECT - WRONG -> VerdictStatus.WRONG - INDETERMINATE -> VerdictStatus.INDETERMINATE - UNDECIDABLE -> VerdictStatus.UNDECIDABLE + fun toDb(): DbVerdictStatus = when(this) { + CORRECT -> DbVerdictStatus.CORRECT + WRONG -> DbVerdictStatus.WRONG + INDETERMINATE -> DbVerdictStatus.INDETERMINATE + UNDECIDABLE -> DbVerdictStatus.UNDECIDABLE } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt deleted file mode 100644 index 3e570bafa..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictType.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.submissions.VerdictType - -/** - * The RESTful API equivalent for the type of a [VerdictType] - * - * @see ApiVerdict - * @author Ralph Gasser - * @version 1.0.0 - */ -enum class ApiVerdictType(val type: VerdictType) { - ITEM(VerdictType.ITEM), - TEMPORAL(VerdictType.TEMPORAL), - TEXT(VerdictType.TEXT) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt index da4351a7a..8a914c10a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.judgement -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbVerdictStatus /** * * @author Ralph Gasser * @version 1.0 */ -data class ApiVote(val verdict: VerdictStatus) +data class ApiVote(val verdict: DbVerdictStatus) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt index 70b13a5df..4f9debf28 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt @@ -1,5 +1,5 @@ package dev.dres.api.rest.types.status -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbVerdictStatus -data class SuccessfulSubmissionsStatus(val submission: VerdictStatus, val description: String) : AbstractStatus(status = true) \ No newline at end of file +data class SuccessfulSubmissionsStatus(val submission: DbVerdictStatus, val description: String) : AbstractStatus(status = true) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt index 5fb8ea15e..c8ff9df77 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt @@ -1,8 +1,8 @@ package dev.dres.api.rest.types.users import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.data.model.admin.Role -import dev.dres.data.model.media.MediaType +import dev.dres.data.model.admin.DbRole +import dev.dres.data.model.media.DbMediaType import io.javalin.security.RouteRole /** @@ -15,15 +15,15 @@ enum class ApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN; /** - * Converts this [ApiMediaType] to a [MediaType] representation. Requires an ongoing transaction. + * Converts this [ApiMediaType] to a [DbMediaType] representation. Requires an ongoing transaction. * - * @return [MediaType] + * @return [DbMediaType] */ - fun toRole(): Role? = when(this) { + fun toDb(): DbRole? = when(this) { ANYONE -> null - VIEWER -> Role.VIEWER - PARTICIPANT -> Role.PARTICIPANT - JUDGE -> Role.JUDGE - ADMIN -> Role.ADMIN + VIEWER -> DbRole.VIEWER + PARTICIPANT -> DbRole.PARTICIPANT + JUDGE -> DbRole.JUDGE + ADMIN -> DbRole.ADMIN } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt index 6c0525e1d..119ac9411 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiUser.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.types.users -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId /** - * A RESTful API representation of a [User] + * A RESTful API representation of a [DbUser] * * @author Loris Sauter * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt index da98dca11..abf537f1d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/PersistentEntity.kt @@ -1,6 +1,5 @@ package dev.dres.data.model -import dev.dres.data.model.template.task.TaskTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEntity import kotlinx.dnq.XdNaturalEntityType diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt similarity index 66% rename from backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt rename to backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt index 7560a9aa4..68ec152df 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Role.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt @@ -7,27 +7,27 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * The [Role]s currently supported by DRES. + * The [DbRole]s currently supported by DRES. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class Role(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbRole(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val VIEWER by enumField { description = "VIEWER" } val PARTICIPANT by enumField { description = "PARTICIPANT" } val JUDGE by enumField { description = "JUDGE" } val ADMIN by enumField { description = "ADMIN" } /** - * Returns a list of all [Role] values. + * Returns a list of all [DbRole] values. * - * @return List of all [Role] values. + * @return List of all [DbRole] values. */ fun values() = listOf(VIEWER, PARTICIPANT, JUDGE, ADMIN) /** - * Parses a [Role] instance from a [String]. + * Parses a [DbRole] instance from a [String]. */ fun parse(string: String) = when(string.uppercase()) { "VIEWER" -> VIEWER @@ -38,16 +38,16 @@ class Role(entity: Entity) : XdEnumEntity(entity) { } } - /** Name / description of the [Role]. */ + /** Name / description of the [DbRole]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [Role] to a RESTful API representation [ApiRole]. + * Converts this [DbRole] to a RESTful API representation [ApiRole]. * * @return [ApiRole] */ - fun toApi(): ApiRole = ApiRole.values().find { it.toRole() == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") + fun toApi(): ApiRole = ApiRole.values().find { it.toDb() == this } ?: throw IllegalStateException("Role ${this.description} is not supported.") override fun toString() = this.description } diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/DbUser.kt similarity index 71% rename from backend/src/main/kotlin/dev/dres/data/model/admin/User.kt rename to backend/src/main/kotlin/dev/dres/data/model/admin/DbUser.kt index 3aea2d7e6..f8619d7df 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/User.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/DbUser.kt @@ -11,13 +11,13 @@ import kotlinx.dnq.xdRequiredStringProp typealias UserId = String /** - * A [User] in the DRES user management model. + * A [DbUser] in the DRES user management model. * * @author Ralph Gasser * @version 2.0.0 */ -class User(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() { +class DbUser(entity: Entity): PersistentEntity(entity) { + companion object : XdNaturalEntityType() { /** The minimum length of a password. */ const val MIN_LENGTH_PASSWORD = 6 @@ -25,29 +25,29 @@ class User(entity: Entity): PersistentEntity(entity) { const val MIN_LENGTH_USERNAME = 4 } - /** The [UserId] of this [User]. */ + /** The [UserId] of this [DbUser]. */ var userId: UserId get() = this.id set(value) { this.id = value } - /** The name held by this [User]. Must be unique!*/ + /** The name held by this [DbUser]. Must be unique!*/ var username by xdRequiredStringProp(unique = true, trimmed = true) { length(MIN_LENGTH_USERNAME, 16, "Username must consist of between 4 and 16 characters")} - /** The password held by this [User]. */ + /** The password held by this [DbUser]. */ var password by xdRequiredStringProp(unique = false, trimmed = true) - /** The [Role] of this [User]. */ - var role by xdLink1(Role) + /** The [DbRole] of this [DbUser]. */ + var role by xdLink1(DbRole) /** - * The [Password.Hashed] held by this [User]. + * The [Password.Hashed] held by this [DbUser]. */ fun hashedPassword() = Password.Hashed(this.password) override fun toString(): String = "User(id=$id, username=${username}, role=$role)" /** - * Converts this [User] to a RESTful API representation [ApiUser]. + * Converts this [DbUser] to a RESTful API representation [ApiUser]. * * This is a convenience method and requires an active transaction context. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt index 68ec78c88..15d7b9567 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/Password.kt @@ -14,7 +14,7 @@ import org.mindrot.jbcrypt.BCrypt sealed class Password(val password: String) { class Plain(password: String): Password(password) { init { - require (password.length >= User.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${User.MIN_LENGTH_PASSWORD} characters long" } + require (password.length >= DbUser.MIN_LENGTH_PASSWORD) { "Password is too short. Must be at least ${DbUser.MIN_LENGTH_PASSWORD} characters long" } } fun hash(): Hashed = Hashed(BCrypt.hashpw(this.password, BCrypt.gensalt())) val length: Int diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt deleted file mode 100644 index 24048e055..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogEntry.kt +++ /dev/null @@ -1,55 +0,0 @@ -package dev.dres.data.model.audit - -import dev.dres.api.rest.types.audit.* -import dev.dres.data.model.PersistentEntity -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdNaturalEntityType -import kotlinx.dnq.xdLink1 -import kotlinx.dnq.xdRequiredDateTimeProp -import kotlinx.dnq.xdStringProp - -/** - * - */ -class AuditLogEntry(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() - - /** The type of [AuditLogEntry]. */ - var type by xdLink1(AuditLogType) - - /** The [AuditLogSource] that generated this [AuditLogEntry]. */ - var source by xdLink1(AuditLogSource) - - /** The timestamp of this [AuditLogEntry]. */ - var timestamp by xdRequiredDateTimeProp() - - /** The ID of the evaluation this [AuditLogEntry] belongs to. */ - var evaluationId by xdStringProp() - - /** The ID of the task this [AuditLogEntry] belongs to. */ - var taskId by xdStringProp() - - /** The ID of the submission this [AuditLogEntry] belongs to. Only valid if [type] is equal to [AuditLogType.SUBMISSION], [AuditLogType.SUBMISSION_VALIDATION] or [AuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ - var submissionId by xdStringProp() - - /** The user ID of the user who generated this [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ - var userId by xdStringProp() - - /** The session ID of the [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ - var session by xdStringProp() - - /** The source address of the [AuditLogEntry]. Only set if [source] is equal to [AuditLogSource.REST]. */ - var address by xdStringProp() - - /** Descriptive metadata for this [AuditLogEntry]. */ - var description by xdStringProp() - - /** - * Converts this [AuditLogEntry] to a RESTful API representation [ApiAuditLogEntry]. - * - * This is a convenience method and requires an active transaction context. - * - * @return [ApiAuditLogEntry] - */ - fun toApi(): ApiAuditLogEntry = ApiAuditLogEntry(this.id, this.type.toApi(), this.source.toApi(), this.timestamp.millis, this.evaluationId, this.userId, this.submissionId, this.session, this.address, this.description) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt new file mode 100644 index 000000000..aefc73ead --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt @@ -0,0 +1,55 @@ +package dev.dres.data.model.audit + +import dev.dres.api.rest.types.audit.* +import dev.dres.data.model.PersistentEntity +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.xdLink1 +import kotlinx.dnq.xdRequiredDateTimeProp +import kotlinx.dnq.xdStringProp + +/** + * + */ +class DbAuditLogEntry(entity: Entity): PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The type of [DbAuditLogEntry]. */ + var type by xdLink1(DbAuditLogType) + + /** The [DbAuditLogSource] that generated this [DbAuditLogEntry]. */ + var source by xdLink1(DbAuditLogSource) + + /** The timestamp of this [DbAuditLogEntry]. */ + var timestamp by xdRequiredDateTimeProp() + + /** The ID of the evaluation this [DbAuditLogEntry] belongs to. */ + var evaluationId by xdStringProp() + + /** The ID of the task this [DbAuditLogEntry] belongs to. */ + var taskId by xdStringProp() + + /** The ID of the submission this [DbAuditLogEntry] belongs to. Only valid if [type] is equal to [DbAuditLogType.SUBMISSION], [DbAuditLogType.SUBMISSION_VALIDATION] or [DbAuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ + var submissionId by xdStringProp() + + /** The user ID of the user who generated this [DbAuditLogEntry]. Only set if [source] is equal to [DbAuditLogSource.REST]. */ + var userId by xdStringProp() + + /** The session ID of the [DbAuditLogEntry]. Only set if [source] is equal to [DbAuditLogSource.REST]. */ + var session by xdStringProp() + + /** The source address of the [DbAuditLogEntry]. Only set if [source] is equal to [DbAuditLogSource.REST]. */ + var address by xdStringProp() + + /** Descriptive metadata for this [DbAuditLogEntry]. */ + var description by xdStringProp() + + /** + * Converts this [DbAuditLogEntry] to a RESTful API representation [ApiAuditLogEntry]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiAuditLogEntry] + */ + fun toApi(): ApiAuditLogEntry = ApiAuditLogEntry(this.id, this.type.toApi(), this.source.toApi(), this.timestamp.millis, this.evaluationId, this.userId, this.submissionId, this.session, this.address, this.description) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt similarity index 65% rename from backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt rename to backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt index b0a846e03..debe5139f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogSource.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt @@ -7,27 +7,27 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * Enumeration of the source of a [AuditLogEntry]. + * Enumeration of the source of a [DbAuditLogEntry]. * * @author Ralph Gasser * @version 1.0.0 */ -class AuditLogSource(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbAuditLogSource(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val REST by enumField { description = "REST" } val CLI by enumField { description = "CLI" } val INTERNAL by enumField { description = "INTERNAL" } } - /** Name / description of the [AuditLogType]. */ + /** Name / description of the [DbAuditLogType]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [AuditLogSource] to a RESTful API representation [ApiAuditLogSource]. + * Converts this [DbAuditLogSource] to a RESTful API representation [ApiAuditLogSource]. * * @return [ApiAuditLogSource] */ fun toApi(): ApiAuditLogSource - = ApiAuditLogSource.values().find { it.source == this } ?: throw IllegalStateException("Audit log source ${this.description} is not supported.") + = ApiAuditLogSource.values().find { it.toDb() == this } ?: throw IllegalStateException("Audit log source ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt similarity index 72% rename from backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt rename to backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt index 1b8f81a14..0b5370a46 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/AuditLogType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt @@ -8,13 +8,13 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * The [AuditLogEntry] types currently supported by DRES. + * The [DbAuditLogEntry] types currently supported by DRES. * * @author Luca Rossetto * @version 2.0.0 */ -class AuditLogType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbAuditLogType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val COMPETITION_START by enumField { description = "COMPETITION_START" } val COMPETITION_END by enumField { description = "COMPETITION_END" } val TASK_START by enumField { description = "TASK_START" } @@ -29,15 +29,17 @@ class AuditLogType(entity: Entity) : XdEnumEntity(entity) { val SUBMISSION_STATUS_OVERWRITE by enumField { description = "SUBMISSION_STATUS_OVERWRITE" } } - /** Name / description of the [AuditLogType]. */ + /** Name / description of the [DbAuditLogType]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [AuditLogSource] to a RESTful API representation [ApiAuditLogSource]. + * Converts this [DbAuditLogSource] to a RESTful API representation [ApiAuditLogSource]. * * @return [ApiAuditLogSource] */ fun toApi(): ApiAuditLogType - = ApiAuditLogType.values().find { it.type == this } ?: throw IllegalStateException("Audit log type ${this.description} is not supported.") + = ApiAuditLogType.values().find { it.toDb() == this } ?: throw IllegalStateException("Audit log type ${this.description} is not supported.") + + override fun toString() = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt similarity index 51% rename from backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt index f65a7c970..8cc5a35f5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt @@ -12,19 +12,19 @@ typealias CollectionId = String * @author Ralph Gasser * @version 2.0.0 */ -class MediaCollection(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() - /** The name of this [MediaItem]. */ +class DbMediaCollection(entity: Entity): PersistentEntity(entity) { + companion object : XdNaturalEntityType() + /** The name of this [DbMediaItem]. */ var name: String by xdRequiredStringProp(unique = true, trimmed = false) - /** The path to the folder containing [MediaItem]s in this [MediaCollection]. */ + /** The path to the folder containing [DbMediaItem]s in this [DbMediaCollection]. */ var path: String by xdRequiredStringProp(unique = true, trimmed = false) - /** A textual description of this [MediaCollection]. */ + /** A textual description of this [DbMediaCollection]. */ var description: String? by xdStringProp(trimmed = false) - /** A list of [MediaItem]s in this [MediaCollection]. */ - val items by xdChildren0_N(MediaItem::collection) + /** A list of [DbMediaItem]s in this [DbMediaCollection]. */ + val items by xdChildren0_N(DbMediaItem::collection) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt similarity index 53% rename from backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt index 9437e0538..8af4988fa 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt @@ -16,37 +16,37 @@ typealias MediaId = String * @author Ralph Gasser * @version 2.0.0 */ -class MediaItem(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() { - /** Combination of [MediaItem] name / competition must be unique. */ +class DbMediaItem(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() { + /** Combination of [DbMediaItem] name / competition must be unique. */ override val compositeIndices = listOf( - listOf(MediaItem::name, MediaItem::collection) + listOf(DbMediaItem::name, DbMediaItem::collection) ) } - /** The name of this [MediaItem]. */ + /** The name of this [DbMediaItem]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) - /** The [MediaType] of this [MediaItem]. */ - var type by xdLink1(MediaType) + /** The [DbMediaType] of this [DbMediaItem]. */ + var type by xdLink1(DbMediaType) - /** The location of this [MediaItem] on disk. */ + /** The location of this [DbMediaItem] on disk. */ var location by xdRequiredStringProp(unique = false, trimmed = false) - /** Frame rate of the [MediaItem] in frames per second. Null for type without temporal development. */ - var fps by xdNullableFloatProp() { requireIf { this.type == MediaType.VIDEO } } + /** Frame rate of the [DbMediaItem] in frames per second. Null for type without temporal development. */ + var fps by xdNullableFloatProp() { requireIf { this.type == DbMediaType.VIDEO } } - /** Duration of the [MediaItem] in milliseconds. Null for type without temporal development. */ - var durationMs by xdNullableLongProp() { requireIf { this.type == MediaType.VIDEO } } + /** Duration of the [DbMediaItem] in milliseconds. Null for type without temporal development. */ + var durationMs by xdNullableLongProp() { requireIf { this.type == DbMediaType.VIDEO } } - /** The [MediaCollection] this [MediaItem] belongs to. */ - var collection: MediaCollection by xdParent(MediaCollection::items) + /** The [DbMediaCollection] this [DbMediaItem] belongs to. */ + var collection: DbMediaCollection by xdParent(DbMediaCollection::items) - /** List of [MediaSegment] that this [MediaItem] contains. */ - val segments by xdChildren0_N(MediaSegment::item) + /** List of [DbMediaSegment] that this [DbMediaItem] contains. */ + val segments by xdChildren0_N(DbMediaSegment::item) /** - * Generates a [ApiMediaItem] this [MediaItem] and returns it. + * Generates a [ApiMediaItem] this [DbMediaItem] and returns it. * * @return [ApiMediaItem] */ @@ -54,14 +54,14 @@ class MediaItem(entity: Entity) : PersistentEntity(entity) { = ApiMediaItem(this.id, this.name, this.type.toApi(), this.collection.id, this.location, this.durationMs, this.fps) /** - * Returns the [Path] to the original file for this [MediaItem]. + * Returns the [Path] to the original file for this [DbMediaItem]. * * @return [Path] */ fun pathToOriginal(): Path = Paths.get(this.collection.path, this.location) /** - * Returns the [Path] to the cached file for this [MediaItem]. + * Returns the [Path] to the cached file for this [DbMediaItem]. * * @param start * @param end diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt new file mode 100644 index 000000000..916f1b028 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt @@ -0,0 +1,39 @@ +package dev.dres.data.model.media + +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.min + +/** + * A segment of a [DbMediaItem] as mostly used by items that exhibit temporal progression. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +class DbMediaSegment(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() { + /** Combination of [DbMediaSegment] name / item must be unique. */ + override val compositeIndices = listOf( + listOf(DbMediaSegment::name, DbMediaSegment::item) + ) + } + + /** The name of this [DbMediaSegment]. */ + var name by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [DbMediaType] of this [DbMediaItem]. */ + var item: DbMediaItem by xdParent(DbMediaItem::segments) + + /** The start frame number of this [DbMediaSegment]. */ + var start by xdRequiredIntProp { min(0L) } + + /** The end frame number of this [DbMediaSegment]. */ + var end by xdRequiredIntProp { min(0L) } + + /** Returns the [range] of this [DbMediaSegment] as [TemporalRange]. */ + val range: TemporalRange + get() = TemporalRange(TemporalPoint.Frame(this.start, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.end, this.item.fps ?: 1.0f)) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt similarity index 69% rename from backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt rename to backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt index 1d1e00fe7..a36090768 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt @@ -12,8 +12,8 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 1.0.0 */ -class MediaType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbMediaType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val IMAGE by enumField { description = "IMAGE"; suffix = "mp4"; } val VIDEO by enumField { description = "VIDEO"; suffix = "jpg"; } val TEXT by enumField { description = "TEXT"; suffix = "txt"; } @@ -22,16 +22,16 @@ class MediaType(entity: Entity) : XdEnumEntity(entity) { var description by xdRequiredStringProp(unique = true) private set - /** The default suffix used for this [MediaType]. */ + /** The default suffix used for this [DbMediaType]. */ var suffix by xdRequiredStringProp(unique = true) /** - * Converts this [MediaType] to a RESTful API representation [ApiMediaType]. + * Converts this [DbMediaType] to a RESTful API representation [ApiMediaType]. * * This is a convenience method and requires an active transaction context. */ fun toApi(): ApiMediaType - = ApiMediaType.values().find { it.toMediaType() == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") + = ApiMediaType.values().find { it.toDb() == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") override fun toString(): String = this.description } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt deleted file mode 100644 index 6764bf19d..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaSegment.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.dres.data.model.media - -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* -import kotlinx.dnq.simple.min - -/** - * A segment of a [MediaItem] as mostly used by items that exhibit temporal progression. - * - * @author Ralph Gasser - * @version 2.0.0 - */ -class MediaSegment(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() { - /** Combination of [MediaSegment] name / item must be unique. */ - override val compositeIndices = listOf( - listOf(MediaSegment::name, MediaSegment::item) - ) - } - - /** The name of this [MediaSegment]. */ - var name by xdRequiredStringProp(unique = false, trimmed = false) - - /** The [MediaType] of this [MediaItem]. */ - var item: MediaItem by xdParent(MediaItem::segments) - - /** The start frame number of this [MediaSegment]. */ - var start by xdRequiredIntProp { min(0L) } - - /** The end frame number of this [MediaSegment]. */ - var end by xdRequiredIntProp { min(0L) } - - /** Returns the [range] of this [MediaSegment] as [TemporalRange]. */ - val range: TemporalRange - get() = TemporalRange(TemporalPoint.Frame(this.start, this.item.fps ?: 1.0f), TemporalPoint.Frame(this.end, this.item.fps ?: 1.0f)) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt index f739f2fca..592af22c1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt @@ -5,7 +5,7 @@ import java.time.Duration /** - * Interface for [MediaItem]s that allow for playback and thus have a notion of duration and frames. + * Interface for [DbMediaItem]s that allow for playback and thus have a notion of duration and frames. * * @author Ralph Gasser * @version 1.0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt index b21c99a47..278682db5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt @@ -1,11 +1,11 @@ package dev.dres.data.model.media.time -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.PlayableMediaItem import java.lang.IllegalArgumentException /** - * Notion of a [TemporalPoint] within a [MediaItem] that exhibits temporal development (e.g. [VideoItem]). + * Notion of a [TemporalPoint] within a [DbMediaItem] that exhibits temporal development (e.g. [VideoItem]). * * @version 2.2.0 * @author Luca Rossetto & Ralph Gasser diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 91f6e27f8..08830c8fe 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.run import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.Run import kotlinx.dnq.util.findById @@ -11,7 +11,7 @@ import kotlinx.dnq.util.findById * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { +abstract class AbstractEvaluation(evaluation: DbEvaluation): EvaluationRun { /** The internal [xdId] of this [AbstractEvaluation]. * @@ -31,17 +31,17 @@ abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { */ override val name: String = evaluation.name - /** The [EvaluationTemplate] used by this [AbstractEvaluation]. + /** The [DbEvaluationTemplate] used by this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is stored in memory. */ - override val description: EvaluationTemplate = evaluation.template + override val description: DbEvaluationTemplate = evaluation.template /** - * Accessor for the [Evaluation] underpinning this [AbstractEvaluation] + * Accessor for the [DbEvaluation] underpinning this [AbstractEvaluation] */ - protected val evaluation: Evaluation - get() = Evaluation.findById(this.xdId) + protected val evaluation: DbEvaluation + get() = DbEvaluation.findById(this.xdId) /** Timestamp of when this [AbstractEvaluation] was started. */ override var started: Long @@ -57,7 +57,7 @@ abstract class AbstractEvaluation(evaluation: Evaluation): EvaluationRun { this.evaluation.ended = value } - /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ + /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ override var participantCanView: Boolean get() = this.evaluation.participantCanView set(value) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index bc01b6577..a5954206a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -1,11 +1,11 @@ package dev.dres.data.model.run -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.options.TargetOption +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.template.task.options.DbTargetOption import dev.dres.data.model.template.team.TeamAggregatorImpl import dev.dres.data.model.template.team.TeamGroupId import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.validation.MediaItemsSubmissionValidator import dev.dres.run.validation.TemporalOverlapSubmissionValidator import dev.dres.run.validation.TextValidator @@ -17,15 +17,15 @@ import dev.dres.run.validation.judged.ItemRange import kotlinx.dnq.query.* /** - * An abstract [Task] implementation for interactive [Task], i.e. [Task]s that rely on human interaction, such as [Submission]s + * An abstract [DbTask] implementation for interactive [DbTask], i.e. [DbTask]s that rely on human interaction, such as [DbSubmission]s * * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -abstract class AbstractInteractiveTask(task: Task): AbstractTask(task) { +abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { - /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ + /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ abstract var duration: Long /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ @@ -33,24 +33,24 @@ abstract class AbstractInteractiveTask(task: Task): AbstractTask(task) { this.competition.description.teamsGroups.asSequence().associate { it.id to it.newAggregator() } } - /** The [SubmissionValidator] used to validate [Submission]s. */ + /** The [SubmissionValidator] used to validate [DbSubmission]s. */ final override val validator: SubmissionValidator init { this.validator = when (val targetOption = this.template.taskGroup.type.target) { - TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) - TargetOption.MEDIA_SEGMENT -> { + DbTargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + DbTargetOption.MEDIA_SEGMENT -> { val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) } - TargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) - TargetOption.JUDGEMENT -> { + DbTargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + DbTargetOption.JUDGEMENT -> { val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() BasicJudgementValidator(knownCorrectRanges = knownRanges) } - TargetOption.VOTE -> { + DbTargetOption.VOTE -> { val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index ebf26df44..dd47d0158 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run -import dev.dres.data.model.template.task.options.TargetOption +import dev.dres.data.model.template.task.options.DbTargetOption import dev.dres.run.validation.MediaItemsSubmissionValidator import dev.dres.run.validation.TemporalOverlapSubmissionValidator import dev.dres.run.validation.TextValidator @@ -11,25 +11,25 @@ import dev.dres.run.validation.judged.ItemRange import kotlinx.dnq.query.* /** - * An abstract [Task] implementation for non-interactive [Task], i.e., [Task]s that do not rely on human interaction and simply process input data in batches + * An abstract [DbTask] implementation for non-interactive [DbTask], i.e., [DbTask]s that do not rely on human interaction and simply process input data in batches * * @author Luca Rossetto * @author Ralph Gasser * @version 2.0.0 */ -abstract class AbstractNonInteractiveTask(task: Task): AbstractTask(task) { +abstract class AbstractNonInteractiveTask(task: DbTask): AbstractTask(task) { /** The [SubmissionValidator] used by this [AbstractNonInteractiveTask]. */ final override val validator: SubmissionValidator init { this.validator = when (val targetOption = this.template.taskGroup.type.target) { - TargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) - TargetOption.MEDIA_SEGMENT -> { + DbTargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + DbTargetOption.MEDIA_SEGMENT -> { val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) } - TargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) - TargetOption.JUDGEMENT -> { + DbTargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + DbTargetOption.JUDGEMENT -> { val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index c7012155e..919fc31c7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -1,9 +1,9 @@ package dev.dres.data.model.run -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.interfaces.SubmissionValidator @@ -16,7 +16,7 @@ import java.util.concurrent.ConcurrentLinkedQueue * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractTask(task: Task): TaskRun { +abstract class AbstractTask(task: DbTask): TaskRun { /** The internal [xdId] of this [AbstractEvaluation]. * @@ -25,10 +25,10 @@ abstract class AbstractTask(task: Task): TaskRun { protected val xdId = task.xdId /** - * Accessor for the [Task] underpinning this [AbstractTask] + * Accessor for the [DbTask] underpinning this [AbstractTask] */ - protected val task: Task - get() = Task.findById(this.xdId) + protected val task: DbTask + get() = DbTask.findById(this.xdId) /** * The [EvaluationId] of this [AbstractTask]. @@ -37,8 +37,8 @@ abstract class AbstractTask(task: Task): TaskRun { */ final override val id: EvaluationId = this.task.id - /** List of [Submission]s* registered for this [AbstractTask]. */ - protected val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() + /** List of [DbSubmission]s* registered for this [AbstractTask]. */ + protected val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() /** Timestamp of when this [AbstractTask] was started. */ final override var started: Long @@ -54,18 +54,18 @@ abstract class AbstractTask(task: Task): TaskRun { this.task.ended = value } - /** Reference to the [TaskTemplate] describing this [AbstractTask]. */ - final override val template: TaskTemplate + /** Reference to the [DbTaskTemplate] describing this [AbstractTask]. */ + final override val template: DbTaskTemplate get() = this.task.template @Volatile final override var status: TaskStatus = TaskStatus.CREATED protected set - /** The [SubmissionFilter] used to filter [Submission]s. */ + /** The [SubmissionFilter] used to filter [DbSubmission]s. */ abstract val filter: SubmissionFilter - /** The [SubmissionValidator] used to validate [Submission]s. */ + /** The [SubmissionValidator] used to validate [DbSubmission]s. */ abstract val validator: SubmissionValidator /** @@ -114,13 +114,13 @@ abstract class AbstractTask(task: Task): TaskRun { this.status = TaskStatus.RUNNING } - /** Returns a [List] of all [Submission]s held by this [AbstractTask]. */ + /** Returns a [List] of all [DbSubmission]s held by this [AbstractTask]. */ override fun getSubmissions() = this.submissions.toList() /** - * Adds a new [Submission] to this [AbstractInteractiveTask]. + * Adds a new [DbSubmission] to this [AbstractInteractiveTask]. * - * @param submission The [Submission] to append. + * @param submission The [DbSubmission] to append. */ - abstract fun postSubmission(submission: Submission) + abstract fun postSubmission(submission: DbSubmission) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt similarity index 58% rename from backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 92b248cdb..3b2765c80 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Evaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -2,7 +2,7 @@ package dev.dres.data.model.run import dev.dres.api.rest.types.evaluation.ApiEvaluation import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -11,38 +11,38 @@ import kotlinx.dnq.query.asSequence typealias TaskId = String /** - * Represents a [Evaluation], i.e., a concrete instance of a [EvaluationTemplate], as executed by DRES. + * Represents a [DbEvaluation], i.e., a concrete instance of a [DbEvaluationTemplate], as executed by DRES. * * @author Ralph Gasser * @version 1.0.0 */ -class Evaluation(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() +class DbEvaluation(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() - /** The [EvaluationId] of this [Evaluation]. */ + /** The [EvaluationId] of this [DbEvaluation]. */ var evaluationId: EvaluationId get() = this.id set(value) { this.id = value } - /** The name held by this [Evaluation]. Must be unique!*/ + /** The name held by this [DbEvaluation]. Must be unique!*/ var name by xdRequiredStringProp(unique = true, trimmed = true) - /** The [EvaluationType] of this [Evaluation]. */ - var type by xdLink1(EvaluationType) + /** The [DbEvaluationType] of this [DbEvaluation]. */ + var type by xdLink1(DbEvaluationType) - /** The [EvaluationTemplate] backing this [Evaluation]. */ - var template by xdLink1(EvaluationTemplate) + /** The [DbEvaluationTemplate] backing this [DbEvaluation]. */ + var template by xdLink1(DbEvaluationTemplate) - /** Timestamp of when this [Evaluation] started. */ + /** Timestamp of when this [DbEvaluation] started. */ var started by xdRequiredLongProp() - /** Timestamp of when this [Evaluation] ended. */ + /** Timestamp of when this [DbEvaluation] ended. */ var ended by xdNullableLongProp() - /** The [Task]s that belong to this [Evaluation]. */ - val tasks by xdChildren0_N(Task::evaluation) + /** The [DbTask]s that belong to this [DbEvaluation]. */ + val tasks by xdChildren0_N(DbTask::evaluation) - /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ + /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ var participantCanView by xdBooleanProp() /** Flag indicating that tasks should be shuffled. is only used for asynchronous runs */ @@ -55,7 +55,7 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { var limitSubmissionPreviews by xdIntProp() /** - * Converts this [Evaluation] to a RESTful API representation [ApiEvaluation]. + * Converts this [DbEvaluation] to a RESTful API representation [ApiEvaluation]. * * This is a convenience method and requires an active transaction context. * @@ -72,14 +72,14 @@ class Evaluation(entity: Entity) : PersistentEntity(entity) { ) /** - * Generates and returns an [EvaluationRun] instance for this [Evaluation]. + * Generates and returns an [EvaluationRun] instance for this [DbEvaluation]. * * @return [EvaluationRun] */ fun toRun(): EvaluationRun = when(this.type) { - EvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) - EvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this, emptyMap()) /* TODO: Not sure about semantics here. */ - EvaluationType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) + DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this, emptyMap()) /* TODO: Not sure about semantics here. */ + DbEvaluationType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) else -> throw IllegalArgumentException("Unsupported run type ${this.type.description}. This is a programmer's error!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt similarity index 54% rename from backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt index 6af1892ed..d21f723e7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/EvaluationType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt @@ -1,19 +1,19 @@ package dev.dres.data.model.run -import dev.dres.api.rest.types.evaluation.ApiRunType +import dev.dres.api.rest.types.evaluation.ApiEvaluationType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * Enumeration of the type of [Evaluation]. + * Enumeration of the type of [DbEvaluation]. * * @author Ralph Gasser * @version 1.0.0 */ -class EvaluationType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbEvaluationType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val INTERACTIVE_SYNCHRONOUS by enumField { description = "INTERACTIVE_SYNCHRONOUS" } val INTERACTIVE_ASYNCHRONOUS by enumField { description = "INTERACTIVE_ASYNCHRONOUS" } val NON_INTERACTIVE by enumField { description = "NON_INTERACTIVE" } @@ -23,9 +23,9 @@ class EvaluationType(entity: Entity) : XdEnumEntity(entity) { private set /** - * Converts this [EvaluationType] to a RESTful API representation [ApiRunType]. + * Converts this [DbEvaluationType] to a RESTful API representation [ApiEvaluationType]. * - * @return [ApiRunType] + * @return [ApiEvaluationType] */ - fun toApi() = ApiRunType.values().find { it.type == this } ?: throw IllegalStateException("Run type ${this.description} is not supported.") + fun toApi() = ApiEvaluationType.values().find { it.toDb() == this } ?: throw IllegalStateException("Run type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt new file mode 100644 index 000000000..1aceaeccc --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -0,0 +1,61 @@ +package dev.dres.data.model.run + +import dev.dres.api.rest.types.evaluation.ApiTask +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.template.team.DbTeam +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence + +typealias EvaluationId = String + +/** + * Represents a [DbTask], i.e., a concrete instance of a [DbTaskTemplate], as executed by DRES. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class DbTask(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The [EvaluationId] of this [DbTask]. */ + var taskId: EvaluationId + get() = this.id + set(value) { this.id = value } + + /** Timestamp of when this [DbEvaluation] started. */ + var started by xdRequiredLongProp() + + /** Timestamp of when this [DbEvaluation] ended. */ + var ended by xdNullableLongProp() + + /** The [DbTaskTemplate] this [DbTask] is an instance of. */ + var template by xdLink1(DbTaskTemplate) + + /** Link to a [DbTeam] this [DbTask] was created for. Can be NULL!*/ + var team by xdLink0_1(DbTeam) + + /** The [DbEvaluation] this [DbTask] belongs to. */ + var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) + + /** List of [DbSubmission]s received by this [DbTask]. */ + val submissions by xdChildren0_N(DbAnswerSet::task) + + /** + * Converts this [DbTask] to a RESTful API representation [ApiTask]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiTask] + */ + fun toApi(): ApiTask = ApiTask( + taskId = this.taskId, + templateId = this.template.id, + started = this.started, + ended = this.ended, + submissions = this.submissions.asSequence().map { it.toApi() }.toList() + ) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 33638f601..11125874d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -1,11 +1,11 @@ package dev.dres.data.model.run -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionFilter @@ -15,15 +15,15 @@ import java.util.* import java.util.concurrent.ConcurrentHashMap /** - * Represents a concrete, interactive and asynchronous [Run] of a [EvaluationTemplate]. + * Represents a concrete, interactive and asynchronous [Run] of a [DbEvaluationTemplate]. * * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [IATaskRun]s and access the current [IATaskRun]. * */ -class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val permutation: Map>) : AbstractEvaluation(evaluation) { +class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val permutation: Map>) : AbstractEvaluation(evaluation) { companion object { - fun generatePermutation(description: EvaluationTemplate, shuffle: Boolean): Map> = + fun generatePermutation(description: DbEvaluationTemplate, shuffle: Boolean): Map> = if (shuffle) { description.teams.asSequence().associate { it.id to makeLoop(description.tasks.size()) } } else { @@ -81,15 +81,15 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm } /** - * Internal constructor to create an [InteractiveAsynchronousEvaluation] from an [EvaluationTemplate]. + * Internal constructor to create an [InteractiveAsynchronousEvaluation] from an [DbEvaluationTemplate]. * Requires a transaction context! * * @param name The name of the new [InteractiveSynchronousEvaluation] * @param shuffle Flag indicating if [IATaskRun]s should be shuffled. - * @param template The [EvaluationTemplate] + * @param template The [DbEvaluationTemplate] */ - constructor(name: String, shuffle: Boolean, template: EvaluationTemplate) : this(Evaluation.new { - this.type = EvaluationType.INTERACTIVE_ASYNCHRONOUS + constructor(name: String, shuffle: Boolean, template: DbEvaluationTemplate) : this(DbEvaluation.new { + this.type = DbEvaluationType.INTERACTIVE_ASYNCHRONOUS this.name = name this.template = template this.shuffleTasks = shuffle @@ -103,8 +103,8 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm /** A [ConcurrentHashMap] that maps a list of [IATaskRun]s to the [TeamId]s they belong to.*/ private val tasksMap = ConcurrentHashMap>() - /** Tracks the current [TaskTemplate] per [TeamId]. */ - private val navigationMap: MutableMap = HashMap() + /** Tracks the current [DbTaskTemplate] per [TeamId]. */ + private val navigationMap: MutableMap = HashMap() init { /* TODO: Reconstruct TaskRuns from stored data. */ @@ -114,7 +114,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).single() } - fun currentTaskDescription(teamId: TeamId): TaskTemplate = + fun currentTaskDescription(teamId: TeamId): DbTaskTemplate = navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) init { @@ -155,15 +155,15 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm /** * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousEvaluation]. */ - inner class IATaskRun internal constructor(task: Task, val teamId: TeamId) : AbstractInteractiveTask(task) { + inner class IATaskRun internal constructor(task: DbTask, val teamId: TeamId) : AbstractInteractiveTask(task) { /** - * Constructor used to generate an [IATaskRun] from a [TaskTemplate]. + * Constructor used to generate an [IATaskRun] from a [DbTaskTemplate]. * - * @param template [TaskTemplate] to generate [IATaskRun] from. + * @param template [DbTaskTemplate] to generate [IATaskRun] from. * @param teamId The [TeamId] this [IATaskRun] is created for. */ - internal constructor(template: TaskTemplate, teamId: TeamId) : this(Task.new { + internal constructor(template: DbTaskTemplate, teamId: TeamId) : this(DbTask.new { this.evaluation = this@InteractiveAsynchronousEvaluation.evaluation this.template = template this.team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId }.singleOrNull() @@ -185,7 +185,7 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") - /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ + /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration init { @@ -198,13 +198,13 @@ class InteractiveAsynchronousEvaluation(evaluation: Evaluation, private val perm } /** - * Adds a [Submission] to this [InteractiveAsynchronousEvaluation.IATaskRun]. + * Adds a [DbSubmission] to this [InteractiveAsynchronousEvaluation.IATaskRun]. * - * @param submission The [Submission] to add. - * @throws IllegalArgumentException If [Submission] could not be added for any reason. + * @param submission The [DbSubmission] to add. + * @throws IllegalArgumentException If [DbSubmission] could not be added for any reason. */ @Synchronized - override fun postSubmission(submission: Submission) { + override fun postSubmission(submission: DbSubmission) { check(this.isRunning) { "Task run '${this@InteractiveAsynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this.teamId == submission.team.id) { "Team ${submission.team.id} is not eligible to submit to this task. This is a programmer's error!" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index ab0822e31..dfbf284a4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -1,42 +1,41 @@ package dev.dres.data.model.run -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.audit.AuditLogger import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TeamTaskScorer import kotlinx.dnq.query.* -import java.util.* /** - * Represents a concrete, interactive and synchronous [Run] of a [EvaluationTemplate]. + * Represents a concrete, interactive and synchronous [Run] of a [DbEvaluationTemplate]. * * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. * * @author Ralph Gasser * @param 2.0.0 */ -class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { +class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { init { - require(this.evaluation.type == EvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } + require(this.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } /** - * Internal constructor to create an [InteractiveSynchronousEvaluation] from an [EvaluationTemplate]. + * Internal constructor to create an [InteractiveSynchronousEvaluation] from an [DbEvaluationTemplate]. * * Requires a transaction context! * * @param name The name of the new [InteractiveSynchronousEvaluation] - * @param template The [EvaluationTemplate] + * @param template The [DbEvaluationTemplate] */ - constructor(name: String, template: EvaluationTemplate) : this(Evaluation.new { - this.type = EvaluationType.INTERACTIVE_SYNCHRONOUS + constructor(name: String, template: DbEvaluationTemplate) : this(DbEvaluation.new { + this.type = DbEvaluationType.INTERACTIVE_SYNCHRONOUS this.template = template this.name = name }) @@ -46,7 +45,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat ISTaskRun(it) }.toMutableList() - /** Reference to the currently active [TaskTemplate]. This is part of the task navigation. */ + /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ var currentTaskTemplate = this.description.tasks.first() private set @@ -66,26 +65,26 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat } /** - * Represents a concrete [Run] of a [TaskTemplate]. [Task]s always exist within a [InteractiveSynchronousEvaluation]. - * As a [InteractiveSynchronousEvaluation], [Task]s can be started and ended and they can be used to register [Submission]s. + * Represents a concrete [Run] of a [DbTaskTemplate]. [DbTask]s always exist within a [InteractiveSynchronousEvaluation]. + * As a [InteractiveSynchronousEvaluation], [DbTask]s can be started and ended and they can be used to register [DbSubmission]s. */ - inner class ISTaskRun(task: Task): AbstractInteractiveTask(task) { + inner class ISTaskRun(task: DbTask): AbstractInteractiveTask(task) { /** - * Constructor used to generate an [ISTaskRun] from a [TaskTemplate]. + * Constructor used to generate an [ISTaskRun] from a [DbTaskTemplate]. * - * @param template [TaskTemplate] to generate [ISTaskRun] from. + * @param template [DbTaskTemplate] to generate [ISTaskRun] from. */ - constructor(template: TaskTemplate) : this(Task.new { + constructor(template: DbTaskTemplate) : this(DbTask.new { this.evaluation = this@InteractiveSynchronousEvaluation.evaluation this.template = template }) - /** The [InteractiveSynchronousEvaluation] this [Task] belongs to.*/ + /** The [InteractiveSynchronousEvaluation] this [DbTask] belongs to.*/ override val competition: InteractiveSynchronousEvaluation get() = this@InteractiveSynchronousEvaluation - /** The position of this [Task] within the [InteractiveSynchronousEvaluation]. */ + /** The position of this [DbTask] within the [InteractiveSynchronousEvaluation]. */ override val position: Int get() = this@InteractiveSynchronousEvaluation.tasks.indexOf(this) @@ -96,7 +95,7 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") - /** The total duration in milliseconds of this task. Usually determined by the [TaskTemplate] but can be adjusted! */ + /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration init { @@ -105,12 +104,12 @@ class InteractiveSynchronousEvaluation(evaluation: Evaluation) : AbstractEvaluat } /** - * Adds a [Submission] to this [Task]. + * Adds a [DbSubmission] to this [DbTask]. * - * @param submission The [Submission] to add. + * @param submission The [DbSubmission] to add. */ @Synchronized - override fun postSubmission(submission: Submission) { + override fun postSubmission(submission: DbSubmission) { check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } check(this@InteractiveSynchronousEvaluation.description.teams.filter { it eq submission.team }.any()) { "Team ${submission.team.teamId} does not exists for evaluation run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index bf807ed41..790ce4813 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -1,28 +1,28 @@ package dev.dres.data.model.run import dev.dres.data.model.run.InteractiveSynchronousEvaluation.ISTaskRun -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.interfaces.TeamTaskScorer import kotlinx.dnq.query.* /** - * Represents a concrete, interactive and synchronous [Run] of a [EvaluationTemplate]. + * Represents a concrete, interactive and synchronous [Run] of a [DbEvaluationTemplate]. * * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. * * @author Luca Rossetto * @param 1.0.0 */ -class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(evaluation) { +class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { init { - require(this.evaluation.type == EvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } + require(this.evaluation.type == DbEvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } @@ -35,7 +35,7 @@ class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(eval /** * The [TaskRun] used by a [NonInteractiveEvaluation]. */ - inner class NITaskRun(task: Task): AbstractNonInteractiveTask(task) { + inner class NITaskRun(task: DbTask): AbstractNonInteractiveTask(task) { /** Reference to the [EvaluationRun] hosting this [NITaskRun]. */ override val competition: EvaluationRun @@ -54,7 +54,7 @@ class NonInteractiveEvaluation(evaluation: Evaluation) : AbstractEvaluation(eval get() = TODO("Can there be submission filters for non-interactive tasks?") @Synchronized - override fun postSubmission(submission: Submission) { + override fun postSubmission(submission: DbSubmission) { check(this@NonInteractiveEvaluation.description.teams.filter { it eq submission.team }.any()) { "Team ${submission.team.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 5e0d12a12..15d2e832e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -2,8 +2,8 @@ package dev.dres.data.model.run import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.admin.Role -import dev.dres.data.model.admin.User +import dev.dres.data.model.admin.DbRole +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager @@ -21,15 +21,15 @@ import kotlinx.dnq.query.query * @author Luca Rossetto & Ralph Gasser * @version 1.1.0 */ -data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: Set) { +data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: Set) { - /** True if the user associated with this [RunActionContext] acts as [Role.ADMIN]*/ + /** True if the user associated with this [RunActionContext] acts as [DbRole.ADMIN]*/ val isAdmin: Boolean - get() = this.roles.contains(Role.ADMIN) + get() = this.roles.contains(DbRole.ADMIN) companion object { - /** A static [RunActionContext] used for internal invocations by DRES. Always acts as an implicit [Role.ADMIN]. */ - val INTERNAL = RunActionContext(null, null, setOf(Role.ADMIN)) + /** A static [RunActionContext] used for internal invocations by DRES. Always acts as an implicit [DbRole.ADMIN]. */ + val INTERNAL = RunActionContext(null, null, setOf(DbRole.ADMIN)) /** * Constructs a [RunActionContext] from a [Context] and a [RunManager]. @@ -39,8 +39,8 @@ data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: */ fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionToken()).mapNotNull { it.toRole() }.toSet() - val teamId = runManager.template.teams.filter { it.users.contains(User.query(User::id eq userId).first()) }.first().teamId + val roles = AccessManager.rolesOfSession(ctx.sessionToken()).mapNotNull { it.toDb() }.toSet() + val teamId = runManager.template.teams.filter { it.users.contains(DbUser.query(DbUser::id eq userId).first()) }.first().teamId return RunActionContext(userId, teamId, roles) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt deleted file mode 100644 index ebd37ecc8..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ /dev/null @@ -1,61 +0,0 @@ -package dev.dres.data.model.run - -import dev.dres.api.rest.types.evaluation.ApiTask -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.template.team.Team -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* -import kotlinx.dnq.query.asSequence - -typealias EvaluationId = String - -/** - * Represents a [Task], i.e., a concrete instance of a [TaskTemplate], as executed by DRES. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class Task(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() - - /** The [EvaluationId] of this [Task]. */ - var taskId: EvaluationId - get() = this.id - set(value) { this.id = value } - - /** Timestamp of when this [Evaluation] started. */ - var started by xdRequiredLongProp() - - /** Timestamp of when this [Evaluation] ended. */ - var ended by xdNullableLongProp() - - /** The [TaskTemplate] this [Task] is an instance of. */ - var template by xdLink1(TaskTemplate) - - /** Link to a [Team] this [Task] was created for. Can be NULL!*/ - var team by xdLink0_1(Team) - - /** The [Evaluation] this [Task] belongs to. */ - var evaluation: Evaluation by xdParent(Evaluation::tasks) - - /** List of [Submission]s received by this [Task]. */ - val submissions by xdChildren0_N(Verdict::task) - - /** - * Converts this [Task] to a RESTful API representation [ApiTask]. - * - * This is a convenience method and requires an active transaction context. - * - * @return [ApiTask] - */ - fun toApi(): ApiTask = ApiTask( - taskId = this.taskId, - templateId = this.template.id, - started = this.started, - ended = this.ended, - submissions = this.submissions.asSequence().map { it.toApi() }.toList() - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index f07b031bf..fc215b34a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -1,10 +1,10 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.run.Evaluation +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.EvaluationId /** - * Represents a [Evaluation] that a DRES user or client takes place in and that groups several [TaskRun]s + * Represents a [DbEvaluation] that a DRES user or client takes place in and that groups several [TaskRun]s * * @author Ralph Gasser * @version 1.0.0 @@ -16,13 +16,13 @@ interface EvaluationRun: Run { /** The name human readable of this [EvaluationRun]. */ val name: String - /** Reference to the [EvaluationTemplate] that describes the content of this [EvaluationRun]. */ - val description: EvaluationTemplate + /** Reference to the [DbEvaluationTemplate] that describes the content of this [EvaluationRun]. */ + val description: DbEvaluationTemplate /** Collection of [TaskRun]s that make up this [EvaluationRun]. */ val tasks: List - /** Flag indicating that participants can also use the viewer for this [Evaluation]. */ + /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ var participantCanView: Boolean /** Flag indicating that tasks can be repeated.*/ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 05cef964b..720ca392b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,8 +1,8 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.TaskStatus import dev.dres.run.score.interfaces.TaskScorer typealias TaskId = String @@ -23,8 +23,8 @@ interface TaskRun: Run { /** The position of this [IATaskRun] within the enclosing [EvaluationRun]. */ val position: Int - /** Reference to the [TaskTemplate] describing this [IATaskRun]. */ - val template: TaskTemplate + /** Reference to the [DbTaskTemplate] describing this [IATaskRun]. */ + val template: DbTaskTemplate /** The [TaskScorer] used to update score for this [IATaskRun]. */ val scorer: TaskScorer @@ -38,9 +38,9 @@ interface TaskRun: Run { fun prepare() /** - * Returns a [List] of all [Submission]s that belong to this [TaskRun]. + * Returns a [List] of all [DbSubmission]s that belong to this [TaskRun]. * - * @return [List] of [Submission]s + * @return [List] of [DbSubmission]s */ - fun getSubmissions(): List + fun getSubmissions(): List } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt new file mode 100644 index 000000000..a6fabaa86 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -0,0 +1,70 @@ +package dev.dres.data.model.submissions + +import dev.dres.api.rest.types.evaluation.ApiVerdict +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.run.DbTask +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.requireIf + +/** + * A [DbVerdictStatus] as submitted by a competition participant. Makes a statement about a [DbTask]. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @version 2.0.0 + */ +class DbAnswerSet(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + /** The [DbVerdictStatus] of this [DbAnswerSet]. */ + var status by xdLink1(DbVerdictStatus) + + /** The [DbAnswerType] of this [DbAnswerSet]. */ + var type by xdLink1(DbAnswerType) + + /** The [DbSubmission] this [DbAnswerSet] belongs to. */ + var submission: DbSubmission by xdParent(DbSubmission::verdicts) + + /** The [DbTask] this [DbAnswerSet] belongs to. */ + var task: DbTask by xdParent(DbTask::submissions) + + /** The [DbMediaItem] submitted. Only for [DbAnswerType.ITEM] or [DbAnswerType.TEMPORAL]. */ + var item by xdLink0_1(DbMediaItem) + + /** The start frame number of this [DbSubmission]. */ + var start by xdNullableLongProp { requireIf { this.type == DbAnswerType.TEMPORAL } } + + /** The end frame number of this [DbSubmission]. */ + var end by xdNullableLongProp { requireIf { this.type == DbAnswerType.TEMPORAL } } + + /** The text submitted. Only for [DbAnswerType.TEXT] . */ + var text by xdStringProp { requireIf { this.type == DbAnswerType.TEXT } } + + /** Returns the [TemporalRange] for this [DbAnswerSet]. */ + val temporalRange: TemporalRange? + get() = try { + TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) + } catch (e: NullPointerException) { + null + } + + /** + * Converts this [DbVerdictStatus] to a RESTful API representation [ApiVerdict]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiVerdict] + */ + fun toApi(): ApiVerdict = ApiVerdict( + status = this.status.toApi(), + type = this.type.toApi(), + item = this.item?.toApi(), + text = this.text, + start = this.start, + end = this.end + ) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt similarity index 55% rename from backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictType.kt rename to backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt index 75535eaf6..bf23026ca 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt @@ -1,33 +1,33 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiVerdictType -import dev.dres.data.model.admin.Role +import dev.dres.api.rest.types.evaluation.ApiAnswerType +import dev.dres.data.model.admin.DbRole import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * The type of [Verdict] with respect to its content + * The type of [DbAnswerSet] with respect to its content * * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class VerdictType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbAnswerType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val ITEM by enumField { description = "ITEM" } val TEMPORAL by enumField { description = "TEMPORAL" } val TEXT by enumField { description = "TEXT" } /** - * Returns a list of all [Role] values. + * Returns a list of all [DbRole] values. * - * @return List of all [Role] values. + * @return List of all [DbRole] values. */ fun values() = listOf(ITEM, TEMPORAL, TEXT) /** - * Parses a [Role] instance from a [String]. + * Parses a [DbRole] instance from a [String]. */ fun parse(string: String) = when (string.uppercase()) { "ITEM" -> ITEM @@ -37,14 +37,14 @@ class VerdictType(entity: Entity) : XdEnumEntity(entity) { } } - /** Name / description of the [VerdictType]. */ + /** Name / description of the [DbAnswerType]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [VerdictType] to a RESTful API representation [VerdictType]. + * Converts this [DbAnswerType] to a RESTful API representation [DbAnswerType]. * - * @return [VerdictType] + * @return [DbAnswerType] */ - fun toApi() = ApiVerdictType.values().find { it.type == this } ?: throw IllegalStateException("Verdict type ${this.description} is not supported.") + fun toApi() = ApiAnswerType.values().find { it.toDb() == this } ?: throw IllegalStateException("Verdict type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt similarity index 53% rename from backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt rename to backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 7369d8b2f..bb5d59346 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -2,10 +2,8 @@ package dev.dres.data.model.submissions import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.team.Team -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.team.DbTeam import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -14,36 +12,36 @@ import kotlinx.dnq.simple.min typealias SubmissionId = String /** - * A [Submission] as submitted by a competition participant and received by DRES. + * A [DbSubmission] as submitted by a competition participant and received by DRES. * - * Contains one to N [Verdict]s regarding a [Task]. + * Contains one to N [DbAnswerSet]s regarding a [Task]. * * @author Ralph Gasser * @author Luca Rossetto * @version 2.0.0 */ -sealed class Submission(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() +class DbSubmission(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() - /** The [SubmissionId] of this [User]. */ + /** The [SubmissionId] of this [DbUser]. */ var submissionId: SubmissionId get() = this.id set(value) { this.id = value } - /** The timestamp of this [Submission]. */ + /** The timestamp of this [DbSubmission]. */ var timestamp by xdRequiredLongProp { min(0L) } - /** The [Team] that submitted this [Submission] */ - var team by xdLink1(Team) + /** The [DbTeam] that submitted this [DbSubmission] */ + var team by xdLink1(DbTeam) - /** The [User] that submitted this [Submission] */ - var user by xdLink1(User) + /** The [DbUser] that submitted this [DbSubmission] */ + var user by xdLink1(DbUser) - /** The [Verdict]s that make-up this [Submission]. For batched submissions, more than one verdict can be possible. */ - val verdicts by xdChildren1_N(Verdict::submission) + /** The [DbAnswerSet]s that make-up this [DbSubmission]. For batched submissions, more than one verdict can be possible. */ + val verdicts by xdChildren1_N(DbAnswerSet::submission) /** - * Converts this [Submission] to a RESTful API representation [ApiSubmission]. + * Converts this [DbSubmission] to a RESTful API representation [ApiSubmission]. * * This is a convenience method and requires an active transaction context. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt similarity index 63% rename from backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt rename to backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt index 4f42a31e6..670b7f21a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt @@ -1,36 +1,34 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.competition.tasks.options.ApiTargetOption import dev.dres.api.rest.types.evaluation.ApiVerdictStatus -import dev.dres.data.model.admin.Role -import dev.dres.data.model.template.task.options.HintOption +import dev.dres.data.model.admin.DbRole import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * Status of a [Submission] with respect to its validation. + * Status of a [DbSubmission] with respect to its validation. * * @author Luca Rossetto * @version 2.0.0 */ -class VerdictStatus(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val CORRECT by enumField { description = "CORRECT" } /** Submission has been deemed as correct. */ val WRONG by enumField { description = "WRONG" } val INDETERMINATE by enumField { description = "INDETERMINATE" } /** Submission has been deemed as wrong. */ val UNDECIDABLE by enumField { description = "UNDECIDABLE" } /** Submission has not been validated yet. */ /** - * Returns a list of all [Role] values. + * Returns a list of all [DbRole] values. * - * @return List of all [Role] values. + * @return List of all [DbRole] values. */ fun values() = listOf(CORRECT, WRONG, INDETERMINATE, UNDECIDABLE) /** - * Parses a [Role] instance from a [String]. + * Parses a [DbRole] instance from a [String]. */ fun parse(string: String) = when (string.uppercase()) { "CORRECT" -> CORRECT @@ -41,16 +39,16 @@ class VerdictStatus(entity: Entity) : XdEnumEntity(entity) { } } - /** Name / description of the [VerdictType]. */ + /** Name / description of the [DbAnswerType]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [VerdictStatus] to a RESTful API representation [ApiVerdictStatus]. + * Converts this [DbVerdictStatus] to a RESTful API representation [ApiVerdictStatus]. * * @return [ApiVerdictStatus] */ - fun toApi() = ApiVerdictStatus.values().find { it.toVerdictStatus() == this } ?: throw IllegalStateException("Verdict status ${this.description} is not supported.") + fun toApi() = ApiVerdictStatus.values().find { it.toDb() == this } ?: throw IllegalStateException("Verdict status ${this.description} is not supported.") override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt deleted file mode 100644 index 92cd06815..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Verdict.kt +++ /dev/null @@ -1,70 +0,0 @@ -package dev.dres.data.model.submissions - -import dev.dres.api.rest.types.evaluation.ApiVerdict -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.run.Task -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* -import kotlinx.dnq.simple.requireIf - -/** - * A [VerdictStatus] as submitted by a competition participant. Makes a statement about a [Task]. - * - * @author Ralph Gasser - * @author Luca Rossetto - * @version 2.0.0 - */ -class Verdict(entity: Entity) : PersistentEntity(entity) { - companion object : XdNaturalEntityType() - - /** The [VerdictStatus] of this [Verdict]. */ - var status by xdLink1(VerdictStatus) - - /** The [VerdictType] of this [Verdict]. */ - var type by xdLink1(VerdictType) - - /** The [Submission] this [Verdict] belongs to. */ - var submission: Submission by xdParent(Submission::verdicts) - - /** The [Task] this [Verdict] belongs to. */ - var task: Task by xdParent(Task::submissions) - - /** The [MediaItem] submitted. Only for [VerdictType.ITEM] or [VerdictType.TEMPORAL]. */ - var item by xdLink0_1(MediaItem) - - /** The start frame number of this [Submission]. */ - var start by xdNullableLongProp { requireIf { this.type == VerdictType.TEMPORAL } } - - /** The end frame number of this [Submission]. */ - var end by xdNullableLongProp { requireIf { this.type == VerdictType.TEMPORAL } } - - /** The text submitted. Only for [VerdictType.TEXT] . */ - var text by xdStringProp { requireIf { this.type == VerdictType.TEXT } } - - /** Returns the [TemporalRange] for this [Verdict]. */ - val temporalRange: TemporalRange? - get() = try { - TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) - } catch (e: NullPointerException) { - null - } - - /** - * Converts this [VerdictStatus] to a RESTful API representation [ApiVerdict]. - * - * This is a convenience method and requires an active transaction context. - * - * @return [ApiVerdict] - */ - fun toApi(): ApiVerdict = ApiVerdict( - status = this.status.toApi(), - type = this.type.toApi(), - item = this.item?.toApi(), - text = this.text, - start = this.start, - end = this.end - ) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt similarity index 52% rename from backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 1960b30fb..3b8088ce6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/EvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -2,14 +2,14 @@ package dev.dres.data.model.template import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType -import dev.dres.data.model.template.task.TaskGroup -import dev.dres.data.model.template.task.TaskType -import dev.dres.data.model.template.team.Team -import dev.dres.data.model.template.team.TeamGroup +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.DbMediaType +import dev.dres.data.model.template.task.DbTaskGroup +import dev.dres.data.model.template.task.DbTaskType +import dev.dres.data.model.template.team.DbTeam +import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.media.time.TemporalRange import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard @@ -24,48 +24,48 @@ typealias TemplateId = String /** * Basic description of a competitions as executed in DRES. * - * Defines basic attributes such as its name and the [TaskType]s and [TaskGroup]s it contains. + * Defines basic attributes such as its name and the [DbTaskType]s and [DbTaskGroup]s it contains. * * @version 2.0.0 * @author Luca Rossetto & Ralph Gasser */ -class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ - companion object: XdNaturalEntityType() +class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity){ + companion object: XdNaturalEntityType() - /** The [TemplateId] of this [EvaluationTemplate]. */ + /** The [TemplateId] of this [DbEvaluationTemplate]. */ var templateId: TemplateId get() = this.id set(value) { this.id = value } - /** The name held by this [EvaluationTemplate]. Must be unique!*/ + /** The name held by this [DbEvaluationTemplate]. Must be unique!*/ var name by xdRequiredStringProp(unique = true, trimmed = true) - /** If set, this [EvaluationTemplate] is considered a template!*/ + /** If set, this [DbEvaluationTemplate] is considered a template!*/ var isTemplate by xdBooleanProp() - /** An optional description of this [EvaluationTemplate]. */ + /** An optional description of this [DbEvaluationTemplate]. */ var description by xdStringProp(trimmed = false) - /** The [TaskType]s defined within this [EvaluationTemplate]. */ - val taskTypes by xdChildren0_N(TaskType::evaluation) + /** The [DbTaskType]s defined within this [DbEvaluationTemplate]. */ + val taskTypes by xdChildren0_N(DbTaskType::evaluation) - /** The [TaskGroup]s that are part of this [EvaluationTemplate]. */ - val taskGroups by xdChildren0_N(TaskGroup::evaluation) + /** The [DbTaskGroup]s that are part of this [DbEvaluationTemplate]. */ + val taskGroups by xdChildren0_N(DbTaskGroup::evaluation) - /** The [TaskTemplate]s contained in this [EvaluationTemplate]*/ - val tasks by xdChildren0_N(TaskTemplate::evaluation) + /** The [DbTaskTemplate]s contained in this [DbEvaluationTemplate]*/ + val tasks by xdChildren0_N(DbTaskTemplate::evaluation) - /** The [Team]s that are part of this [EvaluationTemplate]. */ - val teams by xdChildren0_N(Team::evaluation) + /** The [DbTeam]s that are part of this [DbEvaluationTemplate]. */ + val teams by xdChildren0_N(DbTeam::evaluation) - /** The [Team]s that are part of this [EvaluationTemplate]. */ - val teamsGroups by xdChildren0_N(TeamGroup::evaluation) + /** The [DbTeam]s that are part of this [DbEvaluationTemplate]. */ + val teamsGroups by xdChildren0_N(DbTeamGroup::evaluation) - /** The [User]s that act as judge for this [EvaluationTemplate] */ - val judges by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + /** The [DbUser]s that act as judge for this [DbEvaluationTemplate] */ + val judges by xdLink0_N(DbUser, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) /** - * Converts this [EvaluationTemplate] to a RESTful API representation [ApiEvaluationTemplate]. + * Converts this [DbEvaluationTemplate] to a RESTful API representation [ApiEvaluationTemplate]. * * This is a convenience method and it requires and active transaction context. * @@ -84,7 +84,7 @@ class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ ) /** - * Generates and returns the default [Scoreboard] implementations for this [EvaluationTemplate]. + * Generates and returns the default [Scoreboard] implementations for this [DbEvaluationTemplate]. * * This is a convenience method and requires an active transaction context. * @@ -100,22 +100,22 @@ class EvaluationTemplate(entity: Entity) : PersistentEntity(entity){ } /** - * Generates and returns a list of all [MediaItem] for this [EvaluationTemplate]. + * Generates and returns a list of all [DbMediaItem] for this [DbEvaluationTemplate]. * * This is a convenience method and requires an active transaction context. * - * @return [List] of [MediaItem]s + * @return [List] of [DbMediaItem]s */ - fun getAllVideos(): List> { + fun getAllVideos(): List> { val hints = this.tasks .flatMapDistinct { it.hints } - .filter { (it.item ne null) and (it.item!!.type eq MediaType.VIDEO) }.asSequence().map { + .filter { (it.item ne null) and (it.item!!.type eq DbMediaType.VIDEO) }.asSequence().map { it.item!!to it.range!! } val targets = this.tasks .flatMapDistinct { it.targets } - .filter { (it.item ne null) and (it.item!!.type eq MediaType.VIDEO) }.asSequence().map { + .filter { (it.item ne null) and (it.item!!.type eq DbMediaType.VIDEO) }.asSequence().map { it.item!! to it.range!! } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt similarity index 71% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index 5d6f79198..6552b2ae8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/Hint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -5,12 +5,11 @@ import dev.dres.api.rest.types.competition.tasks.ApiHint import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.simple.min import kotlinx.dnq.simple.requireIf import java.io.FileNotFoundException import java.io.IOException @@ -20,16 +19,16 @@ import java.nio.file.StandardOpenOption import java.util.* /** - * Represents the hint given by a [TaskTemplate], e.g., a media item or text that is shown. + * Represents the hint given by a [DbTaskTemplate], e.g., a media item or text that is shown. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class Hint(entity: Entity) : XdEntity(entity) { - companion object : XdNaturalEntityType() +class DbHint(entity: Entity) : XdEntity(entity) { + companion object : XdNaturalEntityType() - /** The [HintType] of this [Hint]. */ - var type by xdLink1(HintType) + /** The [DbHintType] of this [DbHint]. */ + var type by xdLink1(DbHintType) /** The start of a (potential) range. */ var start by xdNullableLongProp() @@ -37,25 +36,25 @@ class Hint(entity: Entity) : XdEntity(entity) { /** The start of a (potential) range. */ var end by xdNullableLongProp() - /** The parent [TaskTemplate] this [Hint] belongs to. */ - var task: TaskTemplate by xdParent(TaskTemplate::hints) + /** The parent [DbTaskTemplate] this [DbHint] belongs to. */ + var task: DbTaskTemplate by xdParent(DbTaskTemplate::hints) - /** The[MediaItem] shown as part of the [Hint]. Can be null. */ - var item by xdLink0_1(MediaItem) + /** The[DbMediaItem] shown as part of the [DbHint]. Can be null. */ + var item by xdLink0_1(DbMediaItem) /** The target text. Can be null. */ - var text by xdStringProp() { requireIf { type == HintType.TEXT }} + var text by xdStringProp { requireIf { type == DbHintType.TEXT }} /** The target text. Can be null. */ var path by xdStringProp() /** The start of a (potential) range. */ - var temporalRangeStart by xdNullableLongProp { requireIf { type == HintType.VIDEO } } + var temporalRangeStart by xdNullableLongProp { requireIf { type == DbHintType.VIDEO } } /** The start of a (potential) range. */ - var temporalRangeEnd by xdNullableLongProp { requireIf { type == HintType.VIDEO } } + var temporalRangeEnd by xdNullableLongProp { requireIf { type == DbHintType.VIDEO } } - /** Returns the [TemporalRange] of this [TaskTemplateTarget]. */ + /** Returns the [TemporalRange] of this [DbTaskTemplateTarget]. */ val range: TemporalRange? get() = if (this.temporalRangeStart != null && this.temporalRangeEnd != null) { TemporalRange(TemporalPoint.Millisecond(this.temporalRangeStart!!), TemporalPoint.Millisecond(this.temporalRangeEnd!!)) @@ -65,7 +64,7 @@ class Hint(entity: Entity) : XdEntity(entity) { /** - * Converts this [Hint] to a RESTful API representation [ApiHint]. + * Converts this [DbHint] to a RESTful API representation [ApiHint]. * * This is a convenience method and requires an active transaction context. * @@ -82,32 +81,32 @@ class Hint(entity: Entity) : XdEntity(entity) { ) /** - * Generates and returns a textual description of this [Hint]. + * Generates and returns a textual description of this [DbHint]. * * @return Text */ fun textDescription(): String = when (this.type) { - HintType.TEXT -> "\"${this.text}\" from ${this.start ?: "beginning"} to ${end ?: "end"}" - HintType.VIDEO -> { + DbHintType.TEXT -> "\"${this.text}\" from ${this.start ?: "beginning"} to ${end ?: "end"}" + DbHintType.VIDEO -> { if (this.item != null) { "Image ${this.item!!.name} from ${start ?: "beginning"} to ${end ?: "end"}" } else { "Image ${this.path} from ${start ?: "beginning"} to ${end ?: "end"}" } } - HintType.IMAGE -> { + DbHintType.IMAGE -> { if (this.item != null) { "Image ${this.item!!.name}" } else { "Image ${this.path}" } } - HintType.EMPTY -> "Empty item" + DbHintType.EMPTY -> "Empty item" else -> throw IllegalStateException("The task hint type ${this.type.description} is currently not supported.") } /** - * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. + * Generates and returns a [ApiContentElement] object of this [DbHint] to be used by the RESTful interface. * * @param config The [Config] used of path resolution. * @return [ApiContentElement] @@ -117,8 +116,8 @@ class Hint(entity: Entity) : XdEntity(entity) { */ fun toQueryContentElement(config: Config): ApiContentElement { val content = when (this.type) { - HintType.IMAGE, - HintType.VIDEO -> { + DbHintType.IMAGE, + DbHintType.VIDEO -> { val path = if (this.item != null) { Paths.get(config.cachePath, this.item!!.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)) } else if (this.path != null) { @@ -131,16 +130,16 @@ class Hint(entity: Entity) : XdEntity(entity) { } Base64.getEncoder().encodeToString(data) } - HintType.EMPTY -> "" - HintType.TEXT -> this.text ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") + DbHintType.EMPTY -> "" + DbHintType.TEXT -> this.text ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") } val contentType = when (this.type) { - HintType.IMAGE -> ApiContentType.IMAGE - HintType.VIDEO -> ApiContentType.VIDEO - HintType.TEXT -> ApiContentType.TEXT - HintType.EMPTY -> ApiContentType.EMPTY + DbHintType.IMAGE -> ApiContentType.IMAGE + DbHintType.VIDEO -> ApiContentType.VIDEO + DbHintType.TEXT -> ApiContentType.TEXT + DbHintType.EMPTY -> ApiContentType.EMPTY else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHintType.kt similarity index 56% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/DbHintType.kt index 7814d5cde..25aeefe2a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/HintType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHintType.kt @@ -2,37 +2,37 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiHintType import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.template.task.options.ScoreOption +import dev.dres.data.model.template.task.options.DbScoreOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -class HintType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbHintType(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val EMPTY by enumField { description = "EMPTY"; base64 = false } val TEXT by enumField { description = "TEXT"; mimeType = "text/plain"; suffix = "txt"; base64 = false } val VIDEO by enumField { description = "VIDEO"; mimeType = "video/mp4"; suffix = "mp4"; base64 = true } val IMAGE by enumField { description = "IMAGE"; mimeType = "image/jpg"; suffix = "jpg"; base64 = true } } - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var mimeType by xdStringProp() private set - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var suffix by xdStringProp() - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var base64 by xdBooleanProp() /** - * Converts this [HintType] to the RESTful API representation [ApiContentType]. + * Converts this [DbHintType] to the RESTful API representation [ApiContentType]. * - * @return [ApiContentType] equivalent to this [HintType]. + * @return [ApiContentType] equivalent to this [DbHintType]. */ fun toApi(): ApiHintType - = ApiHintType.values().find { it.type == this } ?: throw IllegalStateException("Hint type ${this.description} is not supported.") + = ApiHintType.values().find { it.toDb() == this } ?: throw IllegalStateException("Hint type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTargetType.kt similarity index 66% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/DbTargetType.kt index c186eaf85..a07682fca 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TargetType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTargetType.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiTargetType -import dev.dres.data.model.template.task.options.ScoreOption +import dev.dres.data.model.template.task.options.DbScoreOption import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -10,8 +10,8 @@ import kotlinx.dnq.xdRequiredStringProp /** * The type of target for a */ -class TargetType(entity: Entity): XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbTargetType(entity: Entity): XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val JUDGEMENT by enumField { description = "JUDGEMENT" } val JUDGEMENT_WITH_VOTE by enumField { description = "JUDGEMENT_WITH_VOTE" } val MEDIA_ITEM by enumField { description = "MEDIA_ITEM" } @@ -19,17 +19,17 @@ class TargetType(entity: Entity): XdEnumEntity(entity) { val TEXT by enumField { description = "EXTERNAL_IMAGE" } } - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. + * Converts this [DbTargetType] to a RESTful API representation [ApiTargetType]. * * @return [ApiTargetType] */ fun toApi(): ApiTargetType - = ApiTargetType.values().find { it.toTargetType() == this } ?: throw IllegalStateException("Target type ${this.description} is not supported.") + = ApiTargetType.values().find { it.toDb() == this } ?: throw IllegalStateException("Target type ${this.description} is not supported.") override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt new file mode 100644 index 000000000..1642ecd24 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt @@ -0,0 +1,40 @@ +package dev.dres.data.model.template.task + +import dev.dres.api.rest.types.competition.tasks.ApiTargetType +import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup +import dev.dres.data.model.template.DbEvaluationTemplate +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* + +/** + * A [DbTaskGroup] allows the user to specify common traits among a group of [Task]s. + * + * @author Luca Rossetto & Ralph Gasser + * @version 2.0.0 + */ +class DbTaskGroup(entity: Entity) : XdEntity(entity) { + companion object : XdNaturalEntityType() { + /** Combination of [DbTaskGroup] name / competition must be unique. */ + override val compositeIndices = listOf( + listOf(DbTaskGroup::name, DbTaskGroup::evaluation) + ) + } + + /** The name of this [DbTaskGroup]. */ + var name: String by xdRequiredStringProp(unique = false, trimmed = false) + + /** The [DbTaskType] this [DbTaskGroup] belongs to.*/ + var type by xdLink1(DbTaskType) + + /** The [DbEvaluationTemplate] this [DbTaskGroup] belongs to. */ + var evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::taskGroups) + + /** + * Converts this [DbTargetType] to a RESTful API representation [ApiTargetType]. + * + * This is a convenience method and it requires and active transaction context. + * + * @return [ApiTargetType] + */ + fun toApi(): ApiTaskGroup = ApiTaskGroup(this.name,this.type.name) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt similarity index 72% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 5b3e188a6..4096211e7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -6,11 +6,11 @@ import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.media.MediaCollection +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.interfaces.SubmissionFilterFactory import dev.dres.data.model.template.interfaces.TaskScorerFactory -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId import dev.dres.run.filter.SubmissionFilter @@ -26,46 +26,46 @@ import java.lang.Long.max /** * Basic description of a [TaskRun] as executed in DRES. Defines basic attributes such as its name, its duration, - * the [TaskTemplateTarget] and the [Hint]s, that should be presented to the user. + * the [DbTaskTemplateTarget] and the [DbHint]s, that should be presented to the user. * * @version 2.0.0 * @author Luca Rossetto * @author Ralph Gasser */ -class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { - companion object: XdNaturalEntityType() +class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { + companion object: XdNaturalEntityType() - /** The [TemplateId] of this [TaskTemplate]. */ + /** The [TemplateId] of this [DbTaskTemplate]. */ var templateId: TemplateId get() = this.id set(value) { this.id = value } - /** The name held by this [Team]. Must be unique!*/ + /** The name held by this [DbTeam]. Must be unique!*/ var name by xdRequiredStringProp(unique = false, trimmed = true) - /** If set, this [EvaluationTemplate] is considered a template!*/ + /** If set, this [DbEvaluationTemplate] is considered a template!*/ var template by xdBooleanProp() - /** The [TaskGroup] this [TaskTemplate] belongs to. */ - var taskGroup by xdLink1(TaskGroup) + /** The [DbTaskGroup] this [DbTaskTemplate] belongs to. */ + var taskGroup by xdLink1(DbTaskGroup) - /** The [EvaluationTemplate] this [TaskTemplate] belongs to. */ - var evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::tasks) + /** The [DbEvaluationTemplate] this [DbTaskTemplate] belongs to. */ + var evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::tasks) - /** The [MediaCollection] this [TaskTemplate] operates upon. */ - var collection by xdLink1(MediaCollection) + /** The [DbMediaCollection] this [DbTaskTemplate] operates upon. */ + var collection by xdLink1(DbMediaCollection) - /** The duration of the [TaskTemplate] in seconds. */ + /** The duration of the [DbTaskTemplate] in seconds. */ var duration by xdRequiredLongProp { min(0L) } - /** The [TaskTemplateTarget]s that identify the target. Multiple entries indicate the existence of multiple targets. */ - val targets by xdChildren1_N(TaskTemplateTarget::task) + /** The [DbTaskTemplateTarget]s that identify the target. Multiple entries indicate the existence of multiple targets. */ + val targets by xdChildren1_N(DbTaskTemplateTarget::task) - /** The [Hint]s that act as clues to find the target media. */ - val hints by xdChildren0_N(Hint::task) + /** The [DbHint]s that act as clues to find the target media. */ + val hints by xdChildren0_N(DbHint::task) /** - * Generates a new [TaskScorer] for this [TaskTemplate]. Depending + * Generates a new [TaskScorer] for this [DbTaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * @return [TaskScorer]. @@ -74,7 +74,7 @@ class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory /** - * Generates and returns a [SubmissionValidator] instance for this [TaskTemplate]. Depending + * Generates and returns a [SubmissionValidator] instance for this [DbTaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * @return [SubmissionFilter] @@ -136,14 +136,14 @@ class TaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory } /** - * Produces a Textual description of the content of the [TaskTemplate] if possible + * Produces a Textual description of the content of the [DbTaskTemplate] if possible * - * @return Textual description of this [TaskTemplate]'s content, + * @return Textual description of this [DbTaskTemplate]'s content, */ - fun textualDescription(): String = this.hints.asSequence().filter { it.type == HintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name + fun textualDescription(): String = this.hints.asSequence().filter { it.type == DbHintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name /** - * Converts this [TaskTemplate] to a RESTful API representation [ApiTaskTemplate]. + * Converts this [DbTaskTemplate] to a RESTful API representation [ApiTaskTemplate]. * * This is a convenience method and requires an active transaction context. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt similarity index 59% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt index 3029b8dd6..7f8473d51 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplateTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt @@ -5,8 +5,8 @@ import dev.dres.api.rest.types.competition.tasks.ApiTarget import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.Config -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaType +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity @@ -20,33 +20,33 @@ import java.nio.file.StandardOpenOption import java.util.* /** - * Represents the target of a [TaskTemplate], i.e., the [MediaItem] or part thereof that is considered correct. + * Represents the target of a [DbTaskTemplate], i.e., the [DbMediaItem] or part thereof that is considered correct. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { - companion object: XdNaturalEntityType() +class DbTaskTemplateTarget(entity: Entity) : XdEntity(entity) { + companion object: XdNaturalEntityType() - /** The [TargetType] of this [TaskTemplateTarget]. */ - var type by xdLink1(TargetType) + /** The [DbTargetType] of this [DbTaskTemplateTarget]. */ + var type by xdLink1(DbTargetType) - /** The parent [TaskTemplate] this [TaskTemplateTarget] belongs to. */ - var task: TaskTemplate by xdParent(TaskTemplate::targets) + /** The parent [DbTaskTemplate] this [DbTaskTemplateTarget] belongs to. */ + var task: DbTaskTemplate by xdParent(DbTaskTemplate::targets) - /** The targeted [MediaItem]. Can be null. */ - var item by xdLink0_1(MediaItem) + /** The targeted [DbMediaItem]. Can be null. */ + var item by xdLink0_1(DbMediaItem) /** The target text. Can be null. */ var text by xdStringProp() { requireIf { item == null }} /** The start of a (potential) range. */ - var start by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } + var start by xdNullableLongProp { requireIf { item?.type == DbMediaType.VIDEO } } /** The start of a (potential) range. */ - var end by xdNullableLongProp { requireIf { item?.type == MediaType.VIDEO } } + var end by xdNullableLongProp { requireIf { item?.type == DbMediaType.VIDEO } } - /** Returns the [TemporalRange] of this [TaskTemplateTarget]. */ + /** Returns the [TemporalRange] of this [DbTaskTemplateTarget]. */ val range: TemporalRange? get() = if (this.start != null && this.end != null) { TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) @@ -54,18 +54,17 @@ class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { null } - /** - * Generates and returns a textual description of this [TaskTemplateTarget]. + * Generates and returns a textual description of this [DbTaskTemplateTarget]. * * @return Text */ fun textDescription(): String = when (this.type) { - TargetType.JUDGEMENT -> "Judgement" - TargetType.JUDGEMENT_WITH_VOTE -> "Judgement with vote" - TargetType.MEDIA_ITEM -> "Media item ${this.item?.name}" - TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.start} - ${this.end}" - TargetType.TEXT -> "Text: ${this.text}" + DbTargetType.JUDGEMENT -> "Judgement" + DbTargetType.JUDGEMENT_WITH_VOTE -> "Judgement with vote" + DbTargetType.MEDIA_ITEM -> "Media item ${this.item?.name}" + DbTargetType.MEDIA_ITEM_TEMPORAL_RANGE -> "Media item ${this.item?.name} @ ${this.start} - ${this.end}" + DbTargetType.TEXT -> "Text: ${this.text}" else -> throw IllegalStateException("The task description type ${this.type.description} is currently not supported.") } @@ -73,16 +72,16 @@ class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { * */ fun toApi(): ApiTarget = when(this.type) { - TargetType.JUDGEMENT, - TargetType.JUDGEMENT_WITH_VOTE -> ApiTarget(this.type.toApi(), null) - TargetType.MEDIA_ITEM, - TargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget(this.type.toApi(), this.item?.id, this.range?.let { ApiTemporalRange(it) }) - TargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) + DbTargetType.JUDGEMENT, + DbTargetType.JUDGEMENT_WITH_VOTE -> ApiTarget(this.type.toApi(), null) + DbTargetType.MEDIA_ITEM, + DbTargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget(this.type.toApi(), this.item?.id, this.range?.let { ApiTemporalRange(it) }) + DbTargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) else -> throw IllegalStateException("Task description of type ${this.type.description} is not supported.") } /** - * Generates and returns a [ApiContentElement] object of this [Hint] to be used by the RESTful interface. + * Generates and returns a [ApiContentElement] object of this [DbHint] to be used by the RESTful interface. * * @param config The [Config] used of path resolution. * @return [ApiContentElement] @@ -92,12 +91,12 @@ class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { */ fun toQueryContentElement(config: Config): ApiContentElement { val (content, type) = when (this.type) { - TargetType.JUDGEMENT, - TargetType.JUDGEMENT_WITH_VOTE -> null to ApiContentType.EMPTY - TargetType.MEDIA_ITEM -> { + DbTargetType.JUDGEMENT, + DbTargetType.JUDGEMENT_WITH_VOTE -> null to ApiContentType.EMPTY + DbTargetType.MEDIA_ITEM -> { val type = when (this.item?.type) { - MediaType.VIDEO -> ApiContentType.VIDEO - MediaType.IMAGE -> ApiContentType.IMAGE + DbMediaType.VIDEO -> ApiContentType.VIDEO + DbMediaType.IMAGE -> ApiContentType.IMAGE else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") } val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.start, this.end)) @@ -107,7 +106,7 @@ class TaskTemplateTarget(entity: Entity) : XdEntity(entity) { } Base64.getEncoder().encodeToString(data) to type } - TargetType.TEXT -> this.text to ApiContentType.TEXT + DbTargetType.TEXT -> this.text to ApiContentType.TEXT else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") } return ApiContentElement(contentType = type, content = content, offset = 0L) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt similarity index 55% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index de959f808..bd99982a9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -1,9 +1,9 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.ApiTaskType -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.options.* -import dev.dres.data.model.template.task.options.ConfiguredOption +import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter import dev.dres.run.filter.SubmissionFilterAggregator @@ -15,48 +15,48 @@ import kotlinx.dnq.query.* import kotlinx.dnq.simple.min /** - * Specifies the type of a [TaskTemplate] and allows for many aspects of its configuration. + * Specifies the type of a [DbTaskTemplate] and allows for many aspects of its configuration. * * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class TaskType(entity: Entity) : XdEntity(entity) { - /** Combination of [TaskType] name / competition must be unique. */ - companion object: XdNaturalEntityType() { +class DbTaskType(entity: Entity) : XdEntity(entity) { + /** Combination of [DbTaskType] name / competition must be unique. */ + companion object: XdNaturalEntityType() { override val compositeIndices = listOf( - listOf(TaskType::name, TaskType::evaluation) + listOf(DbTaskType::name, DbTaskType::evaluation) ) } - /** The name of this [TaskType]. */ + /** The name of this [DbTaskType]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) - /** The [EvaluationTemplate] this [TaskType] belongs to. */ - var evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::taskTypes) + /** The [DbEvaluationTemplate] this [DbTaskType] belongs to. */ + var evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::taskTypes) - /** The (default) duration of this [TaskType] in seconds. */ + /** The (default) duration of this [DbTaskType] in seconds. */ var duration by xdRequiredLongProp() { min(0L) } - /** The [TargetOption] for this [TaskType]. Specifies the type of target. */ - var target by xdLink1(TargetOption) + /** The [DbTargetOption] for this [DbTaskType]. Specifies the type of target. */ + var target by xdLink1(DbTargetOption) - /** The [HintOption]s that make-up this [TaskType]. */ - val hints by xdLink0_N(HintOption) + /** The [DbHintOption]s that make-up this [DbTaskType]. */ + val hints by xdLink0_N(DbHintOption) - /** The [SubmissionOption]s for this [TaskType]. */ - val submission by xdLink0_N(SubmissionOption) + /** The [DbSubmissionOption]s for this [DbTaskType]. */ + val submission by xdLink0_N(DbSubmissionOption) - /** The [ScoreOption] for this [TaskType]. Specifies the type of scorer that should be used. */ - var score by xdLink1(ScoreOption) + /** The [DbScoreOption] for this [DbTaskType]. Specifies the type of scorer that should be used. */ + var score by xdLink1(DbScoreOption) - /** The [TaskOption]s for this [TaskType]. */ - val options by xdLink0_N(TaskOption) + /** The [DbTaskOption]s for this [DbTaskType]. */ + val options by xdLink0_N(DbTaskOption) - /** [ConfiguredOption]s registered for this [TaskTemplate]. */ - val configurations by xdChildren0_N(ConfiguredOption::task) + /** [DbConfiguredOption]s registered for this [DbTaskTemplate]. */ + val configurations by xdChildren0_N(DbConfiguredOption::task) /** - * Converts this [TaskType] to a RESTful API representation [ApiTaskType]. + * Converts this [DbTaskType] to a RESTful API representation [ApiTaskType]. * * @return [ApiTaskType] */ @@ -72,7 +72,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { ) /** - * Generates a new [TaskScorer] for this [TaskTemplate]. Depending + * Generates a new [TaskScorer] for this [DbTaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * Calling this method requires an ongoing transaction! @@ -80,13 +80,13 @@ class TaskType(entity: Entity) : XdEntity(entity) { * @return [TaskScorer]. */ fun newScorer(): TaskScorer { - val parameters = this.configurations.query(ConfiguredOption::key eq this.score.description) + val parameters = this.configurations.query(DbConfiguredOption::key eq this.score.description) .asSequence().map { it.key to it.value }.toMap() return this.score.scorer(parameters) } /** - * Generates and returns a [SubmissionValidator] instance for this [TaskTemplate]. Depending + * Generates and returns a [SubmissionValidator] instance for this [DbTaskTemplate]. Depending * on the implementation, the returned instance is a new instance or being re-use. * * Calling this method requires an ongoing transaction! @@ -96,7 +96,7 @@ class TaskType(entity: Entity) : XdEntity(entity) { fun newFilter(): SubmissionFilter { if (this.submission.isEmpty) return AllSubmissionFilter val filters = this.submission.asSequence().map { option -> - val parameters = this.configurations.query(ConfiguredOption::key eq this.score.description) + val parameters = this.configurations.query(DbConfiguredOption::key eq this.score.description) .asSequence().map { it.key to it.value }.toMap() option.newFilter(parameters) }.toList() diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt deleted file mode 100644 index b2f92ea20..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskGroup.kt +++ /dev/null @@ -1,41 +0,0 @@ -package dev.dres.data.model.template.task - -import dev.dres.api.rest.types.competition.tasks.ApiTargetType -import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup -import dev.dres.data.model.template.EvaluationTemplate -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* - -/** - * A [TaskGroup] allows the user to specify common traits among a group of [Task]s. - * - * @author Luca Rossetto & Ralph Gasser - * @version 2.0.0 - */ -class TaskGroup(entity: Entity) : XdEntity(entity) { - companion object : XdNaturalEntityType() { - /** Combination of [TaskGroup] name / competition must be unique. */ - override val compositeIndices = listOf( - listOf(TaskGroup::name, TaskGroup::evaluation) - ) - } - - /** The name of this [TaskGroup]. */ - var name: String by xdRequiredStringProp(unique = false, trimmed = false) - - /** The [TaskType] this [TaskGroup] belongs to.*/ - var type by xdLink1(TaskType) - - /** The [EvaluationTemplate] this [TaskGroup] belongs to. */ - var evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::taskGroups) - - /** - * Converts this [TargetType] to a RESTful API representation [ApiTargetType]. - * - * This is a convenience method and it requires and active transaction context. - * - * @return [ApiTargetType] - */ - fun toApi(): ApiTaskGroup - = ApiTaskGroup(this.name,this.type.name) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbConfiguredOption.kt similarity index 73% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbConfiguredOption.kt index 9294fb73f..6b4d44b39 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ConfiguredOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbConfiguredOption.kt @@ -1,28 +1,28 @@ package dev.dres.data.model.template.task.options -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.task.TaskType +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.template.task.DbTaskType import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* /** - * A helper class that can be used to configure options in a [TaskType]. + * A helper class that can be used to configure options in a [DbTaskType]. * * @author Ralph Gasser * @version 1.0.0 */ -class ConfiguredOption(entity: Entity) : XdEntity(entity) { +class DbConfiguredOption(entity: Entity) : XdEntity(entity) { - companion object: XdNaturalEntityType() + companion object: XdNaturalEntityType() - /** The key for this [ConfiguredOption]. Identifies the option. */ + /** The key for this [DbConfiguredOption]. Identifies the option. */ var key by xdRequiredStringProp() - /** The conifgured value for this [ConfiguredOption]. */ + /** The conifgured value for this [DbConfiguredOption]. */ var value by xdRequiredStringProp() - /** The [TaskTemplate] this [ConfiguredOption] belongs to. */ - val task: TaskType by xdParent(TaskType::configurations) + /** The [DbTaskTemplate] this [DbConfiguredOption] belongs to. */ + val task: DbTaskType by xdParent(DbTaskType::configurations) /** * Tries to parse a named parameter as [Boolean]. Returns null, if the parameter is not set or cannot be converted. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/HintOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbHintOption.kt similarity index 60% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/options/HintOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbHintOption.kt index ce79fe4b0..6ca915fca 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/HintOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbHintOption.kt @@ -1,20 +1,20 @@ package dev.dres.data.model.template.task.options -import dev.dres.api.rest.types.competition.tasks.options.ApiComponentOption -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.api.rest.types.competition.tasks.options.ApiHintOption +import dev.dres.data.model.template.task.DbTaskTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * An enumeration of potential options for [TaskTemplate] targets. + * An enumeration of potential options for [DbTaskTemplate] targets. * * @author Ralph Gasser * @version 2.0.0 */ -class HintOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbHintOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val IMAGE_ITEM by enumField { description = "IMAGE_ITEM" } /** An image [MediaItem]. */ val VIDEO_ITEM_SEGMENT by enumField { description = "VIDEO_ITEM_SEGMENT" } /** Part of a video [MediaItem]. */ val TEXT by enumField { description = "TEXT" } /** A text snippet. */ @@ -22,14 +22,14 @@ class HintOption(entity: Entity) : XdEnumEntity(entity) { val EXTERNAL_VIDEO by enumField { description = "EXTERNAL_VIDEO" } /** An external video that is not part of a collection. */ } - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [HintOption] to a RESTful API representation [ApiComponentOption]. + * Converts this [DbHintOption] to a RESTful API representation [ApiHintOption]. * - * @return [ApiComponentOption] + * @return [ApiHintOption] */ - fun toApi() = ApiComponentOption.values().find { it.option == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiHintOption.values().find { it.toDb() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt similarity index 75% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt index 6d30477f8..9309f49d1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/ScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt @@ -15,13 +15,13 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class ScoreOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbScoreOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val KIS by enumField { description = "KIS" } val AVS by enumField { description = "AVS" } } - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set @@ -37,11 +37,11 @@ class ScoreOption(entity: Entity) : XdEnumEntity(entity) { } /** - * Converts this [HintOption] to a RESTful API representation [ApiScoreOption]. + * Converts this [DbHintOption] to a RESTful API representation [ApiScoreOption]. * * @return [ApiScoreOption] */ - fun toApi() = ApiScoreOption.values().find { it.toScoreOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiScoreOption.values().find { it.toDb() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt similarity index 84% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt index 8bc8f4d13..8afd17192 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/SubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt @@ -13,8 +13,8 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class SubmissionOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbSubmissionOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val NO_DUPLICATES by enumField { description = "NO_DUPLICATES" } val LIMIT_CORRECT_PER_TEAM by enumField { description = "LIMIT_CORRECT_PER_TEAM" } val LIMIT_WRONG_PER_TEAM by enumField { description = "LIMIT_WRONG_PER_TEAM" } @@ -26,7 +26,7 @@ class SubmissionOption(entity: Entity) : XdEnumEntity(entity) { val MINIMUM_TIME_GAP by enumField { description = "MINIMUM_TIME_GAP" } } - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set @@ -49,9 +49,9 @@ class SubmissionOption(entity: Entity) : XdEnumEntity(entity) { } /** - * Converts this [HintOption] to a RESTful API representation [ApiSubmissionOption]. + * Converts this [DbHintOption] to a RESTful API representation [ApiSubmissionOption]. * * @return [ApiSubmissionOption] */ - fun toApi() = ApiSubmissionOption.values().find { it.toSubmissionOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiSubmissionOption.values().find { it.toDb() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbTargetOption.kt similarity index 68% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbTargetOption.kt index 70f5ceba1..4f28ff518 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TargetOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbTargetOption.kt @@ -12,8 +12,8 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 2.0.0 */ -class TargetOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbTargetOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val MEDIA_ITEM by enumField { description = "MEDIA_ITEM" } val MEDIA_SEGMENT by enumField { description = "MEDIA_SEGMENT" } val JUDGEMENT by enumField { description = "JUDGEMENT" } @@ -21,16 +21,16 @@ class TargetOption(entity: Entity) : XdEnumEntity(entity) { val TEXT by enumField { description = "TEXT" } } - /** Name / description of the [TargetOption]. */ + /** Name / description of the [DbTargetOption]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [HintOption] to a RESTful API representation [ApiTargetOption]. + * Converts this [DbHintOption] to a RESTful API representation [ApiTargetOption]. * * @return [ApiTargetOption] */ - fun toApi() = ApiTargetOption.values().find { it.toTargetOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiTargetOption.values().find { it.toDb() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbTaskOption.kt similarity index 64% rename from backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbTaskOption.kt index 6a6d01a17..8b2ea1dd7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/TaskOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbTaskOption.kt @@ -1,33 +1,33 @@ package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiTaskOption -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * An enumeration of potential general options for [TaskTemplate]. + * An enumeration of potential general options for [DbTaskTemplate]. * * @author Ralph Gasser * @version 2.0.0 */ -class TaskOption(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbTaskOption(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val HIDDEN_RESULTS by enumField { description = "HIDDEN_RESULTS" } /** Do not show submissions while task is running. */ val MAP_TO_SEGMENT by enumField { description = "MAP_TO_SEGMENT" } /** Map the time of a submission to a pre-defined segment. */ val PROLONG_ON_SUBMISSION by enumField { description = "PROLONG_ON_SUBMISSION" } /** Prolongs a task if a submission arrives within a certain time limit towards the end. */ } - /** Name / description of the [ScoreOption]. */ + /** Name / description of the [DbScoreOption]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Converts this [HintOption] to a RESTful API representation [ApiTaskOption]. + * Converts this [DbHintOption] to a RESTful API representation [ApiTaskOption]. * * @return [ApiTaskOption] */ - fun toApi() = ApiTaskOption.values().find { it.toTaskOption() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiTaskOption.values().find { it.toDb() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt new file mode 100644 index 000000000..04efce596 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt @@ -0,0 +1,61 @@ +package dev.dres.data.model.template.team + +import dev.dres.api.rest.types.competition.team.ApiTeam +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.DbEvaluationTemplate +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.link.OnDeletePolicy +import kotlinx.dnq.query.asSequence + + +/** The ID of a [DbTeam]. */ +typealias TeamId = String + +/** + * Represents a [DbTeam] that takes part in a competition managed by DRES. + * + * @author Ralph Gasser, Loris Sauter, Luca Rossetto + * @version 2.0.0 + */ +class DbTeam(entity: Entity) : PersistentEntity(entity) { + companion object: XdNaturalEntityType() { + /** Combination of [DbTeam] name / competition must be unique. */ + override val compositeIndices = listOf( + listOf(DbTeam::name, DbTeam::evaluation) + ) + } + + /** The [TeamId] of this [DbTeam]. */ + var teamId: TeamId + get() = this.id + set(value) { this.id = value } + + /** The name held by this [DbTeam]. Must be unique!*/ + var name by xdRequiredStringProp(unique = false, trimmed = true) + + /** The color used by this [DbTeam]. HTML colour code. */ + var color by xdRequiredStringProp(unique = false, trimmed = true) + + /** Logo used by this [DbTeam] as Blob. */ + var logo by xdBlobProp() + + /** The [DbEvaluationTemplate] this [DbTeam] belongs to. */ + val evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::teams) + + /** The [DbTeamGroup] this [DbTeam] belongs to (or null if not assigned to a group). */ + var group: DbTeamGroup? by xdLink0_1(DbTeamGroup::teams) + + /** The [DbUser]s that belong to this [DbTeam]. */ + val users by xdLink0_N(DbUser, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + + /** + * Converts this [DbTeam] to a RESTful API representation [ApiTeam]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [ApiTeam] + */ + fun toApi() = ApiTeam(this.teamId, this.name, this.color, this.users.asSequence().map { it.toApi() }.toList()) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregator.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt similarity index 69% rename from backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregator.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt index 78ba0cf65..2d46f520a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregator.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt @@ -6,34 +6,36 @@ import kotlinx.dnq.XdEnumEntityType import kotlinx.dnq.xdRequiredStringProp /** - * Enumeration of available [TeamAggregator]s. + * Enumeration of available [DbTeamAggregator]s. * * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -class TeamAggregator(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { +class DbTeamAggregator(entity: Entity) : XdEnumEntity(entity) { + companion object : XdEnumEntityType() { val MAX by enumField { description = "MAX" } val MIN by enumField { description = "MIN" } val MEAN by enumField { description = "MEAN" } val LAST by enumField { description = "LAST" } } - /** Name / description of the [TeamAggregator]. */ + /** Name / description of the [DbTeamAggregator]. */ var description by xdRequiredStringProp(unique = true) private set /** - * Creates and returns a new [TeamAggregatorImpl] for this [TeamAggregator]. + * Creates and returns a new [TeamAggregatorImpl] for this [DbTeamAggregator]. * - * @param teams The list of [Team]s to create the [TeamAggregatorImpl] for. + * @param teams The list of [DbTeam]s to create the [TeamAggregatorImpl] for. * @return [TeamAggregatorImpl] */ - fun newInstance(teams: List) = when(this) { + fun newInstance(teams: List) = when(this) { MAX -> TeamAggregatorImpl.Max(teams) MIN -> TeamAggregatorImpl.Min(teams) MEAN -> TeamAggregatorImpl.Mean(teams) LAST -> TeamAggregatorImpl.Last(teams) else -> throw IllegalStateException("Failed to generated aggregator for unknown team group ${this.description}.") } + + override fun toString() = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt similarity index 50% rename from backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt rename to backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt index 39e7d42c1..3138c4087 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt @@ -2,8 +2,8 @@ package dev.dres.data.model.template.team import dev.dres.api.rest.types.competition.team.ApiTeamGroup import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.DbEvaluationTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -12,36 +12,36 @@ import kotlinx.dnq.query.toList typealias TeamGroupId = String /** - * Represents a [TeamGroup] that takes part in a competition managed by DRES. + * Represents a [DbTeamGroup] that takes part in a competition managed by DRES. * - * Can be used to aggregate score values across [Team]s + * Can be used to aggregate score values across [DbTeam]s * * @author Luca Rossetto * @version 1.0.0 */ -class TeamGroup(entity: Entity) : PersistentEntity(entity) { +class DbTeamGroup(entity: Entity) : PersistentEntity(entity) { - companion object: XdNaturalEntityType() + companion object: XdNaturalEntityType() - /** The [UserId] of this [User]. */ + /** The [UserId] of this [DbUser]. */ var teamGroupId: TeamGroupId get() = this.id set(value) { this.id = value } - /** The name held by this [User]. Must be unique!*/ + /** The name held by this [DbUser]. Must be unique!*/ var name by xdRequiredStringProp(unique = false, trimmed = false) - /** The default [TeamAggregator] to use for this [TeamGroup]. */ - var defaultAggregator by xdLink1(TeamAggregator) + /** The default [DbTeamAggregator] to use for this [DbTeamGroup]. */ + var defaultAggregator by xdLink1(DbTeamAggregator) - /** The [EvaluationTemplate] this [Team] belongs to. */ - val evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::teamsGroups) + /** The [DbEvaluationTemplate] this [DbTeam] belongs to. */ + val evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::teamsGroups) - /** The [Team]s that belong to this [TeamGroup]. */ - val teams by xdLink1_N(Team::group) + /** The [DbTeam]s that belong to this [DbTeamGroup]. */ + val teams by xdLink1_N(DbTeam::group) /** - * Converts this [TeamGroup] to a RESTful API representation [ApiTeamGroup]. + * Converts this [DbTeamGroup] to a RESTful API representation [ApiTeamGroup]. * * This is a convenience method and requires an active transaction context. * @@ -50,7 +50,7 @@ class TeamGroup(entity: Entity) : PersistentEntity(entity) { fun toApi() = ApiTeamGroup(this.teamGroupId, this.name, this.teams.asSequence().map { it.toApi() }.toList(), this.defaultAggregator.name) /** - * Returns a new [TeamAggregatorImpl] for this [TeamGroup]. + * Returns a new [TeamAggregatorImpl] for this [DbTeamGroup]. * * This is a convenience method and requires an active transaction context. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt deleted file mode 100644 index 16f678759..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/Team.kt +++ /dev/null @@ -1,61 +0,0 @@ -package dev.dres.data.model.template.team - -import dev.dres.api.rest.types.competition.team.ApiTeam -import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.EvaluationTemplate -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.* -import kotlinx.dnq.link.OnDeletePolicy -import kotlinx.dnq.query.asSequence - - -/** The ID of a [Team]. */ -typealias TeamId = String - -/** - * Represents a [Team] that takes part in a competition managed by DRES. - * - * @author Ralph Gasser, Loris Sauter, Luca Rossetto - * @version 2.0.0 - */ -class Team(entity: Entity) : PersistentEntity(entity) { - companion object: XdNaturalEntityType() { - /** Combination of [Team] name / competition must be unique. */ - override val compositeIndices = listOf( - listOf(Team::name, Team::evaluation) - ) - } - - /** The [TeamId] of this [Team]. */ - var teamId: TeamId - get() = this.id - set(value) { this.id = value } - - /** The name held by this [Team]. Must be unique!*/ - var name by xdRequiredStringProp(unique = false, trimmed = true) - - /** The color used by this [Team]. HTML colour code. */ - var color by xdRequiredStringProp(unique = false, trimmed = true) - - /** Logo used by this [Team] as Blob. */ - var logo by xdBlobProp() - - /** The [EvaluationTemplate] this [Team] belongs to. */ - val evaluation: EvaluationTemplate by xdParent(EvaluationTemplate::teams) - - /** The [TeamGroup] this [Team] belongs to (or null if not assigned to a group). */ - var group: TeamGroup? by xdLink0_1(TeamGroup::teams) - - /** The [User]s that belong to this [Team]. */ - val users by xdLink0_N(User, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) - - /** - * Converts this [Team] to a RESTful API representation [ApiTeam]. - * - * This is a convenience method and requires an active transaction context. - * - * @return [ApiTeam] - */ - fun toApi() = ApiTeam(this.teamId, this.name, this.color, this.users.asSequence().map { it.toApi() }.toList()) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt index dbbbabf3b..cbca9467d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt @@ -1,12 +1,12 @@ package dev.dres.data.model.template.team /** - * Implementation of different [TeamAggregator]s. + * Implementation of different [DbTeamAggregator]s. * * @author Luca Rossetto * @version 1.0.0 */ -sealed class TeamAggregatorImpl constructor(teams: List) { +sealed class TeamAggregatorImpl constructor(teams: List) { /** Internal set of [TeamId]s. */ private val teamIds = teams.map { it.teamId }.toSet() @@ -22,22 +22,22 @@ sealed class TeamAggregatorImpl constructor(teams: List) { return lastValue } - class Max(teams: List) : TeamAggregatorImpl(teams) { + class Max(teams: List) : TeamAggregatorImpl(teams) { override fun computeAggregation(teamScores: Map): Double = teamScores.map { it.value }.maxOrNull() ?: 0.0 } - class Min(teams: List) : TeamAggregatorImpl(teams) { + class Min(teams: List) : TeamAggregatorImpl(teams) { override fun computeAggregation(teamScores: Map): Double = teamScores.map { it.value }.minOrNull() ?: 0.0 } - class Mean(teams: List) : TeamAggregatorImpl(teams) { + class Mean(teams: List) : TeamAggregatorImpl(teams) { override fun computeAggregation(teamScores: Map): Double = if (teamScores.isEmpty()) 0.0 else teamScores.map { it.value }.sum() / teamScores.size } - class Last(teams: List) : TeamAggregatorImpl(teams) { + class Last(teams: List) : TeamAggregatorImpl(teams) { private val lastScores = mutableListOf>() override fun computeAggregation(teamScores: Map): Double { teamScores.forEach{ diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index 54029cc65..a932b79df 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -24,17 +24,17 @@ object UserManager { } /** - * Creates a [User] with the given [username], [password] and [role]. + * Creates a [DbUser] with the given [username], [password] and [role]. * - * @param username The name of the [User]. Must be unique. + * @param username The name of the [DbUser]. Must be unique. * @param password The [Password.Hashed] of the user. - * @param role The [Role] of the new user. + * @param role The [DbRole] of the new user. */ - fun create(username: String, password: Password.Hashed, role: Role): Boolean { + fun create(username: String, password: Password.Hashed, role: DbRole): Boolean { check(::store.isInitialized) { "UserManager requires an initialized store which is unavailable. This is a programmer's error!"} try { this.store.transactional { - User.new { + DbUser.new { this.username = username this.password = password.password this.role = role @@ -47,42 +47,42 @@ object UserManager { } /** - * Creates a [User] with the given [username], [password] and [role]. + * Creates a [DbUser] with the given [username], [password] and [role]. * - * @param username The name of the [User]. Must be unique. + * @param username The name of the [DbUser]. Must be unique. * @param password The [Password.Hashed] of the user. * @param role The [ApiRole] of the new user. */ fun create(username: String, password: Password.Hashed, role: ApiRole): Boolean { check(::store.isInitialized) { "UserManager requires an initialized store which is unavailable. This is a programmer's error!"} - return create(username, password, role.toRole() ?: throw IllegalArgumentException("Invalid Role")) + return create(username, password, role.toDb() ?: throw IllegalArgumentException("Invalid Role")) } /** - * Creates a [User] with the given [username], [password] and [role]. + * Creates a [DbUser] with the given [username], [password] and [role]. * - * @param username The name of the [User]. Must be unique. + * @param username The name of the [DbUser]. Must be unique. * @param password The [Password.Plain] of the user. - * @param role The [Role] of the new user. + * @param role The [DbRole] of the new user. */ - fun create(username: String, password: Password.Plain, role: Role): Boolean { + fun create(username: String, password: Password.Plain, role: DbRole): Boolean { check(::store.isInitialized) { "UserManager requires an initialized store which is unavailable. This is a programmer's error!"} return create(username, password.hash(), role) } /** - * Updates a [User] with the given [UserId], [username], [password] and [role]. + * Updates a [DbUser] with the given [UserId], [username], [password] and [role]. * * @param id The [UserId] of the user to update. - * @param username The name of the [User]. Must be unique. + * @param username The name of the [DbUser]. Must be unique. * @param password The [Password.Hashed] of the user. - * @param role The [Role] of the new user. + * @param role The [DbRole] of the new user. */ - fun update(id: UserId?, username: String?, password: Password.Hashed?, role: Role?): Boolean = this.store.transactional { + fun update(id: UserId?, username: String?, password: Password.Hashed?, role: DbRole?): Boolean = this.store.transactional { val user = if (id != null) { - User.query(User::id eq id).firstOrNull() + DbUser.query(DbUser::id eq id).firstOrNull() } else if (username != null) { - User.query(User::username eq username).firstOrNull() + DbUser.query(DbUser::username eq username).firstOrNull() } else { null } @@ -94,37 +94,37 @@ object UserManager { } /** - * Updates a [User] with the given [UserId], [username], [password] and [role]. + * Updates a [DbUser] with the given [UserId], [username], [password] and [role]. * * @param id The [UserId] of the user to update. - * @param username The name of the [User]. Must be unique. + * @param username The name of the [DbUser]. Must be unique. * @param password The [Password.Plain] of the user. - * @param role The [Role] of the new user. + * @param role The [DbRole] of the new user. */ - fun update(id: UserId?, username: String?, password: Password.Plain?, role: Role?): Boolean + fun update(id: UserId?, username: String?, password: Password.Plain?, role: DbRole?): Boolean = update(id, username, password?.hash(), role) /** - * Updates a [User] for the given [id] based o the [request]. + * Updates a [DbUser] for the given [id] based o the [request]. * * @param id The [UserId] of the user to update. * @param request The [UserRequest] detailing the update * @return True on success, false otherwise. */ fun update(id: UserId?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.toRole()) + = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.toDb()) /** - * Deletes the [User] for the given [UserId]. + * Deletes the [DbUser] for the given [UserId]. * - * @param username The name of the [User] to delete. + * @param username The name of the [DbUser] to delete. * @return True on success, false otherwise. */ fun delete(id: UserId? = null, username: String? = null): Boolean = this.store.transactional { val user = if (id != null) { - User.query(User::id eq id).firstOrNull() + DbUser.query(DbUser::id eq id).firstOrNull() } else if (username != null) { - User.query(User::username eq username).firstOrNull() + DbUser.query(DbUser::username eq username).firstOrNull() } else { null } @@ -137,41 +137,41 @@ object UserManager { } /** - * Lists all [User] objects in DRES. + * Lists all [DbUser] objects in DRES. * - * @return List of all [User]s. + * @return List of all [DbUser]s. */ - fun list(): List = this.store.transactional(readonly = true) { - User.all().toList() + fun list(): List = this.store.transactional(readonly = true) { + DbUser.all().toList() } /** - * Checks for the existence of the [User] with the given [EvaluationId]. + * Checks for the existence of the [DbUser] with the given [EvaluationId]. * * @param id [EvaluationId] to check. - * @return True if [User] exists, false otherwise. + * @return True if [DbUser] exists, false otherwise. */ fun exists(id: UserId? = null, username: String? = null): Boolean = this.store.transactional(readonly = true) { if (id != null) { - User.query(User::id eq id).isNotEmpty + DbUser.query(DbUser::id eq id).isNotEmpty } else if (username != null) { - User.query(User::username eq username).isNotEmpty + DbUser.query(DbUser::username eq username).isNotEmpty } else { throw IllegalArgumentException("Either user ID or username must be non-null!") } } /** - * Returns the [User] for the given [EvaluationId] or null if [User] doesn't exist. + * Returns the [DbUser] for the given [EvaluationId] or null if [DbUser] doesn't exist. * - * @param id The [EvaluationId] of the [User] to fetch. - * @return [User] or null + * @param id The [EvaluationId] of the [DbUser] to fetch. + * @return [DbUser] or null */ - fun get(id: UserId? = null, username: String? = null): User? = this.store.transactional(readonly = true) { + fun get(id: UserId? = null, username: String? = null): DbUser? = this.store.transactional(readonly = true) { if (id != null) { - User.query(User::id eq id).firstOrNull() + DbUser.query(DbUser::id eq id).firstOrNull() } else if (username != null) { - User.query(User::username eq username).firstOrNull() + DbUser.query(DbUser::username eq username).firstOrNull() } else { null } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index b50a2687d..0d66d1550 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -5,16 +5,16 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType -import dev.dres.data.model.admin.Role -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.admin.DbRole +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.SubmissionId -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -56,7 +56,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override val runProperties: RunProperties get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) - /** Tracks the current [TaskTemplate] per [TeamId]. */ + /** Tracks the current [DbTaskTemplate] per [TeamId]. */ private val statusMap: MutableMap = HashMap() /** A [Map] of all viewers, i.e., DRES clients currently registered with this [InteractiveAsynchronousRunManager]. */ @@ -85,8 +85,8 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override val name: String get() = this.evaluation.name - /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template: EvaluationTemplate + /** The [DbEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template: DbEvaluationTemplate get() = this.evaluation.description /** The global [RunManagerStatus] of this [InteractiveAsynchronousRunManager]. */ @@ -117,7 +117,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.store.transactional(true) { this.template.teams.asSequence().forEach { - val teamContext = RunActionContext("", it.teamId, setOf(Role.ADMIN)) + val teamContext = RunActionContext("", it.teamId, setOf(DbRole.ADMIN)) this.updatables.add(EndTaskUpdatable(this, teamContext)) /* Initialize map and set all tasks pointers to the first task. */ @@ -137,7 +137,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(Pair(task, sub)) - if (sub.verdicts.filter { v -> v.status eq VerdictStatus.INDETERMINATE }.any()) { + if (sub.verdicts.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } } @@ -205,27 +205,27 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * Returns the currently active [TaskTemplate] for the given team. Requires [RunManager.status] for the + * Returns the currently active [DbTaskTemplate] for the given team. Requires [RunManager.status] for the * requesting team to be [RunManagerStatus.ACTIVE]. * * @param context The [RunActionContext] used for the invocation. - * @return The [TaskTemplate] for the given team. + * @return The [DbTaskTemplate] for the given team. */ - override fun currentTaskTemplate(context: RunActionContext): TaskTemplate { + override fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate { require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager." } return this.evaluation.currentTaskDescription(context.teamId) } /** - * Prepares this [InteractiveAsynchronousEvaluation] for the execution of previous [TaskTemplate] - * as per order defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting team + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of previous [DbTaskTemplate] + * as per order defined in [DbEvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting team * to be [RunManagerStatus.ACTIVE]. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation * does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [TaskTemplate] was moved, false otherwise. Usually happens if last [TaskTemplate] has been reached. + * @return True if [DbTaskTemplate] was moved, false otherwise. Usually happens if last [DbTaskTemplate] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { @@ -239,14 +239,14 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * Prepares this [InteractiveAsynchronousEvaluation] for the execution of next [TaskTemplate] - * as per order defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of next [DbTaskTemplate] + * as per order defined in [DbEvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting * team to be [RunManagerStatus.ACTIVE]. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [TaskTemplate] was moved, false otherwise. Usually happens if last [TaskTemplate] has been reached. + * @return True if [DbTaskTemplate] was moved, false otherwise. Usually happens if last [DbTaskTemplate] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { @@ -260,14 +260,14 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * Prepares this [InteractiveAsynchronousEvaluation] for the execution of [TaskTemplate] with the given [index] - * as per order defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting + * Prepares this [InteractiveAsynchronousEvaluation] for the execution of [DbTaskTemplate] with the given [index] + * as per order defined in [DbEvaluationTemplate.tasks]. Requires [RunManager.status] for the requesting * team to be [RunManagerStatus.ACTIVE]. * * As all state affecting methods, this method throws an [IllegalStateException] if invocation does not match the current state. * * @param context The [RunActionContext] used for the invocation. - * @return True if [TaskTemplate] was moved, false otherwise. Usually happens if last [TaskTemplate] has been reached. + * @return True if [DbTaskTemplate] was moved, false otherwise. Usually happens if last [DbTaskTemplate] has been reached. * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun goTo(context: RunActionContext, index: Int) = this.stateLock.write { @@ -386,7 +386,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * Returns the time in milliseconds that has elapsed since the start of the current [Task]. + * Returns the time in milliseconds that has elapsed since the start of the current [DbTask]. * Only works if the [RunManager] is in state [RunManagerStatus.ACTIVE]. If no task is running, this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. @@ -449,22 +449,22 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * List of all [Submission]s for this [InteractiveAsynchronousRunManager], irrespective of the [Task] it belongs to. + * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s. + * @return List of [DbSubmission]s. */ - override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { + override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { this.evaluation.tasks.flatMap { it.getSubmissions() } } /** - * Returns the [Submission]s for all currently active [AbstractInteractiveTask]s or an empty [List], if no such task is active. + * Returns the [DbSubmission]s for all currently active [AbstractInteractiveTask]s or an empty [List], if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s. + * @return List of [DbSubmission]s. */ - override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { + override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { this.currentTask(context)?.getSubmissions()?.toList() ?: emptyList() } @@ -495,21 +495,21 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * Invoked by an external caller to post a new [Submission] for the [TaskRun] that is currently being - * executed by this [InteractiveAsynchronousRunManager]. [Submission]s usually cause updates to the + * Invoked by an external caller to post a new [DbSubmission] for the [TaskRun] that is currently being + * executed by this [InteractiveAsynchronousRunManager]. [DbSubmission]s usually cause updates to the * internal state and/or the [Scoreboard] of this [InteractiveRunManager]. * - * This method will not throw an exception and instead returns false if a [Submission] was + * This method will not throw an exception and instead returns false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submission The [Submission] to be posted. + * @param submission The [DbSubmission] to be posted. * - * @return [VerdictStatus] of the [Submission] + * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ - override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } require(submission.verdicts.size() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ @@ -528,29 +528,29 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** - * Invoked by an external caller to update an existing [Submission] by its [Submission.submissionId] with a new [VerdictStatus]. - * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveAsynchronousRunManager]. + * Invoked by an external caller to update an existing [DbSubmission] by its [DbSubmission.submissionId] with a new [DbVerdictStatus]. + * [DbSubmission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveAsynchronousRunManager]. * - * This method will not throw an exception and instead returns false if a [Submission] was + * This method will not throw an exception and instead returns false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [SubmissionId] of the [Submission] to update. - * @param submissionStatus The new [VerdictStatus] + * @param submissionId The [SubmissionId] of the [DbSubmission] to update. + * @param submissionStatus The new [DbVerdictStatus] * * @return Whether the update was successful or not */ - override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: VerdictStatus): Boolean = this.stateLock.read { - val verdict = Verdict.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false - val task = this.taskForId(context, verdict.task.id) ?: return false + override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: DbVerdictStatus): Boolean = this.stateLock.read { + val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false + val task = this.taskForId(context, answerSet.task.id) ?: return false /* Actual update - currently, only status update is allowed */ - if (verdict.status != submissionStatus) { - verdict.status = submissionStatus + if (answerSet.status != submissionStatus) { + answerSet.status = submissionStatus /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, verdict.submission)) + this.scoresUpdatable.enqueue(Pair(task, answerSet.submission)) /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId!!) @@ -665,7 +665,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn if (timeLeft <= 0) { this.stateLock.write { task.end() - AuditLogger.taskEnd(this.id, task.id, AuditLogSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, task.id, DbAuditLogSource.INTERNAL, null) } /* Enqueue WS message for sending */ @@ -675,7 +675,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn val task = this.evaluation.currentTaskForTeam(team.teamId) ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") task.start() - AuditLogger.taskStart(this.id, task.teamId, task.template, AuditLogSource.REST, null) + AuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_START), team.teamId) } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 005f107c5..5e4f8db55 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -1,11 +1,11 @@ package dev.dres.run -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard @@ -19,8 +19,8 @@ interface InteractiveRunManager : RunManager { val scoreHistory: List /** - * Prepares this [InteractiveRunManager] for the execution of previous [TaskTemplate] as per order defined in - * [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of previous [DbTaskTemplate] as per order defined in + * [DbEvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws * an [IllegalStateException] if invocation does not match the current state. @@ -32,8 +32,8 @@ interface InteractiveRunManager : RunManager { fun previous(context: RunActionContext): Boolean /** - * Prepares this [InteractiveRunManager] for the execution of next [TaskTemplate] as per order defined in - * [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of next [DbTaskTemplate] as per order defined in + * [DbEvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws * an [IllegalStateException] if invocation oes not match the current state. @@ -45,8 +45,8 @@ interface InteractiveRunManager : RunManager { fun next(context: RunActionContext): Boolean /** - * Prepares this [InteractiveRunManager] for the execution of the [TaskTemplate] given by the index as per order - * defined in [EvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. + * Prepares this [InteractiveRunManager] for the execution of the [DbTaskTemplate] given by the index as per order + * defined in [DbEvaluationTemplate.tasks]. Requires [RunManager.status] to be [RunManagerStatus.ACTIVE]. * * This is part of the [InteractiveRunManager]'s navigational state. As all state affecting methods, this method throws * an [IllegalStateException] if invocation does not match the current state. @@ -58,13 +58,13 @@ interface InteractiveRunManager : RunManager { fun goTo(context: RunActionContext, index: Int) /** - * Reference to the currently active [TaskTemplate]. This is part of the [InteractiveRunManager]'s + * Reference to the currently active [DbTaskTemplate]. This is part of the [InteractiveRunManager]'s * navigational state. * * @param context The [RunActionContext] used for the invocation. - * @return [TaskTemplate] + * @return [DbTaskTemplate] */ - fun currentTaskTemplate(context: RunActionContext): TaskTemplate + fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate /** * Starts the [currentTask] and thus moves the [RunManager.status] from @@ -146,20 +146,20 @@ interface InteractiveRunManager : RunManager { fun taskForId(context: RunActionContext, taskId: EvaluationId): TaskRun? /** - * List of all [Submission]s for this [InteractiveRunManager], irrespective of the [Task] it belongs to. + * List of all [DbSubmission]s for this [InteractiveRunManager], irrespective of the [DbTask] it belongs to. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s + * @return List of [DbSubmission]s */ - fun allSubmissions(context: RunActionContext): List + fun allSubmissions(context: RunActionContext): List /** - * List of [Submission]s for the current [Task]. + * List of [DbSubmission]s for the current [DbTask]. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s + * @return List of [DbSubmission]s */ - fun currentSubmissions(context: RunActionContext): List + fun currentSubmissions(context: RunActionContext): List /** * Override the ready state for a given viewer ID. @@ -171,19 +171,19 @@ interface InteractiveRunManager : RunManager { fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean /** - * Invoked by an external caller to update an existing [Submission] by its [Submission.uid] with a new [VerdictStatus]. - * [Submission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveRunManager]. + * Invoked by an external caller to update an existing [DbSubmission] by its [DbSubmission.uid] with a new [DbVerdictStatus]. + * [DbSubmission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveRunManager]. * - * This method will not throw an exception and instead returns false if a [Submission] was + * This method will not throw an exception and instead returns false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [EvaluationId] of the [Submission] to update. - * @param submissionStatus The new [VerdictStatus] + * @param submissionId The [EvaluationId] of the [DbSubmission] to update. + * @param submissionStatus The new [DbVerdictStatus] * * @return Whether the update was successful or not * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: VerdictStatus): Boolean + fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: DbVerdictStatus): Boolean } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 826364540..a75a1e047 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -5,15 +5,15 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType -import dev.dres.data.model.audit.AuditLogSource +import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.template.task.options.TaskOption +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.audit.AuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent @@ -33,7 +33,7 @@ import kotlin.math.max /** * An implementation of [RunManager] aimed at distributed execution having a single DRES Server instance and multiple - * viewers connected via WebSocket. Before starting a [Task], all viewer instances are synchronized. + * viewers connected via WebSocket. Before starting a [DbTask], all viewer instances are synchronized. * * @version 3.0.0 * @author Ralph Gasser @@ -61,8 +61,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override val name: String get() = this.evaluation.name - /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template: EvaluationTemplate + /** The [DbEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template: DbEvaluationTemplate get() = this.evaluation.description /** The status of this [RunManager]. */ @@ -130,7 +130,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(Pair(task, sub)) - if (sub.verdicts.filter { v -> v.status eq VerdictStatus.INDETERMINATE }.any()) { + if (sub.verdicts.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } } @@ -174,7 +174,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch TODO("Not yet implemented") } - override fun currentTaskTemplate(context: RunActionContext): TaskTemplate = this.stateLock.write { + override fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate = this.stateLock.write { checkStatus( RunManagerStatus.CREATED, RunManagerStatus.ACTIVE/*, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED*/ @@ -272,14 +272,14 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.evaluation.currentTaskTemplate}") } - /** List of [Task] for this [InteractiveSynchronousRunManager]. */ + /** List of [DbTask] for this [InteractiveSynchronousRunManager]. */ override fun tasks(context: RunActionContext): List = this.evaluation.tasks /** - * Returns the currently active [Task]s or null, if no such task is active. + * Returns the currently active [DbTask]s or null, if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return [Task] or null + * @return [DbTask] or null */ override fun currentTask(context: RunActionContext) = this.stateLock.read { when (this.evaluation.currentTask?.status) { @@ -291,7 +291,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Returns [Task]s for a specific task [EvaluationId]. May be empty. + * Returns [DbTask]s for a specific task [EvaluationId]. May be empty. * * @param taskId The [EvaluationId] of the [TaskRun]. */ @@ -299,34 +299,34 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.tasks.find { it.id == taskId } /** - * List of all [Submission]s for this [InteractiveAsynchronousRunManager], irrespective of the [Task] it belongs to. + * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s. + * @return List of [DbSubmission]s. */ - override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { + override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { this.evaluation.tasks.flatMap { it.getSubmissions() } } /** - * Returns the [Submission]s for all currently active [Task]s or an empty [List], if no such task is active. + * Returns the [DbSubmission]s for all currently active [DbTask]s or an empty [List], if no such task is active. * * @param context The [RunActionContext] used for the invocation. - * @return List of [Submission]s for the currently active [Task] + * @return List of [DbSubmission]s for the currently active [DbTask] */ - override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { + override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { this.currentTask(context)?.getSubmissions()?.toList() ?: emptyList() } /** - * Returns the number of [Task]s held by this [RunManager]. + * Returns the number of [DbTask]s held by this [RunManager]. * - * @return The number of [Task]s held by this [RunManager] + * @return The number of [DbTask]s held by this [RunManager] */ override fun taskCount(context: RunActionContext): Int = this.evaluation.tasks.size /** - * Adjusts the duration of the current [Task] by the specified amount. Amount can be either positive or negative. + * Adjusts the duration of the current [DbTask] by the specified amount. Amount can be either positive or negative. * * @param s The number of seconds to adjust the duration by. * @return Time remaining until the task will end in milliseconds @@ -347,7 +347,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Returns the time in milliseconds that is left until the end of the current [Task]. + * Returns the time in milliseconds that is left until the end of the current [DbTask]. * Only works if the [RunManager] is in wrong [RunManagerStatus]. If no task is running, * this method returns -1L. * @@ -367,7 +367,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Returns the time in milliseconds that has elapsed since the start of the current [Task]. + * Returns the time in milliseconds that has elapsed since the start of the current [DbTask]. * Only works if the [RunManager] is in wrong [RunManagerStatus]. If no task is running, this method returns -1L. * * @return Time remaining until the task will end or -1, if no task is running. @@ -435,17 +435,17 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Processes incoming [Submission]s. If a [Task] is running then that [Submission] will usually - * be associated with that [Task]. + * Processes incoming [DbSubmission]s. If a [DbTask] is running then that [DbSubmission] will usually + * be associated with that [DbTask]. * - * This method will not throw an exception and instead return false if a [Submission] was + * This method will not throw an exception and instead return false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submission [Submission] that should be registered. + * @param submission [DbSubmission] that should be registered. */ - override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus = this.stateLock.read { assureTaskRunning() /* Register submission. */ @@ -453,8 +453,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") task.postSubmission(submission) - /** Checks for the presence of the [TaskOption.PROLONG_ON_SUBMISSION] and applies it. */ - if (task.template.taskGroup.type.options.filter { it eq TaskOption.PROLONG_ON_SUBMISSION }.any()) { + /** Checks for the presence of the [DbTaskOption.PROLONG_ON_SUBMISSION] and applies it. */ + if (task.template.taskGroup.type.options.filter { it eq DbTaskOption.PROLONG_ON_SUBMISSION }.any()) { this.prolongOnSubmit(context, submission) } @@ -467,28 +467,28 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Processes incoming [Submission]s. If a [Task] is running then that [Submission] will usually - * be associated with that [Task]. + * Processes incoming [DbSubmission]s. If a [DbTask] is running then that [DbSubmission] will usually + * be associated with that [DbTask]. * - * This method will not throw an exception and instead return false if a [Submission] was + * This method will not throw an exception and instead return false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submissionId The [EvaluationId] of the [Submission] to update. - * @param submissionStatus The new [VerdictStatus] + * @param submissionId The [EvaluationId] of the [DbSubmission] to update. + * @param submissionStatus The new [DbVerdictStatus] * @return True on success, false otherwise. */ - override fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: VerdictStatus): Boolean = this.stateLock.read { - val verdict = Verdict.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false - val task = this.taskForId(context, verdict.task.id) ?: return false + override fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: DbVerdictStatus): Boolean = this.stateLock.read { + val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false + val task = this.taskForId(context, answerSet.task.id) ?: return false /* Actual update - currently, only status update is allowed */ - if (verdict.status != submissionStatus) { - verdict.status = submissionStatus + if (answerSet.status != submissionStatus) { + answerSet.status = submissionStatus /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, verdict.submission)) + this.scoresUpdatable.enqueue(Pair(task, answerSet.submission)) /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId!!) @@ -580,7 +580,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.write { this.evaluation.currentTask!!.start() //this.status = RunManagerStatus.RUNNING_TASK - AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, AuditLogSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, DbAuditLogSource.INTERNAL, null) } /* Enqueue WS message for sending */ @@ -598,7 +598,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.write { task.end() //this.status = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, AuditLogSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, DbAuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.id)) } @@ -612,9 +612,9 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * Applies the [SimpleOption.PROLONG_ON_SUBMISSION] [Option]. * * @param context [RunActionContext] used for invocation. - * @param sub The [Submission] to apply the [Option] for. + * @param sub The [DbSubmission] to apply the [Option] for. */ - private fun prolongOnSubmit(context: RunActionContext, sub: Submission) { + private fun prolongOnSubmit(context: RunActionContext, sub: DbSubmission) { /* require(option.option == SimpleOption.PROLONG_ON_SUBMISSION) { "Cannot process ${option.option} in prolongOnSubmit()." } val limit = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index b08e82f9c..72b95481c 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -4,10 +4,10 @@ import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.data.model.run.* -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable @@ -40,8 +40,8 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation override val name: String get() = this.evaluation.name - /** The [EvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template: EvaluationTemplate + /** The [DbEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template: DbEvaluationTemplate get() = this.evaluation.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ @@ -165,7 +165,7 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation * */ override fun tasks(context: RunActionContext): List = this.evaluation.tasks - override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus { + override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index f125eea29..48dfc6843 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -4,15 +4,15 @@ import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.data.model.template.EvaluationTemplate +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator /** - * A managing class for concrete executions of [EvaluationTemplate], i.e. [InteractiveSynchronousEvaluation]s. + * A managing class for concrete executions of [DbEvaluationTemplate], i.e. [InteractiveSynchronousEvaluation]s. * * @see InteractiveSynchronousEvaluation * @@ -26,11 +26,11 @@ interface RunManager : Runnable { /** A name for identifying this [RunManager]. */ val name: String - /** The [Evaluation] instance that backs this [RunManager]. */ + /** The [DbEvaluation] instance that backs this [RunManager]. */ val evaluation: EvaluationRun - /** The [EvaluationTemplate] that is executed / run by this [RunManager]. */ - val template: EvaluationTemplate + /** The [DbEvaluationTemplate] that is executed / run by this [RunManager]. */ + val template: DbEvaluationTemplate /** List of [Scoreboard]s for this [RunManager]. */ val scoreboards: List @@ -76,10 +76,10 @@ interface RunManager : Runnable { fun updateProperties(properties: RunProperties) /** - * Returns the number of [Task]s held by this [RunManager]. + * Returns the number of [DbTask]s held by this [RunManager]. * * @param context The [RunActionContext] for this invocation. - * @return The number of [Task]s held by this [RunManager] + * @return The number of [DbTask]s held by this [RunManager] */ fun taskCount(context: RunActionContext): Int @@ -92,22 +92,22 @@ interface RunManager : Runnable { fun tasks(context: RunActionContext): List /** - * Invoked by an external caller to post a new [Submission] for the [TaskRun] that is currently being - * executed by this [InteractiveRunManager]. [Submission]s usually cause updates to the internal state and/or + * Invoked by an external caller to post a new [DbSubmission] for the [TaskRun] that is currently being + * executed by this [InteractiveRunManager]. [DbSubmission]s usually cause updates to the internal state and/or * the [Scoreboard] of this [InteractiveRunManager]. * - * This method will not throw an exception and instead returns false if a [Submission] was + * This method will not throw an exception and instead returns false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * * @param context The [RunActionContext] used for the invocation - * @param submission The [Submission] to be posted. + * @param submission The [DbSubmission] to be posted. * - * @return [VerdictStatus] of the [Submission] + * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus + fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. @@ -119,7 +119,7 @@ interface RunManager : Runnable { /** * Invoked by an external caller such in order to inform the [RunManager] that it has received a [ClientMessage]. * - * This method does not throw an exception and instead returns false if a [Submission] was + * This method does not throw an exception and instead returns false if a [DbSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * diff --git a/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt b/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt index a96d48b19..cb6b64c64 100644 --- a/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt +++ b/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt @@ -1,28 +1,28 @@ package dev.dres.run -import dev.dres.data.model.run.Evaluation -import dev.dres.data.model.run.Task +import dev.dres.data.model.run.DbEvaluation +import dev.dres.data.model.run.DbTask /** - * The status of a [Task] within an [Evaluation]. + * The status of a [DbTask] within an [DbEvaluation]. * * @author Luca Rossetto * @version 1.0.0. */ enum class TaskStatus { /** - * A [Task] was freshly created and is ready for execution. + * A [DbTask] was freshly created and is ready for execution. */ CREATED, /** - * A [Task] is currently being prepared for execution. + * A [DbTask] is currently being prepared for execution. */ PREPARING, - /** A [Task] is currently being executed. */ + /** A [DbTask] is currently being executed. */ RUNNING, - /** A [Task] has been completed. */ + /** A [DbTask] has been completed. */ ENDED; } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index b4c470143..043e45a8d 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -2,15 +2,15 @@ package dev.dres.run.audit import dev.dres.api.rest.handler.users.SessionToken import dev.dres.data.model.admin.UserId -import dev.dres.data.model.audit.AuditLogEntry -import dev.dres.data.model.audit.AuditLogSource -import dev.dres.data.model.audit.AuditLogType +import dev.dres.data.model.audit.DbAuditLogEntry +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.audit.DbAuditLogType import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionValidator @@ -36,13 +36,13 @@ object AuditLogger { /** * Logs the start of a DRES competition. * - * @param description The [EvaluationTemplate]. - * @param api The [AuditLogSource] + * @param description The [DbEvaluationTemplate]. + * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun competitionStart(evaluationId: EvaluationId, description: EvaluationTemplate, api: AuditLogSource, session: SessionToken?) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.COMPETITION_START + fun competitionStart(evaluationId: EvaluationId, description: DbEvaluationTemplate, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.COMPETITION_START this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId @@ -55,12 +55,12 @@ object AuditLogger { * Logs the end of a DRES competition. * * @param evaluationId [EvaluationId] that identifies the competition - * @param api The [AuditLogSource] + * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun competitionEnd(evaluationId: EvaluationId, api: AuditLogSource, session: SessionToken?) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.COMPETITION_END + fun competitionEnd(evaluationId: EvaluationId, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.COMPETITION_END this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId @@ -74,13 +74,13 @@ object AuditLogger { * * @param evaluationId [EvaluationId] that identifies the competition * @param taskId [EvaluationId] that identifies the task - * @param description The [TaskTemplate]. - * @param api The [AuditLogSource] + * @param description The [DbTaskTemplate]. + * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: TaskTemplate, api: AuditLogSource, session: SessionToken?) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.TASK_START + fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: DbTaskTemplate, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.TASK_START this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId @@ -96,12 +96,12 @@ object AuditLogger { * @param evaluationId [EvaluationId] that identifies the competition * @param taskId [EvaluationId] that identifies the task * @param modification Description of the modification. - * @param api The [AuditLogSource] + * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: AuditLogSource, session: String?) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.TASK_MODIFIED + fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: DbAuditLogSource, session: String?) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.TASK_MODIFIED this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId @@ -116,12 +116,12 @@ object AuditLogger { * * @param evaluationId [EvaluationId] that identifies the competition * @param taskId [EvaluationId] that identifies the task - * @param api The [AuditLogSource] + * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionToken?) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.TASK_END + fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.TASK_END this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId @@ -134,14 +134,14 @@ object AuditLogger { /** * Logs an incoming submission to DRES. * - * @param submission The [Submission] that was registered. - * @param api The [AuditLogSource] + * @param submission The [DbSubmission] that was registered. + * @param api The [DbAuditLogSource] * @param sessionToken The identifier of the user session. * @param address The IP address of the submitter. */ - fun submission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?, address: String) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.SUBMISSION + fun submission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.SUBMISSION this.source = api this.timestamp = DateTime.now() this.submissionId = submission.id @@ -154,16 +154,16 @@ object AuditLogger { } /** - * Logs the validation of a [Submission] to DRES. + * Logs the validation of a [DbSubmission] to DRES. * - * @param submission The [Submission] the submission that was validated + * @param submission The [DbSubmission] the submission that was validated * @param validator The [SubmissionValidator] instance. */ - fun validateSubmission(submission: Submission, validator: SubmissionValidator) = this.store.transactional { + fun validateSubmission(submission: DbSubmission, validator: SubmissionValidator) = this.store.transactional { this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.SUBMISSION_VALIDATION - this.source = AuditLogSource.INTERNAL + DbAuditLogEntry.new { + this.type = DbAuditLogType.SUBMISSION_VALIDATION + this.source = DbAuditLogSource.INTERNAL this.timestamp = DateTime.now() this.submissionId = submission.id this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId @@ -176,14 +176,14 @@ object AuditLogger { /** * Logs a submission override to DRES. * - * @param submission The [Submission] that was overriden (new snapshot). - * @param api The [AuditLogSource] + * @param submission The [DbSubmission] that was overriden (new snapshot). + * @param api The [DbAuditLogSource] * @param sessionToken The identifier of the user session. */ - fun overrideSubmission(submission: Submission, api: AuditLogSource, sessionToken: SessionToken?) = this.store.transactional { + fun overrideSubmission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?) = this.store.transactional { this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.SUBMISSION_STATUS_OVERWRITE + DbAuditLogEntry.new { + this.type = DbAuditLogType.SUBMISSION_STATUS_OVERWRITE this.source = api this.timestamp = DateTime.now() this.submissionId = submission.id @@ -198,19 +198,19 @@ object AuditLogger { /** * Logs a submission override to DRES. * - * @param verdict The [Submission] that was overriden (new snapshot). + * @param answerSet The [DbSubmission] that was overriden (new snapshot). * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system */ - fun prepareJudgement(verdict: Verdict, validator: JudgementValidator, token: String) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.PREPARE_JUDGEMENT - this.source = AuditLogSource.INTERNAL + fun prepareJudgement(answerSet: DbAnswerSet, validator: JudgementValidator, token: String) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.PREPARE_JUDGEMENT + this.source = DbAuditLogSource.INTERNAL this.timestamp = DateTime.now() - this.submissionId = verdict.submission.id - this.evaluationId = verdict.task.evaluation.evaluationId - this.taskId = verdict.task.taskId - this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.status.description}" + this.submissionId = answerSet.submission.id + this.evaluationId = answerSet.task.evaluation.evaluationId + this.taskId = answerSet.task.taskId + this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${answerSet.status.description}" } } @@ -220,13 +220,13 @@ object AuditLogger { * @param evaluationId [EvaluationId] that identifies the competition * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system - * @param verdict The [VerdictStatus] submitted by the judge. - * @param api The [AuditLogSource] + * @param verdict The [DbVerdictStatus] submitted by the judge. + * @param api The [DbAuditLogSource] * @param sessionToken The identifier of the user session. */ - fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: VerdictStatus, api: AuditLogSource, sessionToken: SessionToken?) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.JUDGEMENT + fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: DbVerdictStatus, api: DbAuditLogSource, sessionToken: SessionToken?) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.JUDGEMENT this.source = api this.timestamp = DateTime.now() this.evaluationId = evaluationId @@ -239,12 +239,12 @@ object AuditLogger { * Logs a user user login event. * * @param userId [EvaluationId] of the user who logged out. - * @param api The [AuditLogSource] + * @param api The [DbAuditLogSource] * @param sessionToken The [SessionToken] */ - fun login(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.LOGIN + fun login(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.LOGIN this.source = api this.timestamp = DateTime.now() this.userId = userId @@ -256,12 +256,12 @@ object AuditLogger { * Logs a user logout event. * * @param userId [EvaluationId] of the user who logged out. - * @param api The [AuditLogSource] + * @param api The [DbAuditLogSource] * @param sessionToken The [SessionToken] */ - fun logout(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) = this.store.transactional { - AuditLogEntry.new { - this.type = AuditLogType.LOGOUT + fun logout(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) = this.store.transactional { + DbAuditLogEntry.new { + this.type = DbAuditLogType.LOGOUT this.source = api this.timestamp = DateTime.now() this.userId = userId diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index 9d63e0653..ae9627725 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -1,20 +1,20 @@ package dev.dres.run.eventstream import com.fasterxml.jackson.annotation.JsonTypeInfo -import dev.dres.data.model.template.EvaluationTemplate -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") sealed class StreamEvent(var timeStamp : Long = System.currentTimeMillis(), var session: String? = null) -class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val taskTemplate: TaskTemplate) : StreamEvent() +class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val taskTemplate: DbTaskTemplate) : StreamEvent() class TaskEndEvent(val runId: EvaluationId, val taskId: EvaluationId) : StreamEvent() -class RunStartEvent(val runId: EvaluationId, val description: EvaluationTemplate) : StreamEvent() +class RunStartEvent(val runId: EvaluationId, val description: DbEvaluationTemplate) : StreamEvent() class RunEndEvent(val runId: EvaluationId) : StreamEvent() -class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : Submission) : StreamEvent(session = session) +class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : DbSubmission) : StreamEvent(session = session) class QueryEventLogEvent(session: String?, val runId: EvaluationId, val queryEventLog: QueryEventLog) : StreamEvent(session = session) class QueryResultLogEvent(session: String?, val runId: EvaluationId, val queryResultLog: QueryResultLog) : StreamEvent(session = session) class InvalidRequestEvent(session: String?, val runId: EvaluationId, val requestData: String) : StreamEvent(session = session) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt index 60e11def3..a58ae8e14 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt @@ -1,9 +1,9 @@ package dev.dres.run.eventstream.handlers -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.template.task.TaskTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.eventstream.StreamEvent import dev.dres.run.eventstream.StreamEventHandler import jetbrains.exodus.database.TransientEntityStore @@ -14,8 +14,8 @@ class ResultLogStatisticsHandler(private val store: TransientEntityStore) : Stre private val writer = PrintWriter(File("statistics/result_log_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - private val lastActiveTask = mutableMapOf() - private val lastActiveTargets = mutableMapOf>>() + private val lastActiveTask = mutableMapOf() + private val lastActiveTargets = mutableMapOf>>() init { diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 6090f3da2..a88472d3e 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -1,8 +1,8 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.eventstream.* import kotlinx.dnq.query.first import kotlinx.dnq.query.size @@ -14,7 +14,7 @@ class SubmissionStatisticsHandler : StreamEventHandler { private val writer = PrintWriter(File("statistics/submission_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - private val submissionTaskMap = mutableMapOf>() + private val submissionTaskMap = mutableMapOf>() private val taskStartMap = mutableMapOf() private val taskNameMap = mutableMapOf() @@ -59,7 +59,7 @@ class SubmissionStatisticsHandler : StreamEventHandler { * * I assume here, that there this handler requires a single verdict per submission. Is this a valid assumption? */ - private fun computeStatistics(submissions: List, taskStart: Long, task: String) { + private fun computeStatistics(submissions: List, taskStart: Long, task: String) { val submissionsByTeam = submissions.groupBy { it.team.teamId } submissionsByTeam.mapValues { it.value.size }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"totalSubmissionsPerTeam\",$count") @@ -67,14 +67,14 @@ class SubmissionStatisticsHandler : StreamEventHandler { submissionsByTeam.mapValues { it.value.firstOrNull { s -> require(s.verdicts.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.verdicts.first().status == VerdictStatus.CORRECT + s.verdicts.first().status == DbVerdictStatus.CORRECT }?.timestamp?.minus(taskStart) }.filter { it.value != null }.forEach{ (teamId, time) -> writer.println("$task,${teamId},\"timeUntilCorrectSubmission\",$time") } submissionsByTeam.mapValues { it.value.indexOfFirst { s -> require(s.verdicts.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.verdicts.first().status == VerdictStatus.CORRECT + s.verdicts.first().status == DbVerdictStatus.CORRECT } }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"incorrectBeforeCorrectSubmissions\",$count") diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 7235242b6..2f2941e63 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -1,8 +1,8 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.eventstream.* import dev.dres.run.score.interfaces.TeamTaskScorer import java.io.File @@ -18,7 +18,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { /** * */ - private val tasks = mutableMapOf() + private val tasks = mutableMapOf() /** * @@ -29,7 +29,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { /** * */ - private val submissionTaskMap = mutableMapOf>() + private val submissionTaskMap = mutableMapOf>() init { writer.println("task,team1,team2,score") diff --git a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt index a3e34a488..227c8f54d 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt @@ -1,10 +1,10 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission /** - * A [SubmissionFilter] that lets all [Submission] pass. + * A [SubmissionFilter] that lets all [DbSubmission] pass. * * @author Ralph Gasser * @version 1.0 @@ -12,5 +12,5 @@ import dev.dres.data.model.submissions.Submission object AllSubmissionFilter : SubmissionFilter { override val reason = "" //will never be relevant - override fun test(t: Submission): Boolean = true + override fun test(t: DbSubmission): Boolean = true } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index 0c66f42b9..c490a78f4 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -1,14 +1,13 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter -import kotlinx.dnq.query.flatMapDistinct import kotlinx.dnq.query.size /** - * A [SubmissionFilter] that filters correct [Submission]s if the number of correct [Submission] for the team exceed the limit. + * A [SubmissionFilter] that filters correct [DbSubmission]s if the number of correct [DbSubmission] for the team exceed the limit. * * @author Ralph Gasser * @author Luca Rossetto @@ -18,9 +17,9 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { override val reason = "Maximum number of correct submissions ($limit) exceeded for the team." constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) - override fun test(submission: Submission): Boolean { + override fun test(submission: DbSubmission): Boolean { return submission.verdicts.asSequence().all { verdict -> - verdict.task.submissions.filter { (it.status eq VerdictStatus.CORRECT).and(it.submission.team eq submission.team) }.size() < limit + verdict.task.submissions.filter { (it.status eq DbVerdictStatus.CORRECT).and(it.submission.team eq submission.team) }.size() < limit } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index f7cc1ba53..8b0802b29 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -1,7 +1,7 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.size @@ -13,10 +13,10 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { override val reason: String = "Maximum number of correct submissions ($limit) exceeded for this item." - override fun test(submission: Submission): Boolean { + override fun test(submission: DbSubmission): Boolean { return submission.verdicts.asSequence().all { verdict -> verdict.task.submissions.filter { - (it.status eq VerdictStatus.CORRECT).and(it.submission.team eq submission.team) + (it.status eq DbVerdictStatus.CORRECT).and(it.submission.team eq submission.team) }.size() < this.limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index 0a6c4047e..e254be5c6 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -1,13 +1,13 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.size /** - * A [SubmissionFilter] that filters correct [Submission]s if the number of correct [Submission] for the team member exceeds the limit. + * A [SubmissionFilter] that filters correct [DbSubmission]s if the number of correct [DbSubmission] for the team member exceeds the limit. * * @author Ralph Gasser * @author Luca Rossetto @@ -17,10 +17,10 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member." - override fun test(submission: Submission): Boolean { + override fun test(submission: DbSubmission): Boolean { return submission.verdicts.asSequence().all { verdict -> verdict.task.submissions.filter { - (it.status eq VerdictStatus.CORRECT).and(it.submission.team eq submission.team).and(it.submission.user eq submission.user) + (it.status eq DbVerdictStatus.CORRECT).and(it.submission.team eq submission.team).and(it.submission.user eq submission.user) }.size() < this.limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index 96f384d89..42f3b8e7c 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -1,13 +1,13 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.isEmpty /** - * A [SubmissionFilter] that filters duplicate [Submission]s in terms of content. + * A [SubmissionFilter] that filters duplicate [DbSubmission]s in terms of content. * * @author Luca Rossetto * @version 1.1.0 @@ -16,7 +16,7 @@ class DuplicateSubmissionFilter : SubmissionFilter { override val reason = "Duplicate submission received." - override fun test(submission: Submission): Boolean { + override fun test(submission: DbSubmission): Boolean { return submission.verdicts.asSequence().all { verdict -> verdict.task.submissions.filter { (it.text eq verdict.text) and (it.item eq verdict.item) and (it.start le (verdict.start ?: Long.MAX_VALUE)) and (it.end ge (verdict.end ?: Long.MIN_VALUE)) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt index 1bba7ab08..005ec923b 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt @@ -1,7 +1,7 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerType import kotlinx.dnq.query.asSequence /** @@ -12,6 +12,6 @@ import kotlinx.dnq.query.asSequence */ class ItemSubmissionFilter : SubmissionFilter { override val reason = "Submission does include temporal information, but whole item was expected" - override fun test(submission: Submission): Boolean - = submission.verdicts.asSequence().any { it.type == VerdictType.TEMPORAL } + override fun test(submission: DbSubmission): Boolean + = submission.verdicts.asSequence().any { it.type == DbAnswerType.TEMPORAL } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index 83642faea..43c47a273 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -1,6 +1,6 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.size @@ -13,7 +13,7 @@ class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi /** * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? */ - override fun test(submission: Submission): Boolean { + override fun test(submission: DbSubmission): Boolean { return submission.verdicts.asSequence().all { v -> v.task.submissions.filter { it.submission.team.id eq submission.team.id }.size() < max } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index ac8f153f0..e5f734d22 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -1,10 +1,8 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import kotlinx.dnq.query.FilteringContext.eq +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.eq import kotlinx.dnq.query.filter import kotlinx.dnq.query.size @@ -20,9 +18,9 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi /** * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? */ - override fun test(submission: Submission): Boolean { + override fun test(submission: DbSubmission): Boolean { return submission.verdicts.asSequence().all { v -> - v.task.submissions.filter { (it.submission.team.id eq submission.team.id) and (it.status eq VerdictStatus.WRONG) }.size() < max + v.task.submissions.filter { (it.submission.team.id eq submission.team.id) and (it.status eq DbVerdictStatus.WRONG) }.size() < max } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt index 59a92d0b4..383905be7 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt @@ -1,17 +1,17 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import org.slf4j.LoggerFactory import java.util.function.Predicate /** - * A [Predicate] that can be used to filter [Submission]'s prior to them being processed - * by the [Submission] evaluation pipeline. + * A [Predicate] that can be used to filter [DbSubmission]'s prior to them being processed + * by the [DbSubmission] evaluation pipeline. * * @author Ralph Gasser * @version 1.1.0 */ -interface SubmissionFilter : Predicate { +interface SubmissionFilter : Predicate { companion object { private val LOGGER = LoggerFactory.getLogger(this::class.java) } @@ -19,12 +19,12 @@ interface SubmissionFilter : Predicate { val reason: String /** - * Tests the given [Submission] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. + * Tests the given [DbSubmission] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. * - * @param submission The [Submission] to check. + * @param submission The [DbSubmission] to check. * @throws SubmissionRejectedException on failure */ - fun acceptOrThrow(submission: Submission) { + fun acceptOrThrow(submission: DbSubmission) { if (!this.test(submission)) { LOGGER.info("Submission $${submission.submissionId} was rejected by filter: $reason") throw SubmissionRejectedException(submission, reason) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt index b3ff61b74..807f8b96b 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt @@ -1,16 +1,16 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission class SubmissionFilterAggregator(private val filters: List) : SubmissionFilter { override val reason = "" //will never be relevant - override fun acceptOrThrow(submission: Submission) { + override fun acceptOrThrow(submission: DbSubmission) { for (filter in filters) { filter.acceptOrThrow(submission) } } - override fun test(t: Submission): Boolean = filters.all { it.test(t) } + override fun test(t: DbSubmission): Boolean = filters.all { it.test(t) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt index 8d26ee097..bb622cced 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt @@ -1,6 +1,6 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission class SubmissionRateFilter(private val minDelayMS: Int = 500) : SubmissionFilter { @@ -8,7 +8,7 @@ class SubmissionRateFilter(private val minDelayMS: Int = 500) : SubmissionFilter override val reason = "Not enough time has passed since last submission, gap needs to be at least $minDelayMS ms" - override fun test(t: Submission): Boolean { + override fun test(t: DbSubmission): Boolean { TODO("Not sure about the semantic here. Do we need a data model extension for this?") /*val mostRecentSubmissionTime = t.task!!.submissions.maxByOrNull { it.timestamp }?.timestamp ?: 0 return (t.timestamp - mostRecentSubmissionTime) >= minDelayMS*/ diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt index fd9632e9f..d807e2e54 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt @@ -1,12 +1,11 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import org.slf4j.LoggerFactory +import dev.dres.data.model.submissions.DbSubmission /** - * An exception that is thrown, when a [Submission] is rejected by a [SubmissionFilter]. + * An exception that is thrown, when a [DbSubmission] is rejected by a [SubmissionFilter]. * * @author Ralph Gasser * @version 1.0.0 */ -class SubmissionRejectedException(s: Submission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file +class SubmissionRejectedException(s: DbSubmission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt index b27c09ef0..dd18fce8c 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt @@ -1,12 +1,12 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerType import kotlinx.dnq.query.asSequence /** - * A [SubmissionFilter} that checks if a [Submission] contains temporal information. + * A [SubmissionFilter} that checks if a [DbSubmission] contains temporal information. * * @author Luca Rossetto * @version 1.1.0 @@ -14,6 +14,6 @@ import kotlinx.dnq.query.asSequence class TemporalSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include temporal information." - override fun test(submission: Submission): Boolean - = submission.verdicts.asSequence().all { it.type == VerdictType.TEMPORAL && it.start != null && it.end != null } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + override fun test(submission: DbSubmission): Boolean + = submission.verdicts.asSequence().all { it.type == DbAnswerType.TEMPORAL && it.start != null && it.end != null } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt index 19519ab55..4fb32ad8e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt @@ -1,11 +1,11 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerType import kotlinx.dnq.query.asSequence /** - * A [SubmissionFilter} that checks if a [Submission] contains text information. + * A [SubmissionFilter} that checks if a [DbSubmission] contains text information. * * @author Luca Rossetto * @version 1.1.0 @@ -14,6 +14,6 @@ class TextualSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include textual information (or is an empty submission)" - override fun test(submission: Submission): Boolean - = submission.verdicts.asSequence().all { it.text != null && it.type == VerdictType.TEXT } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + override fun test(submission: DbSubmission): Boolean + = submission.verdicts.asSequence().all { it.text != null && it.type == DbAnswerType.TEXT } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt index 17622d40f..879291378 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt @@ -1,10 +1,9 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.DbSubmission /** - * A [TaskScorer] implementation that can update scores incrementally on a [Submission] by [Submission] basis. + * A [TaskScorer] implementation that can update scores incrementally on a [DbSubmission] by [DbSubmission] basis. * * @author Luca Rossetto & Ralph Gasser * @version 1.1.0 @@ -13,7 +12,7 @@ interface IncrementalSubmissionTaskScorer: TaskScorer { /** * Updates this [IncrementalSubmissionTaskScorer]'s score just using a single submission. * - * @param submission The [Submission] to update this [IncrementalSubmissionTaskScorer] with. + * @param submission The [DbSubmission] to update this [IncrementalSubmissionTaskScorer] with. */ - fun update(submission: Submission) + fun update(submission: DbSubmission) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt index 136ccf709..403e75a78 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt @@ -1,13 +1,13 @@ package dev.dres.run.score.interfaces import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext /** * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [TaskRun] based on the - * entire [Submission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. + * entire [DbSubmission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. * * @author Luca Rossetto * @author Ralph Gasser @@ -15,10 +15,10 @@ import dev.dres.run.score.TaskContext */ interface RecalculatingSubmissionTaskScorer: TaskScorer { /** - * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [Submission] history. + * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [DbSubmission] history. * - * @param submissions The [Submission]s used to update this [RecalculatingSubmissionTaskScorer] with. + * @param submissions The [DbSubmission]s used to update this [RecalculatingSubmissionTaskScorer] with. * @param context The [TaskContext] in which scoring takes place. */ - fun computeScores(submissions: Collection, context: TaskContext): Map + fun computeScores(submissions: Collection, context: TaskContext): Map } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt index 7ba4d9697..792baa265 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt @@ -1,6 +1,6 @@ package dev.dres.run.score.interfaces -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.TaskRun @@ -8,7 +8,7 @@ import dev.dres.data.model.run.interfaces.TaskRun typealias ScoreEntry = Triple /** - * A scorer for a [TaskRun]. A score is a [Double] value that captures a [Team] performance. + * A scorer for a [TaskRun]. A score is a [Double] value that captures a [DbTeam] performance. * The [TaskScorer] calculates and tracks these scores per [TeamId]. * * @version 1.1.0 diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index a65cde4ff..715951ba7 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,7 +1,7 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.template.TemplateId @@ -10,14 +10,14 @@ import java.util.concurrent.ConcurrentHashMap import kotlin.math.max /** - * A [Scoreboard] that keeps track of the maximum score per [TaskTemplate] as identified by it [TemplateId]. + * A [Scoreboard] that keeps track of the maximum score per [DbTaskTemplate] as identified by it [TemplateId]. * * @author Luca Rossett * @version 1.1.0 */ -class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (TaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 1000.0) : Scoreboard { +class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (DbTaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 1000.0) : Scoreboard { - /** Tracks the score per [TemplateId] (references a [TaskTemplate]). */ + /** Tracks the score per [TemplateId] (references a [DbTaskTemplate]). */ private val scorePerTaskMap = ConcurrentHashMap>() private val teamIds = teams.map { it.teamId } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index d3c4fcbda..4febeba60 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -1,13 +1,13 @@ package dev.dres.run.score.scoreboard -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.interfaces.TaskScorer /** - * A [Scoreboard] tracks the [Score]s for different [Team]s + * A [Scoreboard] tracks the [Score]s for different [DbTeam]s * * @author Ralph Gasser * @version 1.0.1 @@ -25,10 +25,10 @@ interface Scoreboard { fun scores(): List /** - * Retrieves and returns the score of the given [Team] + * Retrieves and returns the score of the given [DbTeam] * - * @param teamId The [Team]'s [TeamId]. - * @return The score for the given [Team]. + * @param teamId The [DbTeam]'s [TeamId]. + * @return The score for the given [DbTeam]. */ fun score(teamId: TeamId): Double diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 6ede49d21..69d424426 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -1,10 +1,10 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.media.MediaType -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.media.DbMediaType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.ScoreEntry @@ -30,9 +30,9 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { /** * TODO: Check for correctness especially if a submission has more than one verdict. Maybe add sanity checks. */ - override fun computeScores(submissions: Collection, context: TaskContext): Map { - val correctSubmissions = submissions.flatMap {s -> s.verdicts.filter { v -> v.status eq VerdictStatus.CORRECT }.toList() } - val wrongSubmissions = submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq VerdictStatus.WRONG }.toList() } + override fun computeScores(submissions: Collection, context: TaskContext): Map { + val correctSubmissions = submissions.flatMap {s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.CORRECT }.toList() } + val wrongSubmissions = submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.WRONG }.toList() } val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.team.id } val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.team.id } val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() @@ -47,12 +47,12 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { return teamScoreMap() } - private fun countQuantized(submissions: Collection): Int = submissions + private fun countQuantized(submissions: Collection): Int = submissions .filter { it.item != null } .groupBy { it.item }.map { when(it.key!!.type) { - MediaType.IMAGE -> 1 - MediaType.VIDEO -> { + DbMediaType.IMAGE -> 1 + DbMediaType.VIDEO -> { val ranges = it.value.map { s -> s.temporalRange!! } TimeUtil.merge(ranges, overlap = 1).size } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 5d2b53e47..bef9caeef 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -1,8 +1,8 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import dev.dres.run.score.interfaces.ScoreEntry @@ -35,15 +35,15 @@ class KisTaskScorer( private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { + override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("No task start time specified.") val taskDuration = context.taskDuration ?: throw IllegalArgumentException("No task duration specified.") val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition this.lastScores = context.teamIds.associateWith { teamId -> val verdicts = submissions.filter { it.team.id == teamId }.sortedBy { it.timestamp }.flatMap { sub -> - sub.verdicts.filter { (it.status eq VerdictStatus.CORRECT) or (it.status eq VerdictStatus.WRONG) }.toList() + sub.verdicts.filter { (it.status eq DbVerdictStatus.CORRECT) or (it.status eq DbVerdictStatus.WRONG) }.toList() } - val firstCorrect = verdicts.indexOfFirst { it.status == VerdictStatus.CORRECT } + val firstCorrect = verdicts.indexOfFirst { it.status == DbVerdictStatus.CORRECT } val score = if (firstCorrect > -1) { val timeFraction = 1.0 - (verdicts[firstCorrect].submission.timestamp - taskStartTime) / tDur max( diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt index 2e463eab9..b96c4c3d2 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt @@ -1,8 +1,8 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer @@ -57,12 +57,12 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } override fun computeScores( - submissions: Collection, + submissions: Collection, context: TaskContext ): Map { val distinctCorrectVideos = submissions.flatMap { submission -> - submission.verdicts.asSequence().filter { it.status == VerdictStatus.CORRECT && it.item != null } + submission.verdicts.asSequence().filter { it.status == DbVerdictStatus.CORRECT && it.item != null } }.mapNotNullTo(mutableSetOf()) {it.item } .size @@ -80,11 +80,11 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint lastScores = this.lastScoresLock.write { teamScoreMapSanitised(submissions.groupBy { it.team }.map { submissionsPerTeam -> - val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { it.verdicts.asSequence().filter { v -> v.item != null && (v.status == VerdictStatus.CORRECT || v.status == VerdictStatus.WRONG) } } + val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { it.verdicts.asSequence().filter { v -> v.item != null && (v.status == DbVerdictStatus.CORRECT || v.status == DbVerdictStatus.WRONG) } } submissionsPerTeam.key.teamId to max(0.0, //prevent negative total scores verdicts.groupBy { it.item!! }.map { - val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == VerdictStatus.CORRECT } + val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == DbVerdictStatus.CORRECT } if (firstCorrectIdx < 0) { //no correct submissions, only penalty it.value.size * -penaltyConstant } else { //apply penalty for everything before correct submission diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 54c46205b..92f25a081 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -1,8 +1,8 @@ package dev.dres.run.updatables import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.template.task.options.SubmissionOption +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManagerStatus @@ -29,17 +29,17 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte override fun update(status: RunManagerStatus) { val taskRun = this.run.currentTask(this.context) if (taskRun != null) { - if (taskRun.template.taskGroup.type.submission.contains(SubmissionOption.LIMIT_CORRECT_PER_TEAM)) { - val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq SubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 + if (taskRun.template.taskGroup.type.submission.contains(DbSubmissionOption.LIMIT_CORRECT_PER_TEAM)) { + val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 if (this.run.timeLeft(this.context) > 0) { if (this.submissions.getAndSet(taskRun.getSubmissions().size) < taskRun.getSubmissions().size) { val allDone = if (this.isAsync) { - val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } + val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == DbVerdictStatus.CORRECT } numberOfSubmissions >= limit } else { /* Determine of all teams have submitted . */ this.run.template.teams.asSequence().all { team -> - val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == team.teamId && it.verdicts.first().status == VerdictStatus.CORRECT } + val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == team.teamId && it.verdicts.first().status == DbVerdictStatus.CORRECT } numberOfSubmissions >= limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index f23f6a623..f87e1d504 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -4,8 +4,8 @@ import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.run.RunManagerStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.interfaces.IncrementalSubmissionTaskScorer @@ -14,7 +14,7 @@ import kotlinx.dnq.query.asSequence import java.util.* /** - * This is a [Updatable] that runs necessary post-processing after a [Submission] has been validated and updates the scores for the respective [TaskContext]. + * This is a [Updatable] that runs necessary post-processing after a [DbSubmission] has been validated and updates the scores for the respective [TaskContext]. * * @author Ralph Gasser * @version 1.2.0 @@ -25,14 +25,14 @@ class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreb private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) } - /** Internal list of [Verdict] that pend processing. */ - private val list = LinkedList>() + /** Internal list of [DbAnswerSet] that pend processing. */ + private val list = LinkedList>() /** The [Phase] this [ScoresUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN - /** Enqueues a new [Verdict] for post-processing. */ - fun enqueue(submission: Pair) = this.list.add(submission) + /** Enqueues a new [DbAnswerSet] for post-processing. */ + fun enqueue(submission: Pair) = this.list.add(submission) override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt index 7528fd522..31128a2e3 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -12,10 +12,10 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 1.1.0 */ -class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { +class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { companion object{ - fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { + fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { return when { validator.size < 2 -> throw IllegalArgumentException("Chain needs at least two validators") validator.size == 2 -> ChainedSubmissionValidator(validator[0], continueStates, validator[1]) @@ -32,11 +32,11 @@ class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator } /** - * Validates a [Submission] based on two [SubmissionValidator]s. + * Validates a [DbSubmission] based on two [SubmissionValidator]s. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) { + override fun validate(submission: DbSubmission) { this.firstValidator.validate(submission) if (submission.verdicts.asSequence().any { this.continueStates.contains(it.status) }) { this.secondValidator.validate(submission) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index 14b5a007a..1c0e376a4 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -1,18 +1,18 @@ package dev.dres.run.validation -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence /** - * A [SubmissionValidator] that checks if the items specified by a [Submission] match the items in the provided set. + * A [SubmissionValidator] that checks if the items specified by a [DbSubmission] match the items in the provided set. * * @author Luca Rossetto * @version 1.0.1 */ -class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { +class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { /** This type of [SubmissionValidator] can be executed directly.*/ override val deferring = false @@ -20,14 +20,14 @@ class MediaItemsSubmissionValidator(private val items : Set) : Submis /** * Performs the validation. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) { + override fun validate(submission: DbSubmission) { submission.verdicts.asSequence().forEach {verdict -> if (verdict.item == null || verdict.item !in this.items) { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG } else { - verdict.status = VerdictStatus.CORRECT + verdict.status = DbVerdictStatus.CORRECT } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 714d83d90..db1f42a4e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,15 +1,15 @@ package dev.dres.run.validation -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence /** * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the - * complete containment of the [Submission] within the provided [MediaSegmentTaskDescription]. + * complete containment of the [DbSubmission] within the provided [MediaSegmentTaskDescription]. * * @author Luca Rossetto * @author Ralph Gasser @@ -21,16 +21,16 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien get() = false /** - * Validates a [Submission] based on the target segment and the temporal overlap of the - * [Submission] with the [TaskTemplate]. + * Validates a [DbSubmission] based on the target segment and the temporal overlap of the + * [DbSubmission] with the [DbTaskTemplate]. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) { + override fun validate(submission: DbSubmission) { submission.verdicts.asSequence().forEach { verdict -> /* Perform sanity checks. */ - if (verdict.type != VerdictType.TEMPORAL) { - verdict.status = VerdictStatus.WRONG + if (verdict.type != DbAnswerType.TEMPORAL) { + verdict.status = DbVerdictStatus.WRONG return@forEach } @@ -38,23 +38,23 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien val end = verdict.end val item = verdict.item if (item == null || start == null || end == null || start > end) { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG return@forEach } /* Perform item validation. */ if (verdict.item != this.targetSegment.first) { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG return@forEach } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() if (outer.first <= start && outer.second >= end) { - verdict.status = VerdictStatus.CORRECT + verdict.status = DbVerdictStatus.CORRECT } else { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index f94205478..266b23eba 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -1,20 +1,20 @@ package dev.dres.run.validation -import dev.dres.data.model.template.task.TaskTemplate -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence /** */ -typealias TransientMediaSegment = Pair +typealias TransientMediaSegment = Pair /** * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the - * temporal overlap of the [Submission] with the provided [TransientMediaSegment]. + * temporal overlap of the [DbSubmission] with the provided [TransientMediaSegment]. * * @author Luca Rossetto * @author Ralph Gasser @@ -25,16 +25,16 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed override val deferring: Boolean = false /** - * Validates a [Submission] based on the target segment and the temporal overlap of the - * [Submission] with the [TaskTemplate]. + * Validates a [DbSubmission] based on the target segment and the temporal overlap of the + * [DbSubmission] with the [DbTaskTemplate]. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) { + override fun validate(submission: DbSubmission) { submission.verdicts.asSequence().forEach { verdict -> /* Perform sanity checks. */ - if (verdict.type != VerdictType.TEMPORAL) { - verdict.status = VerdictStatus.WRONG + if (verdict.type != DbAnswerType.TEMPORAL) { + verdict.status = DbVerdictStatus.WRONG return@forEach } @@ -42,22 +42,22 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed val end = verdict.end val item = verdict.item if (item == null || start == null || end == null || start > end) { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG return@forEach } /* Perform item validation. */ if (verdict.item != this.targetSegment.first) { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG return@forEach } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { - verdict.status = VerdictStatus.CORRECT + verdict.status = DbVerdictStatus.CORRECT } else { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 3f9dd7f77..8d9c46827 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -1,8 +1,8 @@ package dev.dres.run.validation -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -42,29 +42,29 @@ class TextValidator(targets: List) : SubmissionValidator { } /** - * Validates a textual [Submission] based on the provided [Regex]. + * Validates a textual [DbSubmission] based on the provided [Regex]. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) { + override fun validate(submission: DbSubmission) { submission.verdicts.asSequence().forEach { verdict -> /* Perform sanity checks. */ - if (verdict.type != VerdictType.TEXT) { - verdict.status = VerdictStatus.WRONG + if (verdict.type != DbAnswerType.TEXT) { + verdict.status = DbVerdictStatus.WRONG return@forEach } /* Perform text validation. */ val text = verdict.text if (text == null) { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG return@forEach } if (regex.any { it matches text }) { - verdict.status = VerdictStatus.CORRECT + verdict.status = DbVerdictStatus.CORRECT } else { - verdict.status = VerdictStatus.WRONG + verdict.status = DbVerdictStatus.WRONG } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index d38d91ae5..eaef729db 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -1,8 +1,8 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus /** * A [SubmissionValidator] that bases validation on human (manual) verdicts. @@ -16,31 +16,31 @@ interface JudgementValidator { /** unique id to identify the [JudgementValidator]*/ val id: String - /** The number of [Submission]s that are currently pending a judgement. */ + /** The number of [DbSubmission]s that are currently pending a judgement. */ val pending: Int - /** The number of [Submission]s which have not yet been presented to a judge */ + /** The number of [DbSubmission]s which have not yet been presented to a judge */ val open: Int - /** Returns true, if this [JudgementValidator] has open [Submission]s. */ + /** Returns true, if this [JudgementValidator] has open [DbSubmission]s. */ val hasOpen: Boolean get() = open > 0 /** * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]' - * internal queue. If such an element exists, then the [Submission] is returned alongside a - * unique token, that can be used to update the [Submission]'s [VerdictStatus]. + * internal queue. If such an element exists, then the [DbSubmission] is returned alongside a + * unique token, that can be used to update the [DbSubmission]'s [DbVerdictStatus]. * - * @return Optional [Pair] containing a string token and the [Submission] that should be judged. + * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - fun next(queue: String): Pair? + fun next(queue: String): Pair? /** - * Places a verdict for the [Submission] identified by the given token. + * Places a verdict for the [DbSubmission] identified by the given token. * - * @param token The token used to identify the [Submission]. + * @param token The token used to identify the [DbSubmission]. * @param verdict The verdict of the judge. */ - fun judge(token: String, verdict: VerdictStatus) + fun judge(token: String, verdict: DbVerdictStatus) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt index 0bdc2fccb..fe164d353 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt @@ -1,21 +1,21 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus /** - * A validator class that checks, if a [Submission] is correct. + * A validator class that checks, if a [DbSubmission] is correct. * * @author Luca Rossetto & Ralph Gasser * @version 1.1 */ interface SubmissionValidator { /** - * Validates the [Submission] and updates its [VerdictStatus]. + * Validates the [DbSubmission] and updates its [DbVerdictStatus]. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - fun validate(submission: Submission) + fun validate(submission: DbSubmission) /** * Indicates whether this [SubmissionValidator] needs to defer the validation to some later point in time diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index c2fc75dc9..f132bfffa 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus /** * @@ -21,10 +21,10 @@ interface VoteValidator : JudgementValidator { /** * Places a verdict for the currently active Submission */ - fun vote(verdict: VerdictStatus) + fun vote(verdict: DbVerdictStatus) /** * */ - fun nextSubmissionToVoteOn() : Verdict? + fun nextSubmissionToVoteOn() : DbAnswerSet? } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index d45a4e33a..9990593f0 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,8 +1,8 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator @@ -40,24 +40,24 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e /** Internal lock on relevant data structures. */ private val updateLock = ReentrantReadWriteLock() - /** Internal queue that keeps track of all the [Verdict]s in need of judgement. */ - private val queue: Queue = LinkedList() + /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ + private val queue: Queue = LinkedList() - /** Internal queue that keeps track of all the [Verdict]s in need of judgement. */ - private val queuedItemRanges: MutableMap> = HashMap() + /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ + private val queuedItemRanges: MutableMap> = HashMap() - /** Internal map of all [Verdict]s that have been retrieved by a judge and are pending a verdict. */ - private val waiting = HashMap() + /** Internal map of all [DbAnswerSet]s that have been retrieved by a judge and are pending a verdict. */ + private val waiting = HashMap() /** Helper structure to keep track when a request needs to be re-scheduled */ private val timeouts = mutableListOf>() - /** Internal map of already judged [Submission]s, independent of their source. */ - private val cache: MutableMap = ConcurrentHashMap() + /** Internal map of already judged [DbSubmission]s, independent of their source. */ + private val cache: MutableMap = ConcurrentHashMap() init { - knownCorrectRanges.forEach { cache[it] = VerdictStatus.CORRECT } - knownWrongRanges.forEach { cache[it] = VerdictStatus.WRONG } + knownCorrectRanges.forEach { cache[it] = DbVerdictStatus.CORRECT } + knownWrongRanges.forEach { cache[it] = DbVerdictStatus.WRONG } } private fun checkTimeOuts() = updateLock.write { @@ -72,7 +72,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e timeouts.removeAll(due) } - /** Returns the number of [Submission]s that are currently pending a judgement. */ + /** Returns the number of [DbSubmission]s that are currently pending a judgement. */ override val pending: Int get() = updateLock.read { this.queue.size + this.waiting.size } @@ -89,15 +89,15 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e } /** - * Enqueues a [Submission] with the internal judgment queue and updates its [VerdictStatus] - * to [VerdictStatus.INDETERMINATE]. + * Enqueues a [DbSubmission] with the internal judgment queue and updates its [DbVerdictStatus] + * to [DbVerdictStatus.INDETERMINATE]. * - * @param submission The [Submission] to validate. + * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) = this.updateLock.read { + override fun validate(submission: DbSubmission) = this.updateLock.read { for (verdict in submission.verdicts.asSequence()) { //only validate submissions which are not already validated - if (verdict.status != VerdictStatus.INDETERMINATE){ + if (verdict.status != DbVerdictStatus.INDETERMINATE){ continue } @@ -109,7 +109,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e } else if (itemRange !in queuedItemRanges.keys) { updateLock.write { this.queue.offer(verdict) - verdict.status = VerdictStatus.INDETERMINATE + verdict.status = DbVerdictStatus.INDETERMINATE this.queuedItemRanges[itemRange] = mutableListOf(verdict) } } else { @@ -122,12 +122,12 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e /** * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]' - * internal queue. If such an element exists, then the [Submission] is returned alongside a - * unique token, that can be used to update the [Submission]'s [VerdictStatus]. + * internal queue. If such an element exists, then the [DbSubmission] is returned alongside a + * unique token, that can be used to update the [DbSubmission]'s [DbVerdictStatus]. * - * @return Optional [Pair] containing a string token and the [Submission] that should be judged. + * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - override fun next(queue: String): Pair? = updateLock.write { + override fun next(queue: String): Pair? = updateLock.write { checkTimeOuts() val next = this.queue.poll() return if (next != null) { @@ -142,19 +142,19 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e } /** - * Places a verdict for the [Submission] identified by the given token. + * Places a verdict for the [DbSubmission] identified by the given token. * - * @param token The token used to identify the [Submission]. + * @param token The token used to identify the [DbSubmission]. * @param verdict The verdict of the judge. */ - override fun judge(token: String, verdict: VerdictStatus) { + override fun judge(token: String, verdict: DbVerdictStatus) { processSubmission(token, verdict).status = verdict } /** * */ - fun processSubmission(token: String, status: VerdictStatus) : Verdict = this.updateLock.write { + fun processSubmission(token: String, status: DbVerdictStatus) : DbAnswerSet = this.updateLock.write { val verdict = this.waiting[token] ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged val itemRange = ItemRange(verdict) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 6c3065a43..cc7198340 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.validation.interfaces.VoteValidator import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue @@ -25,8 +25,8 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() private val defaultVoteDifference = 1 } - private val submissionQueue = ConcurrentLinkedQueue() - private val voteCountMap = ConcurrentHashMap() + private val submissionQueue = ConcurrentLinkedQueue() + private val voteCountMap = ConcurrentHashMap() private val updateLock = ReentrantReadWriteLock() override val isActive: Boolean @@ -35,8 +35,8 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() override val voteCount: Map get() = voteCountMap.mapKeys { it.toString() } - override fun vote(status: VerdictStatus) = updateLock.write { - if (status == VerdictStatus.INDETERMINATE || status == VerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does + override fun vote(status: DbVerdictStatus) = updateLock.write { + if (status == DbVerdictStatus.INDETERMINATE || status == DbVerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does return@write } @@ -59,16 +59,16 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() return max - others >= voteDifference } - override fun nextSubmissionToVoteOn(): Verdict? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? + override fun nextSubmissionToVoteOn(): DbAnswerSet? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? //siphon of undecidable submission from logic of super class - override fun judge(token: String, status: VerdictStatus) { + override fun judge(token: String, status: DbVerdictStatus) { val verdict = super.processSubmission(token, status) when (status){ - VerdictStatus.CORRECT, - VerdictStatus.WRONG -> verdict.status = status - VerdictStatus.INDETERMINATE -> {} - VerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) + DbVerdictStatus.CORRECT, + DbVerdictStatus.WRONG -> verdict.status = status + DbVerdictStatus.INDETERMINATE -> {} + DbVerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index fc00c1ed4..ad095136e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -1,9 +1,8 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.Verdict -import dev.dres.data.model.submissions.VerdictType +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbAnswerType /** * Helper class to store submission information independent of source. @@ -12,14 +11,14 @@ import dev.dres.data.model.submissions.VerdictType * @version 2.0.0 */ data class ItemRange(val element: String, val start: Long, val end: Long){ - constructor(item: MediaItem): this(item.id, 0, 0) - constructor(item: MediaItem, start: Long, end: Long): this(item.id, start, end) - constructor(verdict: Verdict): this(when (verdict.type){ - VerdictType.ITEM, - VerdictType.TEMPORAL -> verdict.item!!.id - VerdictType.TEXT -> verdict.text!! + constructor(item: DbMediaItem): this(item.id, 0, 0) + constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) + constructor(answerSet: DbAnswerSet): this(when (answerSet.type){ + DbAnswerType.ITEM, + DbAnswerType.TEMPORAL -> answerSet.item!!.id + DbAnswerType.TEXT -> answerSet.text!! else -> throw IllegalStateException("Submission contains neither item nor text.") - }, verdict.start ?: 0, verdict.end ?: 0) + }, answerSet.start ?: 0, answerSet.end ?: 0) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt index 5646467b9..2a573af6c 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/FFmpegUtil.kt @@ -7,13 +7,10 @@ import com.github.kokorin.jaffree.ffmpeg.UrlOutput import com.github.kokorin.jaffree.ffprobe.FFprobe import com.github.kokorin.jaffree.ffprobe.FFprobeResult import dev.dres.DRES -import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalRange import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory -import java.io.File -import java.io.FileNotFoundException -import java.io.InputStream import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -134,13 +131,13 @@ object FFmpegUtil { } /** - * Extracts and renders the previews for a [MediaItem]. + * Extracts and renders the previews for a [DbMediaItem]. * - * @param item The [MediaItem] to handle. - * @param range The [TemporalRange] within the [MediaItem] to handle. + * @param item The [DbMediaItem] to handle. + * @param range The [TemporalRange] within the [DbMediaItem] to handle. * @param cacheLocation The cache location [Path] */ - fun extractSegment(item: MediaItem, range: TemporalRange, cacheLocation: Path) { + fun extractSegment(item: DbMediaItem, range: TemporalRange, cacheLocation: Path) { Files.createDirectories(cacheLocation) val start = range.start.toMilliseconds() val end = range.end.toMilliseconds() diff --git a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt index aa11d09bb..bbe274b61 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/TimeUtil.kt @@ -1,6 +1,6 @@ package dev.dres.utilities -import dev.dres.data.model.media.MediaSegment +import dev.dres.data.model.media.DbMediaSegment import dev.dres.data.model.media.time.TemporalRange import kotlin.math.abs @@ -41,17 +41,17 @@ object TimeUtil { } /** - * Converts a shot number to a timestamp in milliseconds given a list of [MediaSegment]s. + * Converts a shot number to a timestamp in milliseconds given a list of [DbMediaSegment]s. */ - fun shotToTime(shot: String, segments: List): Pair? { + fun shotToTime(shot: String, segments: List): Pair? { val segment = segments.find { it.name == shot } ?: return null return segment.range.toMilliseconds() } /** - * Converts a shot number to a timestamp in milliseconds given a list of [MediaSegment]s. + * Converts a shot number to a timestamp in milliseconds given a list of [DbMediaSegment]s. */ - fun timeToSegment(time: Long, segments: List): Pair? { + fun timeToSegment(time: Long, segments: List): Pair? { if (segments.isEmpty()) return null val segment = segments.find { val range = it.range.toMilliseconds() diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index a7467bda7..5824294bc 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -1,8 +1,8 @@ package dres.run.score.scorer -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals @@ -15,27 +15,27 @@ class LegacyAvsTaskScorerTest { private lateinit var scorer: AvsTaskScorer private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids private val defaultTaskDuration = 5 * 60L - private val dummyImageItems: List - private val dummyVideoItems: List + private val dummyImageItems: List + private val dummyVideoItems: List init { val collectionId = EvaluationId() dummyImageItems = listOf( - MediaItem.ImageItem(EvaluationId(), "Image 1", "images/1", collectionId), - MediaItem.ImageItem(EvaluationId(), "Image 2", "images/2", collectionId), - MediaItem.ImageItem(EvaluationId(), "Image 3", "images/3", collectionId), - MediaItem.ImageItem(EvaluationId(), "Image 4", "images/4", collectionId), - MediaItem.ImageItem(EvaluationId(), "Image 5", "images/5", collectionId), - MediaItem.ImageItem(EvaluationId(), "Image 6", "images/6", collectionId) + DbMediaItem.ImageItem(EvaluationId(), "Image 1", "images/1", collectionId), + DbMediaItem.ImageItem(EvaluationId(), "Image 2", "images/2", collectionId), + DbMediaItem.ImageItem(EvaluationId(), "Image 3", "images/3", collectionId), + DbMediaItem.ImageItem(EvaluationId(), "Image 4", "images/4", collectionId), + DbMediaItem.ImageItem(EvaluationId(), "Image 5", "images/5", collectionId), + DbMediaItem.ImageItem(EvaluationId(), "Image 6", "images/6", collectionId) ) dummyVideoItems = listOf( - MediaItem.VideoItem(EvaluationId(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), - MediaItem.VideoItem(EvaluationId(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), - MediaItem.VideoItem(EvaluationId(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), - MediaItem.VideoItem(EvaluationId(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), - MediaItem.VideoItem(EvaluationId(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) + DbMediaItem.VideoItem(EvaluationId(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), + DbMediaItem.VideoItem(EvaluationId(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), + DbMediaItem.VideoItem(EvaluationId(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), + DbMediaItem.VideoItem(EvaluationId(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), + DbMediaItem.VideoItem(EvaluationId(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) ) } @@ -57,9 +57,9 @@ class LegacyAvsTaskScorerTest { fun allWrongSameImage() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -71,9 +71,9 @@ class LegacyAvsTaskScorerTest { fun allWrongDifferentImages() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = VerdictStatus.WRONG } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -85,9 +85,9 @@ class LegacyAvsTaskScorerTest { fun allSameImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -99,9 +99,9 @@ class LegacyAvsTaskScorerTest { fun allDifferentImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) @@ -120,20 +120,20 @@ class LegacyAvsTaskScorerTest { */ //3 out of 4 correct - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, //1 out of 3 correct - Submission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, //1 out of 2 correct - Submission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = VerdictStatus.WRONG } + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG } ) @@ -172,9 +172,9 @@ class LegacyAvsTaskScorerTest { fun allSameVideoSameSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -187,9 +187,9 @@ class LegacyAvsTaskScorerTest { fun allSameVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) @@ -202,9 +202,9 @@ class LegacyAvsTaskScorerTest { fun allDifferentVideoDifferentSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) @@ -216,17 +216,17 @@ class LegacyAvsTaskScorerTest { fun allSameVideoOverlappingSegmentAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -238,21 +238,21 @@ class LegacyAvsTaskScorerTest { fun allSameVideoOverlappingSegmentSomeCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = VerdictStatus.WRONG }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = VerdictStatus.WRONG }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = VerdictStatus.WRONG } + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG } ) @@ -270,40 +270,40 @@ class LegacyAvsTaskScorerTest { val submissions = listOf( //team 1 //gets merged to one - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, //plus 2 independent correct ones - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = DbVerdictStatus.CORRECT }, //and an incorrect one directly next to it - Submission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = DbVerdictStatus.WRONG }, //c = 5, q(c) = 3, i = 1 //team 2 //the center part is missing, so it's counted as two in the quantization - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, //another correct one, same as team 1 - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, //and two wrong ones - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = VerdictStatus.WRONG }, - Submission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, //c = 3, q(c) = 3, i = 2 //team 3 //same as team 2, but with all 3 segments of the 1st video, same as team 1 - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = VerdictStatus.CORRECT }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, //another correct one, same as team 1 - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, //and two wrong ones - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = VerdictStatus.WRONG }, - Submission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, //c = 4, q(c) = 2, i = 2 diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index f52d99b19..982fc1ed4 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -1,8 +1,8 @@ package dres.run.score.scorer -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.KisTaskScorer import org.junit.jupiter.api.Assertions.assertEquals @@ -13,7 +13,7 @@ class KisTaskScorerTest { private lateinit var scorer: KisTaskScorer private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids - private val dummyImageItems = listOf(MediaItem.ImageItem(EvaluationId(), "image 1", "images/1", EvaluationId())) + private val dummyImageItems = listOf(DbMediaItem.ImageItem(EvaluationId(), "image 1", "images/1", EvaluationId())) private val defaultTaskDuration = 5 * 60L private val maxPointsPerTask = 100.0 private val maxPointsAtTaskEnd = 50.0 @@ -36,9 +36,9 @@ class KisTaskScorerTest { fun allWrong() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -50,7 +50,7 @@ class KisTaskScorerTest { fun immediatelyRight() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -62,7 +62,7 @@ class KisTaskScorerTest { fun rightAtTheEnd() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) @@ -72,7 +72,7 @@ class KisTaskScorerTest { fun rightInTheMiddle() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = listOf( - Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT } + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) @@ -84,19 +84,19 @@ class KisTaskScorerTest { val submissions = listOf( //incorrect submissions - Submission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = VerdictStatus.WRONG }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, //correct submissions at 1/2 the task time - Submission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, - Submission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = VerdictStatus.CORRECT }, + DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, + DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, ) val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt index 426e4d137..45a301b4b 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt @@ -2,7 +2,7 @@ package dres.run.score.scorer import dev.dres.data.model.UID import dev.dres.data.model.basics.media.MediaItem -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.NewAvsTaskScorer @@ -49,7 +49,7 @@ class NewAvsTaskScorerTest { fun onlyTeamOneWithAllEqualsOneCorrect(){ val taskStart = 100_000L val subs = listOf( - Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} + DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} ) val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -62,9 +62,9 @@ class NewAvsTaskScorerTest { fun allTeamsWithAllEuqalsOneCorrect(){ val taskStart = 100_000L val subs = listOf( - Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} + DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} ) val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -77,9 +77,9 @@ class NewAvsTaskScorerTest { fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ val taskStart = 100_000L val subs = listOf( - Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} + DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} ) val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -93,31 +93,31 @@ class NewAvsTaskScorerTest { val taskStart = 100_000L val subs = listOf( /* Team One: All correct */ - Submission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 20_000, 30_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[0], UID(), taskStart+3000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 20_000, 30_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[0], UID(), taskStart+3000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, /* Team Two: One correct, One correct with one wrong */ - Submission.Temporal(teams[1], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - Submission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[1], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ /* v1 */ - Submission.Temporal(teams[2], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[2], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, /* v2 */ - Submission.Temporal(teams[2], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[2], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, /* v3 */ - Submission.Temporal(teams[2], UID(), taskStart+4000, dummyVideoItems[2], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+5000, dummyVideoItems[2], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+6000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, + DbSubmission.Temporal(teams[2], UID(), taskStart+4000, dummyVideoItems[2], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+5000, dummyVideoItems[2], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+6000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, /* v4 */ - Submission.Temporal(teams[2], UID(), taskStart+7000, dummyVideoItems[3], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+8000, dummyVideoItems[3], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+9000, dummyVideoItems[3], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+7000, dummyVideoItems[3], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+8000, dummyVideoItems[3], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+9000, dummyVideoItems[3], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, /* v5 */ - Submission.Temporal(teams[2], UID(), taskStart+10_000, dummyVideoItems[4], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+11_000, dummyVideoItems[4], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, - Submission.Temporal(teams[2], UID(), taskStart+12_000, dummyVideoItems[4], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+10_000, dummyVideoItems[4], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+11_000, dummyVideoItems[4], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, + DbSubmission.Temporal(teams[2], UID(), taskStart+12_000, dummyVideoItems[4], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, ) val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) /* From 59d267f8dd2fac692036783dae0cfaab34e85605 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 3 Feb 2023 15:44:46 +0100 Subject: [PATCH 057/498] Made task scorers generally stateless and introduced caching decorator --- .../admin/ListSubmissionsHandler.kt | 2 +- .../scores/CurrentTaskScoreHandler.kt | 3 +- .../scores/HistoryTaskScoreHandler.kt | 3 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 2 +- .../dev/dres/data/model/run/AbstractTask.kt | 2 +- .../run/InteractiveAsynchronousEvaluation.kt | 3 +- .../run/InteractiveSynchronousEvaluation.kt | 4 +- .../model/run/NonInteractiveEvaluation.kt | 5 +- .../dres/data/model/run/interfaces/TaskRun.kt | 7 +- .../template/interfaces/TaskScorerFactory.kt | 19 - .../model/template/task/DbTaskTemplate.kt | 9 +- .../data/model/template/task/DbTaskType.kt | 5 +- .../template/task/options/DbScoreOption.kt | 18 +- .../handlers/TeamCombinationScoreHandler.kt | 5 - .../IncrementalSubmissionTaskScorer.kt | 18 - .../dres/run/score/interfaces/TaskScorer.kt | 24 - .../run/score/interfaces/TeamTaskScorer.kt | 13 - .../scoreboard/MaxNormalizingScoreBoard.kt | 5 +- .../dres/run/score/scoreboard/Scoreboard.kt | 5 +- .../scoreboard/SumAggregateScoreBoard.kt | 5 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 49 +- .../run/score/scorer/CachingTaskScorer.kt | 21 + .../scorer/InferredAveragePrecisionScorer.kt | 106 +-- .../dres/run/score/scorer/KisTaskScorer.kt | 27 +- .../dres/run/score/scorer/NewAvsTaskScorer.kt | 54 +- .../TaskScorer.kt} | 11 +- .../dres/run/updatables/EndTaskUpdatable.kt | 3 +- .../dres/run/updatables/ScoresUpdatable.kt | 19 +- .../run/score/scorer/AvsTaskScorerTest.kt | 652 +++++++++--------- .../run/score/scorer/KisTaskScorerTest.kt | 188 ++--- .../run/score/scorer/NewAvsTaskScorerTest.kt | 244 ++++--- 31 files changed, 715 insertions(+), 816 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt rename backend/src/main/kotlin/dev/dres/run/score/{interfaces/RecalculatingSubmissionTaskScorer.kt => scorer/TaskScorer.kt} (67%) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index a2c7aac74..5aea04d7f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -40,7 +40,7 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional(true) { evaluationManager.tasks(RunActionContext.runActionContext(ctx, evaluationManager)).filter { it.template.templateId == templateId }.map { - ApiSubmissionInfo(evaluationId, it.id, it.getSubmissions().map { sub -> sub.toApi() }) + ApiSubmissionInfo(evaluationId, it.id, it.getSubmissions().map { sub -> sub.toApi() }.toList()) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index d8281bcf4..0c77eede0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -11,7 +11,6 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask import dev.dres.run.InteractiveRunManager -import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.score.scoreboard.ScoreOverview import io.javalin.http.Context import io.javalin.openapi.* @@ -52,7 +51,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, manager) val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No active task run in evaluation ${ctx.evaluationId()}.", ctx) - val scores = (scorer as? TeamTaskScorer)?.teamScoreMap() ?: throw ErrorStatusException(400, "Scorer has more than one score per team for evaluation ${ctx.evaluationId()}.", ctx) + val scores = scorer.teamScoreMap() ApiScoreOverview("task", manager.currentTaskTemplate(rac).taskGroup.name, manager.template.teams.asSequence().map { team -> ApiScore(team.id, scores[team.id] ?: 0.0) }.toList() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index 71200489f..a6691e1c8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -11,7 +11,6 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask import dev.dres.run.InteractiveRunManager -import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.run.score.scoreboard.ScoreOverview import io.javalin.http.Context import io.javalin.openapi.* @@ -55,7 +54,7 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, manager) val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run ${manager.id}.", ctx) - val scores = (scorer as? TeamTaskScorer)?.teamScoreMap() ?: throw ErrorStatusException(400, "Scorer has more than one score per team for evaluation ${ctx.evaluationId()}.", ctx) + val scores = scorer.teamScoreMap() ApiScoreOverview("task", manager.currentTaskTemplate(rac).taskGroup.name, manager.template.teams.asSequence().map { ApiScore(it.teamId, scores[it.teamId] ?: 0.0) }.toList() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index aee637400..1585d8adc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -50,7 +50,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq DbTaskOption.HIDDEN_RESULTS }.any() manager.currentSubmissions(rac).map { it.toApi(hidden) } } else { - manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() } ?: emptyList() + manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() }?.toList() ?: emptyList() } } emptyList() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 919fc31c7..a48f25e84 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -115,7 +115,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { } /** Returns a [List] of all [DbSubmission]s held by this [AbstractTask]. */ - override fun getSubmissions() = this.submissions.toList() + override fun getSubmissions() = this.submissions.asSequence() /** * Adds a new [DbSubmission] to this [AbstractInteractiveTask]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 11125874d..5a669c3e9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -9,7 +9,6 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TeamTaskScorer import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -182,7 +181,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe override val filter: SubmissionFilter = this.template.newFilter() /** The [TeamTaskScorer] instance used by this [InteractiveAsynchronousEvaluation].*/ - override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer + override val scorer = this.template.newScorer() ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index dfbf284a4..21efa2359 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -7,7 +7,6 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.audit.AuditLogger import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TeamTaskScorer import kotlinx.dnq.query.* /** @@ -92,8 +91,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu override val filter: SubmissionFilter = this.template.newFilter() /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ - override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer - ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") + override val scorer = this.template.newScorer() /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 790ce4813..4719037c5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -7,7 +7,6 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TeamTaskScorer import kotlinx.dnq.query.* @@ -46,9 +45,7 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev get() = this@NonInteractiveEvaluation.tasks.indexOf(this) /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ - override val scorer: TeamTaskScorer = this.template.newScorer() as? TeamTaskScorer - ?: throw IllegalArgumentException("Specified scorer is not of type TeamTaskScorer. This is a programmer's error!") - + override val scorer = this.template.newScorer() /** */ override val filter: SubmissionFilter get() = TODO("Can there be submission filters for non-interactive tasks?") diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 720ca392b..e5b370118 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -4,7 +4,8 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.TaskStatus -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer typealias TaskId = String /** @@ -27,7 +28,7 @@ interface TaskRun: Run { val template: DbTaskTemplate /** The [TaskScorer] used to update score for this [IATaskRun]. */ - val scorer: TaskScorer + val scorer: CachingTaskScorer /** The current status of this [IATaskRun]. */ val status: TaskStatus @@ -42,5 +43,5 @@ interface TaskRun: Run { * * @return [List] of [DbSubmission]s */ - fun getSubmissions(): List + fun getSubmissions(): Sequence } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt deleted file mode 100644 index 6e3c4d449..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/TaskScorerFactory.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.dres.data.model.template.interfaces - -import dev.dres.run.score.interfaces.TaskScorer - -/** - * A factory for [TaskScorer]s. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -interface TaskScorerFactory { - /** - * Generates a new [TaskScorer]. Depending on the implementation, the returned instance - * is a new instance or being re-use. - * - * @return [TaskScorer]. - */ - fun newScorer(): TaskScorer -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 4096211e7..0271f1f8a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -9,12 +9,13 @@ import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.interfaces.SubmissionFilterFactory -import dev.dres.data.model.template.interfaces.TaskScorerFactory +import dev.dres.data.model.template.interfaces.CachingTaskScorerFactory import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -32,7 +33,7 @@ import java.lang.Long.max * @author Luca Rossetto * @author Ralph Gasser */ -class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFactory, SubmissionFilterFactory { +class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), CachingTaskScorerFactory, SubmissionFilterFactory { companion object: XdNaturalEntityType() /** The [TemplateId] of this [DbTaskTemplate]. */ @@ -70,7 +71,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskScorerFacto * * @return [TaskScorer]. */ - override fun newScorer(): TaskScorer = this.taskGroup.type.newScorer() + override fun newScorer(): CachingTaskScorer = this.taskGroup.type.newScorer() /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index bd99982a9..b367f720d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -7,7 +7,8 @@ import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter import dev.dres.run.filter.SubmissionFilterAggregator -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -79,7 +80,7 @@ class DbTaskType(entity: Entity) : XdEntity(entity) { * * @return [TaskScorer]. */ - fun newScorer(): TaskScorer { + fun newScorer(): CachingTaskScorer { val parameters = this.configurations.query(DbConfiguredOption::key eq this.score.description) .asSequence().map { it.key to it.value }.toMap() return this.score.scorer(parameters) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt index 9309f49d1..60055e359 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt @@ -1,8 +1,9 @@ package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiScoreOption -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.TaskScorer import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity @@ -30,18 +31,21 @@ class DbScoreOption(entity: Entity) : XdEnumEntity(entity) { * * @param parameters The parameter [Map] used to configure the [TaskScorer] */ - fun scorer(parameters: Map): TaskScorer = when(this) { - KIS -> KisTaskScorer(parameters) - AVS -> AvsTaskScorer() - else -> throw IllegalStateException("The task score option ${this.description} is currently not supported.") - } + fun scorer(parameters: Map): CachingTaskScorer = CachingTaskScorer( + when (this) { + KIS -> KisTaskScorer(parameters) + AVS -> AvsTaskScorer + else -> throw IllegalStateException("The task score option ${this.description} is currently not supported.") + } + ) /** * Converts this [DbHintOption] to a RESTful API representation [ApiScoreOption]. * * @return [ApiScoreOption] */ - fun toApi() = ApiScoreOption.values().find { it.toDb() == this } ?: throw IllegalStateException("Option ${this.description} is not supported.") + fun toApi() = ApiScoreOption.values().find { it.toDb() == this } + ?: throw IllegalStateException("Option ${this.description} is not supported.") override fun toString(): String = this.description } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 2f2941e63..14771d36a 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -4,7 +4,6 @@ import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.eventstream.* -import dev.dres.run.score.interfaces.TeamTaskScorer import java.io.File import java.io.PrintWriter @@ -50,11 +49,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { val taskDescription = tasks[event.taskId] ?: return - val scorer = taskDescription.newScorer() - if (scorer !is TeamTaskScorer) { - return - } val submissions = submissionTaskMap[event.taskId] ?: return diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt deleted file mode 100644 index 879291378..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/IncrementalSubmissionTaskScorer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.submissions.DbSubmission - -/** - * A [TaskScorer] implementation that can update scores incrementally on a [DbSubmission] by [DbSubmission] basis. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 - */ -interface IncrementalSubmissionTaskScorer: TaskScorer { - /** - * Updates this [IncrementalSubmissionTaskScorer]'s score just using a single submission. - * - * @param submission The [DbSubmission] to update this [IncrementalSubmissionTaskScorer] with. - */ - fun update(submission: DbSubmission) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt deleted file mode 100644 index 792baa265..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TaskScorer.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.template.team.DbTeam -import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.run.interfaces.TaskRun - -/** Type alias for a */ -typealias ScoreEntry = Triple - -/** - * A scorer for a [TaskRun]. A score is a [Double] value that captures a [DbTeam] performance. - * The [TaskScorer] calculates and tracks these scores per [TeamId]. - * - * @version 1.1.0 - * @author Luca Rossetto - */ -interface TaskScorer { - /** - * Generates and returns the current scores for all teams in the relevant Task. - * - * @return Map of team [EvaluationId] to team score - */ - fun scores(): List -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt deleted file mode 100644 index 9fe2929cf..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/TeamTaskScorer.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.run.score.interfaces - -import dev.dres.data.model.template.team.TeamId - -/** - * A [TaskScorer] that aggregates scores per [TeamId]. - * - * @author Luca Rossetto - * @versio 1.0.0 - */ -interface TeamTaskScorer : TaskScorer { - fun teamScoreMap() : Map -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 715951ba7..518e205dd 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -5,7 +5,8 @@ import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.template.TemplateId -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max @@ -42,7 +43,7 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, p override fun score(teamId: TeamId) = overallScoreMap()[teamId] ?: 0.0 - override fun update(scorers: Map) { + override fun update(scorers: Map) { this.scorePerTaskMap.clear() this.scorePerTaskMap.putAll(scorers.map { it.key to it.value.scores().groupBy { it.first }.mapValues { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index 4febeba60..03182123f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -4,7 +4,8 @@ import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer /** * A [Scoreboard] tracks the [Score]s for different [DbTeam]s @@ -40,7 +41,7 @@ interface Scoreboard { /** * Updates using a map of the [TaskId] ids to the corresponding [TaskScorer]s */ - fun update(scorers: Map) + fun update(scorers: Map) /** * Returns a summary of all current scores in a [ScoreOverview] diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index b36cf4a52..777e38b24 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -3,7 +3,8 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.template.TemplateId -import dev.dres.run.score.interfaces.TaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer /** * A [Scoreboard] that keeps tracks the total score per team and task group. @@ -23,7 +24,7 @@ class SumAggregateScoreBoard(override val name: String, private val boards: List //since calls are delegated, nothing needs to be done here } - override fun update(scorers: Map) { + override fun update(scorers: Map) { //since calls are delegated, nothing needs to be done here } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 69d424426..d5a90eb74 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -6,15 +6,11 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext -import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer -import dev.dres.run.score.interfaces.ScoreEntry -import dev.dres.run.score.interfaces.TeamTaskScorer import dev.dres.utilities.TimeUtil +import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.toList -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read -import kotlin.concurrent.write + /** * A [TeamTaskScorer] used for AVS tasks. @@ -22,35 +18,34 @@ import kotlin.concurrent.write * @author Luca Rossetto * @version 1.1.0 */ -class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { - - private var lastScores: Map = emptyMap() - private val lastScoresLock = ReentrantReadWriteLock() +object AvsTaskScorer : TaskScorer { /** * TODO: Check for correctness especially if a submission has more than one verdict. Maybe add sanity checks. */ - override fun computeScores(submissions: Collection, context: TaskContext): Map { - val correctSubmissions = submissions.flatMap {s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.CORRECT }.toList() } - val wrongSubmissions = submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.WRONG }.toList() } + override fun computeScores(submissions: Sequence, context: TaskContext): Map { + val correctSubmissions = + submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.CORRECT }.asSequence() } + val wrongSubmissions = + submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.WRONG }.asSequence() } val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.team.id } val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.team.id } val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() - this.lastScores = this.lastScoresLock.write { - context.teamIds.map { teamid -> - val correctSubs = correctSubmissionsPerTeam[teamid] ?: return@map teamid to 0.0 - val correct = correctSubs.size - val wrong = wrongSubmissionsPerTeam[teamid]?.size ?: 0 - teamid to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs).toDouble() / totalCorrectQuantized) - }.toMap() - } - return teamScoreMap() + + return context.teamIds.map { teamid -> + val correctSubs = correctSubmissionsPerTeam[teamid] ?: return@map teamid to 0.0 + val correct = correctSubs.size + val wrong = wrongSubmissionsPerTeam[teamid]?.size ?: 0 + teamid to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs.asSequence()).toDouble() / totalCorrectQuantized) + }.toMap() + } - private fun countQuantized(submissions: Collection): Int = submissions + private fun countQuantized(submissions: Sequence): Int = submissions .filter { it.item != null } - .groupBy { it.item }.map { - when(it.key!!.type) { + .groupBy { it.item } + .map { + when (it.key!!.type) { DbMediaType.IMAGE -> 1 DbMediaType.VIDEO -> { val ranges = it.value.map { s -> s.temporalRange!! } @@ -60,9 +55,5 @@ class AvsTaskScorer: RecalculatingSubmissionTaskScorer, TeamTaskScorer { } }.sum() - override fun teamScoreMap(): Map = this.lastScoresLock.read { this.lastScores } - override fun scores(): List = this.lastScoresLock.read { - this.lastScores.map { ScoreEntry(it.key, null, it.value) } - } } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt new file mode 100644 index 000000000..529edd59b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt @@ -0,0 +1,21 @@ +package dev.dres.run.score.scorer + +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.template.team.TeamId +import dev.dres.run.score.TaskContext + +class CachingTaskScorer(private val innerScorer: TaskScorer) : TaskScorer { + + private var latest: Map = emptyMap() + + override fun computeScores(submissions: Sequence, context: TaskContext): Map { + latest = innerScorer.computeScores(submissions, context) + return latest + } + + fun scores(): List = latest.map { ScoreEntry(it.key, null, it.value) } + + fun teamScoreMap() : Map = latest + + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index 74b69d508..2bd26a97f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,75 +1,75 @@ package dev.dres.run.score.scorer +import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.interfaces.ScoreEntry -import dev.dres.run.score.interfaces.TaskScorer -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read +import dev.dres.run.score.TaskContext + /** * @author Luca Rossetto * @version 1.0.0 */ -class InferredAveragePrecisionScorer : TaskScorer { +object InferredAveragePrecisionScorer : TaskScorer { - companion object { - private val epsilon = 0.01 //TODO check what TRECVID uses + private val epsilon = 0.01 //TODO check what TRECVID uses + override fun computeScores(submissions: Sequence, context: TaskContext): Map { + TODO("Not yet implemented") + } - // TODO: Fix and make purpose and functionality of class explicit through proper documentation. - //see https://www-nlpir.nist.gov/projects/tv2006/infap/inferredAP.pdf - /*fun infAP(elements: List): Double { + // TODO: Fix and make purpose and functionality of class explicit through proper documentation. + //see https://www-nlpir.nist.gov/projects/tv2006/infap/inferredAP.pdf + /*fun infAP(elements: List): Double { - if (elements.isEmpty()) { - return 0.0 - } + if (elements.isEmpty()) { + return 0.0 + } - var infAPSum = 0.0 - var judgements = 0 - var correct = 0 - var wrong = 0 + var infAPSum = 0.0 + var judgements = 0 + var correct = 0 + var wrong = 0 - elements.forEachIndexed { index, statusAspect -> + elements.forEachIndexed { index, statusAspect -> - val k = index + 1.0 - when(statusAspect.status) { - VerdictStatus.CORRECT -> { - ++judgements // |d100| - ++correct // |rel| + val k = index + 1.0 + when(statusAspect.status) { + VerdictStatus.CORRECT -> { + ++judgements // |d100| + ++correct // |rel| - val ap = if (index == 0){ //special case for first document - 1.0 //all are relevant so far, since there is only one so far and it is relevant - } else { - (1.0 / k) + ((k - 1.0) / k) * ((judgements / (k - 1.0)) * ((correct + epsilon) / (correct + wrong + 2.0 * epsilon))) - } + val ap = if (index == 0){ //special case for first document + 1.0 //all are relevant so far, since there is only one so far and it is relevant + } else { + (1.0 / k) + ((k - 1.0) / k) * ((judgements / (k - 1.0)) * ((correct + epsilon) / (correct + wrong + 2.0 * epsilon))) + } - println(ap) + println(ap) - infAPSum += ap + infAPSum += ap - } - VerdictStatus.WRONG -> { - ++judgements - ++wrong // |nonrel| - } - else -> {} } - - } - - if (correct == 0){ - return 0.0 + VerdictStatus.WRONG -> { + ++judgements + ++wrong // |nonrel| + } + else -> {} } - return infAPSum / correct + } + if (correct == 0){ + return 0.0 } - fun score(submissions: List): Double = infAP(submissions) - fun score(batch: ResultBatch<*>): Double = infAP(batch.results) - */ + return infAPSum / correct + } + fun score(submissions: List): Double = infAP(submissions) + fun score(batch: ResultBatch<*>): Double = infAP(batch.results) + */ + /*override fun computeScores(batch: ResultBatch<*>): Double = this.lastScoresLock.write { val score = score(batch) @@ -77,12 +77,12 @@ class InferredAveragePrecisionScorer : TaskScorer { return@write score }*/ - private var lastScores: MutableMap, Double> = mutableMapOf() - - private val lastScoresLock = ReentrantReadWriteLock() - - - override fun scores(): List = this.lastScoresLock.read { - this.lastScores.map { ScoreEntry(it.key.first, it.key.second, it.value) } - } +// private var lastScores: MutableMap, Double> = mutableMapOf() +// +// private val lastScoresLock = ReentrantReadWriteLock() +// +// +// override fun scores(): List = this.lastScoresLock.read { +// this.lastScores.map { ScoreEntry(it.key.first, it.key.second, it.value) } +// } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index bef9caeef..e9f62a127 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -4,21 +4,15 @@ import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext -import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer -import dev.dres.run.score.interfaces.ScoreEntry -import dev.dres.run.score.interfaces.TeamTaskScorer +import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter -import kotlinx.dnq.query.toList -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read -import kotlin.concurrent.write import kotlin.math.max class KisTaskScorer( private val maxPointsPerTask: Double = defaultmaxPointsPerTask, private val maxPointsAtTaskEnd: Double = defaultmaxPointsAtTaskEnd, private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission -) : RecalculatingSubmissionTaskScorer, TeamTaskScorer { +) : TaskScorer { constructor(parameters: Map) : this( parameters.getOrDefault("maxPointsPerTask", "$defaultmaxPointsPerTask").toDoubleOrNull() ?: defaultmaxPointsPerTask, @@ -32,17 +26,15 @@ class KisTaskScorer( private const val defaultpenaltyPerWrongSubmission: Double = 100.0 } - private var lastScores: Map = emptyMap() - private val lastScoresLock = ReentrantReadWriteLock() - override fun computeScores(submissions: Collection, context: TaskContext): Map = this.lastScoresLock.write { + override fun computeScores(submissions: Sequence, context: TaskContext): Map { val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("No task start time specified.") val taskDuration = context.taskDuration ?: throw IllegalArgumentException("No task duration specified.") val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition - this.lastScores = context.teamIds.associateWith { teamId -> + return context.teamIds.associateWith { teamId -> val verdicts = submissions.filter { it.team.id == teamId }.sortedBy { it.timestamp }.flatMap { sub -> - sub.verdicts.filter { (it.status eq DbVerdictStatus.CORRECT) or (it.status eq DbVerdictStatus.WRONG) }.toList() - } + sub.verdicts.filter { (it.status eq DbVerdictStatus.CORRECT) or (it.status eq DbVerdictStatus.WRONG) }.asSequence() + }.toList() val firstCorrect = verdicts.indexOfFirst { it.status == DbVerdictStatus.CORRECT } val score = if (firstCorrect > -1) { val timeFraction = 1.0 - (verdicts[firstCorrect].submission.timestamp - taskStartTime) / tDur @@ -57,13 +49,6 @@ class KisTaskScorer( } score } - - return this.lastScores } - override fun teamScoreMap(): Map = this.lastScoresLock.read { this.lastScores } - - override fun scores(): List = this.lastScoresLock.read { - this.lastScores.map { ScoreEntry(it.key, null, it.value) } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt index b96c4c3d2..4994a7599 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt @@ -5,9 +5,6 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext -import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer -import dev.dres.run.score.interfaces.ScoreEntry -import dev.dres.run.score.interfaces.TeamTaskScorer import kotlinx.dnq.query.asSequence import java.lang.Double.max import java.util.concurrent.locks.ReentrantReadWriteLock @@ -18,9 +15,7 @@ import kotlin.math.abs /** * The new AVS Scorer. */ -class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPointsPerTask: Double) : - RecalculatingSubmissionTaskScorer, - TeamTaskScorer { +class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPointsPerTask: Double) : TaskScorer { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() @@ -57,13 +52,13 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } override fun computeScores( - submissions: Collection, + submissions: Sequence, context: TaskContext ): Map { val distinctCorrectVideos = submissions.flatMap { submission -> submission.verdicts.asSequence().filter { it.status == DbVerdictStatus.CORRECT && it.item != null } - }.mapNotNullTo(mutableSetOf()) {it.item } + }.mapNotNullTo(mutableSetOf()) { it.item } .size @@ -77,23 +72,24 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } } - lastScores = this.lastScoresLock.write { - teamScoreMapSanitised(submissions.groupBy { it.team }.map { - submissionsPerTeam -> - val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { it.verdicts.asSequence().filter { v -> v.item != null && (v.status == DbVerdictStatus.CORRECT || v.status == DbVerdictStatus.WRONG) } } - submissionsPerTeam.key.teamId to - max(0.0, //prevent negative total scores - verdicts.groupBy { it.item!! }.map { - val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == DbVerdictStatus.CORRECT } - if (firstCorrectIdx < 0) { //no correct submissions, only penalty - it.value.size * -penaltyConstant - } else { //apply penalty for everything before correct submission - 1.0 - firstCorrectIdx * penaltyConstant - } - }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize - ) - }.toMap(), context.teamIds) - } + return teamScoreMapSanitised(submissions.groupBy { it.team }.map { submissionsPerTeam -> + val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { + it.verdicts.asSequence() + .filter { v -> v.item != null && (v.status == DbVerdictStatus.CORRECT || v.status == DbVerdictStatus.WRONG) } + } + submissionsPerTeam.key.teamId to + max(0.0, //prevent negative total scores + verdicts.groupBy { it.item!! }.map { + val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == DbVerdictStatus.CORRECT } + if (firstCorrectIdx < 0) { //no correct submissions, only penalty + it.value.size * -penaltyConstant + } else { //apply penalty for everything before correct submission + 1.0 - firstCorrectIdx * penaltyConstant + } + }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize + ) + }.toMap(), context.teamIds) + // lastScores = this.lastScoresLock.write { // teamScoreMapSanitised( @@ -122,15 +118,7 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint // ) // } - this.lastScoresLock.read { - return lastScores - } } - override fun teamScoreMap(): Map = this.lastScoresLock.read { this.lastScores } - - override fun scores(): List = this.lastScoresLock.read { - this.lastScores.map { ScoreEntry(it.key, null, it.value) } - } } diff --git a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt similarity index 67% rename from backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt rename to backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt index 403e75a78..2b93b2dce 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/interfaces/RecalculatingSubmissionTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt @@ -1,24 +1,27 @@ -package dev.dres.run.score.interfaces +package dev.dres.run.score.scorer import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext +/** Type alias for a */ +typealias ScoreEntry = Triple + /** - * A [TaskScorer] implementation that re-computes the current scores of all teams for a given [TaskRun] based on the + * A [TaskScorer] that re-computes the current scores of all teams for a given [TaskRun] based on the * entire [DbSubmission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. * * @author Luca Rossetto * @author Ralph Gasser * @version 1.1.0 */ -interface RecalculatingSubmissionTaskScorer: TaskScorer { +interface TaskScorer { /** * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [DbSubmission] history. * * @param submissions The [DbSubmission]s used to update this [RecalculatingSubmissionTaskScorer] with. * @param context The [TaskContext] in which scoring takes place. */ - fun computeScores(submissions: Collection, context: TaskContext): Map + fun computeScores(submissions: Sequence, context: TaskContext): Map } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 92f25a081..9c87987a9 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -32,7 +32,8 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte if (taskRun.template.taskGroup.type.submission.contains(DbSubmissionOption.LIMIT_CORRECT_PER_TEAM)) { val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 if (this.run.timeLeft(this.context) > 0) { - if (this.submissions.getAndSet(taskRun.getSubmissions().size) < taskRun.getSubmissions().size) { + val submissionCount = taskRun.getSubmissions().count() + if (this.submissions.getAndSet(submissionCount) < submissionCount) { val allDone = if (this.isAsync) { val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == DbVerdictStatus.CORRECT } numberOfSubmissions >= limit diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index f87e1d504..8727ad419 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -8,8 +8,6 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.run.RunManagerStatus import dev.dres.run.score.TaskContext -import dev.dres.run.score.interfaces.IncrementalSubmissionTaskScorer -import dev.dres.run.score.interfaces.RecalculatingSubmissionTaskScorer import kotlinx.dnq.query.asSequence import java.util.* @@ -36,24 +34,15 @@ class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreb override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { - val scorersToUpdate = mutableSetOf>() val removed = this.list.removeIf { - when(val scorer = it.first.scorer) { - is RecalculatingSubmissionTaskScorer -> scorersToUpdate.add(Pair(it.first, scorer)) - is IncrementalSubmissionTaskScorer -> scorer.update(it.second) - else -> { } - } - true - } - - /* Update scorers. */ - scorersToUpdate.forEach { val task = it.first - val scores = it.second.computeScores( + val scores = it.first.scorer.computeScores( task.getSubmissions(), TaskContext(task.id, task.competition.description.teams.asSequence().map { t -> t.id }.toList(), task.started, task.template.duration, task.ended) ) - it.first.updateTeamAggregation(scores) + task.updateTeamAggregation(scores) + + true } /* If elements were removed, then update scoreboards and tasks. */ diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 5824294bc..73fcd7b2f 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -11,331 +11,331 @@ import org.junit.jupiter.api.Test class LegacyAvsTaskScorerTest { - - private lateinit var scorer: AvsTaskScorer - private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids - private val defaultTaskDuration = 5 * 60L - private val dummyImageItems: List - private val dummyVideoItems: List - - init { - val collectionId = EvaluationId() - - dummyImageItems = listOf( - DbMediaItem.ImageItem(EvaluationId(), "Image 1", "images/1", collectionId), - DbMediaItem.ImageItem(EvaluationId(), "Image 2", "images/2", collectionId), - DbMediaItem.ImageItem(EvaluationId(), "Image 3", "images/3", collectionId), - DbMediaItem.ImageItem(EvaluationId(), "Image 4", "images/4", collectionId), - DbMediaItem.ImageItem(EvaluationId(), "Image 5", "images/5", collectionId), - DbMediaItem.ImageItem(EvaluationId(), "Image 6", "images/6", collectionId) - ) - - dummyVideoItems = listOf( - DbMediaItem.VideoItem(EvaluationId(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), - DbMediaItem.VideoItem(EvaluationId(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), - DbMediaItem.VideoItem(EvaluationId(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), - DbMediaItem.VideoItem(EvaluationId(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), - DbMediaItem.VideoItem(EvaluationId(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) - ) - - } - - @BeforeEach - fun setup() { - this.scorer = AvsTaskScorer() - } - - @Test - fun noSubmissions() { - val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) - assertEquals(0.0, scores[teams[0]]) - assertEquals(0.0, scores[teams[1]]) - assertEquals(0.0, scores[teams[2]]) - } - - @Test - fun allWrongSameImage() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(0.0, scores[teams[0]]) - assertEquals(0.0, scores[teams[1]]) - assertEquals(0.0, scores[teams[2]]) - } - - @Test - fun allWrongDifferentImages() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.WRONG } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(0.0, scores[teams[0]]) - assertEquals(0.0, scores[teams[1]]) - assertEquals(0.0, scores[teams[2]]) - } - - @Test - fun allSameImageAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(100.0, scores[teams[0]]) - assertEquals(100.0, scores[teams[1]]) - assertEquals(100.0, scores[teams[2]]) - } - - @Test - fun allDifferentImageAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) - assertEquals(100.0 / 3.0, scores[teams[1]]!!, 0.001) - assertEquals(100.0 / 3.0, scores[teams[2]]!!, 0.001) - } - - @Test - fun someDifferentImageSomeCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - - /* - total correct: 4 (0, 1, 2, 3) - total wrong: 2 (4, 5) - */ - - //3 out of 4 correct - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, - - - //1 out of 3 correct - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, - - //1 out of 2 correct - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG } - ) - - - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - - /* - c = q(c) = 3, i = 1, q(p) = 4 - - 100 * 3 3 - ------- * - = 64.28571428571429 - 3 + 1/2 4 - */ - assertEquals(64.28571428571429, scores[teams[0]]!!, 0.001) - - /* - c = q(c) = 1, i = 1, q(p) = 4 - - 100 * 1 1 - ------- * - = 12.5 - 1 + 2/2 4 - */ - assertEquals(12.5, scores[teams[1]]!!, 0.001) - - /* - c = q(c) = 3, i = 1, q(p) = 4 - - 100 * 1 1 - ------- * - = 16.66666666666667 - 1 + 1/2 4 - */ - assertEquals(16.66666666666667, scores[teams[2]]!!, 0.001) - } - - - @Test - fun allSameVideoSameSegmentAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(100.0, scores[teams[0]]) - assertEquals(100.0, scores[teams[1]]) - assertEquals(100.0, scores[teams[2]]) - } - - - @Test - fun allSameVideoDifferentSegmentAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) - assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) - assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) - } - - - @Test - fun allDifferentVideoDifferentSegmentAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) - assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) - assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) - } - - @Test - fun allSameVideoOverlappingSegmentAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(100.0, scores[teams[0]]) - assertEquals(100.0, scores[teams[1]]) - assertEquals(100.0, scores[teams[2]]) - } - - @Test - fun allSameVideoOverlappingSegmentSomeCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, - - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG } - - - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - - assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) - assertEquals(85.71428571428571, scores[teams[1]]!!, 0.001) - assertEquals(85.71428571428571, scores[teams[2]]!!, 0.001) - } - - - @Test - fun partiallyMergedSubmissionSomeCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - //team 1 - //gets merged to one - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, - //plus 2 independent correct ones - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = DbVerdictStatus.CORRECT }, - //and an incorrect one directly next to it - DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = DbVerdictStatus.WRONG }, - - //c = 5, q(c) = 3, i = 1 - - - //team 2 - //the center part is missing, so it's counted as two in the quantization - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, - //another correct one, same as team 1 - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, - //and two wrong ones - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, - - //c = 3, q(c) = 3, i = 2 - - //team 3 - //same as team 2, but with all 3 segments of the 1st video, same as team 1 - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, - //another correct one, same as team 1 - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, - //and two wrong ones - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, - - //c = 4, q(c) = 2, i = 2 - - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - - /* - c = 5, q(c) = 3, i = 1, q(p) = 3 - - 100 * 5 3 - ------- * - = 90.909090909090 - 5 + 1/2 3 - */ - assertEquals(90.909090909090, scores[teams[0]]!!, 0.001) - - /* - c = 3, q(c) = 3, i = 2, q(p) = 3 - - 100 * 3 3 - ------- * - = 75 - 3 + 2/2 3 - */ - assertEquals(75.0, scores[teams[1]]) - - /* - c = 4, q(c) = 2, i = 2, q(p) = 3 - - 100 * 4 2 - ------- * - = 53.33333333333 - 4 + 2/2 3 - */ - assertEquals(53.33333333333, scores[teams[2]]!!, 0.001) - } +// +// private lateinit var scorer: AvsTaskScorer +// private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids +// private val defaultTaskDuration = 5 * 60L +// private val dummyImageItems: List +// private val dummyVideoItems: List +// +// init { +// val collectionId = EvaluationId() +// +// dummyImageItems = listOf( +// DbMediaItem.ImageItem(EvaluationId(), "Image 1", "images/1", collectionId), +// DbMediaItem.ImageItem(EvaluationId(), "Image 2", "images/2", collectionId), +// DbMediaItem.ImageItem(EvaluationId(), "Image 3", "images/3", collectionId), +// DbMediaItem.ImageItem(EvaluationId(), "Image 4", "images/4", collectionId), +// DbMediaItem.ImageItem(EvaluationId(), "Image 5", "images/5", collectionId), +// DbMediaItem.ImageItem(EvaluationId(), "Image 6", "images/6", collectionId) +// ) +// +// dummyVideoItems = listOf( +// DbMediaItem.VideoItem(EvaluationId(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), +// DbMediaItem.VideoItem(EvaluationId(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), +// DbMediaItem.VideoItem(EvaluationId(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), +// DbMediaItem.VideoItem(EvaluationId(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), +// DbMediaItem.VideoItem(EvaluationId(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) +// ) +// +// } +// +// @BeforeEach +// fun setup() { +// this.scorer = AvsTaskScorer() +// } +// +// @Test +// fun noSubmissions() { +// val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) +// assertEquals(0.0, scores[teams[0]]) +// assertEquals(0.0, scores[teams[1]]) +// assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// fun allWrongSameImage() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(0.0, scores[teams[0]]) +// assertEquals(0.0, scores[teams[1]]) +// assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// fun allWrongDifferentImages() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.WRONG } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(0.0, scores[teams[0]]) +// assertEquals(0.0, scores[teams[1]]) +// assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// fun allSameImageAllCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(100.0, scores[teams[0]]) +// assertEquals(100.0, scores[teams[1]]) +// assertEquals(100.0, scores[teams[2]]) +// } +// +// @Test +// fun allDifferentImageAllCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) +// assertEquals(100.0 / 3.0, scores[teams[1]]!!, 0.001) +// assertEquals(100.0 / 3.0, scores[teams[2]]!!, 0.001) +// } +// +// @Test +// fun someDifferentImageSomeCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// +// /* +// total correct: 4 (0, 1, 2, 3) +// total wrong: 2 (4, 5) +// */ +// +// //3 out of 4 correct +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, +// +// +// //1 out of 3 correct +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, +// +// //1 out of 2 correct +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG } +// ) +// +// +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// +// /* +// c = q(c) = 3, i = 1, q(p) = 4 +// +// 100 * 3 3 +// ------- * - = 64.28571428571429 +// 3 + 1/2 4 +// */ +// assertEquals(64.28571428571429, scores[teams[0]]!!, 0.001) +// +// /* +// c = q(c) = 1, i = 1, q(p) = 4 +// +// 100 * 1 1 +// ------- * - = 12.5 +// 1 + 2/2 4 +// */ +// assertEquals(12.5, scores[teams[1]]!!, 0.001) +// +// /* +// c = q(c) = 3, i = 1, q(p) = 4 +// +// 100 * 1 1 +// ------- * - = 16.66666666666667 +// 1 + 1/2 4 +// */ +// assertEquals(16.66666666666667, scores[teams[2]]!!, 0.001) +// } +// +// +// @Test +// fun allSameVideoSameSegmentAllCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(100.0, scores[teams[0]]) +// assertEquals(100.0, scores[teams[1]]) +// assertEquals(100.0, scores[teams[2]]) +// } +// +// +// @Test +// fun allSameVideoDifferentSegmentAllCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) +// assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) +// assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) +// } +// +// +// @Test +// fun allDifferentVideoDifferentSegmentAllCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) +// assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) +// assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) +// } +// +// @Test +// fun allSameVideoOverlappingSegmentAllCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, +// +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, +// +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(100.0, scores[teams[0]]) +// assertEquals(100.0, scores[teams[1]]) +// assertEquals(100.0, scores[teams[2]]) +// } +// +// @Test +// fun allSameVideoOverlappingSegmentSomeCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, +// +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, +// +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, +// +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG } +// +// +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// +// assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) +// assertEquals(85.71428571428571, scores[teams[1]]!!, 0.001) +// assertEquals(85.71428571428571, scores[teams[2]]!!, 0.001) +// } +// +// +// @Test +// fun partiallyMergedSubmissionSomeCorrect() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// //team 1 +// //gets merged to one +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, +// //plus 2 independent correct ones +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = DbVerdictStatus.CORRECT }, +// //and an incorrect one directly next to it +// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = DbVerdictStatus.WRONG }, +// +// //c = 5, q(c) = 3, i = 1 +// +// +// //team 2 +// //the center part is missing, so it's counted as two in the quantization +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, +// //another correct one, same as team 1 +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, +// //and two wrong ones +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, +// +// //c = 3, q(c) = 3, i = 2 +// +// //team 3 +// //same as team 2, but with all 3 segments of the 1st video, same as team 1 +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, +// //another correct one, same as team 1 +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, +// //and two wrong ones +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, +// +// //c = 4, q(c) = 2, i = 2 +// +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// +// /* +// c = 5, q(c) = 3, i = 1, q(p) = 3 +// +// 100 * 5 3 +// ------- * - = 90.909090909090 +// 5 + 1/2 3 +// */ +// assertEquals(90.909090909090, scores[teams[0]]!!, 0.001) +// +// /* +// c = 3, q(c) = 3, i = 2, q(p) = 3 +// +// 100 * 3 3 +// ------- * - = 75 +// 3 + 2/2 3 +// */ +// assertEquals(75.0, scores[teams[1]]) +// +// /* +// c = 4, q(c) = 2, i = 2, q(p) = 3 +// +// 100 * 4 2 +// ------- * - = 53.33333333333 +// 4 + 2/2 3 +// */ +// assertEquals(53.33333333333, scores[teams[2]]!!, 0.001) +// } } diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 982fc1ed4..b8c380a6b 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -10,99 +10,99 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class KisTaskScorerTest { - - private lateinit var scorer: KisTaskScorer - private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids - private val dummyImageItems = listOf(DbMediaItem.ImageItem(EvaluationId(), "image 1", "images/1", EvaluationId())) - private val defaultTaskDuration = 5 * 60L - private val maxPointsPerTask = 100.0 - private val maxPointsAtTaskEnd = 50.0 - private val penaltyPerWrongSubmission = 10.0 - - @BeforeEach - fun setup() { - this.scorer = KisTaskScorer(maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) - } - - @Test - fun noSubmissions() { - val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) - assertEquals(0.0, scores[teams[0]]) - assertEquals(0.0, scores[teams[1]]) - assertEquals(0.0, scores[teams[2]]) - } - - @Test - fun allWrong() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(0.0, scores[teams[0]]) - assertEquals(0.0, scores[teams[1]]) - assertEquals(0.0, scores[teams[2]]) - } - - @Test - fun immediatelyRight() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(maxPointsPerTask, scores[teams[0]]) - assertEquals(0.0, scores[teams[1]]) - assertEquals(0.0, scores[teams[2]]) - } - - @Test - fun rightAtTheEnd() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) - } - - @Test - fun rightInTheMiddle() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) - } - - @Test - fun wrongSubmissionPenalty() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = listOf( - - //incorrect submissions - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, - - //correct submissions at 1/2 the task time - DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, - ) - val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) - - assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission, scores[teams[0]]) - assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 2, scores[teams[1]]) - assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 3, scores[teams[2]]) - } +// +// private lateinit var scorer: KisTaskScorer +// private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids +// private val dummyImageItems = listOf(DbMediaItem.ImageItem(EvaluationId(), "image 1", "images/1", EvaluationId())) +// private val defaultTaskDuration = 5 * 60L +// private val maxPointsPerTask = 100.0 +// private val maxPointsAtTaskEnd = 50.0 +// private val penaltyPerWrongSubmission = 10.0 +// +// @BeforeEach +// fun setup() { +// this.scorer = KisTaskScorer(maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) +// } +// +// @Test +// fun noSubmissions() { +// val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) +// assertEquals(0.0, scores[teams[0]]) +// assertEquals(0.0, scores[teams[1]]) +// assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// fun allWrong() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(0.0, scores[teams[0]]) +// assertEquals(0.0, scores[teams[1]]) +// assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// fun immediatelyRight() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(maxPointsPerTask, scores[teams[0]]) +// assertEquals(0.0, scores[teams[1]]) +// assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// fun rightAtTheEnd() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) +// } +// +// @Test +// fun rightInTheMiddle() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) +// } +// +// @Test +// fun wrongSubmissionPenalty() { +// val taskStartTime = System.currentTimeMillis() - 100_000 +// val submissions = listOf( +// +// //incorrect submissions +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, +// +// //correct submissions at 1/2 the task time +// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, +// ) +// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) +// +// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission, scores[teams[0]]) +// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 2, scores[teams[1]]) +// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 3, scores[teams[2]]) +// } } \ No newline at end of file diff --git a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt index 45a301b4b..1fc9762de 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt @@ -1,9 +1,7 @@ package dres.run.score.scorer -import dev.dres.data.model.UID -import dev.dres.data.model.basics.media.MediaItem + import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.SubmissionStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.NewAvsTaskScorer import org.junit.jupiter.api.Assertions @@ -13,124 +11,124 @@ import org.junit.jupiter.api.Test class NewAvsTaskScorerTest { - private lateinit var scorer: NewAvsTaskScorer - private val teams = listOf(UID(),UID(),UID()) // 3 teams - private val defaultTaskDuration = 3 * 60L // 3min - private val dummyVideoItems: List - private val maxPointsPerTask = 1000.0 - private val penalty = 0.2 - - init { - val collectionId = UID() - val list = mutableListOf() - for (i in 1..10){ - list.add(MediaItem.VideoItem(UID(), "Video $i", "videos/$i", collectionId, i*10*60*1000L,24f)) - } - dummyVideoItems = list - } - - @BeforeEach - fun setup(){ - this.scorer = NewAvsTaskScorer(penaltyConstant = penalty, maxPointsPerTask) - } - - @Test - @DisplayName("Three teams all without a submission. Expected score: 0.0") - fun noSubmissions(){ - val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) - - Assertions.assertEquals(0.0, scores[teams[0]]) - Assertions.assertEquals(0.0, scores[teams[1]]) - Assertions.assertEquals(0.0, scores[teams[2]]) - } - - @Test - @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") - fun onlyTeamOneWithAllEqualsOneCorrect(){ - val taskStart = 100_000L - val subs = listOf( - DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} - ) - val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) - Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) - Assertions.assertEquals(0.0, scores[teams[1]]) - Assertions.assertEquals(0.0, scores[teams[2]]) - } - - @Test - @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") - fun allTeamsWithAllEuqalsOneCorrect(){ - val taskStart = 100_000L - val subs = listOf( - DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} - ) - val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) - Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) - Assertions.assertEquals(maxPointsPerTask, scores[teams[1]]) - Assertions.assertEquals(maxPointsPerTask, scores[teams[2]]) - } - - @Test - @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") - fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ - val taskStart = 100_000L - val subs = listOf( - DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} - ) - val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) - Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) - Assertions.assertEquals(maxPointsPerTask/2.0, scores[teams[1]]) - Assertions.assertEquals(0.0, scores[teams[2]]) - } - - @Test - @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") - fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ - val taskStart = 100_000L - val subs = listOf( - /* Team One: All correct */ - DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 20_000, 30_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[0], UID(), taskStart+3000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, - /* Team Two: One correct, One correct with one wrong */ - DbSubmission.Temporal(teams[1], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, - /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ - /* v1 */ - DbSubmission.Temporal(teams[2], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, - /* v2 */ - DbSubmission.Temporal(teams[2], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, - /* v3 */ - DbSubmission.Temporal(teams[2], UID(), taskStart+4000, dummyVideoItems[2], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+5000, dummyVideoItems[2], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+6000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, - /* v4 */ - DbSubmission.Temporal(teams[2], UID(), taskStart+7000, dummyVideoItems[3], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+8000, dummyVideoItems[3], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+9000, dummyVideoItems[3], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, - /* v5 */ - DbSubmission.Temporal(teams[2], UID(), taskStart+10_000, dummyVideoItems[4], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+11_000, dummyVideoItems[4], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, - DbSubmission.Temporal(teams[2], UID(), taskStart+12_000, dummyVideoItems[4], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, - ) - val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) - /* - Team One: No penalty => 1000 = 1000 * [1/3 * 1+1+1] => 1000 * 3/3 - */ - Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) - /* - Team Two: One penalty => 600 = 1000 * [1/3 * (1 + 1-0.2)] => 1000 * 1.8/3 - */ - Assertions.assertEquals(600.0, scores[teams[1]]!!, 0.001) - /* - Team Three: Lucky Brute Force => 400 = 1000 * [1/3 * (1 + {1-0.2} + {1-0.4} + {-3*0.2} + {-3*0.2})] => 1000 * 1.2/3 - */ - Assertions.assertEquals(400.0, scores[teams[2]]!!, 0.001) - } +// private lateinit var scorer: NewAvsTaskScorer +// private val teams = listOf(UID(),UID(),UID()) // 3 teams +// private val defaultTaskDuration = 3 * 60L // 3min +// private val dummyVideoItems: List +// private val maxPointsPerTask = 1000.0 +// private val penalty = 0.2 +// +// init { +// val collectionId = UID() +// val list = mutableListOf() +// for (i in 1..10){ +// list.add(MediaItem.VideoItem(UID(), "Video $i", "videos/$i", collectionId, i*10*60*1000L,24f)) +// } +// dummyVideoItems = list +// } +// +// @BeforeEach +// fun setup(){ +// this.scorer = NewAvsTaskScorer(penaltyConstant = penalty, maxPointsPerTask) +// } +// +// @Test +// @DisplayName("Three teams all without a submission. Expected score: 0.0") +// fun noSubmissions(){ +// val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) +// +// Assertions.assertEquals(0.0, scores[teams[0]]) +// Assertions.assertEquals(0.0, scores[teams[1]]) +// Assertions.assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") +// fun onlyTeamOneWithAllEqualsOneCorrect(){ +// val taskStart = 100_000L +// val subs = listOf( +// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} +// ) +// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) +// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) +// Assertions.assertEquals(0.0, scores[teams[1]]) +// Assertions.assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") +// fun allTeamsWithAllEuqalsOneCorrect(){ +// val taskStart = 100_000L +// val subs = listOf( +// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} +// ) +// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) +// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) +// Assertions.assertEquals(maxPointsPerTask, scores[teams[1]]) +// Assertions.assertEquals(maxPointsPerTask, scores[teams[2]]) +// } +// +// @Test +// @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") +// fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ +// val taskStart = 100_000L +// val subs = listOf( +// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} +// ) +// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) +// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) +// Assertions.assertEquals(maxPointsPerTask/2.0, scores[teams[1]]) +// Assertions.assertEquals(0.0, scores[teams[2]]) +// } +// +// @Test +// @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") +// fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ +// val taskStart = 100_000L +// val subs = listOf( +// /* Team One: All correct */ +// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 20_000, 30_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[0], UID(), taskStart+3000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, +// /* Team Two: One correct, One correct with one wrong */ +// DbSubmission.Temporal(teams[1], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, +// /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ +// /* v1 */ +// DbSubmission.Temporal(teams[2], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, +// /* v2 */ +// DbSubmission.Temporal(teams[2], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, +// /* v3 */ +// DbSubmission.Temporal(teams[2], UID(), taskStart+4000, dummyVideoItems[2], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+5000, dummyVideoItems[2], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+6000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, +// /* v4 */ +// DbSubmission.Temporal(teams[2], UID(), taskStart+7000, dummyVideoItems[3], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+8000, dummyVideoItems[3], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+9000, dummyVideoItems[3], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, +// /* v5 */ +// DbSubmission.Temporal(teams[2], UID(), taskStart+10_000, dummyVideoItems[4], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+11_000, dummyVideoItems[4], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, +// DbSubmission.Temporal(teams[2], UID(), taskStart+12_000, dummyVideoItems[4], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, +// ) +// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) +// /* +// Team One: No penalty => 1000 = 1000 * [1/3 * 1+1+1] => 1000 * 3/3 +// */ +// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) +// /* +// Team Two: One penalty => 600 = 1000 * [1/3 * (1 + 1-0.2)] => 1000 * 1.8/3 +// */ +// Assertions.assertEquals(600.0, scores[teams[1]]!!, 0.001) +// /* +// Team Three: Lucky Brute Force => 400 = 1000 * [1/3 * (1 + {1-0.2} + {1-0.4} + {-3*0.2} + {-3*0.2})] => 1000 * 1.2/3 +// */ +// Assertions.assertEquals(400.0, scores[teams[2]]!!, 0.001) +// } } From 5dcf7b3ad3cd1179f38c73eaefa055349a23726a Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 3 Feb 2023 16:32:12 +0100 Subject: [PATCH 058/498] Addressing #387 fixing compilation errors in src/app/auditlog and src/app/collection --- .../collection/AddCollectionHandler.kt | 8 +- .../collection/ListCollectionHandler.kt | 12 +-- .../collection/ShowCollectionHandler.kt | 14 +-- .../collection/UpdateCollectionHandler.kt | 8 +- .../types/collection/ApiMediaCollection.kt | 22 +++++ .../collection/ApiPopulatedMediaCollection.kt | 6 ++ .../collection/RestFullMediaCollection.kt | 3 - .../types/collection/RestMediaCollection.kt | 23 ----- doc/oas.json | 94 +++++-------------- .../admin-auditlog-overview.component.ts | 63 +++++-------- .../auditlog.datasource.ts | 5 +- .../collection-builder-dialog.component.ts | 8 +- .../media-item-builder-dialog.component.ts | 2 +- .../collection-list.component.ts | 12 +-- .../collection-viewer.component.ts | 6 +- ...mpetition-builder-team-dialog.component.ts | 16 ++-- frontend/src/app/services/services.module.ts | 2 +- 17 files changed, 128 insertions(+), 176 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiPopulatedMediaCollection.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index 7cfea592a..0e1493a5d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -26,7 +26,7 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand path = "/api/v2/collection", tags = ["Collection"], methods = [HttpMethod.POST], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaCollection::class)]), responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -35,7 +35,7 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand ) override fun doPost(ctx: Context): SuccessStatus { val restCollection = try { - ctx.bodyAsClass(RestMediaCollection::class.java) + ctx.bodyAsClass(ApiMediaCollection::class.java) } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } @@ -53,4 +53,4 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand } return SuccessStatus("Collection ${collection.id} added successfully.") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt index c9cf6e9b9..45eee1c68 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.media.DbMediaCollection import io.javalin.http.Context @@ -17,7 +17,7 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 1.0.0 */ -class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { +class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { override val route: String = "collection/list" @@ -27,13 +27,13 @@ class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan tags = ["Collection"], methods = [HttpMethod.GET], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): List { + override fun doGet(ctx: Context): List { return this.store.transactional(true) { - DbMediaCollection.all().asSequence().map { RestMediaCollection.fromMediaCollection(it) }.toList() + DbMediaCollection.all().asSequence().map { ApiMediaCollection.fromMediaCollection(it) }.toList() } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index 214595f0c..d840434aa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -1,8 +1,8 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.collection.RestFullMediaCollection -import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.collection.ApiPopulatedMediaCollection +import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.media.DbMediaItem import io.javalin.http.Context @@ -17,7 +17,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0.0 */ -class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { +class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { override val route: String = "collection/{collectionId}" @@ -27,15 +27,15 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], tags = ["Collection"], responses = [ - OpenApiResponse("200", [OpenApiContent(RestFullMediaCollection::class)]), + OpenApiResponse("200", [OpenApiContent(ApiPopulatedMediaCollection::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): RestFullMediaCollection = this.store.transactional(true) { + override fun doGet(ctx: Context): ApiPopulatedMediaCollection = this.store.transactional(true) { val collection = collectionFromContext(ctx) //also checks if collection exists val items = DbMediaItem.query(DbMediaItem::collection eq collection).asSequence().map { it.toApi() }.toList() - RestFullMediaCollection(RestMediaCollection.fromMediaCollection(collection), items) + ApiPopulatedMediaCollection(ApiMediaCollection.fromMediaCollection(collection), items) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt index d9e255937..ddd41effc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.types.collection.RestMediaCollection +import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -29,7 +29,7 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH path = "/api/v2/collection", tags = ["Collection"], methods = [HttpMethod.PATCH], - requestBody = OpenApiRequestBody([OpenApiContent(RestMediaCollection::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaCollection::class)]), responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -39,7 +39,7 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH override fun doPatch(ctx: Context): SuccessStatus { val restCollection = try { - ctx.bodyAsClass(RestMediaCollection::class.java) + ctx.bodyAsClass(ApiMediaCollection::class.java) } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } @@ -55,4 +55,4 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH return status } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt new file mode 100644 index 000000000..96b4abcb1 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt @@ -0,0 +1,22 @@ +package dev.dres.api.rest.types.collection + +import dev.dres.data.model.media.CollectionId +import dev.dres.data.model.media.DbMediaCollection + +/** + * The RESTful API equivalent for [DbMediaCollection]. + * + * @see DbMediaCollection + * @author Ralph Gasser + * @version 1.0 + */ +data class ApiMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { + companion object { + /** + * Generates a [ApiMediaCollection] from a [DbMediaCollection] and returns it. + * + * @param collection The [DbMediaCollection] to convert. + */ + fun fromMediaCollection(collection: DbMediaCollection) = ApiMediaCollection(collection.id, collection.name, collection.description, collection.path) + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiPopulatedMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiPopulatedMediaCollection.kt new file mode 100644 index 000000000..ecfb9607c --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiPopulatedMediaCollection.kt @@ -0,0 +1,6 @@ +package dev.dres.api.rest.types.collection + +/** + * A [ApiMediaCollection] populated with its items. No pagination, populated with all items. + */ +data class ApiPopulatedMediaCollection (val collection:ApiMediaCollection, val items: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt deleted file mode 100644 index 705e36cd8..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestFullMediaCollection.kt +++ /dev/null @@ -1,3 +0,0 @@ -package dev.dres.api.rest.types.collection - -data class RestFullMediaCollection (val collection:RestMediaCollection, val items: List) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt deleted file mode 100644 index 8ec716699..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/RestMediaCollection.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.dres.api.rest.types.collection - -import dev.dres.data.model.media.CollectionId -import dev.dres.data.model.media.DbMediaCollection -import dev.dres.data.model.template.task.DbTaskTemplate - -/** - * The RESTful API equivalent for [DbMediaCollection]. - * - * @see DbMediaCollection - * @author Ralph Gasser - * @version 1.0 - */ -data class RestMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { - companion object { - /** - * Generates a [ApiMediaItem] from a [DbTaskTemplate] and returns it. - * - * @param task The [DbTaskTemplate] to convert. - */ - fun fromMediaCollection(item: DbMediaCollection) = RestMediaCollection(item.id, item.name, item.description, item.path) - } -} \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 916424655..118b43a1c 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -131,7 +131,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "$ref" : "#/components/schemas/ApiMediaCollection" } } }, @@ -177,7 +177,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "$ref" : "#/components/schemas/ApiMediaCollection" } } }, @@ -321,7 +321,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/RestFullMediaCollection" + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" } } } @@ -473,7 +473,7 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "$ref" : "#/components/schemas/ApiMediaCollection" } } } @@ -4450,11 +4450,11 @@ }, "type" : { "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE", "type" ] + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] }, "source" : { "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL", "source" ] + "enum" : [ "REST", "CLI", "INTERNAL" ] }, "timestamp" : { "type" : "number", @@ -4490,7 +4490,7 @@ }, "required" : [ "description" ] }, - "RestMediaCollection" : { + "ApiMediaCollection" : { "type" : "object", "properties" : { "id" : { @@ -4547,11 +4547,11 @@ }, "required" : [ "id", "name", "type", "collectionId", "location" ] }, - "RestFullMediaCollection" : { + "ApiPopulatedMediaCollection" : { "type" : "object", "properties" : { "collection" : { - "$ref" : "#/components/schemas/RestMediaCollection" + "$ref" : "#/components/schemas/ApiMediaCollection" }, "items" : { "type" : "array", @@ -4592,7 +4592,7 @@ }, "type" : { "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE", "type" ] + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] }, "properties" : { "$ref" : "#/components/schemas/RunProperties" @@ -4729,7 +4729,7 @@ }, "type" : { "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE", "type" ] + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] }, "properties" : { "$ref" : "#/components/schemas/RunProperties" @@ -4902,7 +4902,7 @@ }, "verdict" : { "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE", "status" ] + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] } }, "required" : [ "token", "validator", "verdict" ] @@ -4911,7 +4911,7 @@ "type" : "object", "properties" : { "verdict" : { - "$ref" : "#/components/schemas/VerdictStatus" + "$ref" : "#/components/schemas/DbVerdictStatus" } }, "required" : [ "verdict" ] @@ -4992,7 +4992,7 @@ "type" : "object", "properties" : { "submission" : { - "$ref" : "#/components/schemas/VerdictStatus" + "$ref" : "#/components/schemas/DbVerdictStatus" }, "description" : { "type" : "string" @@ -5283,11 +5283,11 @@ "properties" : { "type" : { "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT", "type" ] + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] }, "status" : { "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE", "status" ] + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] }, "item" : { "$ref" : "#/components/schemas/ApiMediaItem" @@ -5365,7 +5365,7 @@ }, "required" : [ "contentType", "offset" ] }, - "VerdictStatus" : { + "DbVerdictStatus" : { "type" : "object", "properties" : { "description" : { @@ -5445,7 +5445,7 @@ "properties" : { "type" : { "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT", "type" ] + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] }, "target" : { "type" : "string" @@ -5461,7 +5461,7 @@ "properties" : { "type" : { "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE", "type" ] + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] }, "start" : { "type" : "number", @@ -5501,12 +5501,12 @@ }, "targetOption" : { "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT", "option" ] + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] }, "hintOptions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiComponentOption" + "$ref" : "#/components/schemas/ApiHintOption" } }, "submissionOptions" : { @@ -5523,7 +5523,7 @@ }, "scoreOption" : { "type" : "string", - "enum" : [ "KIS", "AVS", "option" ] + "enum" : [ "KIS", "AVS" ] }, "configuration" : { "type" : "object", @@ -5645,32 +5645,17 @@ }, "required" : [ "start", "end" ] }, - "ApiComponentOption" : { + "ApiHintOption" : { "type" : "object", - "properties" : { - "option" : { - "$ref" : "#/components/schemas/HintOption" - } - }, - "required" : [ "option" ] + "properties" : { } }, "ApiSubmissionOption" : { "type" : "object", - "properties" : { - "option" : { - "$ref" : "#/components/schemas/SubmissionOption" - } - }, - "required" : [ "option" ] + "properties" : { } }, "ApiTaskOption" : { "type" : "object", - "properties" : { - "option" : { - "$ref" : "#/components/schemas/TaskOption" - } - }, - "required" : [ "option" ] + "properties" : { } }, "ApiTemporalPoint" : { "type" : "object", @@ -5684,33 +5669,6 @@ } }, "required" : [ "value", "unit" ] - }, - "HintOption" : { - "type" : "object", - "properties" : { - "description" : { - "type" : "string" - } - }, - "required" : [ "description" ] - }, - "SubmissionOption" : { - "type" : "object", - "properties" : { - "description" : { - "type" : "string" - } - }, - "required" : [ "description" ] - }, - "TaskOption" : { - "type" : "object", - "properties" : { - "description" : { - "type" : "string" - } - }, - "required" : [ "description" ] } }, "securitySchemes" : { } diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts index 8f027fa10..3c1e09548 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts +++ b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts @@ -1,22 +1,11 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; -import { - AuditService, - ApiAuditLogEntry, - ApiCompetitionEndAuditLogEntry, - ApiJudgementAuditLogEntry, - ApiLoginAuditLogEntry, - ApiLogoutAuditLogEntry, - ApiSubmissionAuditLogEntry, - ApiTaskEndAuditLogEntry, - ApiTaskModifiedAuditLogEntry, - ApiTaskStartAuditLogEntry, -} from '../../../../openapi'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Subscription, timer } from 'rxjs'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { switchMap } from 'rxjs/operators'; import { AuditlogDatasource } from './auditlog.datasource'; +import {ApiAuditLogEntry, AuditLogInfo, AuditService} from '../../../../openapi'; @Component({ selector: 'app-admin-auditlog-overview', @@ -60,8 +49,10 @@ export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy public ngAfterViewInit(): void { /* Initialize subscription for loading audit logs. */ this.pollingSub = timer(0, this.pollingFrequency) - .pipe(switchMap((s) => this.logService.getApiV1AuditInfo())) - .subscribe((i) => { + .pipe(switchMap((s) => this.logService + .apiV2AuditInfoGet())) + //.getApiV1AuditInfo())) + .subscribe((i: AuditLogInfo) => { this.length = i.size; if (this.paginator.pageIndex === 0) { /* Only the first page needs refreshing because logs are ordered chronologically. */ @@ -92,39 +83,37 @@ export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy public detailsOf(log: ApiAuditLogEntry): string { switch (log.type) { + case 'PREPARE_JUDGEMENT': + return `Judgement preparation in competition ${log?.competitionId} | ${log?.description}.`; + case 'SUBMISSION_VALIDATION': + return `Submission validation in competition ${log?.competitionId} for submission ${log?.submissionId} | ${log?.description}.`; + case 'SUBMISSION_STATUS_OVERWRITE': + return `Submission status overwrite in competition ${log?.competitionId} for submission ${log.submissionId} | ${log?.description}.`; + case 'type': + return `Log with tyhpe 'type'. This is potentially an erroneous state and should not happen (logId ${log.id}). | ${log?.description}.`; case 'COMPETITION_START': - const cs = log as unknown as RestCompetitionEndAuditLogEntry; - return `Competition ${cs.competition} has been started by user ${cs.user}`; + return `Competition ${log.competitionId} has been started by user ${log.userId} | ${log?.description}.`; case 'COMPETITION_END': - const ce = log as unknown as RestCompetitionEndAuditLogEntry; - return `Competition ${ce.competition} has been ended by user ${ce.user}`; + return `Competition ${log.competitionId} has been ended by user ${log.userId} | ${log?.description}.`; case 'TASK_START': - const ts = log as unknown as RestTaskStartAuditLogEntry; - return `Task ${ts.taskName} in competition ${ts.competition} has been started by user ${ts.user}`; + // return `Task ${log.taskId} in competition ${log.competitionId} has been started by user ${log.userId}`; + return `Task *** in competition ${log.competitionId} has been started by user ${log.userId} | ${log?.description}.`; case 'TASK_MODIFIED': - const tm = log as unknown as RestTaskModifiedAuditLogEntry; - return `Task ${tm.taskName} in competition ${tm.competition} has been modified by user ${tm.user}: ${tm.modification}`; + // return `Task ${log.taskId} in competition ${log.competitionId} has been modified by user ${log.userId}: ${log.modification}`; + return `Task *** in competition ${log.competitionId} has been modified by user ${log.userId}: *** | ${log?.description}.`; case 'TASK_END': - const te = log as unknown as RestTaskEndAuditLogEntry; - return `Task ${te.taskName} in competition ${te.competition} has been ended by user ${te.user}`; + return `Task *** in competition ${log.competitionId} has been ended by user ${log.userId} | ${log?.description}.`; case 'SUBMISSION': - const subm = log as unknown as RestSubmissionAuditLogEntry; - return `For ${subm.taskName}, a submission ${JSON.stringify(subm.submission)} was made in competition ${ - subm.competition + return `For task ***, a submission ${log.submissionId} was made in competition ${ + log.competitionId } - by user ${subm.user} from ${subm.address}`.replace('\n', ''); + by user ${log.userId} from ${log.address} | ${log?.description}.`.replace('\n', ''); case 'JUDGEMENT': - const judgement = log as unknown as RestJudgementAuditLogEntry; - return `Judge ${judgement.user ? judgement.user : ''} published verdict ${judgement.verdict} for token ${ - judgement.token - } - based on validator ${judgement.validator} in competition ${judgement.competition}`.replace('\n', ''); + return `Judge ${log.userId ? log.userId : ''} published a verdict in competition ${log.competitionId} | ${log?.description}.`.replace('\n', ''); case 'LOGIN': - const login = log as unknown as RestLoginAuditLogEntry; - return `${login.user} has logged in using ${login.session}`; + return `${log.userId} has logged in using ${log.session} | ${log?.description}.`; case 'LOGOUT': - const logout = log as unknown as RestLogoutAuditLogEntry; - return `${logout.session} was logged out`; + return `${log.session} was logged out | ${log?.description}.`; default: return JSON.stringify(log); } diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts index 57ea8fd35..dfaa1602c 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts +++ b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts @@ -1,7 +1,7 @@ import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { AuditService, ApiAuditLogEntry } from '../../../../openapi'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, finalize, first } from 'rxjs/operators'; +import {ApiAuditLogEntry, AuditService} from '../../../../openapi'; /** * {@link DataSource} implementation of {@link ApiAuditLogEntry}. @@ -40,7 +40,8 @@ export class AuditlogDatasource implements DataSource { refresh(pageIndex = 0, pageSize = 100) { this.loadingSubject.next(true); this.logService - .getApiV1AuditLogListLimitWithLimitWithPage(pageSize, pageIndex) + //.getApiV1AuditLogListLimitWithLimitWithPage(pageSize, pageIndex) + .apiV2AuditLogListLimitLimitPageGet(pageSize, pageIndex) .pipe( first(), catchError(() => of([])), diff --git a/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts index 3f53e038a..2dea523f6 100644 --- a/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { RestMediaCollection } from '../../../../../openapi'; import { FormControl, FormGroup, Validators } from '@angular/forms'; +import {ApiMediaCollection} from '../../../../../openapi'; @Component({ selector: 'app-collection-builder-dialog', @@ -13,7 +13,7 @@ export class CollectionBuilderDialogComponent implements OnInit { constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: RestMediaCollection + @Inject(MAT_DIALOG_DATA) public data: ApiMediaCollection ) { this.form = new FormGroup({ id: new FormControl(data?.id), @@ -59,12 +59,12 @@ export class CollectionBuilderDialogComponent implements OnInit { /** * Fetchs the data from the form and transforms it to wireformat */ - private fetchFormData(): RestMediaCollection { + private fetchFormData(): ApiMediaCollection { const col = { name: this.form.get('name').value, description: this.form.get('description').value, basePath: this.form.get('basePath').value, - } as RestMediaCollection; + } as ApiMediaCollection; /* Only set id, if pre-existing, i.e. this is an edit dialog */ if (this.form.get('id')?.value) { col.id = this.form.get('id').value; diff --git a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts index 2ca5b96b0..bdfe98718 100644 --- a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { ApiMediaItem } from '../../../../../openapi'; +import {ApiMediaItem} from '../../../../../openapi'; export interface MediaItemBuilderData { item?: ApiMediaItem; diff --git a/frontend/src/app/collection/collection-list/collection-list.component.ts b/frontend/src/app/collection/collection-list/collection-list.component.ts index e60904502..bcec63c65 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.ts +++ b/frontend/src/app/collection/collection-list/collection-list.component.ts @@ -1,10 +1,10 @@ import { AfterViewInit, Component } from '@angular/core'; -import { CollectionService, RestMediaCollection } from '../../../../openapi'; import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { CollectionBuilderDialogComponent } from '../collection-builder/collection-builder-dialog/collection-builder-dialog.component'; import { filter, flatMap } from 'rxjs/operators'; +import {ApiMediaCollection, CollectionService} from '../../../../openapi'; @Component({ selector: 'app-collection-list', @@ -13,7 +13,7 @@ import { filter, flatMap } from 'rxjs/operators'; }) export class CollectionListComponent implements AfterViewInit { displayedColumns = ['actions', 'id', 'name', 'description', 'basePath']; - collections: RestMediaCollection[] = []; + collections: ApiMediaCollection[] = []; constructor( private collectionService: CollectionService, @@ -24,7 +24,7 @@ export class CollectionListComponent implements AfterViewInit { refresh() { this.collectionService.apiV2CollectionListGet().subscribe( - (results: RestMediaCollection[]) => { + (results: ApiMediaCollection[]) => { this.collections = results; }, (r) => { @@ -39,7 +39,7 @@ export class CollectionListComponent implements AfterViewInit { } create(id?: string) { - const config = { width: '500px' } as MatDialogConfig; + const config = { width: '500px' } as MatDialogConfig; if (id) { config.data = this.collections.find((c) => c.id === id); } else { @@ -50,7 +50,7 @@ export class CollectionListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - flatMap((r: RestMediaCollection) => { + flatMap((r: ApiMediaCollection) => { if (id) { return this.collectionService.apiV2CollectionPatch(r); } else { @@ -87,7 +87,7 @@ export class CollectionListComponent implements AfterViewInit { } } - resolveMediaCollectionById(_: number, item: RestMediaCollection) { + resolveMediaCollectionById(_: number, item: ApiMediaCollection) { return item.id; } } diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index 2e4edc9a2..c0d089403 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -1,5 +1,4 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; -import { CollectionService, RestFullMediaCollection, ApiMediaItem } from '../../../../openapi'; import { ActivatedRoute, Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; @@ -13,6 +12,7 @@ import { import { MatPaginator } from '@angular/material/paginator'; import { MatTableDataSource } from '@angular/material/table'; import { MatSort } from '@angular/material/sort'; +import {ApiMediaItem, ApiPopulatedMediaCollection, CollectionService} from '../../../../openapi'; @Component({ selector: 'app-collection-viewer', @@ -35,7 +35,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { collectionId: Observable; /** Observable containing the media collection information. */ - collection: Observable; + collection: Observable; /** A subject used to trigger refrehs of the list. */ refreshSubject: Subject = new BehaviorSubject(null); @@ -86,7 +86,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { ), shareReplay({ bufferSize: 1, refCount: true }) ); - this.subscription = this.collection.subscribe((s: RestFullMediaCollection) => { + this.subscription = this.collection.subscribe((s: ApiPopulatedMediaCollection) => { this.dataSource.data = s.items; }); } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index 42ed260be..811369147 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -1,11 +1,11 @@ import { Component, Inject } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { RestTeam, ApiUser, UserService } from '../../../../../openapi'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { map, shareReplay } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { AppConfig } from '../../../app.config'; +import {ApiTeam, ApiUser, UserService} from '../../../../../openapi'; @Component({ selector: 'app-competition-builder-add-team-dialog', @@ -42,7 +42,7 @@ export class CompetitionBuilderTeamDialogComponent { private dialogRef: MatDialogRef, private userService: UserService, private config: AppConfig, - @Inject(MAT_DIALOG_DATA) private team?: RestTeam + @Inject(MAT_DIALOG_DATA) private team?: ApiTeam ) { this.form = new FormGroup({ name: new FormControl(team?.name, [Validators.required, Validators.minLength(3)]), @@ -51,12 +51,14 @@ export class CompetitionBuilderTeamDialogComponent { Validators.minLength(7), Validators.maxLength(7), ]), - logoId: new FormControl(team?.logoId), + logoId: new FormControl(/*team?.logoId*/''), logoData: new FormControl(team?.logoData), users: new FormControl(team?.users != null ? team.users : []), userInput: new FormControl(''), }); - this.availableUsers = this.userService.getApiV1UserList().pipe( + this.availableUsers = this.userService.apiV2UserListGet() + //getApiV1UserList() + .pipe( map((value) => { return value.filter((user) => user.role !== 'JUDGE' && user.role !== 'VIEWER'); }), @@ -132,7 +134,7 @@ export class CompetitionBuilderTeamDialogComponent { } /** - * Saves all changs in the dialog and closes it. + * Saves all changes in the dialog and closes it. */ public save(): void { if (this.form.valid) { @@ -149,9 +151,9 @@ export class CompetitionBuilderTeamDialogComponent { name: this.form.get('name').value, color: this.form.get('color').value, logoData: this.form.get('logoData').value, - logoId: this.form.get('logoId').value, + // logoId: this.form.get('logoId').value, users: this.form.get('users').value, - } as RestTeam; + } as ApiTeam; } /** diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 817d9a4f5..f8d6e345f 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -1,4 +1,3 @@ -import { ApiModule, Configuration } from '../../../openapi'; import { NgModule } from '@angular/core'; import { AppConfig } from '../app.config'; import { AuthenticationService } from './session/authentication.sevice'; @@ -12,6 +11,7 @@ import { CanDeactivateGuard } from './can-deactivate.guard'; import { EnhanceTaskPastInfoPipe } from './pipes/enhance-task-past-info.pipe'; import { ResolveTeamPipe } from './pipes/resolve-team.pipe'; import { EnhanceTaskSubmissionInfoPipe } from './pipes/enhance-task-submission-info.pipe'; +import {ApiModule, Configuration} from '../../../openapi'; /** * Provides the {@link AppConfig} reference. From b9ec922245012d74b26aa2564f8faacff0b9144a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 3 Feb 2023 16:39:15 +0100 Subject: [PATCH 059/498] DbAnswerSet now actually has a set of answers --- .../dev/dres/api/cli/EvaluationRunCommand.kt | 2 +- .../judgement/DequeueJudgementHandler.kt | 14 +++-- .../handler/judgement/DequeueVoteHandler.kt | 16 +++--- .../preview/SubmissionPreviewHandler.kt | 8 +-- .../handler/submission/SubmissionHandler.kt | 38 +++++++------ .../{ApiVerdict.kt => ApiAnswer.kt} | 12 +--- .../api/rest/types/evaluation/ApiAnswerSet.kt | 15 +++++ .../rest/types/evaluation/ApiAnswerType.kt | 2 +- .../rest/types/evaluation/ApiSubmission.kt | 2 +- .../dres/api/rest/types/evaluation/ApiTask.kt | 2 +- .../rest/types/evaluation/ApiVerdictStatus.kt | 2 +- .../data/model/submissions/DbAnswerSet.kt | 44 +++------------ .../data/model/submissions/DbSubmission.kt | 6 +- .../run/InteractiveAsynchronousRunManager.kt | 6 +- .../run/InteractiveSynchronousRunManager.kt | 4 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 18 +++--- .../handlers/SubmissionStatisticsHandler.kt | 8 +-- .../dres/run/filter/CorrectPerTeamFilter.kt | 2 +- .../run/filter/CorrectPerTeamItemFilter.kt | 2 +- .../run/filter/CorrectPerTeamMemberFilter.kt | 2 +- .../run/filter/DuplicateSubmissionFilter.kt | 17 ++++-- .../dres/run/filter/ItemSubmissionFilter.kt | 2 +- .../run/filter/MaximumTotalPerTeamFilter.kt | 2 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 2 +- .../run/filter/TemporalSubmissionFilter.kt | 2 +- .../run/filter/TextualSubmissionFilter.kt | 2 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 13 +++-- .../dres/run/score/scorer/KisTaskScorer.kt | 2 +- .../dres/run/score/scorer/NewAvsTaskScorer.kt | 11 ++-- .../dres/run/updatables/EndTaskUpdatable.kt | 4 +- .../validation/ChainedSubmissionValidator.kt | 2 +- .../MediaItemsSubmissionValidator.kt | 9 +-- .../TemporalContainmentSubmissionValidator.kt | 55 +++++++++++-------- .../TemporalOverlapSubmissionValidator.kt | 54 ++++++++++-------- .../dev/dres/run/validation/TextValidator.kt | 40 ++++++++------ .../judged/BasicJudgementValidator.kt | 6 +- .../dres/run/validation/judged/ItemRange.kt | 10 ++-- 37 files changed, 226 insertions(+), 212 deletions(-) rename backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/{ApiVerdict.kt => ApiAnswer.kt} (53%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt index dc01e1d13..32a2e2e4a 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt @@ -384,7 +384,7 @@ class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpClikt writeRow(header) tasks.asSequence().forEach { task -> val submittedItems = task.submissions.asSequence().groupBy {s -> - Triple(s.item?.name?: "unknown", s.start, s.end) + Triple(s.answers.firstOrNull()?.item?.name?: "unknown", s.answers.firstOrNull()?.start, s.answers.firstOrNull()?.end) //TODO flatten? } submittedItems.entries.forEach { items -> val status = items.value.map { s -> s.status }.toSet() //should only contain one element diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 12b86e246..75766161e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -11,6 +11,7 @@ import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.firstOrNull /** * A [GetRestHandler] to dequeue the next [ApiJudgementRequest] that is ready for judgement. @@ -47,19 +48,20 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break val next = validator.next(ctx.sessionToken()!!) ?: break val taskDescription = next.second.task.template.textualDescription() - when (next.second.type) { + when (next.second.answers.firstOrNull()?.type) { DbAnswerType.TEXT -> { - val text = next.second.text ?: continue + val text = next.second.answers.firstOrNull()?.text ?: continue return@transactional ApiJudgementRequest(next.first, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } DbAnswerType.ITEM -> { - val item = next.second.item ?: continue + val item = next.second.answers.firstOrNull()?.item ?: continue return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) } DbAnswerType.TEMPORAL -> { - val item = next.second.item ?: continue - val start = next.second.start ?: continue - val end = next.second.end ?: continue + val answer = next.second.answers.firstOrNull() ?: continue + val item = answer.item ?: continue + val start = answer.start ?: continue + val end = answer.end ?: continue return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, start, end) } else -> continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 39dd1a895..5384c25b4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -11,6 +11,7 @@ import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.firstOrNull /** * A [GetRestHandler] to dequeue the next [ApiJudgementRequest] that is ready for public voting. @@ -40,24 +41,25 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( val evaluationManager = ctx.eligibleManagerForId() /* Start transaction. */ - this.store.transactional { + this.store.transactional {//TODO needs adjustment to deal with answerSets do { val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break val next = validator.nextSubmissionToVoteOn() ?: break val taskDescription = next.task.template.textualDescription() - when (next.type) { + when (next.answers.firstOrNull()?.type) { DbAnswerType.TEXT -> { - val text = next.text ?: continue + val text = next.answers.firstOrNull()?.text ?: continue return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } DbAnswerType.ITEM -> { - val item = next.item ?: continue + val item = next.answers.firstOrNull()?.item ?: continue return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) } DbAnswerType.TEMPORAL -> { - val item = next.item ?: continue - val start = next.start ?: continue - val end = next.end ?: continue + val answer = next.answers.firstOrNull() ?: continue + val item = answer.item ?: continue + val start = answer.start ?: continue + val end = answer.end ?: continue return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, start, end) } else -> continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index ec183045e..c23048be5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -50,14 +50,14 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab val rac = RunActionContext.runActionContext(ctx, run) val submission = run.allSubmissions(rac).find { it.id == submissionId } ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) - val verdict = submission.verdicts.firstOrNull() + val verdict = submission.answerSets.firstOrNull() ?: throw ErrorStatusException(404, "Submission '$submissionId' not found", ctx) when { - verdict.item != null -> { - handlePreviewRequest(verdict.item!!, if (verdict.start != null) verdict.start else null, ctx) + verdict.answers.firstOrNull()?.item != null -> { + handlePreviewRequest(verdict.answers.firstOrNull()?.item!!, if (verdict.answers.firstOrNull()?.start != null) verdict.answers.firstOrNull()?.start else null, ctx) } - verdict.text != null -> { + verdict.answers.firstOrNull()?.text != null -> { ctx.header("Cache-Control", "max-age=31622400") ctx.contentType("image/png") ctx.result(this.javaClass.getResourceAsStream("/img/text.png")!!) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 7d5dec096..c8fcd2f98 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -16,10 +16,7 @@ import dev.dres.data.model.media.* import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.* import dev.dres.run.InteractiveRunManager import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -110,7 +107,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con AuditLogger.submission(submission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS)) { //pre-generate preview - generatePreview(submission.verdicts.first()) + generatePreview(submission.answerSets.first()) } submission to result } @@ -180,11 +177,11 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con this.status = DbVerdictStatus.INDETERMINATE this.task = task } - submission.verdicts.add(answerSet) + submission.answerSets.add(answerSet) if (textParam != null) { - answerSet.type = DbAnswerType.TEXT - answerSet.text = textParam + answerSet.answers.firstOrNull()?.type = DbAnswerType.TEXT + answerSet.answers.firstOrNull()?.text = textParam return submission } else if (itemParam != null) { val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ @@ -228,16 +225,21 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con else -> null } + val answer = DbAnswer.new() + /* Assign information to submission. */ if (range != null) { - answerSet.item = item - answerSet.type = DbAnswerType.TEMPORAL - answerSet.start = range.first - answerSet.end = range.second + answer.item = item + answer.type = DbAnswerType.TEMPORAL + answer.start = range.first + answer.end = range.second } else { - answerSet.item = item - answerSet.type = DbAnswerType.ITEM + answer.item = item + answer.type = DbAnswerType.ITEM } + + answerSet.answers.add(answer) + } else { throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) } @@ -251,12 +253,12 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con * @param answerSet The [DbAnswerSet] to generate preview for. */ private fun generatePreview(answerSet: DbAnswerSet) { - if (answerSet.type != DbAnswerType.TEMPORAL) return - if (answerSet.item == null) return - val destinationPath = Paths.get(this.config.cachePath, "previews", answerSet.item!!.collection.name, answerSet.item!!.name, "${answerSet.start}.jpg") + if (answerSet.answers.firstOrNull()?.type != DbAnswerType.TEMPORAL) return + if (answerSet.answers.firstOrNull()?.item == null) return + val destinationPath = Paths.get(this.config.cachePath, "previews", answerSet.answers.firstOrNull()?.item!!.collection.name, answerSet.answers.firstOrNull()?.item!!.name, "${answerSet.answers.firstOrNull()?.start}.jpg") if (Files.exists(destinationPath)){ return } - FFmpegUtil.extractFrame(answerSet.item!!.pathToOriginal(), answerSet.start!!, destinationPath) + FFmpegUtil.extractFrame(answerSet.answers.firstOrNull()?.item!!.pathToOriginal(), answerSet.answers.firstOrNull()?.start!!, destinationPath) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt similarity index 53% rename from backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt index 01150fbd1..0ac2301e4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdict.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt @@ -2,18 +2,10 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.collection.ApiMediaItem -/** - * The RESTful API equivalent for the type of a [ApiVerdict]. - * - * @see ApiVerdict - * @author Ralph Gasser - * @version 1.0.0 - */ -data class ApiVerdict( +data class ApiAnswer( val type: ApiAnswerType, - val status: ApiVerdictStatus, val item: ApiMediaItem?, val text: String?, val start: Long?, val end: Long? -) \ No newline at end of file + ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt new file mode 100644 index 000000000..49df6dae2 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -0,0 +1,15 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.api.rest.types.collection.ApiMediaItem + +/** + * The RESTful API equivalent for the type of a [ApiAnswerSet]. + * + * @see ApiAnswerSet + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiAnswerSet( + val status: ApiVerdictStatus, + val answers: Sequence +) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt index a3671e633..6d722a12d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt @@ -5,7 +5,7 @@ import dev.dres.data.model.submissions.DbAnswerType /** * The RESTful API equivalent for the type of a [DbAnswerType] * - * @see ApiVerdict + * @see ApiAnswerSet * @author Ralph Gasser * @version 1.0.0 */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index 708b96cce..9f5dc8cfe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -17,7 +17,7 @@ data class ApiSubmission( val memberId: String, val memberName: String, val timestamp: Long, - val verdicts: List, + val verdicts: List, ) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index a064757b7..f53284a83 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -16,5 +16,5 @@ data class ApiTask( val templateId: TemplateId, val started: Long?, val ended: Long?, - val submissions: List + val submissions: List ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt index 75ea25043..a008fdbad 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt @@ -5,7 +5,7 @@ import dev.dres.data.model.submissions.DbVerdictStatus /** * The RESTful API equivalent for the type of a [DbVerdictStatus] * - * @see ApiVerdict + * @see ApiAnswerSet * @author Ralph Gasser * @version 1.0.0 */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index a6fabaa86..487768320 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -1,14 +1,11 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiVerdict +import dev.dres.api.rest.types.evaluation.ApiAnswerSet import dev.dres.data.model.PersistentEntity -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.run.DbTask import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.simple.requireIf +import kotlinx.dnq.query.asSequence /** * A [DbVerdictStatus] as submitted by a competition participant. Makes a statement about a [DbTask]. @@ -23,48 +20,23 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity) { /** The [DbVerdictStatus] of this [DbAnswerSet]. */ var status by xdLink1(DbVerdictStatus) - /** The [DbAnswerType] of this [DbAnswerSet]. */ - var type by xdLink1(DbAnswerType) - /** The [DbSubmission] this [DbAnswerSet] belongs to. */ - var submission: DbSubmission by xdParent(DbSubmission::verdicts) + var submission: DbSubmission by xdParent(DbSubmission::answerSets) /** The [DbTask] this [DbAnswerSet] belongs to. */ var task: DbTask by xdParent(DbTask::submissions) - /** The [DbMediaItem] submitted. Only for [DbAnswerType.ITEM] or [DbAnswerType.TEMPORAL]. */ - var item by xdLink0_1(DbMediaItem) - - /** The start frame number of this [DbSubmission]. */ - var start by xdNullableLongProp { requireIf { this.type == DbAnswerType.TEMPORAL } } - - /** The end frame number of this [DbSubmission]. */ - var end by xdNullableLongProp { requireIf { this.type == DbAnswerType.TEMPORAL } } - - /** The text submitted. Only for [DbAnswerType.TEXT] . */ - var text by xdStringProp { requireIf { this.type == DbAnswerType.TEXT } } - - /** Returns the [TemporalRange] for this [DbAnswerSet]. */ - val temporalRange: TemporalRange? - get() = try { - TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) - } catch (e: NullPointerException) { - null - } + val answers by xdChildren1_N(DbAnswer::answerSet) /** - * Converts this [DbVerdictStatus] to a RESTful API representation [ApiVerdict]. + * Converts this [DbVerdictStatus] to a RESTful API representation [ApiAnswerSet]. * * This is a convenience method and requires an active transaction context. * - * @return [ApiVerdict] + * @return [ApiAnswerSet] */ - fun toApi(): ApiVerdict = ApiVerdict( + fun toApi(): ApiAnswerSet = ApiAnswerSet( status = this.status.toApi(), - type = this.type.toApi(), - item = this.item?.toApi(), - text = this.text, - start = this.start, - end = this.end + answers = this.answers.asSequence().map { it.toApi() } ) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index bb5d59346..48e4895c6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -38,14 +38,14 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity) { var user by xdLink1(DbUser) /** The [DbAnswerSet]s that make-up this [DbSubmission]. For batched submissions, more than one verdict can be possible. */ - val verdicts by xdChildren1_N(DbAnswerSet::submission) + val answerSets by xdChildren1_N(DbAnswerSet::submission) /** * Converts this [DbSubmission] to a RESTful API representation [ApiSubmission]. * * This is a convenience method and requires an active transaction context. * - * @param blind True, if a "blind" Submission (without [verdicts]) should be generated. + * @param blind True, if a "blind" Submission (without [answerSets]) should be generated. * @return [ApiSubmission] */ fun toApi(blind: Boolean = false): ApiSubmission = ApiSubmission( @@ -55,6 +55,6 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity) { memberId = this.user.id, memberName = this.user.username, timestamp = this.timestamp, - verdicts = if (blind) { emptyList() } else { this.verdicts.asSequence().map { it.toApi() }.toList()} + verdicts = if (blind) { emptyList() } else { this.answerSets.asSequence().map { it.toApi() }.toList()} ) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 0d66d1550..eae457c14 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -137,7 +137,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(Pair(task, sub)) - if (sub.verdicts.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { + if (sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } } @@ -512,7 +512,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } - require(submission.verdicts.size() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ + require(submission.answerSets.size() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ /* Register submission. */ val task = this.currentTask(context) @@ -524,7 +524,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId) - return submission.verdicts.first().status + return submission.answerSets.first().status } /** diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index a75a1e047..f10236c4f 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -130,7 +130,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(Pair(task, sub)) - if (sub.verdicts.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { + if (sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } } @@ -463,7 +463,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED)) - return submission.verdicts.first().status + return submission.answerSets.first().status } /** diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 043e45a8d..beba5d7c5 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -145,12 +145,12 @@ object AuditLogger { this.source = api this.timestamp = DateTime.now() this.submissionId = submission.id - this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId - this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ + this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId + this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ this.session = sessionToken this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.verdicts.first().task.evaluation.evaluationId, submission.verdicts.first().task.id, submission)) + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.answerSets.first().task.evaluation.evaluationId, submission.answerSets.first().task.id, submission)) } /** @@ -166,9 +166,9 @@ object AuditLogger { this.source = DbAuditLogSource.INTERNAL this.timestamp = DateTime.now() this.submissionId = submission.id - this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId - this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ - this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.verdicts.first().status.description}" /* TODO: Here name, there ID. Why? */ + this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId + this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ + this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets.first().status.description}" /* TODO: Here name, there ID. Why? */ } } } @@ -187,9 +187,9 @@ object AuditLogger { this.source = api this.timestamp = DateTime.now() this.submissionId = submission.id - this.evaluationId = submission.verdicts.first().task.evaluation.evaluationId - this.taskId = submission.verdicts.first().task.id /* TODO: Multiple verdicts. */ - this.description = "Verdict: ${submission.verdicts.first().status.description}" + this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId + this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ + this.description = "Verdict: ${submission.answerSets.first().status.description}" this.session = sessionToken } } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index a88472d3e..05762da8e 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -66,15 +66,15 @@ class SubmissionStatisticsHandler : StreamEventHandler { } submissionsByTeam.mapValues { it.value.firstOrNull { s -> - require(s.verdicts.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.verdicts.first().status == DbVerdictStatus.CORRECT + require(s.answerSets.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } + s.answerSets.first().status == DbVerdictStatus.CORRECT }?.timestamp?.minus(taskStart) }.filter { it.value != null }.forEach{ (teamId, time) -> writer.println("$task,${teamId},\"timeUntilCorrectSubmission\",$time") } submissionsByTeam.mapValues { it.value.indexOfFirst { s -> - require(s.verdicts.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.verdicts.first().status == DbVerdictStatus.CORRECT + require(s.answerSets.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } + s.answerSets.first().status == DbVerdictStatus.CORRECT } }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"incorrectBeforeCorrectSubmissions\",$count") diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index c490a78f4..6b186edb0 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -18,7 +18,7 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) override fun test(submission: DbSubmission): Boolean { - return submission.verdicts.asSequence().all { verdict -> + return submission.answerSets.asSequence().all { verdict -> verdict.task.submissions.filter { (it.status eq DbVerdictStatus.CORRECT).and(it.submission.team eq submission.team) }.size() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index 8b0802b29..d3c065ba8 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -14,7 +14,7 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { override val reason: String = "Maximum number of correct submissions ($limit) exceeded for this item." override fun test(submission: DbSubmission): Boolean { - return submission.verdicts.asSequence().all { verdict -> + return submission.answerSets.asSequence().all { verdict -> verdict.task.submissions.filter { (it.status eq DbVerdictStatus.CORRECT).and(it.submission.team eq submission.team) }.size() < this.limit diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index e254be5c6..7cb8b2347 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -18,7 +18,7 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member." override fun test(submission: DbSubmission): Boolean { - return submission.verdicts.asSequence().all { verdict -> + return submission.answerSets.asSequence().all { verdict -> verdict.task.submissions.filter { (it.status eq DbVerdictStatus.CORRECT).and(it.submission.team eq submission.team).and(it.submission.user eq submission.user) }.size() < this.limit diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index 42f3b8e7c..a5613161f 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -16,11 +16,16 @@ class DuplicateSubmissionFilter : SubmissionFilter { override val reason = "Duplicate submission received." - override fun test(submission: DbSubmission): Boolean { - return submission.verdicts.asSequence().all { verdict -> - verdict.task.submissions.filter { - (it.text eq verdict.text) and (it.item eq verdict.item) and (it.start le (verdict.start ?: Long.MAX_VALUE)) and (it.end ge (verdict.end ?: Long.MIN_VALUE)) - }.isEmpty - } + override fun test(submission: DbSubmission): Boolean { //TODO semantics unclear +// return submission.answerSets.asSequence().all { verdict -> +// verdict.task.submissions.filter {set -> +// set.answers.filter { +// (it.text eq verdict.text) and (it.item eq verdict.item) and (it.start le (verdict.start +// ?: Long.MAX_VALUE)) and (it.end ge (verdict.end ?: Long.MIN_VALUE)) +// } +// }.isEmpty +// } + + return true } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt index 005ec923b..3ebfc35f1 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt @@ -13,5 +13,5 @@ import kotlinx.dnq.query.asSequence class ItemSubmissionFilter : SubmissionFilter { override val reason = "Submission does include temporal information, but whole item was expected" override fun test(submission: DbSubmission): Boolean - = submission.verdicts.asSequence().any { it.type == DbAnswerType.TEMPORAL } + = submission.answerSets.asSequence().any { it.answers.asSequence().any { it.type == DbAnswerType.TEMPORAL } } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index 43c47a273..4fdb6b25e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -14,7 +14,7 @@ class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? */ override fun test(submission: DbSubmission): Boolean { - return submission.verdicts.asSequence().all { v -> + return submission.answerSets.asSequence().all { v -> v.task.submissions.filter { it.submission.team.id eq submission.team.id }.size() < max } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index e5f734d22..f2420d48a 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -19,7 +19,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? */ override fun test(submission: DbSubmission): Boolean { - return submission.verdicts.asSequence().all { v -> + return submission.answerSets.asSequence().all { v -> v.task.submissions.filter { (it.submission.team.id eq submission.team.id) and (it.status eq DbVerdictStatus.WRONG) }.size() < max } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt index dd18fce8c..78abe34cc 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt @@ -15,5 +15,5 @@ class TemporalSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include temporal information." override fun test(submission: DbSubmission): Boolean - = submission.verdicts.asSequence().all { it.type == DbAnswerType.TEMPORAL && it.start != null && it.end != null } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + = submission.answerSets.asSequence().all { it.answers.asSequence().all { it.type == DbAnswerType.TEMPORAL && it.start != null && it.end != null } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt index 4fb32ad8e..2bdd88c07 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt @@ -15,5 +15,5 @@ class TextualSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include textual information (or is an empty submission)" override fun test(submission: DbSubmission): Boolean - = submission.verdicts.asSequence().all { it.text != null && it.type == DbAnswerType.TEXT } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + = submission.answerSets.asSequence().all { it.answers.asSequence().all { it.text != null && it.type == DbAnswerType.TEXT } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index d5a90eb74..0dbe6cfa1 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -9,7 +9,8 @@ import dev.dres.run.score.TaskContext import dev.dres.utilities.TimeUtil import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter -import kotlinx.dnq.query.toList +import kotlinx.dnq.query.first +import kotlinx.dnq.query.firstOrNull /** @@ -25,9 +26,9 @@ object AvsTaskScorer : TaskScorer { */ override fun computeScores(submissions: Sequence, context: TaskContext): Map { val correctSubmissions = - submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.CORRECT }.asSequence() } + submissions.flatMap { s -> s.answerSets.filter { v -> v.status eq DbVerdictStatus.CORRECT }.asSequence() } val wrongSubmissions = - submissions.flatMap { s -> s.verdicts.filter { v -> v.status eq DbVerdictStatus.WRONG }.asSequence() } + submissions.flatMap { s -> s.answerSets.filter { v -> v.status eq DbVerdictStatus.WRONG }.asSequence() } val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.team.id } val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.team.id } val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() @@ -42,13 +43,13 @@ object AvsTaskScorer : TaskScorer { } private fun countQuantized(submissions: Sequence): Int = submissions - .filter { it.item != null } - .groupBy { it.item } + .filter { it.answers.firstOrNull()?.item != null } + .groupBy { it.answers.first().item } .map { when (it.key!!.type) { DbMediaType.IMAGE -> 1 DbMediaType.VIDEO -> { - val ranges = it.value.map { s -> s.temporalRange!! } + val ranges = it.value.asSequence().map { s -> s.answers.first().temporalRange!! }.toList() TimeUtil.merge(ranges, overlap = 1).size } else -> throw IllegalStateException("Unsupported media type ${it.key!!.type} for AVS task scorer.") diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index e9f62a127..499ea3c34 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -33,7 +33,7 @@ class KisTaskScorer( val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition return context.teamIds.associateWith { teamId -> val verdicts = submissions.filter { it.team.id == teamId }.sortedBy { it.timestamp }.flatMap { sub -> - sub.verdicts.filter { (it.status eq DbVerdictStatus.CORRECT) or (it.status eq DbVerdictStatus.WRONG) }.asSequence() + sub.answerSets.filter { (it.status eq DbVerdictStatus.CORRECT) or (it.status eq DbVerdictStatus.WRONG) }.asSequence() }.toList() val firstCorrect = verdicts.indexOfFirst { it.status == DbVerdictStatus.CORRECT } val score = if (firstCorrect > -1) { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt index 4994a7599..46e1499a8 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt @@ -6,6 +6,7 @@ import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.firstOrNull import java.lang.Double.max import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -57,8 +58,8 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint ): Map { val distinctCorrectVideos = submissions.flatMap { submission -> - submission.verdicts.asSequence().filter { it.status == DbVerdictStatus.CORRECT && it.item != null } - }.mapNotNullTo(mutableSetOf()) { it.item } + submission.answerSets.asSequence().filter { it.status == DbVerdictStatus.CORRECT && it.answers.firstOrNull()?.item != null } + }.mapNotNullTo(mutableSetOf()) { it.answers.firstOrNull()?.item } .size @@ -74,12 +75,12 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint return teamScoreMapSanitised(submissions.groupBy { it.team }.map { submissionsPerTeam -> val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { - it.verdicts.asSequence() - .filter { v -> v.item != null && (v.status == DbVerdictStatus.CORRECT || v.status == DbVerdictStatus.WRONG) } + it.answerSets.asSequence() + .filter { v -> v.answers.firstOrNull()?.item != null && (v.status == DbVerdictStatus.CORRECT || v.status == DbVerdictStatus.WRONG) } } submissionsPerTeam.key.teamId to max(0.0, //prevent negative total scores - verdicts.groupBy { it.item!! }.map { + verdicts.groupBy { it.answers.firstOrNull()?.item!! }.map { val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == DbVerdictStatus.CORRECT } if (firstCorrectIdx < 0) { //no correct submissions, only penalty it.value.size * -penaltyConstant diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 9c87987a9..4367f488b 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -35,12 +35,12 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte val submissionCount = taskRun.getSubmissions().count() if (this.submissions.getAndSet(submissionCount) < submissionCount) { val allDone = if (this.isAsync) { - val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.verdicts.first().status == DbVerdictStatus.CORRECT } + val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } numberOfSubmissions >= limit } else { /* Determine of all teams have submitted . */ this.run.template.teams.asSequence().all { team -> - val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == team.teamId && it.verdicts.first().status == DbVerdictStatus.CORRECT } + val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == team.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } numberOfSubmissions >= limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt index 31128a2e3..5229daffc 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt @@ -38,7 +38,7 @@ class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator */ override fun validate(submission: DbSubmission) { this.firstValidator.validate(submission) - if (submission.verdicts.asSequence().any { this.continueStates.contains(it.status) }) { + if (submission.answerSets.asSequence().any { this.continueStates.contains(it.status) }) { this.secondValidator.validate(submission) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index 1c0e376a4..ec8233a2f 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -23,11 +23,12 @@ class MediaItemsSubmissionValidator(private val items : Set) : Subm * @param submission The [DbSubmission] to validate. */ override fun validate(submission: DbSubmission) { - submission.verdicts.asSequence().forEach {verdict -> - if (verdict.item == null || verdict.item !in this.items) { - verdict.status = DbVerdictStatus.WRONG + submission.answerSets.asSequence().forEach { answerSet -> + + if (answerSet.answers.asSequence().any { it.item == null || it.item !in this.items} ) { + answerSet.status = DbVerdictStatus.WRONG } else { - verdict.status = DbVerdictStatus.CORRECT + answerSet.status = DbVerdictStatus.CORRECT } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index db1f42a4e..d15f43e01 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -27,35 +27,42 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien * @param submission The [DbSubmission] to validate. */ override fun validate(submission: DbSubmission) { - submission.verdicts.asSequence().forEach { verdict -> - /* Perform sanity checks. */ - if (verdict.type != DbAnswerType.TEMPORAL) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + submission.answerSets.asSequence().forEach { answerSet -> - val start = verdict.start - val end = verdict.end - val item = verdict.item - if (item == null || start == null || end == null || start > end) { - verdict.status = DbVerdictStatus.WRONG - return@forEach + answerSet.answers.asSequence().forEach { answer -> - } + /* Perform sanity checks. */ + if (answer.type != DbAnswerType.TEMPORAL) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } - /* Perform item validation. */ - if (verdict.item != this.targetSegment.first) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + val start = answer.start + val end = answer.end + val item = answer.item + if (item == null || start == null || end == null || start > end) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + + } + + /* Perform item validation. */ + if (answer.item != this.targetSegment.first) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } + + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if (outer.first <= start && outer.second >= end) { + answerSet.status = DbVerdictStatus.CORRECT + } else { + answerSet.status = DbVerdictStatus.WRONG + } - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if (outer.first <= start && outer.second >= end) { - verdict.status = DbVerdictStatus.CORRECT - } else { - verdict.status = DbVerdictStatus.WRONG } + + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 266b23eba..8fe519c1c 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -31,34 +31,40 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed * @param submission The [DbSubmission] to validate. */ override fun validate(submission: DbSubmission) { - submission.verdicts.asSequence().forEach { verdict -> - /* Perform sanity checks. */ - if (verdict.type != DbAnswerType.TEMPORAL) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + submission.answerSets.asSequence().forEach { answerSet -> - val start = verdict.start - val end = verdict.end - val item = verdict.item - if (item == null || start == null || end == null || start > end) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + answerSet.answers.asSequence().forEach { answer -> - /* Perform item validation. */ - if (verdict.item != this.targetSegment.first) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + /* Perform sanity checks. */ + if (answer.type != DbAnswerType.TEMPORAL) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } + + val start = answer.start + val end = answer.end + val item = answer.item + if (item == null || start == null || end == null || start > end) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { - verdict.status = DbVerdictStatus.CORRECT - } else { - verdict.status = DbVerdictStatus.WRONG + /* Perform item validation. */ + if (answer.item != this.targetSegment.first) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } + + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { + answerSet.status = DbVerdictStatus.CORRECT + } else { + answerSet.status = DbVerdictStatus.WRONG + } } + + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 8d9c46827..30a6f91cd 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -47,25 +47,33 @@ class TextValidator(targets: List) : SubmissionValidator { * @param submission The [DbSubmission] to validate. */ override fun validate(submission: DbSubmission) { - submission.verdicts.asSequence().forEach { verdict -> - /* Perform sanity checks. */ - if (verdict.type != DbAnswerType.TEXT) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + submission.answerSets.asSequence().forEach { answerSet -> - /* Perform text validation. */ - val text = verdict.text - if (text == null) { - verdict.status = DbVerdictStatus.WRONG - return@forEach - } + answerSet.answers.asSequence().forEach { + answer -> + + /* Perform sanity checks. */ + if (answer.type != DbAnswerType.TEXT) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } + + /* Perform text validation. */ + val text = answer.text + if (text == null) { + answerSet.status = DbVerdictStatus.WRONG + return@forEach + } + + if (regex.any { it matches text }) { + answerSet.status = DbVerdictStatus.CORRECT + } else { + answerSet.status = DbVerdictStatus.WRONG + } - if (regex.any { it matches text }) { - verdict.status = DbVerdictStatus.CORRECT - } else { - verdict.status = DbVerdictStatus.WRONG } + + } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 9990593f0..71b8a93c2 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -95,14 +95,14 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * @param submission The [DbSubmission] to validate. */ override fun validate(submission: DbSubmission) = this.updateLock.read { - for (verdict in submission.verdicts.asSequence()) { + for (verdict in submission.answerSets.asSequence()) { //only validate submissions which are not already validated if (verdict.status != DbVerdictStatus.INDETERMINATE){ continue } //check cache first - val itemRange = ItemRange(submission.verdicts.first()) + val itemRange = ItemRange(submission.answerSets.first().answers.first()) //TODO reason about semantics val cachedStatus = this.cache[itemRange] if (cachedStatus != null) { verdict.status = cachedStatus @@ -157,7 +157,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e fun processSubmission(token: String, status: DbVerdictStatus) : DbAnswerSet = this.updateLock.write { val verdict = this.waiting[token] ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged - val itemRange = ItemRange(verdict) + val itemRange = ItemRange(verdict.answers.first()) //TODO reason about semantics //add to cache this.cache[itemRange] = status diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index ad095136e..2e1ddbee4 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.judged import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbAnswer import dev.dres.data.model.submissions.DbAnswerType /** @@ -13,12 +13,12 @@ import dev.dres.data.model.submissions.DbAnswerType data class ItemRange(val element: String, val start: Long, val end: Long){ constructor(item: DbMediaItem): this(item.id, 0, 0) constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) - constructor(answerSet: DbAnswerSet): this(when (answerSet.type){ + constructor(answer: DbAnswer): this(when (answer.type){ DbAnswerType.ITEM, - DbAnswerType.TEMPORAL -> answerSet.item!!.id - DbAnswerType.TEXT -> answerSet.text!! + DbAnswerType.TEMPORAL -> answer.item!!.id + DbAnswerType.TEXT -> answer.text!! else -> throw IllegalStateException("Submission contains neither item nor text.") - }, answerSet.start ?: 0, answerSet.end ?: 0) + }, answer.start ?: 0, answer.end ?: 0) override fun equals(other: Any?): Boolean { if (this === other) return true From 616fda8ac2870ca1af2654878ab065c018ce1868 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 3 Feb 2023 20:27:11 +0100 Subject: [PATCH 060/498] User Rest endpoints are functional again some things can probably be improved though --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 18 +++---- .../api/rest/handler/system/LoginHandler.kt | 32 ++++++++----- .../api/rest/handler/system/LogoutHandler.kt | 12 +++-- .../rest/handler/users/CreateUsersHandler.kt | 27 ++++++++--- .../rest/handler/users/DeleteUsersHandler.kt | 10 ++-- .../handler/users/ListActiveUsersHandler.kt | 15 +++--- .../rest/handler/users/ListUsersHandler.kt | 7 ++- .../handler/users/ShowCurrentUserHandler.kt | 7 +-- .../rest/handler/users/UpdateUsersHandler.kt | 30 +++++++----- .../rest/handler/users/UserDetailsHandler.kt | 5 +- .../dres/data/model/submissions/DbAnswer.kt | 47 +++++++++++++++++++ 11 files changed, 150 insertions(+), 60 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index f88a46479..8cbca75b5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -76,16 +76,16 @@ object RestApi { val apiRestHandlers = listOf( // User - LoginHandler(), - LogoutHandler(), - ListUsersHandler(), - ListActiveUsersHandler(), - ShowCurrentUserHandler(), + LoginHandler(store), + LogoutHandler(store), + ListUsersHandler(store), + ListActiveUsersHandler(store), + ShowCurrentUserHandler(store), ShowCurrentSessionHandler(), - CreateUsersHandler(), - DeleteUsersHandler(), - UpdateUsersHandler(), - UserDetailsHandler(), + CreateUsersHandler(store), + DeleteUsersHandler(store), + UpdateUsersHandler(store), + UserDetailsHandler(store), // Media MediaPreviewHandler(store, config), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index 73f1e4565..88eb09098 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -14,6 +14,7 @@ import dev.dres.utilities.extensions.getOrCreateSessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore /** * A [PostRestHandler] that handles user-requests to login. @@ -21,7 +22,7 @@ import io.javalin.openapi.* * @version 1.0.0 * @author Luca Rossetto */ -class LoginHandler : RestHandler, PostRestHandler { +class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostRestHandler { override val apiVersion = "v2" @@ -46,21 +47,28 @@ class LoginHandler : RestHandler, PostRestHandler { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error.", ctx) } - /* Validate login request. */ - val username = loginRequest.username - val password = Password.Plain(loginRequest.password) - val user = UserManager.getMatchingApiUser(username, password) ?: throw ErrorStatusException(401, "Invalid credentials. Please try again!", ctx) + return this.store.transactional { - val sessionToken = ctx.getOrCreateSessionToken() + /* Validate login request. */ + val username = loginRequest.username + val password = Password.Plain(loginRequest.password) + val user = UserManager.getMatchingApiUser(username, password) ?: throw ErrorStatusException( + 401, + "Invalid credentials. Please try again!", + ctx + ) - AccessManager.registerUserForSession(sessionToken, user) - AuditLogger.login(loginRequest.username, DbAuditLogSource.REST, sessionToken) + val sessionToken = ctx.getOrCreateSessionToken() - //explicitly set cookie on login - ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) + AccessManager.registerUserForSession(sessionToken, user) + AuditLogger.login(loginRequest.username, DbAuditLogSource.REST, sessionToken) - user.sessionId = sessionToken - return user + //explicitly set cookie on login + ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) + + user.sessionId = sessionToken + return@transactional user + } } override val route = "login" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index ca5a10a5e..7fd3c5f5e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -11,6 +11,7 @@ import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore /** * A [GetRestHandler] that handles user-requests to logout. @@ -18,7 +19,7 @@ import io.javalin.openapi.* * @version 2.0.0 * @author Luca Rossetto */ -class LogoutHandler : RestHandler, GetRestHandler { +class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetRestHandler { override val apiVersion = "v2" @OpenApi(summary = "Clears all user roles of the current session.", @@ -36,9 +37,12 @@ class LogoutHandler : RestHandler, GetRestHandler { override fun doGet(ctx: Context): SuccessStatus { val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) - AuditLogger.logout(userId, DbAuditLogSource.REST, ctx.sessionToken()!!) - AccessManager.deregisterUserSession(ctx.sessionToken()!!) - return SuccessStatus("User '${username}' logged out successfully.") + return store.transactional { + AuditLogger.logout(userId, DbAuditLogSource.REST, ctx.sessionToken()!!) + AccessManager.deregisterUserSession(ctx.sessionToken()!!) + + SuccessStatus("User '${username}' logged out successfully.") + } } override val route = "logout" } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index cba8cc784..5e0de0970 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -13,6 +13,7 @@ import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore /** * An [AbstractUserHandler] to create new [DbUser]s. @@ -20,7 +21,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { +class CreateUsersHandler(private val store: TransientEntityStore) : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { override val route = "user" /** [CreateUsersHandler] requires [ApiRole.ADMIN]. */ @@ -51,11 +52,25 @@ class CreateUsersHandler : AbstractUserHandler(), PostRestHandler, Acce if (req.role == null) throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) - val success = UserManager.create(req.username, Password.Plain(req.password), req.role.toDb() ?: throw ErrorStatusException(400, "Invalid parameters. Provided role is undefined or invalid!", ctx)) - if (success) { - return UserManager.get(username = req.username)!!.toApi() - } else { - throw ErrorStatusException(400, "The request could not be fulfilled.", ctx) + val success = this.store.transactional { + UserManager.create( + req.username, + Password.Plain(req.password), + req.role.toDb() ?: throw ErrorStatusException( + 400, + "Invalid parameters. Provided role is undefined or invalid!", + ctx + ) + ) + } + //needs to be in a new transaction + //TODO is there a nicer way of doing this? + return this.store.transactional { + if (success) { + return@transactional UserManager.get(username = req.username)!!.toApi() + } else { + throw ErrorStatusException(400, "The request could not be fulfilled.", ctx) + } } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index ea69493a6..c1dd69557 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -10,6 +10,7 @@ import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore /** * An [AbstractUserHandler] to delete [DbUser]s. @@ -17,7 +18,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { +class DeleteUsersHandler(private val store: TransientEntityStore) : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.ADMIN) /** [DeleteUsersHandler] requires [ApiRole.ADMIN]. */ @@ -26,7 +27,7 @@ class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, Ac @OpenApi( summary = "Deletes the specified user. Requires ADMIN privileges", path = "/api/v2/user/{userId}", methods = [HttpMethod.DELETE], - pathParams = [OpenApiParam("userId", Long::class, "User ID")], + pathParams = [OpenApiParam("userId", String::class, "User ID")], tags = ["User"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), @@ -34,10 +35,11 @@ class DeleteUsersHandler : AbstractUserHandler(), DeleteRestHandler, Ac OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doDelete(ctx: Context): ApiUser { + override fun doDelete(ctx: Context): ApiUser = this.store.transactional { val user = userFromContext(ctx) + val apiUser = user.toApi() //needs to be done before, since it can no longer be transformed to ApiUser after successful delete if (UserManager.delete(id = user.id)) { - return user.toApi() + return@transactional apiUser } else { throw ErrorStatusException(500, "Could not delete the user (${user.id})", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index 2ad62ad2c..e734af5cc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -13,6 +13,7 @@ import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi import io.javalin.openapi.OpenApiContent import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore /** * An [AbstractUserHandler] to list all [DbUser]s that are currently logged in. @@ -20,7 +21,7 @@ import io.javalin.openapi.OpenApiResponse * @author Loris Sauter * @version 2.0.0 */ -class ListActiveUsersHandler : GetRestHandler>, AccessManagedRestHandler { +class ListActiveUsersHandler(private val store: TransientEntityStore) : GetRestHandler>, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.ADMIN) /** All [UserDetailsHandler] requires [ApiRole.ADMIN]. */ @@ -38,11 +39,11 @@ class ListActiveUsersHandler : GetRestHandler>, AccessManagedRestH ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = AccessManager.currentSessions.map { session -> - AccessManager.userIdForSession(session)?.let { - UserManager.get(id = it) - }?.let { - it.toApi() - } ?: return@map ApiUser("??", "??", ApiRole.VIEWER, session) + override fun doGet(ctx: Context): List = this.store.transactional { + AccessManager.currentSessions.map { session -> + AccessManager.userIdForSession(session)?.let { + UserManager.get(id = it) + }?.toApi() ?: return@map ApiUser("??", "??", ApiRole.VIEWER, session) + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt index 48a555e77..d5ae97791 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -11,6 +11,7 @@ import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi import io.javalin.openapi.OpenApiContent import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore /** * An [AbstractUserHandler] to list all [DbUser]s. @@ -18,7 +19,7 @@ import io.javalin.openapi.OpenApiResponse * @author Loris Sauter * @version 2.0.0 */ -class ListUsersHandler: AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { +class ListUsersHandler(private val store: TransientEntityStore): AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { override val route = "user/list" @@ -32,5 +33,7 @@ class ListUsersHandler: AbstractUserHandler(), GetRestHandler>, Ac responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = UserManager.list().map { it.toApi() } + override fun doGet(ctx: Context) = this.store.transactional { + UserManager.list().map { it.toApi() } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt index 6eb4117cb..23bb34db5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt @@ -11,13 +11,14 @@ import io.javalin.openapi.HttpMethod import io.javalin.openapi.OpenApi import io.javalin.openapi.OpenApiContent import io.javalin.openapi.OpenApiResponse +import jetbrains.exodus.database.TransientEntityStore /** * * @author Ralph Gasser * @version 1.0 */ -class ShowCurrentUserHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class ShowCurrentUserHandler(private val store: TransientEntityStore) : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user" /** [ShowCurrentUserHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ @@ -33,9 +34,9 @@ class ShowCurrentUserHandler : AbstractUserHandler(), GetRestHandler, A ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiUser { + override fun doGet(ctx: Context): ApiUser = this.store.transactional(readonly = true){ val user = userFromSession(ctx).toApi() user.sessionId = ctx.sessionToken() - return user + user } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt index 45cf30c72..2007a5a4c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -13,6 +13,7 @@ import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore /** * An [AbstractUserHandler] to update an existing [DbUser]s. @@ -20,7 +21,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, AccessManagedRestHandler { +class UpdateUsersHandler(private val store: TransientEntityStore) : AbstractUserHandler(), PatchRestHandler, AccessManagedRestHandler { /** [UpdateUsersHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.ADMIN, ApiRole.PARTICIPANT) @@ -47,19 +48,26 @@ class UpdateUsersHandler : AbstractUserHandler(), PatchRestHandler, Acc throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - /* Fetch existing objects. */ - val user = userFromContext(ctx) - val caller = userFromSession(ctx) + return this.store.transactional { - if (caller.role == DbRole.ADMIN || user.id == caller.id) { - val success = UserManager.update(id = user.id, request = request) - if (success) { - return UserManager.get(id = user.id)!!.toApi() + /* Fetch existing objects. */ + val user = userFromContext(ctx) + val caller = userFromSession(ctx) + + if (caller.role == DbRole.ADMIN || user.id == caller.id) { + val success = UserManager.update(id = user.id, request = request) + if (success) { + return@transactional UserManager.get(id = user.id)!!.toApi() + } else { + throw ErrorStatusException(500, "Could not update user!", ctx) + } } else { - throw ErrorStatusException(500, "Could not update user!", ctx) + throw ErrorStatusException( + 403, + "You do not have permissions to edit user (${user.id}) as $caller!", + ctx + ) } - } else { - throw ErrorStatusException(403, "You do not have permissions to edit user (${user.id}) as $caller!", ctx) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt index 86cb1eab1..602a40eb6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser import io.javalin.http.Context import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore /** * An [AbstractUserHandler] to show [DbUser] details. @@ -15,7 +16,7 @@ import io.javalin.openapi.* * @author Loris Sauter * @version 2.0.0 */ -class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class UserDetailsHandler(private val store: TransientEntityStore) : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user/{userId}" /** [UserDetailsHandler] requires [ApiRole.ADMIN]. */ @@ -35,7 +36,7 @@ class UserDetailsHandler : AbstractUserHandler(), GetRestHandler, Acces ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = userFromContext(ctx).toApi() + override fun doGet(ctx: Context) = this.store.transactional { userFromContext(ctx).toApi() } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt new file mode 100644 index 000000000..c9218491f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt @@ -0,0 +1,47 @@ +package dev.dres.data.model.submissions + +import dev.dres.api.rest.types.evaluation.ApiAnswer +import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.time.TemporalPoint +import dev.dres.data.model.media.time.TemporalRange +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.* +import kotlinx.dnq.simple.requireIf + +class DbAnswer(entity: Entity) : PersistentEntity(entity) { + companion object : XdNaturalEntityType() + + var answerSet: DbAnswerSet by xdParent(DbAnswerSet::answers) + + /** The [DbAnswerType] of this [DbAnswerSet]. */ + var type by xdLink1(DbAnswerType) + + /** The [DbMediaItem] submitted. Only for [DbAnswerType.ITEM] or [DbAnswerType.TEMPORAL]. */ + var item by xdLink0_1(DbMediaItem) + + /** The start frame number of this [DbSubmission]. */ + var start by xdNullableLongProp { requireIf { this.type == DbAnswerType.TEMPORAL } } + + /** The end frame number of this [DbSubmission]. */ + var end by xdNullableLongProp { requireIf { this.type == DbAnswerType.TEMPORAL } } + + /** The text submitted. Only for [DbAnswerType.TEXT] . */ + var text by xdStringProp { requireIf { this.type == DbAnswerType.TEXT } } + + /** Returns the [TemporalRange] for this [DbAnswerSet]. */ + val temporalRange: TemporalRange? + get() = try { + TemporalRange(TemporalPoint.Millisecond(this.start!!), TemporalPoint.Millisecond(this.end!!)) + } catch (e: NullPointerException) { + null + } + + fun toApi() = ApiAnswer( + type = this.type.toApi(), + item = this.item?.toApi(), + start = this.start, + end = this.end, + text = this.text + ) +} \ No newline at end of file From 876d0c4fbfd1d00771fc4d0a2140d1e48147b576 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 3 Feb 2023 20:59:27 +0100 Subject: [PATCH 061/498] Added missing file --- .../interfaces/CachingTaskScorerFactory.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt new file mode 100644 index 000000000..3dc8a3aa7 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt @@ -0,0 +1,20 @@ +package dev.dres.data.model.template.interfaces + +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer + +/** + * A factory for [TaskScorer]s. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +interface CachingTaskScorerFactory { + /** + * Generates a new [TaskScorer]. Depending on the implementation, the returned instance + * is a new instance or being re-use. + * + * @return [TaskScorer]. + */ + fun newScorer(): CachingTaskScorer +} \ No newline at end of file From d577e11eaaa1eb5ed16df9f166bc424d2539d7d9 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 3 Feb 2023 23:34:30 +0100 Subject: [PATCH 062/498] Updated gradle and openapi plugin, using auto-generated operation id --- backend/build.gradle | 14 +- .../handler/audit/GetAuditLogInfoHandler.kt | 3 +- .../handler/audit/ListAuditLogsHandler.kt | 3 +- .../audit/ListAuditLogsInRangeHandler.kt | 1 + .../collection/AddCollectionHandler.kt | 1 + .../handler/collection/AddMediaItemHandler.kt | 3 +- .../collection/DeleteCollectionHandler.kt | 3 +- .../collection/DeleteMediaItemHandler.kt | 3 +- .../collection/ListCollectionHandler.kt | 6 +- .../collection/ListExternalItemHandler.kt | 3 +- .../collection/ListMediaItemHandler.kt | 3 +- .../collection/RandomMediaItemHandler.kt | 3 +- .../ResolveMediaItemListByNameHandler.kt | 3 +- .../collection/ShowCollectionHandler.kt | 1 + .../collection/ShowMediaItemHandler.kt | 3 +- .../collection/UpdateCollectionHandler.kt | 1 + .../collection/UpdateMediaItemHandler.kt | 3 +- .../download/EvaluationDownloadHandler.kt | 3 +- .../EvaluationTemplateDownloadHandler.kt | 3 +- .../handler/download/ScoreDownloadHandler.kt | 3 +- .../evaluation/admin/AdjustDurationHandler.kt | 3 +- .../admin/AdjustPropertiesHandler.kt | 1 + .../admin/CreateEvaluationHandler.kt | 3 +- .../admin/EvaluationOverviewHandler.kt | 3 +- .../evaluation/admin/ForceViewerHandler.kt | 3 +- .../evaluation/admin/ListPastTaskHandler.kt | 3 +- .../admin/ListSubmissionsHandler.kt | 3 +- .../evaluation/admin/ListViewersHandler.kt | 1 + .../evaluation/admin/NextTaskHandler.kt | 3 +- .../admin/OverrideSubmissionHandler.kt | 3 +- .../evaluation/admin/PreviousTaskHandler.kt | 1 + .../admin/StartEvaluationHandler.kt | 3 +- .../evaluation/admin/StartTaskHandler.kt | 3 +- .../evaluation/admin/StopEvaluationHandler.kt | 3 +- .../evaluation/admin/StopTaskHandler.kt | 3 +- .../evaluation/admin/SwitchTaskHandler.kt | 3 +- .../client/ClientListEvaluationsHandler.kt | 6 +- .../client/ClientTaskInfoHandler.kt | 3 +- .../scores/CurrentTaskScoreHandler.kt | 3 +- .../scores/HistoryTaskScoreHandler.kt | 1 + .../scores/ListEvaluationScoreHandler.kt | 3 +- .../scores/ListScoreSeriesHandler.kt | 1 + .../scores/ListScoreboardsHandler.kt | 3 +- .../scores/TeamGroupScoreHandler.kt | 3 +- .../viewer/GetCurrentTaskHandler.kt | 3 +- .../viewer/GetEvaluationInfoHandler.kt | 3 +- .../viewer/GetEvaluationStateHandler.kt | 3 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 3 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 1 + .../viewer/GetSubmissionInfoHandler.kt | 3 +- .../evaluation/viewer/GetTaskHintHandler.kt | 3 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 3 +- .../viewer/ListEvaluationInfoHandler.kt | 8 +- .../viewer/ListEvaluationStatesHandler.kt | 8 +- .../judgement/DequeueJudgementHandler.kt | 3 +- .../handler/judgement/DequeueVoteHandler.kt | 3 +- .../judgement/JudgementStatusHandler.kt | 1 + .../handler/judgement/PostJudgementHandler.kt | 3 +- .../rest/handler/judgement/PostVoteHandler.kt | 3 +- .../api/rest/handler/log/QueryLogHandler.kt | 1 + .../api/rest/handler/log/ResultLogHandler.kt | 3 +- .../rest/handler/preview/GetMediaHandler.kt | 8 +- .../handler/preview/MediaPreviewHandler.kt | 3 +- .../preview/SubmissionPreviewHandler.kt | 3 +- .../submission/BatchSubmissionHandler.kt | 1 + .../handler/submission/SubmissionHandler.kt | 1 + .../rest/handler/system/CurrentTimeHandler.kt | 8 +- .../api/rest/handler/system/InfoHandler.kt | 8 +- .../api/rest/handler/system/LoginHandler.kt | 3 +- .../api/rest/handler/system/LogoutHandler.kt | 3 +- .../CreateEvaluationTemplateHandler.kt | 3 +- .../DeleteEvaluationTemplateHandler.kt | 1 + .../handler/template/GetTeamLogoHandler.kt | 8 +- .../ListEvaluationTemplatesHandler.kt | 6 +- .../rest/handler/template/ListTasksHandler.kt | 3 +- .../rest/handler/template/ListTeamHandler.kt | 3 +- .../template/ShowEvaluationTemplateHandler.kt | 3 +- .../template/UpdateEvaluationHandler.kt | 1 + .../rest/handler/users/CreateUsersHandler.kt | 3 +- .../rest/handler/users/DeleteUsersHandler.kt | 3 +- .../handler/users/ListActiveUsersHandler.kt | 8 +- .../rest/handler/users/ListUsersHandler.kt | 8 +- .../users/ShowCurrentSessionHandler.kt | 3 +- .../handler/users/ShowCurrentUserHandler.kt | 8 +- .../rest/handler/users/UpdateUsersHandler.kt | 3 +- .../rest/handler/users/UserDetailsHandler.kt | 3 +- .../dev/dres/data/model/run/RunProperties.kt | 2 +- .../kotlin/dev/dres/mgmt/admin/UserManager.kt | 1 + doc/oas.json | 4105 ++++++++++------- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 263 +- gradlew.bat | 180 +- 94 files changed, 2802 insertions(+), 2046 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 4e30e2eeb..4d68e1d45 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -30,7 +30,10 @@ configurations { repositories { mavenCentral() - maven { url 'https://maven.reposilite.com/snapshots' } //javalin openapi snapshot + maven { + url 'https://maven.reposilite.com/snapshots' + allowInsecureProtocol = true + } //javalin openapi snapshot } compileKotlin { @@ -44,7 +47,8 @@ compileTestKotlin { } dependencies { - def javalin = '5.1.2' + def javalin = '5.3.2' + def javalinOpenapi = '5.3.3-SNAPSHOT' def log4jVersion = '2.17.1' ///// Frontend files (produced by sub-project). @@ -62,10 +66,10 @@ dependencies { ////// Javalin implementation group: 'io.javalin', name: 'javalin', version: javalin - kapt("io.javalin.community.openapi:openapi-annotation-processor:5.1.2-SNAPSHOT") + kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalinOpenapi") - implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: "5.1.2-SNAPSHOT" - implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: "5.1.2-SNAPSHOT" + implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: javalinOpenapi + implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: javalinOpenapi implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: javalin ////// Bcrypt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt index 191bef569..eeaf44f21 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt @@ -18,6 +18,7 @@ class GetAuditLogInfoHandler(store: TransientEntityStore) : AbstractAuditLogHand summary = "Gives information about the audit log. Namely size and latest timestamp of known entries.", path = "/api/v2/audit/info", tags = ["Audit"], + operationId = OpenApiOperation.AUTO_GENERATE, responses = [ OpenApiResponse("200", [OpenApiContent(AuditLogInfo::class)], description = "The audit log info."), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user executes the call.") @@ -27,4 +28,4 @@ class GetAuditLogInfoHandler(store: TransientEntityStore) : AbstractAuditLogHand override fun doGet(ctx: Context): AuditLogInfo = this.store.transactional(true) { AuditLogInfo(size = DbAuditLogEntry.all().size(), latest = DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, true).lastOrNull()?.timestamp?.millis) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index f877851fc..c7e285102 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -40,6 +40,7 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.") ], tags = ["Audit"], + operationId = OpenApiOperation.AUTO_GENERATE, responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") @@ -51,4 +52,4 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) DbAuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt index 6a1859f0a..77b644a28 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -28,6 +28,7 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include"), OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.") ], + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Audit"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index 0e1493a5d..609304b6d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -25,6 +25,7 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand summary = "Adds a new media collection.", path = "/api/v2/collection", tags = ["Collection"], + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaCollection::class)]), responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index fb27bc54d..3bd638ee2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -26,6 +26,7 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl summary = "Adds a media item to the specified media collection.", path = "/api/v2/mediaItem", methods = [HttpMethod.POST], + operationId = OpenApiOperation.AUTO_GENERATE, requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaItem::class)]), tags = ["Collection"], responses = [ @@ -65,4 +66,4 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl SuccessStatus("Media item added successfully.") } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt index 1aa707d58..5e1a4a501 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt @@ -19,6 +19,7 @@ class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionH path = "/api/v2/collection/{collectionId}", pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], tags = ["Collection"], + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.DELETE], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -37,4 +38,4 @@ class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionH override val route: String = "collection/{collectionId}" -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt index dba78f263..b38c2592a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -24,6 +24,7 @@ class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa @OpenApi( summary = "Tries to delete a specific media item.", path = "/api/v2/mediaItem/{mediaId}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.DELETE], pathParams = [ OpenApiParam("mediaId", String::class, "Media item ID") @@ -48,4 +49,4 @@ class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa SuccessStatus("Media item ${item.id} deleted successfully.") } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt index 45eee1c68..3b91aa915 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -5,10 +5,7 @@ import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.media.DbMediaCollection import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence @@ -24,6 +21,7 @@ class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan @OpenApi( summary = "Lists all available media collections with basic information about their content.", path = "/api/v2/collection/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Collection"], methods = [HttpMethod.GET], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index 2357112b2..96712dd4b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -35,6 +35,7 @@ class ListExternalItemHandler(config: Config) : GetRestHandler> { @OpenApi( summary = "Lists items from the external media collection whose name start with the given string.", path = "/api/v2/external/", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("startsWith", String::class, "Name starts with.", required = false) @@ -56,4 +57,4 @@ class ListExternalItemHandler(config: Config) : GetRestHandler> { } override val route: String = "external/" -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt index e66c68248..c824d1da9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -23,6 +23,7 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand @OpenApi( summary = "Lists media items from a given media collection whose name start with the given string.", path = "/api/v2/collection/{collectionId}/{startsWith}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID"), @@ -46,4 +47,4 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand } query.take(50).asSequence().map { it.toApi() }.toList() } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt index a23d575f4..7f44fa4a9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -27,6 +27,7 @@ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa @OpenApi( summary = "Selects and returns a random media item from a given media collection.", path = "/api/v2/collection/{collectionId}/random", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID") @@ -45,4 +46,4 @@ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa throw ErrorStatusException(404, "Failed to ferch media item. It seems that the given collection ${collection.id} is empty.", ctx) item.toApi() } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt index 8a3b9d6dd..5f5440468 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt @@ -22,6 +22,7 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC @OpenApi( summary = "Resolves a list of media item names to media items", path = "/api/v2/collection/{collectionId}/resolve", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [ OpenApiParam("collectionId", String::class, "Collection ID") @@ -54,4 +55,4 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index d840434aa..43818b20f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -24,6 +24,7 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan @OpenApi( summary = "Shows the content of the specified media collection.", path = "/api/v2/collection/{collectionId}", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], tags = ["Collection"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt index ef77332ee..6bc25f428 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt @@ -23,6 +23,7 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand @OpenApi( summary = "Selects and returns a specific media item.", path = "/api/v2/mediaItem/{mediaId}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("mediaId", String::class, "Media item ID") @@ -46,4 +47,4 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand item.toApi() } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt index ddd41effc..5856b13eb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -27,6 +27,7 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH @OpenApi( summary = "Updates a media collection", path = "/api/v2/collection", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Collection"], methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaCollection::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 9ca1edeb5..cff789568 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -26,6 +26,7 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa @OpenApi( summary = "Updates a Media Item to the specified Media Collection.", path = "/api/v2/mediaitem", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(ApiMediaItem::class)]), tags = ["Collection"], @@ -57,4 +58,4 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa SuccessStatus("Media item ${item.id} updated successfully.") } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index a271aa9ef..09e59fd64 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -26,6 +26,7 @@ class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadH @OpenApi( summary = "Provides a JSON download of the entire evaluation structure.", path = "/api/v2/download/evaluation/{evaluationId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Download"], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID.", required = true) @@ -53,4 +54,4 @@ class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadH val mapper = jacksonObjectMapper() return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(evaluation) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index cc3cf1ce8..8ef0660e1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -26,6 +26,7 @@ class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractD @OpenApi( summary = "Provides a JSON download of the entire evaluation template structure.", path = "/api/v2/download/template/{templateId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Download"], pathParams = [ OpenApiParam("templateId", String::class, "The evaluation template ID", required = true) @@ -53,4 +54,4 @@ class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractD val mapper = jacksonObjectMapper() return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index 9d9fb71d6..89f74bc32 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -23,6 +23,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle @OpenApi( summary = "Provides a CSV download with the scores for a given evaluation.", path = "/api/v2/download/evaluation/{evaluationId}/scores", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Download"], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID.", required = true) @@ -55,4 +56,4 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 071910774..67e34327b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -28,6 +28,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi @OpenApi( summary = "Adjusts the duration of a running task. This is a method for admins.", path = "/api/v2/run/admin/{runId}/adjust/{duration}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), @@ -60,4 +61,4 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index 09851052c..bd87cfab1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -28,6 +28,7 @@ class AdjustPropertiesHandler(store: TransientEntityStore): AbstractEvaluationAd summary = "Changes the properties of an evaluation.", path = "/api/v2/evaluation/admin/{evaluationId}/properties", methods = [HttpMethod.PATCH], + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), ], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index bd5d0fac3..4f10c006c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -49,6 +49,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs summary = "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", path = "/api/v2/evaluation/admin/create", methods = [HttpMethod.POST], + operationId = OpenApiOperation.AUTO_GENERATE, requestBody = OpenApiRequestBody([OpenApiContent(ApiEvaluationStartMessage::class)]), tags = ["Evaluation Administrator"], responses = [ @@ -121,4 +122,4 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs /* Schedule newly created run manager. */ return SuccessStatus("Evaluation '${message.name}' was started and is running with ID ${evaluation.id}.") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt index 06f01890e..b6175c8c5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -19,6 +19,7 @@ class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluation @OpenApi( summary = "Provides a complete overview of a run.", path = "/api/v2/run/admin/{runId}/overview", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), @@ -38,4 +39,4 @@ class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluation ApiEvaluationOverview.of(evaluationManager) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index 118162b5c..59cb217a5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -24,6 +24,7 @@ class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHa @OpenApi( summary = "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), @@ -55,4 +56,4 @@ class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHa } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index f806e0416..38e5efa61 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -22,6 +22,7 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH @OpenApi( summary = "Lists all past tasks for a given evaluation.", path = "/api/v2/evaluation/admin/{evaluationId}/task/past/list", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) @@ -44,4 +45,4 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index 5aea04d7f..b289611ec 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -21,6 +21,7 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm @OpenApi( summary = "Lists all submissions for a given evaluation and task.", path = "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ OpenApiParam("runId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), @@ -44,4 +45,4 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index 852e7b45e..425c35335 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -20,6 +20,7 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa @OpenApi( summary = "Lists all registered viewers for a evaluation. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/viewer/list", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index f0a9940dc..99b11d831 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -34,6 +34,7 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl @OpenApi( summary = "Moves to and selects the next task within the evaluation. This is a method for admins.", path = "/api/v2/evaluation/admin/{runId}/task/next", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], @@ -72,4 +73,4 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 9998a5d8c..1b905a9fb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -31,6 +31,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation @OpenApi( summary = "Override the submission status for a given submission.", path = "/api/v2/evaluation/admin/{evaluationId}/submission/override", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.PATCH], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) @@ -78,4 +79,4 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index c34b7f7b1..ab2183983 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -25,6 +25,7 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH @OpenApi( summary = "Moves to and selects the previous task. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/task/previous", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index e5bacae75..fdfb3a327 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -27,6 +27,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd @OpenApi( summary = "Starts a evaluation. This is a method for administrators.", path = "/api/v2/evaluation/admin/{evaluationId}/start", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], @@ -53,4 +54,4 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 845b954ad..fe3f90b61 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -34,6 +34,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand @OpenApi( summary = "Starts the currently active task as a new task run. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/task/start", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evalation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], @@ -63,4 +64,4 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index 45096af7f..21c4cf46a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -28,6 +28,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi @OpenApi( summary = "Terminates an evaluation. This is a method for administrators.", path = "/api/v2/evaluation/admin/{runId}/terminate", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Competition Run Admin"], @@ -54,4 +55,4 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 4c3dda288..429310fff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -29,6 +29,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl summary = "Aborts the currently running task run. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/task/abort", methods = [HttpMethod.POST], + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], tags = ["Evaluation Administrator"], responses = [ @@ -54,4 +55,4 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index 0c6c7406a..4ef120c9b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -24,6 +24,7 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan @OpenApi( summary = "Moves to and selects the specified task. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), @@ -54,4 +55,4 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt index e390b465c..45653d58d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -8,10 +8,7 @@ import dev.dres.data.model.run.DbEvaluation import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** @@ -29,6 +26,7 @@ class ClientListEvaluationsHandler(store: TransientEntityStore): AbstractEvaluat @OpenApi( summary = "Lists an overview of all evaluation runs visible to the current client.", path = "/api/v2/client/evaluation/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Client"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index 56e6456fe..a83cfd3d1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -25,6 +25,7 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie @OpenApi( summary = "Returns an overview of the currently active task for a run.", path = "/api/v2/client/evaluation/currentTask/{evaluationId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Client Run Info"], queryParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) @@ -43,4 +44,4 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) ApiTaskTemplateInfo(task.template) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 0c77eede0..1b7ff7b7b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -32,6 +32,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle @OpenApi( summary = "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", path = "/api/v2/score/evaluation/{evaluationId}/current", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Scores"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ @@ -58,4 +59,4 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle ) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index a6691e1c8..bc4ee6b47 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -33,6 +33,7 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle @OpenApi( summary = "Returns the overviews of all score boards for the specified task.", path = "/api/v2/score/evaluation/{evaluationId}/history/{taskId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Scores"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt index 817b244b7..48c2a68fa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt @@ -24,6 +24,7 @@ class ListEvaluationScoreHandler(store: TransientEntityStore) : AbstractScoreHan @OpenApi( summary = "Returns the score overviews of a specific evaluation run.", path = "/api/v2/score/evaluation/{evaluationId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Scores"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ @@ -39,4 +40,4 @@ class ListEvaluationScoreHandler(store: TransientEntityStore) : AbstractScoreHan manager.scoreboards.map { it.overview().toApi() } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt index f7a51e3db..57ab699cc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt @@ -21,6 +21,7 @@ class ListScoreSeriesHandler(store: TransientEntityStore) : AbstractScoreHandler @OpenApi( summary = "Returns a time series for a given run and scoreboard.", path = "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Scores"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt index 998a9b0c2..c26ffdb34 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt @@ -16,6 +16,7 @@ class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler @OpenApi( summary = "Returns a list of available scoreboard names for the given evaluation.", path = "/api/v2/evaluation/{evaluationId}/scoreboard/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Scores"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true) @@ -32,4 +33,4 @@ class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler val manager = ctx.eligibleManagerForId() return manager.scoreboards.map { it.name } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index 6be410614..1df1faeec 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -23,6 +23,7 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( @OpenApi( summary = "Returns team group aggregated values of the current task.", path = "/api/v2/score/evaluation/{evaluationId}/teamGroup/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Competition Run Scores"], pathParams = [ OpenApiParam("runId", String::class, "ID of the competition run.", required = true), @@ -50,4 +51,4 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( emptyList() } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index f6f9d9fe5..8edbfdd79 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -35,6 +35,7 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView @OpenApi( summary = "Returns the information for the currently active task template (i.e., the one that is currently selected).", path = "/api/v2/evaluation/{evaluationId}/task", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ @@ -55,4 +56,4 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt index 8f8c000cb..1f878562d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -20,6 +20,7 @@ class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluation @OpenApi( summary = "Returns basic information about a specific evaluation.", path = "/api/v2/evaluation/{evaluationId}/info", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ @@ -39,4 +40,4 @@ class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluation ApiEvaluationInfo(manager) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index 8be8468f0..1f06b118d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -23,6 +23,7 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation @OpenApi( summary = "Returns the state of a specific evaluation.", path = "/api/v2/evaluation/{evaluationId}/state", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], responses = [ @@ -43,4 +44,4 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation ApiEvaluationState(manager, rac) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index c145d4e20..0067d4e0d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -24,6 +24,7 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua @OpenApi( summary = "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", path = "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class,"The evaluation ID.", required = true), @@ -51,4 +52,4 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua manager.currentSubmissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index 1585d8adc..6626645f5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -23,6 +23,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval @OpenApi( summary = "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", path = "/api/v2/evaluation/{evaluationId}/task/{taskRunId}/submission/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index 530281f76..683d56db8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -25,6 +25,7 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV @OpenApi( summary = "Returns all submissions for the current task run, if one is either running or has just ended.", path = "/api/v2/evaluation/{evaluationId}/submission/list/{timestamp}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) @@ -75,4 +76,4 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV }.flatMap { it.map { s -> s.toApi(blind) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index e3a14cb4e..e4188675c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -38,6 +38,7 @@ class GetTaskHintHandler(store: TransientEntityStore, private val config: Config summary = "Returns the task hint for the specified task.", path = "/api/v2/run/{evaluationId}/hint/{taskId}", tags = ["Evaluation"], + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), OpenApiParam("taskId", String::class, "The task ID.", required = true) @@ -76,4 +77,4 @@ class GetTaskHintHandler(store: TransientEntityStore, private val config: Config } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 2ba4a872a..94ef4554c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -27,6 +27,7 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf @OpenApi( summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", path = "/api/v2/evaluation/{evaluationId}/target/{taskId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), @@ -69,4 +70,4 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt index 3e7a0a074..0cf2f526a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt @@ -4,10 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo import dev.dres.api.rest.types.status.ErrorStatus import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** @@ -20,6 +17,7 @@ class ListEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluatio @OpenApi( summary = "Lists an overview of all evaluations visible to the current user.", path = "/api/v2/evaluation/info/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), @@ -32,4 +30,4 @@ class ListEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluatio ApiEvaluationInfo(manager) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt index d7f0bd591..958fa250d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt @@ -5,10 +5,7 @@ import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.run.RunActionContext import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** @@ -21,6 +18,7 @@ class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluati @OpenApi( summary = "Lists an overview of all evaluation visible to the current user.", path = "/api/v2/evaluation/state/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), @@ -34,4 +32,4 @@ class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluati ApiEvaluationState(it, rac) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 75766161e..73110b314 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -25,6 +25,7 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa @OpenApi( summary = "Gets the next open submission that is waiting to be judged.", path = "/api/v2/evaluation/{evaluationId}/judge/next", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], tags = ["Judgement"], responses = [ @@ -70,4 +71,4 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa } throw ErrorStatusException(202, "There is currently no submission awaiting judgement.", ctx) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 5384c25b4..28c21ebc7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -25,6 +25,7 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( @OpenApi( summary = "Gets the next open submission that is waiting to be voted on.", path = "/api/v2/evaluation/{evaluationId}/vote/next", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], tags = ["Judgement"], responses = [ @@ -68,4 +69,4 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( } throw ErrorStatusException(202, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx, true) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt index fbe5e07fd..509f0cb56 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -24,6 +24,7 @@ class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHand @OpenApi( summary = "Retrieves the status of all judgement validators.", path = "/api/v2/evaluation/{evaluationId}/judge/status", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], tags = ["Judgement"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 05994feac..5b89cdb7c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -28,6 +28,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle @OpenApi( summary = "Endpoint to post a judgement for a previously detached judgement request.", path = "/api/v2/run/{runId}/judge", methods = [HttpMethod.POST], + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(ApiJudgement::class)]), tags = ["Judgement"], @@ -63,4 +64,4 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle } return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index 6332a1d9a..bcd804967 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -27,6 +27,7 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto @OpenApi( summary = "Returns a Vote.", path = "/api/v2/evaluation/{evaluationId}/judge/vote", methods = [HttpMethod.POST], + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(ApiVote::class)]), tags = ["Judgement"], @@ -53,4 +54,4 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto } return SuccessStatus("vote received") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt index aa1c611e2..6a6310564 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/QueryLogHandler.kt @@ -26,6 +26,7 @@ class QueryLogHandler : AbstractLogHandler() { @OpenApi(summary = "Accepts query logs from participants", path = "/api/v2/log/query", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(QueryEventLog::class)]), tags = ["Log"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt index 3a15a69b8..9e88ebebe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/log/ResultLogHandler.kt @@ -26,6 +26,7 @@ class ResultLogHandler: AbstractLogHandler() { @OpenApi(summary = "Accepts result logs from participants.", path = "/api/v2/log/result", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(QueryResultLog::class)]), tags = ["Log"], @@ -48,4 +49,4 @@ class ResultLogHandler: AbstractLogHandler() { EventStreamProcessor.event(QueryResultLogEvent(ctx.sessionToken(), evaluationManager.id, queryResultLog)) return SuccessStatus("Log entry received.") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt index 84f6412e2..69854114e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt @@ -7,10 +7,7 @@ import dev.dres.data.model.media.DbMediaItem import dev.dres.utilities.extensions.errorResponse import dev.dres.utilities.extensions.streamFile import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiParam -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.eq import kotlinx.dnq.query.firstOrNull @@ -37,6 +34,7 @@ class GetMediaHandler(private val store: TransientEntityStore) : GetRestHandler< @OpenApi(summary = "Returns a collection item", path = "/api/v2/media/{itemId}", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("itemId", String::class, "The media item ID.") ], @@ -68,4 +66,4 @@ class GetMediaHandler(private val store: TransientEntityStore) : GetRestHandler< //is triggered by a client abruptly stopping playback, can be safely ignored } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt index 368d2c209..31405f61b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/MediaPreviewHandler.kt @@ -21,6 +21,7 @@ class MediaPreviewHandler(store: TransientEntityStore, config: Config) : Abstrac @OpenApi( summary = "Returns a preview image from a collection item", path = "/api/v2/preview/item/{collection}/{item}/{time}", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("collectionId", String::class, "Unique ID of the media collection."), OpenApiParam("item", String::class, "Name of the media item-"), @@ -49,4 +50,4 @@ class MediaPreviewHandler(store: TransientEntityStore, config: Config) : Abstrac override fun doGet(ctx: Context): Any { throw UnsupportedOperationException("MediaPreviewHandler::doGet() is not supported and should not be executed!") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index c23048be5..6fd895b51 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -25,6 +25,7 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab @OpenApi( summary = "Returns a preview image for a specific submission.", path = "/api/v2/preview/submission/{evaluationId}/{submissionId}", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID."), OpenApiParam("submissionId", String::class, "The submission ID") @@ -77,4 +78,4 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab override fun doGet(ctx: Context): Any { throw UnsupportedOperationException("SubmissionPreviewHandler::doGet() is not supported and should not be executed!") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt index 087a9dfa2..04290e878 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt @@ -45,6 +45,7 @@ class BatchSubmissionHandler(private val store: TransientEntityStore, private va @OpenApi(summary = "Endpoint to accept batch submissions in JSON format", path = "/api/v2/submit/{evaluationId}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], requestBody = OpenApiRequestBody([OpenApiContent(RunResult::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index c8fcd2f98..853fb354d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -65,6 +65,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con @OpenApi(summary = "Endpoint to accept submissions", path = "/api/v2/submit", + operationId = OpenApiOperation.AUTO_GENERATE, queryParams = [ OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt index 3a8cb68bf..779a18a19 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/CurrentTimeHandler.kt @@ -3,10 +3,7 @@ package dev.dres.api.rest.handler.system import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.system.CurrentTime import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* /** * A [GetRestHandler] that returns the current server time. @@ -21,10 +18,11 @@ class CurrentTimeHandler : GetRestHandler { @OpenApi(summary = "Returns the current time on the server.", path = "/api/v2/status/time", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], tags = ["Status"], responses = [ OpenApiResponse("200", [OpenApiContent(CurrentTime::class)]) ]) override fun doGet(ctx: Context): CurrentTime = CurrentTime() -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt index b8c3ab28a..bda385e05 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/InfoHandler.kt @@ -10,10 +10,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.system.DresInfo import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import java.lang.management.ManagementFactory /** @@ -31,6 +28,7 @@ class InfoHandler : GetRestHandler { @OpenApi(summary = "Returns an overview of the server properties.", path = "/api/v2/status/info", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], tags = ["Status"], responses = [ @@ -55,4 +53,4 @@ class InfoHandler : GetRestHandler { DresInfo (DRES.VERSION, ManagementFactory.getRuntimeMXBean().startTime, ManagementFactory.getRuntimeMXBean().uptime) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index 88eb09098..abebc551f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -30,6 +30,7 @@ class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostR @OpenApi(summary = "Sets roles for session based on user account and returns a session cookie.", path = "/api/v2/login", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], tags = ["User"], requestBody = OpenApiRequestBody([OpenApiContent(LoginRequest::class)]), @@ -72,4 +73,4 @@ class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostR } override val route = "login" -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index 7fd3c5f5e..e0038629d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -24,6 +24,7 @@ class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetR @OpenApi(summary = "Clears all user roles of the current session.", path = "/api/v2/logout", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["User"], queryParams = [ OpenApiParam("session", String::class, "Session Token") @@ -45,4 +46,4 @@ class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetR } } override val route = "logout" -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index e32604bf0..3f51eabb8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -28,6 +28,7 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva @OpenApi( summary = "Creates a new evaluation template.", path = "/api/v2/template", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], requestBody = OpenApiRequestBody([OpenApiContent(ApiCreateEvaluation::class)]), tags = ["Template"], @@ -55,4 +56,4 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva } return SuccessStatus("Competition description with ID $newId was created successfully.") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index c1ee8f708..8c81aa780 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -22,6 +22,7 @@ class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva @OpenApi( summary = "Deletes the evaluation template with the given template ID.", path = "/api/v2/template/{templateId}", + operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.DELETE], pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index 3c652c3a0..ae725c3c5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -4,10 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.template.team.DbTeam import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiParam -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.eq import kotlinx.dnq.query.firstOrNull @@ -32,6 +29,7 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla @OpenApi( summary = "Returns the logo for the given team ID.", path = "/api/v2/template/logo/{teamId}", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation", "Media"], pathParams = [OpenApiParam("teamId", String::class, "The ID of the team to list load the logo for.")], responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], @@ -54,4 +52,4 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index cb42bf409..72f3e31ba 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -5,10 +5,7 @@ import dev.dres.api.rest.types.competition.ApiEvaluationOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.size @@ -27,6 +24,7 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEval @OpenApi( summary = "Lists an overview of all available competitions with basic information about their content.", path = "/api/v2/template/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index b34150fc4..9900410a8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -24,6 +24,7 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplate @OpenApi( summary = "Lists the task templates contained in a specific evaluation template.", path = "/api/v2/competition/{templateId}/task/list", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ @@ -38,4 +39,4 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplate override fun doGet(ctx: Context) = this.store.transactional(true) { competitionFromContext(ctx).tasks.asSequence().map { it.toApi() }.toList() } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index ce6db1239..b5f1d956a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -24,6 +24,7 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateH @OpenApi( summary = "Lists all the teams of a specific competition.", path = "/api/v2/competition/{templateId}/team/list", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ @@ -38,4 +39,4 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateH override fun doGet(ctx: Context) = this.store.transactional(true) { competitionFromContext(ctx).teams.asSequence().map { it.toApi() }.toList() } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index ad4e421c2..083dd9af8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -22,6 +22,7 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvalu @OpenApi( summary = "Loads the detailed definition of a specific evaluation template.", path = "/api/v2/template/{templateId}", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], tags = ["Template"], responses = [ @@ -35,4 +36,4 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvalu override fun doGet(ctx: Context)= this.store.transactional(true) { competitionFromContext(ctx).toApi() } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt index c6d64584a..d1aa7bfa5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationHandler.kt @@ -37,6 +37,7 @@ class UpdateEvaluationHandler(store: TransientEntityStore, val config: Config) : @OpenApi( summary = "Updates an existing evaluation template.", path = "/api/v2/template/{templateId}", + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], methods = [HttpMethod.PATCH], requestBody = OpenApiRequestBody([OpenApiContent(ApiEvaluationTemplate::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index 5e0de0970..dc44d7a7b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -30,6 +30,7 @@ class CreateUsersHandler(private val store: TransientEntityStore) : AbstractUser @OpenApi( summary = "Creates a new user, if the username is not already taken. Requires ADMIN privileges", path = "/api/v2/user", methods = [HttpMethod.POST], + operationId = OpenApiOperation.AUTO_GENERATE, requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], responses = [ @@ -73,4 +74,4 @@ class CreateUsersHandler(private val store: TransientEntityStore) : AbstractUser } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index c1dd69557..d1cdbb310 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -27,6 +27,7 @@ class DeleteUsersHandler(private val store: TransientEntityStore) : AbstractUser @OpenApi( summary = "Deletes the specified user. Requires ADMIN privileges", path = "/api/v2/user/{userId}", methods = [HttpMethod.DELETE], + operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("userId", String::class, "User ID")], tags = ["User"], responses = [ @@ -44,4 +45,4 @@ class DeleteUsersHandler(private val store: TransientEntityStore) : AbstractUser throw ErrorStatusException(500, "Could not delete the user (${user.id})", ctx) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index e734af5cc..e3f7973f6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -9,10 +9,7 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** @@ -32,6 +29,7 @@ class ListActiveUsersHandler(private val store: TransientEntityStore) : GetRestH @OpenApi( summary = "Get details of all current user sessions", path = "/api/v2/user/session/active/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["User"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), @@ -46,4 +44,4 @@ class ListActiveUsersHandler(private val store: TransientEntityStore) : GetRestH }?.toApi() ?: return@map ApiUser("??", "??", ApiRole.VIEWER, session) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt index d5ae97791..17d3d2385 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -7,10 +7,7 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context -import io.javalin.openapi.HttpMethod -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiContent -import io.javalin.openapi.OpenApiResponse +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore /** @@ -29,6 +26,7 @@ class ListUsersHandler(private val store: TransientEntityStore): AbstractUserHan @OpenApi( summary = "Lists all available users.", path = "/api/v2/user/list", + operationId = OpenApiOperation.AUTO_GENERATE, tags = ["User"], responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], methods = [HttpMethod.GET] @@ -36,4 +34,4 @@ class ListUsersHandler(private val store: TransientEntityStore): AbstractUserHan override fun doGet(ctx: Context) = this.store.transactional { UserManager.list().map { it.toApi() } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt index 21543c15f..9472bf7a5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentSessionHandler.kt @@ -23,6 +23,7 @@ class ShowCurrentSessionHandler : AbstractUserHandler(), GetRestHandler" : { + "/api/v2/collection/{collectionId}/{startsWith}" : { "get" : { "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2Collection{collectionId}{startsWith}", "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { "name" : "startsWith", "in" : "path", - "description" : "Name starts with.", + "description" : "Name the item(s) should start with.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -510,18 +650,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "type" : "string" + "$ref" : "#/components/schemas/ApiMediaItem" } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -531,6 +673,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -540,6 +683,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -553,24 +697,15 @@ "security" : [ ] } }, - "/api/v2/collection/{collectionId}/{startsWith}" : { + "/api/v2/competition/{templateId}/task/list" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2Competition{templateId}TaskList", "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", + "name" : "templateId", "in" : "path", - "description" : "Name the item(s) should start with.", + "description" : "The evaluation template ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -580,18 +715,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" + "$ref" : "#/components/schemas/ApiTaskTemplate" } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -601,6 +738,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -610,6 +748,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -623,71 +762,15 @@ "security" : [ ] } }, - "/api/v2/collection/{collectionId}/random" : { + "/api/v2/competition/{templateId}/team/list" : { "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific competition.", + "operationId" : "getApiV2Competition{templateId}TeamList", "parameters" : [ { - "name" : "collectionId", + "name" : "templateId", "in" : "path", - "description" : "Collection ID", + "description" : "The evaluation template ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -695,33 +778,22 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" + "$ref" : "#/components/schemas/ApiTeam" } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -731,6 +803,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -740,45 +813,7 @@ } }, "404" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -796,6 +831,7 @@ "get" : { "tags" : [ "Download" ], "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluation{evaluationId}", "parameters" : [ { "name" : "runId", "in" : "path", @@ -809,6 +845,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "text/plain" : { "schema" : { @@ -818,6 +855,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -827,6 +865,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -836,6 +875,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -849,14 +889,15 @@ "security" : [ ] } }, - "/api/v2/download/template/{templateId}" : { + "/api/v2/download/evaluation/{evaluationId}/scores" : { "get" : { "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluation{evaluationId}Scores", "parameters" : [ { - "name" : "templateId", + "name" : "runId", "in" : "path", - "description" : "The evaluation template ID", + "description" : "The evaluation ID.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -866,6 +907,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "text/plain" : { "schema" : { @@ -875,6 +917,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -884,6 +927,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -893,6 +937,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -906,14 +951,15 @@ "security" : [ ] } }, - "/api/v2/download/evaluation/{evaluationId}/scores" : { + "/api/v2/download/template/{templateId}" : { "get" : { "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given evaluation.", + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplate{templateId}", "parameters" : [ { - "name" : "runId", + "name" : "templateId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "The evaluation template ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -923,6 +969,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "text/plain" : { "schema" : { @@ -932,6 +979,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -941,6 +989,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -950,6 +999,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -963,34 +1013,25 @@ "security" : [ ] } }, - "/api/v2/run/admin/{runId}/adjust/{duration}" : { + "/api/v2/evaluation/admin/create" : { "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1000,6 +1041,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1009,15 +1051,7 @@ } }, "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1035,6 +1069,7 @@ "patch" : { "tags" : [ "Competition Run Admin" ], "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdmin{evaluationId}Properties", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1058,6 +1093,7 @@ }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1067,6 +1103,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1076,6 +1113,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1085,6 +1123,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1098,23 +1137,25 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/create" : { + "/api/v2/evaluation/admin/{evaluationId}/start" : { "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdmin{evaluationId}Start", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1124,6 +1165,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1133,6 +1175,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1146,14 +1189,25 @@ "security" : [ ] } }, - "/api/v2/run/admin/{runId}/overview" : { + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { "get" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Provides a complete overview of a run.", + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdmin{evaluationId}SubmissionList{templateId}", "parameters" : [ { "name" : "runId", "in" : "path", - "description" : "The evaluation ID", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -1163,15 +1217,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1181,6 +1240,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1190,6 +1250,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1203,10 +1264,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { + "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "summary" : "Override the submission status for a given submission.", + "operationId" : "patchApiV2EvaluationAdmin{evaluationId}SubmissionOverride", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1217,28 +1279,30 @@ "schema" : { "type" : "string" } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiSubmission" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1248,6 +1312,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1257,6 +1322,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1270,10 +1336,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdmin{evaluationId}TaskAbort", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1287,18 +1354,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1308,15 +1374,7 @@ } }, "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1330,12 +1388,13 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { "get" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdmin{evaluationId}TaskPastList", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", "required" : true, @@ -1344,31 +1403,23 @@ "schema" : { "type" : "string" } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1378,6 +1429,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1387,6 +1439,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1400,10 +1453,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdmin{evaluationId}TaskPrevious", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1417,18 +1471,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1438,15 +1491,7 @@ } }, "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1460,14 +1505,15 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{runId}/task/next" : { + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdmin{evaluationId}TaskStart", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "The evalation ID.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -1477,6 +1523,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1486,6 +1533,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1495,6 +1543,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1508,10 +1557,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { - "patch" : { + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the submission status for a given submission.", + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdmin{evaluationId}TaskSwitch{idx}", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1522,28 +1572,31 @@ "schema" : { "type" : "string" } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1553,15 +1606,7 @@ } }, "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1575,10 +1620,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdmin{evaluationId}ViewerList", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1592,15 +1638,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1610,6 +1661,17 @@ } }, "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1623,10 +1685,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdmin{evaluationId}ViewerList{viewerId}Force", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1637,9 +1700,20 @@ "schema" : { "type" : "string" } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1649,6 +1723,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1658,45 +1733,7 @@ } }, "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1705,7 +1742,8 @@ } } }, - "401" : { + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1719,10 +1757,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{runId}/terminate" : { + "/api/v2/evaluation/admin/{runId}/task/next" : { "post" : { - "tags" : [ "Competition Run Admin" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdmin{runId}TaskNext", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1736,6 +1775,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1745,6 +1785,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1754,6 +1795,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1767,10 +1809,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "/api/v2/evaluation/admin/{runId}/terminate" : { "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", + "tags" : [ "Competition Run Admin" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdmin{runId}Terminate", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1784,6 +1827,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -1793,6 +1837,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1802,6 +1847,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1815,52 +1861,28 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1874,25 +1896,28 @@ "security" : [ ] } }, - "/api/v2/client/evaluation/list" : { + "/api/v2/evaluation/state/list" : { "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", "parameters" : [ ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" + "$ref" : "#/components/schemas/ApiEvaluationState" } } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1906,13 +1931,14 @@ "security" : [ ] } }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "/api/v2/evaluation/{evaluationId}/info" : { "get" : { - "tags" : [ "Client Run Info" ], - "summary" : "Returns an overview of the currently active task for a run.", + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2Evaluation{evaluationId}Info", "parameters" : [ { "name" : "evaluationId", - "in" : "query", + "in" : "path", "description" : "The evaluation ID.", "required" : true, "deprecated" : false, @@ -1923,15 +1949,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + "$ref" : "#/components/schemas/ApiEvaluationInfo" } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -1940,7 +1968,8 @@ } } }, - "404" : { + "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -1948,21 +1977,32 @@ } } } - } - }, + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, "deprecated" : false, "security" : [ ] } }, - "/api/v2/score/evaluation/{evaluationId}/current" : { + "/api/v2/evaluation/{evaluationId}/judge/next" : { "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2Evaluation{evaluationId}JudgeNext", "parameters" : [ { "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : true, + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1971,15 +2011,27 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -1989,6 +2041,17 @@ } }, "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -1998,6 +2061,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2011,25 +2075,16 @@ "security" : [ ] } }, - "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "/api/v2/evaluation/{evaluationId}/judge/status" : { "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task.", + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2Evaluation{evaluationId}JudgeStatus", "parameters" : [ { "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID", - "required" : true, + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2038,15 +2093,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2055,7 +2115,8 @@ } } }, - "401" : { + "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2065,6 +2126,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2078,35 +2140,45 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2Evaluation{evaluationId}JudgeVote", "parameters" : [ { "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : true, + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2116,6 +2188,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2125,6 +2198,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2138,10 +2212,11 @@ "security" : [ ] } }, - "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { "get" : { "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2Evaluation{evaluationId}ScoreboardList", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -2152,31 +2227,23 @@ "schema" : { "type" : "string" } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiScoreSeries" + "type" : "string" } } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2186,6 +2253,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2195,6 +2263,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2208,14 +2277,15 @@ "security" : [ ] } }, - "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "/api/v2/evaluation/{evaluationId}/state" : { "get" : { - "tags" : [ "Competition Run Scores" ], - "summary" : "Returns team group aggregated values of the current task.", + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2Evaluation{evaluationId}State", "parameters" : [ { - "name" : "runId", + "name" : "evaluationId", "in" : "path", - "description" : "ID of the competition run.", + "description" : "The evaluation ID.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -2225,18 +2295,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroupValue" - } + "$ref" : "#/components/schemas/ApiEvaluationState" } } } }, - "400" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2246,6 +2315,7 @@ } }, "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2255,6 +2325,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2268,10 +2339,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/task" : { + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2Evaluation{evaluationId}SubmissionListAfter{timestamp}", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -2282,18 +2354,34 @@ "schema" : { "type" : "string" } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2303,6 +2391,7 @@ } }, "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2312,6 +2401,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2325,10 +2415,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/info" : { + "/api/v2/evaluation/{evaluationId}/submission/list/{timestamp}" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2Evaluation{evaluationId}SubmissionList{timestamp}", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -2342,24 +2433,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } } } } }, "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2369,6 +2456,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2382,10 +2470,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/state" : { + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2Evaluation{evaluationId}Target{taskId}", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -2396,18 +2485,30 @@ "schema" : { "type" : "string" } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" + "$ref" : "#/components/schemas/ApiTargetContent" } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2417,6 +2518,7 @@ } }, "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2426,6 +2528,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2439,10 +2542,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "/api/v2/evaluation/{evaluationId}/task" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2Evaluation{evaluationId}Task", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -2453,32 +2557,20 @@ "schema" : { "type" : "string" } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "number", - "format" : "int64" - } } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2488,6 +2580,7 @@ } }, "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2497,6 +2590,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2514,6 +2608,7 @@ "get" : { "tags" : [ "Evaluation" ], "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2Evaluation{evaluationId}Task{taskRunId}SubmissionList", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -2537,6 +2632,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -2549,6 +2645,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2558,6 +2655,7 @@ } }, "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -2567,6 +2665,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2580,15 +2679,16 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/submission/list/{timestamp}" : { + "/api/v2/evaluation/{evaluationId}/vote/next" : { "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2Evaluation{evaluationId}VoteNext", "parameters" : [ { "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : true, + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2597,18 +2697,37 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" } } } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2618,6 +2737,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2631,25 +2751,16 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "/api/v2/external/" : { "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2External", "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", + "name" : "startsWith", "in" : "path", - "description" : "The task ID.", - "required" : true, + "description" : "Name starts with.", + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2658,15 +2769,20 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" + "type" : "array", + "items" : { + "type" : "string" + } } } } }, - "401" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2675,7 +2791,8 @@ } } }, - "403" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2685,6 +2802,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2698,25 +2816,45 @@ "security" : [ ] } }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "parameters" : [ ], + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2724,31 +2862,9 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2762,32 +2878,45 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "202" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2796,7 +2925,8 @@ } } }, - "400" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2804,17 +2934,41 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } }, - "401" : { + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/ApiUser" } } } }, - "403" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2823,7 +2977,8 @@ } } }, - "404" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2837,14 +2992,15 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { + "/api/v2/logout" : { "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", + "name" : "session", + "in" : "query", + "description" : "Session Token", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -2854,15 +3010,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "202" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2870,26 +3028,41 @@ } } } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" } } }, - "401" : { + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "404" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2903,14 +3076,15 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItem{mediaId}", "parameters" : [ { - "name" : "evaluationId", + "name" : "mediaId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "Media item ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -2920,18 +3094,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -2940,7 +3113,8 @@ } } }, - "403" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -2950,6 +3124,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -2961,16 +3136,15 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v2/run/{runId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + }, + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItem{mediaId}", "parameters" : [ { - "name" : "evaluationId", + "name" : "mediaId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "Media item ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -2978,27 +3152,19 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiMediaItem" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3008,6 +3174,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3016,7 +3183,8 @@ } } }, - "403" : { + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3024,18 +3192,41 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } }, - "408" : { - "description" : "On timeout: Judgement took too long", + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "404" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3049,33 +3240,36 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "/api/v2/run/admin/{runId}/adjust/{duration}" : { "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "postApiV2RunAdmin{runId}Adjust{duration}", "parameters" : [ { "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3085,6 +3279,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3094,6 +3289,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3103,6 +3299,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3116,14 +3313,15 @@ "security" : [ ] } }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", + "/api/v2/run/admin/{runId}/overview" : { + "get" : { + "tags" : [ "Competition Run Admin" ], + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2RunAdmin{runId}Overview", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", + "name" : "runId", + "in" : "path", + "description" : "The evaluation ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -3131,27 +3329,91 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } } } }, - "required" : false + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/run/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2Run{evaluationId}Hint{taskId}", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiHintContent" } } } }, - "400" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3160,7 +3422,18 @@ } } }, - "401" : { + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3174,15 +3447,16 @@ "security" : [ ] } }, - "/api/v2/log/result" : { + "/api/v2/run/{runId}/judge" : { "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2Run{runId}Judge", "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3193,7 +3467,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" + "$ref" : "#/components/schemas/ApiJudgement" } } }, @@ -3201,6 +3475,7 @@ }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3210,6 +3485,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3219,6 +3495,37 @@ } }, "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", "content" : { "application/json" : { "schema" : { @@ -3236,6 +3543,7 @@ "get" : { "tags" : [ "Evaluation Scores" ], "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluation{evaluationId}", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -3249,6 +3557,7 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3261,6 +3570,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3270,6 +3580,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3283,42 +3594,35 @@ "security" : [ ] } }, - "/api/v2/submit/{evaluationId}" : { - "post" : { - "tags" : [ "Batch Submission" ], - "summary" : "Endpoint to accept batch submissions in JSON format", + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluation{evaluationId}Current", "parameters" : [ { "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunResult" - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiScoreOverview" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3328,6 +3632,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3337,6 +3642,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3350,77 +3656,98 @@ "security" : [ ] } }, - "/api/v2/submit" : { + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluation{evaluationId}History{taskId}", "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, - "allowEmptyValue" : true, + "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { "type" : "string" } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluation{evaluationId}Series{scoreboard}", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, "deprecated" : false, - "allowEmptyValue" : true, + "allowEmptyValue" : false, "schema" : { "type" : "string" } }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3429,24 +3756,30 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } } } } }, - "202" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "400" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3455,7 +3788,8 @@ } } }, - "401" : { + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3463,8 +3797,44 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Competition Run Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluation{evaluationId}TeamGroupList", + "parameters" : [ { + "name" : "runId", + "in" : "path", + "description" : "ID of the competition run.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } }, - "404" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3473,7 +3843,8 @@ } } }, - "412" : { + "403" : { + "description" : "Forbidden", "content" : { "application/json" : { "schema" : { @@ -3481,23 +3852,13 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "parameters" : [ ], - "responses" : { - "200" : { + }, + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/CurrentTime" + "$ref" : "#/components/schemas/ErrorStatus" } } } @@ -3511,9 +3872,11 @@ "get" : { "tags" : [ "Status" ], "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", "parameters" : [ ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3523,6 +3886,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3536,45 +3900,19 @@ "security" : [ ] } }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/CurrentTime" } } } @@ -3584,11 +3922,74 @@ "security" : [ ] } }, - "/api/v2/logout" : { + "/api/v2/submit" : { "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV2Submit", "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { "name" : "session", "in" : "query", "description" : "Session Token", @@ -3601,54 +4002,37 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } }, - "400" : { + "202" : { + "description" : "Accepted", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } }, - "required" : false - }, - "responses" : { - "200" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ErrorStatus" } } } }, - "400" : { + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3657,7 +4041,8 @@ } } }, - "401" : { + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3666,7 +4051,8 @@ } } }, - "404" : { + "412" : { + "description" : "Precondition Failed", "content" : { "application/json" : { "schema" : { @@ -3680,14 +4066,15 @@ "security" : [ ] } }, - "/api/v2/template/{templateId}" : { - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", + "/api/v2/submit/{evaluationId}" : { + "post" : { + "tags" : [ "Batch Submission" ], + "summary" : "Endpoint to accept batch submissions in JSON format", + "operationId" : "postApiV2Submit{evaluationId}", "parameters" : [ { - "name" : "templateId", + "name" : "evaluationId", "in" : "path", - "description" : "The evaluation template ID.", + "description" : "The evaluation ID.", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3695,8 +4082,19 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunResult" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3706,6 +4104,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3715,6 +4114,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3724,6 +4124,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3735,32 +4136,37 @@ }, "deprecated" : false, "security" : [ ] - }, - "get" : { + } + }, + "/api/v2/template" : { + "post" : { "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3770,6 +4176,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3779,6 +4186,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3790,10 +4198,48 @@ }, "deprecated" : false, "security" : [ ] - }, - "patch" : { + } + }, + "/api/v2/template/list" : { + "get" : { "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2Template{templateId}", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -3805,18 +4251,9 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -3826,6 +4263,7 @@ } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3835,6 +4273,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3844,6 +4283,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3855,44 +4295,11 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/competition/{templateId}/task/list" : { + }, "get" : { "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2Template{templateId}", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -3906,18 +4313,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3927,6 +4333,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3936,6 +4343,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3947,12 +4355,11 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v2/competition/{templateId}/team/list" : { - "get" : { + }, + "patch" : { "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific competition.", + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2Template{templateId}", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -3964,20 +4371,29 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } }, "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -3987,6 +4403,7 @@ } }, "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -3996,6 +4413,7 @@ } }, "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4013,6 +4431,7 @@ "post" : { "tags" : [ "User" ], "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], "requestBody" : { "content" : { @@ -4026,6 +4445,7 @@ }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -4045,6 +4465,7 @@ } }, "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4060,9 +4481,11 @@ "get" : { "tags" : [ "User" ], "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -4072,6 +4495,7 @@ } }, "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4085,34 +4509,60 @@ "security" : [ ] } }, - "/api/v2/user/{userId}" : { - "delete" : { + "/api/v2/user/list" : { + "get" : { "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", + "name" : "session", + "in" : "query", + "description" : "Session Token", "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { - "type" : "number", - "format" : "int64" + "type" : "string" } } ], "responses" : { "200" : { + "description" : "OK", "content" : { - "application/json" : { + "text/plain" : { "schema" : { - "$ref" : "#/components/schemas/ApiUser" + "type" : "string" } } } }, - "404" : { - "description" : "If the user could not be found", + "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4120,8 +4570,34 @@ } } } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } }, "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4133,10 +4609,13 @@ }, "deprecated" : false, "security" : [ ] - }, - "patch" : { + } + }, + "/api/v2/user/{userId}" : { + "delete" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2User{userId}", "parameters" : [ { "name" : "userId", "in" : "path", @@ -4148,18 +4627,9 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { @@ -4168,16 +4638,8 @@ } } }, - "400" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { + "description" : "If the user could not be found", "content" : { "application/json" : { "schema" : { @@ -4187,6 +4649,7 @@ } }, "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4199,13 +4662,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2User{userId}", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -4213,59 +4677,29 @@ "type" : "string" } } ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" } } }, - "500" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } + "required" : false }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "parameters" : [ ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } + "$ref" : "#/components/schemas/ApiUser" } } } }, - "500" : { + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -4273,61 +4707,9 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "parameters" : [ ], - "responses" : { - "200" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } }, - "500" : { + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4335,30 +4717,9 @@ } } } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/info" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "The audit log info.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/AuditLogInfo" - } - } - } }, - "403" : { - "description" : "Whenever a non-admin user executes the call.", + "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4370,27 +4731,16 @@ }, "deprecated" : false, "security" : [ ] - } - }, - "/api/v2/run/{evaluationId}/hint/{taskId}" : { + }, "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2User{userId}", "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", + "name" : "userId", "in" : "path", - "description" : "The task ID.", - "required" : true, + "description" : "User's UID", + "required" : false, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4399,24 +4749,17 @@ } ], "responses" : { "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/ApiUser" } } } }, - "403" : { + "404" : { + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -4425,7 +4768,8 @@ } } }, - "404" : { + "500" : { + "description" : "Server Error", "content" : { "application/json" : { "schema" : { @@ -4442,22 +4786,34 @@ }, "components" : { "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, "ApiAuditLogEntry" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" }, "type" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + "$ref" : "#/components/schemas/ApiAuditLogType" }, "source" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] + "$ref" : "#/components/schemas/ApiAuditLogSource" }, "timestamp" : { - "type" : "number", + "type" : "integer", "format" : "int64" }, "competitionId" : { @@ -4481,17 +4837,36 @@ }, "required" : [ "id", "type", "source", "timestamp" ] }, - "ErrorStatus" : { + "ApiAuditLogSource" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "ApiAuditLogType" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + }, + "AuditLogInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "description" : { - "type" : "string" + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "description" ] + "required" : [ "timestamp", "size" ] }, "ApiMediaCollection" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" @@ -4508,17 +4883,9 @@ }, "required" : [ "id", "name" ] }, - "SuccessStatus" : { - "type" : "object", - "properties" : { - "description" : { - "type" : "string" - } - }, - "required" : [ "description" ] - }, "ApiMediaItem" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" @@ -4527,8 +4894,7 @@ "type" : "string" }, "type" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + "$ref" : "#/components/schemas/ApiMediaType" }, "collectionId" : { "type" : "string" @@ -4537,7 +4903,7 @@ "type" : "string" }, "durationMs" : { - "type" : "number", + "type" : "integer", "format" : "int64" }, "fps" : { @@ -4547,8 +4913,13 @@ }, "required" : [ "id", "name", "type", "collectionId", "location" ] }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, "ApiPopulatedMediaCollection" : { "type" : "object", + "additionalProperties" : false, "properties" : { "collection" : { "$ref" : "#/components/schemas/ApiMediaCollection" @@ -4562,37 +4933,77 @@ }, "required" : [ "collection", "items" ] }, - "RunProperties" : { + "ApiTemporalPoint" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" + "value" : { + "type" : "string" }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" } }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + "required" : [ "value", "unit" ] }, - "ApiEvaluationStartMessage" : { + "ApiTemporalRange" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "templateId" : { - "type" : "string" - }, + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, "name" : { "type" : "string" }, "type" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + "$ref" : "#/components/schemas/ApiEvaluationType" }, "properties" : { "$ref" : "#/components/schemas/RunProperties" @@ -4600,8 +5011,9 @@ }, "required" : [ "templateId", "name", "type", "properties" ] }, - "ApiEvaluationOverview" : { + "ApiEvaluationTemplate" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" @@ -4612,790 +5024,989 @@ "description" : { "type" : "string" }, - "taskCount" : { - "type" : "integer", - "format" : "int32" + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } }, - "teamCount" : { - "type" : "integer", - "format" : "int32" + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } } }, - "required" : [ "id", "name", "taskCount", "teamCount" ] + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] }, - "ApiTaskTemplateInfo" : { + "ApiHint" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "templateId" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { "type" : "string" }, - "name" : { + "path" : { "type" : "string" }, - "taskGroup" : { + "dataType" : { "type" : "string" }, - "taskType" : { + "mediaItem" : { "type" : "string" }, - "duration" : { - "type" : "number", - "format" : "int64" + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" } }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + "required" : [ "type" ] }, - "ApiSubmissionInfo" : { + "ApiHintContent" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "evaluationId" : { - "type" : "string" - }, "taskId" : { "type" : "string" }, - "submissions" : { + "sequence" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiSubmission" + "$ref" : "#/components/schemas/ApiContentElement" } + }, + "loop" : { + "type" : "boolean" } }, - "required" : [ "evaluationId", "taskId", "submissions" ] + "required" : [ "taskId", "sequence", "loop" ] }, - "ApiViewerInfo" : { + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "viewersId" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { "type" : "string" }, - "username" : { + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { "type" : "string" }, - "host" : { + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { "type" : "string" }, - "ready" : { - "type" : "boolean" + "type" : { + "type" : "string" } }, - "required" : [ "viewersId", "username", "host", "ready" ] + "required" : [ "name", "type" ] }, - "ApiSubmission" : { + "ApiTaskTemplate" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" }, - "teamId" : { + "name" : { "type" : "string" }, - "teamName" : { + "taskGroup" : { "type" : "string" }, - "memberId" : { + "taskType" : { "type" : "string" }, - "memberName" : { + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { "type" : "string" }, - "timestamp" : { - "type" : "number", - "format" : "int64" + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } }, - "verdicts" : { + "hints" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiVerdict" + "$ref" : "#/components/schemas/ApiHint" } } }, - "required" : [ "id", "teamId", "teamName", "memberId", "memberName", "timestamp", "verdicts" ] + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] }, - "ApiEvaluationInfo" : { + "ApiTaskType" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "id" : { - "type" : "string" - }, "name" : { "type" : "string" }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" + "duration" : { + "type" : "integer", + "format" : "int64" }, - "type" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } }, - "teams" : { + "submissionOptions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" + "$ref" : "#/components/schemas/ApiSubmissionOption" } }, - "tasks" : { + "taskOptions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" } } }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] }, - "ApiScoreOverview" : { + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { "type" : "object", + "additionalProperties" : false, "properties" : { + "teamId" : { + "type" : "string" + }, "name" : { "type" : "string" }, - "taskGroup" : { + "color" : { "type" : "string" }, - "scores" : { + "users" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiScore" + "$ref" : "#/components/schemas/ApiUser" } + }, + "logoData" : { + "type" : "string" } }, - "required" : [ "name", "scores" ] + "required" : [ "teamId", "name", "color", "users" ] }, - "ApiScoreSeries" : { + "ApiTeamGroup" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "team" : { + "id" : { "type" : "string" }, "name" : { "type" : "string" }, - "points" : { + "teams" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + "$ref" : "#/components/schemas/ApiTeam" } + }, + "aggregation" : { + "type" : "string" } }, - "required" : [ "team", "name", "points" ] + "required" : [ "id", "name", "teams", "aggregation" ] }, - "ApiTeamGroupValue" : { + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "answers" : { + "$ref" : "#/components/schemas/Sequence" + } + }, + "required" : [ "status", "answers" ] + }, + "ApiEvaluationInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { + "id" : { + "type" : "string" + }, "name" : { "type" : "string" }, - "value" : { - "type" : "number", - "format" : "double" + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } } }, - "required" : [ "name", "value" ] + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] }, "ApiEvaluationState" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" }, "runStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + "$ref" : "#/components/schemas/RunManagerStatus" }, "taskRunStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "status" ] + "$ref" : "#/components/schemas/ApiTaskStatus" }, "currentTask" : { "$ref" : "#/components/schemas/ApiTaskTemplateInfo" }, "timeLeft" : { - "type" : "number", + "type" : "integer", "format" : "int64" }, "timeElapsed" : { - "type" : "number", + "type" : "integer", "format" : "int64" } }, "required" : [ "id", "runStatus", "taskRunStatus", "timeLeft", "timeElapsed" ] }, - "ApiTargetContent" : { + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiSubmission" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "taskId" : { + "id" : { "type" : "string" }, - "sequence" : { + "teamId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "verdicts" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiContentElement" + "$ref" : "#/components/schemas/ApiAnswerSet" } } }, - "required" : [ "taskId", "sequence" ] + "required" : [ "id", "teamId", "teamName", "memberId", "memberName", "timestamp", "verdicts" ] }, - "ApiJudgementRequest" : { + "ApiSubmissionInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "token" : { + "evaluationId" : { "type" : "string" }, - "mediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + "taskId" : { + "type" : "string" }, - "validator" : { + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { "type" : "string" }, - "collection" : { + "name" : { "type" : "string" }, - "item" : { + "type" : { "type" : "string" }, - "taskDescription" : { + "group" : { "type" : "string" }, - "startTime" : { - "type" : "number", + "duration" : { + "type" : "integer", "format" : "int64" }, - "endTime" : { - "type" : "number", - "format" : "int64" - } - }, - "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "properties" : { - "validatorName" : { + "taskId" : { "type" : "string" }, - "pending" : { + "status" : { + "$ref" : "#/components/schemas/TaskStatus" + }, + "started" : { "type" : "integer", - "format" : "int32" + "format" : "int64" }, - "open" : { + "ended" : { "type" : "integer", - "format" : "int32" + "format" : "int64" } }, - "required" : [ "validatorName", "pending", "open" ] + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] }, - "ApiJudgement" : { + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] + }, + "ApiTaskTemplateInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "token" : { + "templateId" : { "type" : "string" }, - "validator" : { + "name" : { "type" : "string" }, - "verdict" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "token", "validator", "verdict" ] + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] }, - "ApiVote" : { + "ApiTeamInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/DbVerdictStatus" + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" } }, - "required" : [ "verdict" ] + "required" : [ "id", "name", "color" ] }, - "QueryEventLog" : { + "ApiTeamTaskOverview" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "timestamp" : { - "type" : "number", - "format" : "int64" + "teamId" : { + "type" : "string" }, - "events" : { + "tasks" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/QueryEvent" + "$ref" : "#/components/schemas/ApiTaskOverview" } - }, - "serverTimeStamp$backend" : { - "type" : "number", - "format" : "int64" } }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + "required" : [ "teamId", "tasks" ] }, - "QueryResultLog" : { + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiViewerInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "timestamp" : { - "type" : "number", - "format" : "int64" - }, - "sortType" : { + "viewersId" : { "type" : "string" }, - "resultSetAvailability" : { + "username" : { "type" : "string" }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } + "host" : { + "type" : "string" }, - "serverTimeStamp$backend" : { - "type" : "number", - "format" : "int64" + "ready" : { + "type" : "boolean" } }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + "required" : [ "viewersId", "username", "host", "ready" ] }, - "RunResult" : { + "ApiScore" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskResult" - } - }, - "timeStamp" : { - "type" : "integer", - "format" : "int32" + "teamId" : { + "type" : "string" }, - "serverTimeStamp$backend" : { + "score" : { "type" : "number", - "format" : "int64" + "format" : "double" } }, - "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] + "required" : [ "teamId", "score" ] }, - "SuccessfulSubmissionsStatus" : { + "ApiScoreOverview" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "submission" : { - "$ref" : "#/components/schemas/DbVerdictStatus" + "name" : { + "type" : "string" }, - "description" : { + "taskGroup" : { "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } } }, - "required" : [ "submission", "description" ] + "required" : [ "name", "scores" ] }, - "CurrentTime" : { + "ApiScoreSeries" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "timeStamp" : { - "type" : "number", + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", "format" : "int64" } }, - "required" : [ "timeStamp" ] + "required" : [ "score", "timestamp" ] }, - "DresInfo" : { + "ApiTeamGroupValue" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "version" : { + "name" : { "type" : "string" }, - "startTime" : { + "value" : { "type" : "number", - "format" : "int64" + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" }, - "uptime" : { - "type" : "number", - "format" : "int64" + "validator" : { + "type" : "string" }, - "os" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { "type" : "string" }, - "jvm" : { + "mediaType" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "validator" : { "type" : "string" }, - "args" : { + "collection" : { "type" : "string" }, - "cores" : { - "type" : "integer", - "format" : "int32" + "item" : { + "type" : "string" }, - "freeMemory" : { - "type" : "number", + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", "format" : "int64" }, - "totalMemory" : { - "type" : "number", + "endTime" : { + "type" : "integer", "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" }, - "load" : { - "type" : "number", - "format" : "double" + "pending" : { + "type" : "integer", + "format" : "int32" }, - "availableSeverThreads" : { + "open" : { "type" : "integer", "format" : "int32" } }, - "required" : [ "version", "startTime", "uptime" ] + "required" : [ "validatorName", "pending", "open" ] }, - "LoginRequest" : { + "ApiVote" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" + "verdict" : { + "$ref" : "#/components/schemas/DbVerdictStatus" } }, - "required" : [ "username", "password" ] + "required" : [ "verdict" ] }, - "ApiUser" : { + "ErrorStatus" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "id" : { - "type" : "string" + "status" : { + "type" : "boolean" }, - "username" : { + "description" : { "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" }, - "role" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "sessionId" : { + "description" : { "type" : "string" } }, - "required" : [ "id", "username", "role" ] + "required" : [ "status", "description" ] }, - "ApiCreateEvaluation" : { + "SuccessfulSubmissionsStatus" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "name" : { - "type" : "string" + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/DbVerdictStatus" }, "description" : { "type" : "string" } }, - "required" : [ "name", "description" ] + "required" : [ "status", "submission", "description" ] }, - "ApiTaskTemplate" : { + "ResultElement" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "id" : { + "item" : { "type" : "string" }, - "name" : { + "text" : { "type" : "string" }, - "taskGroup" : { + "startTimeCode" : { "type" : "string" }, - "taskType" : { + "endTimeCode" : { "type" : "string" }, - "duration" : { - "type" : "number", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" + "index" : { + "type" : "integer", + "format" : "int32" }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } + "rank" : { + "type" : "integer", + "format" : "int32" }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } + "weight" : { + "type" : "number", + "format" : "float" } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + } }, - "ApiTeam" : { + "RunResult" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "teamId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { + "tasks" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiUser" + "$ref" : "#/components/schemas/TaskResult" } }, - "logoData" : { - "type" : "string" + "timeStamp" : { + "type" : "integer", + "format" : "int32" + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "teamId", "name", "color", "users" ] + "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] }, - "ApiEvaluationTemplate" : { + "TaskResult" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "id" : { + "task" : { "type" : "string" }, - "name" : { + "resultName" : { "type" : "string" }, - "description" : { + "resultType" : { "type" : "string" }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { + "results" : { "type" : "array", "items" : { - "type" : "string" + "$ref" : "#/components/schemas/ResultElement" } } }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + "required" : [ "task", "resultName", "results" ] }, - "UserRequest" : { + "CurrentTime" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + "timeStamp" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "username" ] + "required" : [ "timeStamp" ] }, - "AuditLogInfo" : { + "DresInfo" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "timestamp" : { - "type" : "number", - "format" : "int64" + "version" : { + "type" : "string" }, - "size" : { + "startTime" : { "type" : "integer", - "format" : "int32" + "format" : "int64" }, - "latest" : { - "type" : "number", + "uptime" : { + "type" : "integer", "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, - "ApiHintContent" : { - "type" : "object", - "properties" : { - "taskId" : { + }, + "os" : { "type" : "string" }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } + "jvm" : { + "type" : "string" }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "properties" : { - "teamId" : { + "args" : { "type" : "string" }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "teamId", "tasks" ] + "required" : [ "version", "startTime", "uptime" ] }, - "ApiVerdict" : { + "ApiContentElement" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "type" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "status" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" }, - "text" : { + "content" : { "type" : "string" }, - "start" : { - "type" : "number", - "format" : "int64" - }, - "end" : { - "type" : "number", + "offset" : { + "type" : "integer", "format" : "int64" } }, - "required" : [ "type", "status" ] + "required" : [ "contentType", "offset" ] }, - "ApiTeamInfo" : { + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { "type" : "object", + "additionalProperties" : false, "properties" : { "id" : { "type" : "string" }, - "name" : { + "username" : { "type" : "string" }, - "color" : { + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { "type" : "string" } }, - "required" : [ "id", "name", "color" ] + "required" : [ "id", "username", "role" ] }, - "ApiScore" : { + "UserRequest" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "teamId" : { + "username" : { "type" : "string" }, - "score" : { - "type" : "number", - "format" : "double" + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" } }, - "required" : [ "teamId", "score" ] + "required" : [ "username" ] }, - "ApiScoreSeriesPoint" : { + "QueryEvent" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, "timestamp" : { - "type" : "number", + "type" : "integer", "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiContentElement" : { - "type" : "object", - "properties" : { - "contentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE", "mimeType", "base64" ] }, - "content" : { + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { "type" : "string" }, - "offset" : { - "type" : "number", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "DbVerdictStatus" : { - "type" : "object", - "properties" : { - "description" : { + "value" : { "type" : "string" } }, - "required" : [ "description" ] + "required" : [ "timestamp", "category", "type", "value" ] }, - "QueryEvent" : { + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { "type" : "object", + "additionalProperties" : false, "properties" : { "timestamp" : { - "type" : "number", + "type" : "integer", "format" : "int64" }, - "category" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "type" : { - "type" : "string" + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } }, - "value" : { - "type" : "string" + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "timestamp", "category", "type", "value" ] + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] }, "QueryResult" : { "type" : "object", + "additionalProperties" : false, "properties" : { "item" : { "type" : "string" @@ -5419,256 +6030,288 @@ }, "required" : [ "item" ] }, - "TaskResult" : { + "QueryResultLog" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "task" : { - "type" : "string" + "timestamp" : { + "type" : "integer", + "format" : "int64" }, - "resultName" : { + "sortType" : { "type" : "string" }, - "resultType" : { + "resultSetAvailability" : { "type" : "string" }, "results" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ResultElement" + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "task", "resultName", "results" ] + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, - "ApiTarget" : { + "RunProperties" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "type" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + "participantCanView" : { + "type" : "boolean" }, - "target" : { - "type" : "string" + "shuffleTasks" : { + "type" : "boolean" }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "type" ] + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, - "ApiHint" : { + "DbVerdictStatus" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "type" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + "entityId" : { + "$ref" : "#/components/schemas/EntityId" }, - "start" : { - "type" : "number", - "format" : "int64" + "xdId" : { + "type" : "string" }, - "end" : { - "type" : "number", - "format" : "int64" + "new" : { + "type" : "boolean" }, - "description" : { - "type" : "string" + "removed" : { + "type" : "boolean" }, - "path" : { + "entity" : { + "$ref" : "#/components/schemas/Entity" + }, + "name" : { "type" : "string" }, - "dataType" : { + "displayName" : { "type" : "string" }, - "mediaItem" : { + "description" : { "type" : "string" + } + }, + "required" : [ "entityId", "xdId", "new", "removed", "entity", "name", "displayName", "description" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "TaskStatus" : { + "type" : "string", + "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] + }, + "Comparable" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "ByteIterable" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "bytesUnsafe" : { + "type" : "string", + "format" : "binary" }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" + "length" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "type" ] + "required" : [ "length" ] }, - "ApiTaskType" : { + "Entity" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "name" : { + "store" : { + "$ref" : "#/components/schemas/EntityStore" + }, + "id" : { + "$ref" : "#/components/schemas/EntityId" + }, + "type" : { "type" : "string" }, - "duration" : { - "type" : "number", - "format" : "int64" + "rawProperty" : { + "$ref" : "#/components/schemas/ByteIterable" }, - "targetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + "property" : { + "$ref" : "#/components/schemas/Comparable" }, - "hintOptions" : { + "propertyNames" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiHintOption" + "type" : "string" } }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } + "blob" : { + "type" : "string", + "format" : "binary" }, - "taskOptions" : { + "blobSize" : { + "type" : "integer", + "format" : "int64" + }, + "blobString" : { + "type" : "string" + }, + "blobNames" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" + "type" : "string" } }, - "scoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS" ] + "link" : { + "$ref" : "#/components/schemas/Entity" }, - "configuration" : { - "type" : "object", - "additionalProperties" : { + "links" : { + "$ref" : "#/components/schemas/EntityIterable" + }, + "linkNames" : { + "type" : "array", + "items" : { "type" : "string" } } }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] + "required" : [ "store", "id", "type", "propertyNames", "blobSize", "blobNames", "links", "links", "linkNames" ] }, - "ApiTeamGroup" : { + "EntityId" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } + "typeId" : { + "type" : "integer", + "format" : "int32" }, - "aggregation" : { - "type" : "string" + "localId" : { + "type" : "integer", + "format" : "int64" } }, - "required" : [ "id", "name", "teams", "aggregation" ] + "required" : [ "typeId", "localId" ] }, - "ApiTaskOverview" : { + "EntityIterable" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" + "transaction" : { + "$ref" : "#/components/schemas/StoreTransaction" }, - "group" : { - "type" : "string" + "empty" : { + "type" : "boolean" }, - "duration" : { - "type" : "number", + "roughCount" : { + "type" : "integer", "format" : "int64" }, - "taskId" : { - "type" : "string" + "roughSize" : { + "type" : "integer", + "format" : "int64" }, - "status" : { - "type" : "string", - "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] + "first" : { + "$ref" : "#/components/schemas/Entity" }, - "started" : { - "type" : "number", - "format" : "int64" + "last" : { + "$ref" : "#/components/schemas/Entity" }, - "ended" : { - "type" : "number", - "format" : "int64" + "sortResult" : { + "type" : "boolean" } }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + "required" : [ "transaction", "empty", "roughCount", "roughSize", "sortResult" ] }, - "ResultElement" : { + "EntityStore" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "item" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "startTimeCode" : { + "name" : { "type" : "string" }, - "endTimeCode" : { + "location" : { "type" : "string" }, - "index" : { - "type" : "integer", - "format" : "int32" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - }, - "weight" : { - "type" : "number", - "format" : "float" - } - } - }, - "ApiTemporalRange" : { - "type" : "object", - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" + "currentTransaction" : { + "$ref" : "#/components/schemas/StoreTransaction" } }, - "required" : [ "start", "end" ] - }, - "ApiHintOption" : { - "type" : "object", - "properties" : { } + "required" : [ "name", "location" ] }, - "ApiSubmissionOption" : { + "QueryCancellingPolicy" : { "type" : "object", + "additionalProperties" : false, "properties" : { } }, - "ApiTaskOption" : { + "Sequence" : { "type" : "object", + "additionalProperties" : false, "properties" : { } }, - "ApiTemporalPoint" : { + "StoreTransaction" : { "type" : "object", + "additionalProperties" : false, "properties" : { - "value" : { - "type" : "string" + "store" : { + "$ref" : "#/components/schemas/EntityStore" }, - "unit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + "idempotent" : { + "type" : "boolean" + }, + "readonly" : { + "type" : "boolean" + }, + "finished" : { + "type" : "boolean" + }, + "snapshot" : { + "$ref" : "#/components/schemas/StoreTransaction" + }, + "entity" : { + "$ref" : "#/components/schemas/Entity" + }, + "entityTypes" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "all" : { + "$ref" : "#/components/schemas/EntityIterable" + }, + "singletonIterable" : { + "$ref" : "#/components/schemas/EntityIterable" + }, + "sequence" : { + "$ref" : "#/components/schemas/Sequence" + }, + "queryCancellingPolicy" : { + "$ref" : "#/components/schemas/QueryCancellingPolicy" } }, - "required" : [ "value", "unit" ] + "required" : [ "store", "idempotent", "readonly", "finished", "entity", "entityTypes", "all", "singletonIterable", "sequence", "sequence" ] } }, "securitySchemes" : { } diff --git a/gradle.properties b/gradle.properties index 67e1d1a96..d361cde29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,8 @@ # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xms64m -Xmx512m +systemProp.https.protocols=TLSv1.1,TLSv1.2,TLSv1.3 + # Dependency versions version_bcrypt=0.4 version_clikt=2.8.0 @@ -18,4 +20,4 @@ version_kotlin_csv=1.6.0 version_log4j=2.18.0 version_picnic=0.6.0 version_xodus=2.0.1 -version_xodus_dnq=2.0.0 \ No newline at end of file +version_xodus_dnq=2.0.0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 21931 zcmaI6V~n8R6E)b==Cp0wc2C>3ZQD=Vwry+L)3)8ywrx-EZ{KV-`6rwGaFa@IRdPR6 z)u~hG4$gort%Eht{y(MI%kt z0Y0nYm>z`rdM7Lh=##-Ps^6h>FU7m~cgyxqs;Nqi&~ytk^e7KkJL>mWt4%qL*DKv= zcgsip(fRo@w)aGHJ&cRiJs;2cc4v+b>Y#M1j_&4}9i`o^*Uzg;mkN44%!|HxGTNmY za%+!%)BkmU@yFRSA8-3+6za3Rpa>0d>aP z|6x$gEo6tjC%O4IHwK@zhTuzcDM38z%iFcrUhI%h?s07}F{H1l!3u%>r`EgBk|m$r z87XPla{FK=fulv&qhyZ!oAD=l1}cy0X;ZOYTNqV6ux_FyBqy_7sRMe%ATeaSNf3#n zOHbG+%dn12N=ywJWtQcx6Vgpi+L_Aqs+4YL0kAFnwH`6{_7&pk8r>@_Sny}{j|w^r zLwLjOoTacOZKW)xkrBEW;+RmJLgpQK^{Q}vgg3n+^)Vw+pd)tvl37o*JRsA1Kbtr& zZNxVRV*JxYrwfU#Eet%gT$cq^7wurj4!-w)gR+f|=z6GTNnLF}F% zyYZeGV{!;%ZnkOP%w9!_VmGqu&WcTF*+vHiL}YHYZUe^Y0{djWLG^Go2y*z_pek+h zHj7WjmG0S6)jN(4zViLQbm-Ap2>C=?GRqH?R0!u95VvshKy^ew)53}k#lg#Y2yl7= z9Z^hYIZKXs3L3Yx2)!c? z;Kx4g%hVUnY!fQi3^`@vHe?08(_)T6K)gL-8ySjtjFyR1&(8SX3+N<&Mq8sLxve~z zzAV>jq2O*jsJ1)7Jh{io`FJPg@INV_KcD>*0$9G~#NO;Zs0ssiX)cDYrr>NMg|ueU zfPDk!onCalx;;Tp;eLRfhYXEb1XXOHJi=Hm#W4zEmHU^dH4Ei4`GGr`xhV#r~yJKHLGIJQyU&h%j=sVb-S?Wx&QV9@(T$Y)QhJt|4A~U}c zcsipTok4DLxZY?S?pG@X8?#Ckt%hhQ1&vrL320UYq)O%UJCrVJv!fbvGdr`yl$m&x zS5(FPkgt?3(L*qab)6Sg=}c%%Y%)(%!F*F-G6WkAyTZ$e!jKnM7X{96lH!+Zr%Gfd zM(2EUxW0s_M%j|w@E{uY3MxRqqR3)CbX6%kIhGph!o-r&l93|=XRTYv+VqLZTkF-i z?fE=YV<+!qSV+KfdFjsVP^5?Eu0prF$I^oyAKFP<9;h#ke&W<_dyrcR8uFiq!x zuhJ99bAm~;x|HpTHl66_p*LNw9Qi3V$0SxTI3TJAeP#c{s6Nb{Mm=_45nKr550Q#fz5ZEAv3 z&}MY$SXbrSQo^%cWPCD?rZ{p@@<*u|3m=;L&#_yl7Vk063P=Z6w*+mu+Pn@-mE%zg z*494lJ#6X(B_T0_GG_X=_5=SB$MfqaW?waGXzxGQbFnJ4S^*~w^C?BdgJ+-}404_s z)3Wn{!Zfk1(~redky}&R+amHQ1;KF3%5HVz9e(^EOE=b`}a?DLEs3Sax>ZOkn5mBnnu@!WcUnC|gK1(OfE7 zsX#cWxT>bc58uUVCq}{>jyg5GLQ7Nd?m_(#Hwoh!(X&#FN6Ums z+X!9VKu|p&$PWHUVcZyZlZ(LQ$U0+)dM%22Jz$<=k}+dKOCVkyyd4pZ^mEUh(l`B0 zpGQ_y25>@_cx4a9At)&sq$s8015AA~>R zUU$W#q`Km>izXR~7{ccVrRaUbl7iw9))M>FlT{V=qXl~^w!|8Q4LU_qH$|rCr}AjM z6hhys6DdDXoI^jz06n4I=OXKkt(ls9_d&!CJ9)bUGiD6Ow3^nurrxGSLzsX8KQh0%pBpSH#o z13n-moFP;!N$rQ-Nmiv>O6(@FNamVg3GzYWmDy1(i4m0}BAsaMHv3IaiR>4iA;ao} zK9abGwb(uK%%foHY(9A=>qBL^Jf12)tAiZ!gJR>0Rr~S#_-Z12NH&0B#6gQBl zWQ;zxGLAIqD0!7n6U^faRR%Ou&|QPA<)E1Jf8~WVuZ)XoSRudGC>@D#)|#tm%e`^A zD|^v{R?0es6ZS$t+@F|HQHP#ygZW;&fj(N?02&8@Ad5sH-I%`x&V0)`?5dc z$Lf$17$pl=q%9=1=ezsFkQM!G2A9o#PEQ^ubCt-5tnSz@2?M(c9_qUD+7LRJ26h&O zDbX@|*wXEoN!X)mI~9Pn?!tn^nz|4aL2wU|&*siR=lIPWU*fNkYW17WB#g9!iNn zYOH@~;oBN9K5KCW6{|kjxAOKdMs4i?Wpm&uT zUeI-Jk&(sHChg*t(I|;1$f7jtDPb%s1~8H>9bE3;Q^nn$O31%{k&)IMbz#sd8Cz1r zJ`urAk}O!Y;U`%q)0cH{@J-xYs>B9rwpK7<)& zA>_DT9h=CRaxm?#(~p;~{;rj4vF~%g;^?d?c7waRU|MiUl>f8QFDT^pV>GcJ#&tel zmau7PXprj6y(4DX(MtH-)jA2XzO7x_BINY6e)0OR@QK9V?9-+$7J2`dZ1yFyH?17QneiwTs5?R_8i%vW~j=NRA|~l z8#tikYP7IcHabK&IMU>3qSZ6x9S9o?UF~Z^-(do;OX)qQ$%~iBq^AMNXyD5wKl5&GaljASzVc#d5k zH|hy+XO5cGPNcz*)gCfW5o5F|G}EU;QRK<%Y(#KwLJ|*S#ekc^<~ZDkCNgwKgTBY= ziow^LRQcL{88KBgo1Pw;PfcZ!R#-@fr?eMn$n|@5gxO))jZeSl+y~u2wHl%e2U;VP zK>v9->T0=a!zaW5#lElaJ_J~CzuM&+JX!*Nfak$AIiwNuou@|Hxb(XZr>-vq-CDc` ziO|wR)DPuqU2oh2e$04u>uO=w%ud0pIflJc@ao&8PD^{sRRsYqP3-Ux(<3gJC6#PVyV9(iQ_TQ!$e{hBmZO2(UQ!NxhwND4s;Ow|; z3-R$W;tCcAsNqqne}Ua-W{A%Zz~lferyX9)eKDan8SG4y{5K1Y*T1s&BDCF3Pgxh) zIUCZ4T2)A9a6M-SKHBZ~z;ropiAA0P)m+h=T{-$qG;*HYeko4rVON}>+!idY} zZrJjxxKf2mK5t@oPIB$!iB}s(?G^5mBVz($^;oa1I)x)Td-8I!TLly4_gw%OC#RyK zalPpfGkYha{D-|YYjjUr6`r!T?I`oOnTn;%XX|C5ul{pFtEtKw4KHM4GPTyztB?6*e#|DZjfe=Sum9vhKmO z$Zxmjc4~UFEs}yELZ4V~I3@Mc7BN|vpMyA$6lhvXtv+g)@DX}9nZc&|0mg@MaXm`!i_F2yX`JC@XG6LSZ&?M$YY5bV&)MojT z#knO+ciCJ-N0cu*shmA0+mLjnW+e*qfBakQvp}q%q`>gqsJEa6bR#?WasO%C)5YXW@Q{@!t7wW# z;0zvdiYtIe;8o*w7jSX;5r-U1f*GfDuO(2R zyLyRLsXP27^)WCI(P^a*3m9?BVMS64pc07M?apF!Js_cQ)r~4Z>Mx0#g!FbC76K)t zb;v($uR6dHN$<5+OZEy2EV@W_F;hsf&D^*ZEhYK0S<}qR4Tg|fTi7?6?S7;z57DqjGnsM|B?}GQBIoCMW z7;?d5??`t*A!6WjoNk?_mqaiMtA5sSX@8EFPdliC*X9&Xylp?`$h9#-OO+2+)lb|| zR>aONPcokH1$^~6y1s<8#sq!O=6qIBRGYRm09r~Vt!I_TW!BteYe6OZ zWCoC38)tV!!WkK2|wwdL1&H`i=xHN(_uu}LKRS@<(G zTd8F``wfkv0N$&;k)9`N9wo<_k#wmB?9$^$NVBpeqfx^4o`83?7GIq`vJ|o9xv~;v zulzdp0$Wz>)Ewd*iw?A(Ojg(roGxfEz7brudm#=-P=|Ru_1vx7TShCRESpT8ft|fM z&IZZzDiKEWp73Xo#PA3PhkmT8V%~nM3esoNpEj=$0Kdv$udywmW;Z$q|2=LeibNS9 zNh2Sh@+hs&=^usu9&bTONeG{)9;&_@w0+d~0KQU(Io6zELe1g)_TXN_eFxQBg#_6! zP<=7RZHj87LWe#4B&@Xbz6%@$@$dtga7L2FPa;m_n_IC3l-iGwPs1!746PLaeG|XSa2z)5oyChBbAXH(` z#ymUnCbE)px)k!1G9OLY7P?Z`!jRIrITY@Gp#pjspEFz6=d+evYSyV9cgu@^FFll6 zO`%dJ**Dp~cYZH8kwsndIEy1!iS-GT{QV3?HAb5gntpJ{{0V~#%01OxmT*qCvfCE9!iY`VAQPoJSa zxc-_-U5a*#O5Hlg&~Oar(r`b%4Uzggy!k0~TeYIhlfs{Q^$iAl5Cqx-aQv=681LtF zeB(0o>9PP9wV$4+2m%Uw55q5@^K{75%JXy&bJ^XSgUj8*Z0xYBRk|mI%eprtclAL9 z|G}E~saucYQ7VD{FlMA!HH6vk0ZiKN5fP0AD4P1=bVlUqQX0<4dJ#!$^;ed{v!fy_ z_FQKC=;gO%A^-7-Q6RTC-GDjDxD{9;Hu6Sr& z;c6VJ1j=5TN64w9G&f3K^_o~}o~nCT$rv%iF{V1I3Z*e+Wu63%Bvm)L4Q2$S=B^o9(5o=31ZCmFI26hH_lnT%Sij zZxhvc1kSK2Q!_)=MZbNl6DD@zQE`_^ZNzjNDNv}l{#Gef_il-QZ4*Ecs@ z)Es=MTB>Won(zlq=IUz8ySo0=BJy6I!?^>$Umjns&SBl%Aw{k-vC*`m@=jwjLvj+w};ZAuW=)mtkL)thl>Bur^tS>&^p| zLa=P6iy0#~hgSaf4lB-!Z9&(`%(1&`AXbeXin)F~wI^LGzlp;cn7{kQ->Ie`KJ=G@ zXF3u3r~8a-Yhcs^#50ezgowq#0jDviI|k)CMX-*8ScLW&Nk8@tAi z$rNWPlV~K$Wl6dSL*NBKYr7UjL`Yy#FD-{h8Xqm|iBlf4oK)i7aT<+W$P|*0XOcWg zg}JjQ*Y~X&A&M|s1N0vrmaj!8;(q*5gvDXu;CFE5K_lF>$?!{5BF*D)nFyW@bYhrr z?8|G(l+0%8E{r$sBtw~mpfLx68$YGUOA)cZ#!t~c+=_O~&^XZLX}cBnzF-N*m?bhW z6r84_Dn|s%1CV&ISf9Wkc*;XFXgurH6vQCQNsPplMin@d0s<_UI3YblR)ZRe(Rl6J z@>o`C?Bfw8Ogn2jCF|(bIcdWX7PV6@S*8-Xbi0Y-8Li;O8g+`ZaUOL-SuwMRX=%~pG&K}Nt^i-;;w$XXxT9f~ik@na#9S**V?%q1XKkR~1TAH`Gn)sW z8T!|PCry4k12-3mJtzO6;Z7pI+YWRKL1 zvn6Jr_zD>-IKpZDXyz?h>~kiiqa>poo`)02#(dW@!g)6hyHj*W+@p37|6qp$1R?%M z+m-X#{*e)`ysA9rjpSqenZ31Of5-FFFD7-BEZ#UnqS=6l(gyC4UxX`$@)u8kcB&MY zpIRB34Y8pjz$E_1bJ+gz5&oJ%URolAX?PBkNk|>AA zUpx(ej2n5m$4p#l?kH6=mn6-}4@}s9Zo>};duh{;=2RG0g`5(wIICnhk z>e`Em6)}esmor3=VM%xM0V6v{7Gf@VkyK12gT{Mh0f5yw+PP_h<9)E!0drt8Y7sZJ z{8!FtZ1k}go8}#;EvE>JxO?_eJ?1cs&yn2BHjx{2#+{I`LRn0}-(-Jr!BKL>eVGHy zH?+k)y9@8G;4KY^ca?o6d_TWzFqYp?ur5ACalDp7@%=N@CPAy`l%4uhXDCmkVoRuwW`eiU1-T9#$;JW!%sJ!iAd(r;~|&v;7N- zIt(-u{j#%&g6AwRP<&LR)ppGcu;$w7r6rE ze{o51d)#@ZoaH)N`(1|}_};kb(nj<0QF-7B7CDn*Zrb!!T%xyeVH+t4!?}nChz!o& zmfyr$chSoyIE}{oh6|bk;7X1`Rip^mfh1N%wI4n!j{E97Mdh8bU}e52wxfF76i}fr zahs_V2zs2@eeKrA1M(2lJ#D-w``*4%PmiUG)M7^t?}9$Mkr!1anwmyh$Zk>g{=-um z`I!{yH7U6ABvunQiG0+9Ee<#l+1Jey@pX!K`%*&Cui(+3I}TzV2`_pHyi@*=?tlw z_LI#vTmc&RDc+Lf-dqy-5I$%_JKcQ2Xgv)>E}+IgKv+MBz$=0ia#Lm{G@jzrnQN$^ zwYb&7-l=T!@GEKtq=Tdsd=-h?xCJV%t z?O6BZ3ykmCuL+_kCEQ%10ClmS--QwOWe*i`@W!2ie23*ar%3N@C`vGXIT&+xkCB_N zOe6VIxB%>d!bz-P@SO$Rh`^ny*bb$B^}SEm*Kn|k|D8MJ_g2z3!NOc`dQZf&Ou;1) zC-)tFedST-JF2R45T41QuQz(+!!@>h2UJe}PG@t9y(7nd8569|o?dHf4rOH?i#uR|Kz ztxD3B2t!Acp?rVky9Ez-ObfEF%3L z6q0(u>#9?VA)H;aCPuCHgb?!jqvhwglc6%nIj;-ES`w=&RcP$&+6UC%mCnwR#Pk(= z~5t&g-t+t)q!vByWOS{)4rfRPN zT`p<|CY=TwwAR^6EbRQsA$TXVaD+m`jGe!pqtX~~-NR8h({?ypXX%}+H@7_M%UVbZ zw>p8*PA!bSE^l(u=HKn|j9JO4x}Txvuc?1hPaSvAQd`5*=GFF|6)7>AaCyyxvJ5Q2 zwwc@wsnVXS>ZUwX=6u$`cadRTa&_JRC9C$H#p;^5$^d zmP;PcAhBJ2(`H?wm}%Qyjnfa~cui&QJXclmaw3jG+GiAef~OOR-Y$CyRPpUVdG^b< zn5>5gfI{*d$R%$$6=sT&>(7@DA?tYfWE|K*mWgnonT(v_JFEJw>4vc%&@d|Z>6 zU4)DHCboPb@iyZc#pVe;JRY*goSU4JK$e^^M&ic}KGnja+k-p%cm#76f@)puY|jJq zf1!0GK&K1sR|}Ou$9RnK22M|)RjE{n6t1Fq>lJdMd8-t*nS#Qi@*>Zpf_%&B(seLY zWCh$yD+#2ez~nRp8&G(`dcp%P@XG1IdbZb@VRMrT@rAIrbrbCDp^ko*%<+~6 zi#-bxmiuS=lf7M>z3d{<-n2)$K~&2O-SAyaJ)q?fDVxe5JfB|F2?jTvUDSulW3Ru1 zSg{bb5)+;KYFHFofH1452#Rmi{{6+1F%=LEL8OSaNi{=oxf`nf01^)(F!$>3W5tsH@u^~lV*;DZZoaakHO8(jIX({XKa@e>O3D(aiFK}K~J@kimbXW zzqy;AYVRH3%Ngi4w8DP7>s%t2#? z=@*SL*KE?Ni^FNW=jz6EjZ*_#>@+MCpK2tFPZ(Uin1$YOJ!!GlhS7Zx`-(x=KA`hZ2JoaSfvBcq7e&*PV;54ELwPxW6i#?a<}0rI&P|c_6#> z0J?DEi=U5t%FA$li_wym(CRhzkJ58P$XAm+Ji%Y z{siP1^4i9G@?Z_CuZRPtann_&5CL9Zk_G`?)Z~ zx|D-tw5#T51HE$G(wE1=V07Z0r!)iRpO)-?N@L&nw#7_WXY`v(}29T$ahFy zmvAXi1~lStMASz}dKF29ZYH&}-54Jf0jap@bG_rwJ4(4ju`^PHsQ&`zxGQ`)f84{} z{lUi5=aEM2k^$hFtBA*8lhM|Yl1ofblS4U1!N19YresAM7fl2IyXVr}B2$(K z0isiAW63z%2ZlZ+WH2nmm<@*Qhp@0r=H<_9DRYaJH7(Gmf_3e9?^W6-fyOB5#oHu`VzCQl>-(0PI^F1;JxV?^&v<&PM z_YcB(Hh4+i?*e0%BGLm!*uP?AxJX3AqcA1jE}n_>#~z|RPloxrL&8n?Hi=2&(kD&_ zCq3I;kgo?O-!AO2>-%Uk57k)oV|{`=5t4g2B32t~Rwq5dw#RrKs`~zTvZD5craROM zfjd+Sp*frwkwkoWe&DlgM|zB7nbYojaw6U&-fk0ZV**1T!LLF{gemheh_ff&NEHNG6?re5 zE!hQ@uBFx@mg8-)y!;i98(+$~&Ff(>?hF|xN%NA7FSSiQ13&Mi=f#LyvtF7TIB3n1 zv~>6E%LaJwr6L7YYsvsoy*7UlfQCWwikelG72}!`lvN!5hlQv@ofd6FXG$6a>sduu zziYOeibpH#rW!Aykr9$~ZBvhai;7Ea-3IOMO+yjd6ZuwWktowc>UYxH|Sadmx51HAxeMv0Tnm|}m(gh)Mbln2b{zSkuAS`w0sLO^8WQbtLhgVN51E6Z8T8!qu1`a*xHepGf@YOSQskKtF|4^{lc z6g!(T)awGGrcRXSu`l(BI7|J6rVcA}7SL&TR%1=6Am#Yu7)>RZeC1mr3Uu31Iam&p z=%89YJ}6Ea%TW#p{8QBiFsr20dg*>NcL1h_D(tj1#a@(Mr=Lxp))U%-s(yMUBS_)F z8%m&f*Jz6Bl@2lg;Lq#<9BfYnBlRmw56NCNY)@D-Y)_nnq^D><=UqjRgOT_^8@eyl z4my>Cd_`;VuFtCgb}9tOS)Ea+V8X2kgrIR9;Q=Lzf7PzVYe$gFYiN+cJ~Kr80iXfv zKm85_V;PmHu(E}(!2oDZlLFE~d3_G#pYr`TcTf<(P(IoxHbA_O(nluhrb$hzZK5qN zH_@%9u%!57nF~X%NQ>xid8O2(EomiS7XBU9OY4ck3QB8HyqeB}&tG>G;#@Npnu~Y1 z{D4kt#W)hvck~b>zPlbxH*dQDu+~PeyBl#UE@p0>IoiHsoGJ8Z+b5+mPp_3Wt?J`Twp^J(kgtWEUg zU@Q=~P`|zTCj_uVq4H*)TlS2IM_n>I%EJB2vTfzy;hkW&UDj`>1WIcnm*zw@MG(o^UXKjFoziK zr-;AV*z+u&+-kfggV-^JjjdqtLrTEw;Rha?lqCznp^7#YsWPjjEAs!;ll4?T|K-^l z`5lTF(z)NJv{HMK&sJbtA=zsgJt`q-8S>r1=gR$WBhu zSeij(|GTkhp^d^jC)To#fvquam7-^h@|Ez^kgw;M(Wxjj;ISk6K&q(PFGmu3SecSV zB=IQYOypf`9?L!3675Cyd+Y_9CBJ5P#}URR%c`$*$Ox%$$Pv}@TO{*+BK{`(d@8(` zN?8pDO@>}l_~jh{P)*+Q;eZVxEcZ7a-qN*T96 z!m9z0%&h2mz`Pt`(YK|gikrNd5v=Gki#!;w-WS?UL2+>xD)NNj?4Y+Ff2PSFEaZ(? z(PaxIlpRqj7(q;@mm-tAr-J3poqrI$#Sh+W2-|$KsXx^Ld3}GoDQadi=Z+EISc`_( z7-kdH4Ss>kS?NlNS}l2oYc#$le6!^piHzMyb$Jun+_9~+bd=9xSkfY<=8v$0qW&E) zScUc0hui=aleU(Uq7t(AUNJ=r1zoJb<@&di8FP5gcD`t7==Z1)9-A4=qF}e^gl9|3 zh=Na%m={V1bbHsfJ({J_L%=@#U1=06x$VUJRl#Qy9=|^?1Y>lvnNs6gTY7V}*q(Ra z=-;Q8!xxMY{OPuaW}gv=1SP6Vb~@U5Q z;IyB4U^zFW2DKf#d@fuf@|^jyto#NfZ$O|CG}xFF2pi(K#N2SI<_ZWV`5DVrIWW@D zPKfM;p>zk$UpZ?e%J)N$FH-4FAtv&HNl%tANOoO4F>EVj7d7OOUscaPX)EB*BRY}P zht!V10DB=%@R#--7v!0$q3)*4@89{J@sU1`aB3xp3X#^EQD9_8csP3FNA0ogXhknG zKb6T;fpx1uUxE)ZaPmj~*x^*pB)zxPk^ zTgB5@e~tJujkqFg@vmo-=@SjXuaV#!EF1}4Z~WqGKFC4-Xq6D})9S2e?Y*z3d3(Xb z?;roGbj=?ro%{UvvkV&&1mp+(|B#W_a26!_gt8x44f20oWbD~A_Lo$q?N+Ybz8w%Ezi;q(|TVDAOdAVy}4=@(+56vo@-nf@RhY9|MGO?2iIM7??M+w$9rO5?RMHP zI<0argWj#ZgHBbW`8;mrZ!t#xAS@-7G?{=M0hg*%4s6(gM(D{UuW>wamY(luLJeDo zU(2+@KpA`;&v*@5H2mA;nB#bqFP-}jau`iQFeXp7^#V1Rbu8Q^Ac$hauCj+Glf_CE zhkh2L$@V1xoor}`U6(U)fE<+~i2`0Wt38&NX9WvG+|_g+(thGL!l?7&YWljcAsc{a z{T{85t1^z;t^ohziCj_&mQ`AckAs?$%sU5DYKwp~4RS!a&rA1$@NQAsDa*`o(*R#L zw+XMlS7nUVG;S1vhbuhxj10<&yk;o*w~$Wy04+6tj6V0*TQreK|bEGaG()-~G@|3~0(kcH7Qcnm#8(deZ%}_8U|zSD*?YIXkpwZ#Q#anpCFxE0cP=v%qp`Uf~n73$FQKgwQa*=GnBJ3P^3Aem{`Gx8XD_h8>w@4P^*&|AR<((EzK7IVjsVh%5PwfFm?1tMq(bB3@cs{+? zSqepoQ@XrPyUszw%*nmif~e~{1*sB{>wXI_I9d`fgSyZWE_itoG9%Rr$2H6=R-)&B zo!X;-#ba;)=X#D&>;51CduH&dWF=5`7s?~{Mv}{TymjvyR`aiYB;E28CO4^xxIcZO zZc=oA(#>1KcAkEk*Ee)T!`c@;c^*q<-JDE$$L@E2xxR^dk$^EpvU(DAL#GiSx0O~l zZBcJ;yVhOlw462_i<>pOt=Z;9ucEZraV+0VVLZ}lt$iuVwW2o3RwuylX#A|sn$+~^ z%bv`La&z8}&_q>uNU zjYqgq2X-ALZ!6@Bx6c5JosAop6)U|*s$urxU_tI)o$5f#;GL#jhsh)0*e&i#Qf4`u zYe6Gut(;H+HcB~QNA0zf6u}hhF*ZuqWjeNe1GmpGP%?+6~^Q1(=M@HD6>0so@gf(D+tkS*u;+>=dbj#)G!&_r>+B3HLDnAwwa(Omv5X8dt$X-sX`r#?UAwKgGyENr$TS|v0ntr z3%R(pPfD*B9G#(Ip;;){#XObR=9p$G-LTP$H!p3 z>WQi?!Saw=)9jC<;^NbZ<`EA@o^t0k6Gu~pO}bMp+EV_hWcl>0dDXgHX#R-|m5juG z8dt76{{TU+$zueJj+Nr@$BJkV=rJ$KlRLhClFs22BY==wts(sB%cF|VrZ172!qTl)*mCILG&5ScqC!MqP^ z&5_tYxv&i&w$KeKQAB_nlXgDk7*bZE#XaJW5~)WT4SQf!YBr@KJ-h9k%Uz0^7;I#? ze&dd<2V2@W;P>NBjI-KqFn1;`=o0#=TIe1RT1-hoF(+r1wf1l_YjN%@mzvrA{*8Tk z?|mZEER+>Gs7r^zu!-s(2Dhrx0S~0@bmWh|Wh?u7lvB{jP62Q>j z2ujDr;JM1(u%32C~8>M4#zo4fPDe+_fW1mOIddls+ zT_8Ab1IaHPkNs%#9{WpSE*a7*^`{ISA^pn_;ak%(l1w0*1H2e3rK&zJ!aJ;btU$?kEyN z!Smm-V8MjP5^MsNx5b}Wi=Cu0|EVRY!fdoYb7q)SKr|uesr-9U|IYCZ)+e~?#Fh?F zY75&|O+^sn(P@X^p2cK83RBv+ph??z)k$If4AC{6tKKl(Wc+I*=2;RMc@w?0>m+q# zX;q8_r=?2{Hx@nT;{A$=^KWv5N%0nD2%evF8rb|fo9IdDN#R_9s$#z*iZP|A50h88 zEld_nLt0$0P&yC8AO63Y5fX)jyou646nT!ZeI7K%6n#Q)#f@>>HygWdGu%#3V8n{2XQ~Pn0mR&4E#9lH6HPtYid>W!TC0ZM5g~HZQM_` zKgPGLpEdnEspThzLf%@w!VYkw3;uNAc@pSO?Xb4sova4vZ&zFMT+$S?P2@5F{67L; z1l1mgTg2CJ$Ztsy!R0?Jrm*c~4i+=JgbxZKa|)$zYtV&Fsm1+_5#rrN3U^KU7HP3H zAPD|SY17`{B#H+HSf4WfQX`R9z-fC z?$6TVd{68eL>skuI_txu7mxG;%&%>qRU^HuuP>ia!QW%Rz#~I>58EsIzvlk>2V5#E zy-BLz?R`#!f6-X?^#5$c2TiOggTE;nKW-qugALeU^Np{ui(?y&M47qhIq^>9Bk{K} znm;ECJkE9?dj~zhV#5_cWnJQ%2!!8qGcn4=|8(5`6+CBc)u5QvPPKXmK71ul_Q~67 zrV3j+c#(IGe8uAkwU!H3{#&V#!l$WhvB){mN-d{{q}-Ur#x8VGPhGh~lscxH#i#fi zYrUObq*@hY9O-JxQoH;qf+mo!U@^yms{_w8D37Mys4VyriF8Gg_Anu^frgcL*XWnfEUweL~u5bn8kfVq?*vLX#2Z32+U1d*oBG zmoS&GjCn__|E7M~UiAoBzo_W0@x0&gIk5N&v|I#|k6O~%Sa7x(pvP}G{%E$~hi$^x zko{{M2&4SWo>_?%6+Fa)O4_{X;1TwRjCOY5vF)hhNP;nX_MPb*CDO@^p)&;z`GjC7 zQr^vO+>^{qXaA-cXgz$7UcM0h1nv0LKG3z20|g(AK$fqYS!$mvU;81N>w5ShfyazI zwUpiH3D0glv}bOeIXN7CA686SjZbrOD~LNEb7#o<`=J}C!;{hq@;>w`Qr zhttcG#!rW$*#B;w!i~IjKO=$f*jP^Z@O_HjRQ@WgrVMltXm=%==#H0#o3X6j^fQB{ zcw+Zajmzq2g2Q|66s|Me*CVx=5KvuKqP(}0#k`kwi8~)#hbN~Lkugr>9j2#WGcZ4r zU8=HNLE8y!u$TUv%t`DTd4dOyN##hiXM*1CS?56!8KIXy|MB&lYCyvTBewCd)? z^{eDgIg&4c1DI-JV=*IqJT20I5N9JuE5aY*I zSL94+g|7B7rlIsFe&j}t(iis1=}@E#gxAIrmL422+7g4lEZGu9FE|r6oWd_lK%^uu zgi>8$KrPQ3rH2pei%u^ZdCy$%tfbIDYfS-lr8x7i?j1<%=wL|#=k8T`kz$W)vWP%T zJlrb)X(eqV)`vM(UsXd;uy~>STKi}8y&fJ|pvevVo>^<4ZOmfbw^%GuJM-JYJXDn(akqmnI zI?^-evB0ojdl21Nyt(Kn`g#VWT0Kug(@+rA_T=O7l$g~I5BX#V*$hXK5nFfsY(3^k z4867`wv00Lz?ePly|(ejQ8!(Geq6Sz#1?yq7f)~rJb6_-I$3syzR6|`S+$+awBDiJ z68N$>I|&>_B|e1E5XG*m8r*Jc4&O;pM*kFs(%!mz`>t9|wh=v=v@F|becUK2;xKOG z0KHwK(M+H2+3@Z|NOabNnfWhtBBC1S3L{%hi;T8-pJHmt%n}qRDgvBzq;vYHShA5H z$oza4h0JvL+M4g?c8v}B%gE>0(~&8@tsQk3S9n-M_5M5AtQ6%?dCQP|js$!ad-1Lt zBe7U4(g&2Y=Q2WOzCF2j`f|1IC~4jcCyPsgr~$b_!q{cGFSKUOx;tXy(obLXDYp2^ zKGB^ZXOqSl+3W9ovcorkL)ZZY@=CQtDg5*E`3LgB;qoIFcIPu*>E?$WW6;dw^0ey$ z8TrOX4RW4XaVir15600Nvi7?#!YMW$mw@rMl)TSa(}Vb)ty>_?KZX?#A=7u9p3(kT zQn+M(FKZo^6BI-qOPjG{+6`FAVfqChS$V&h*1V39iJ`@ZvHRH5#i?J=B&h=7OP_KJ zY#COQCaX|o)aiDVD0MBBnzp8W?jv1L0GsvXQlTLTWrhq4b(LK(=SK9W(k|7=bA&PS zby!}GF4aBd^HsU3%*}iT2aB>wdR?aWN93Q)%^2`+l3&}vdGD%*u~xA7~wf zbFM2|GE2Rp;;ENeT75jfD^G&FXDG zA80I6%noM1g-b__+@-d{g@^oPt_|jL2&1@s^s3SgJXxPOAMt4?{bPFM00w;1MzNc` z?XLH{;0SrWI;oA8AL98jeN7#+hdqhE^aAu&uyMrE4+%WqlS+PsvYbv1=SO(I?7w~vsvxY)uO!%%mh zl)CJZmHh3nCAVUU%Kf5^EX;MvfzPRytsARpo2E}B(aj9)N2lisg4RbL$W2`kC?T&L zxj3g!NANFu5ggg6dit7$z~Gb1x!#uYz5?Nj8g?Cz6-}DIW*MlgRa?Fc{h^wl*O~2& z_fT5tl}oXUscMRtUNq#Kl9uCJHCX&!T9xiR=hga>!`i*3Q=9i&3CRHlXIVPvYHnwM zxeVvnN)qY~F)JYO1juO=3?WnWBe` zX%bGAnNPD;9I?jS63A}cMaSxq`}s+rIb z^FHcuNqkbh_S!;l=8!1H<$Qz+IW5GmsU=9Xt;Lc)Hm#YYC(?qG+aC<^tJF1~glZ*tQ#|r>l?#sHM;S!tGTyMR(s>Nj#UrXT(>ieW`m*&%*j{lx8@O1pKTd4Uf_+-z7>?UQ45?w@Lpi*~H zMp|L=Jp(spWIh*01@&_TaNX$26t>GquTA?&hIHNXqs~YXUST!lh2CpC%z_uJm{xw} zNp-q5ApoCud?vc@_&T@ROWxtZRd87zGkR3r7Jksr#1HokCa zvnr+uohrZJB4rOMSJr%MJqdX?AG%$4eVb>@HSuV%JJ%0GzFw5kb^A4occ`^>Xp%&Z zX-hpf&#LMNy+X+4Ku84>El2Hsu5D=2t?hafAGM@I>x*HReU@0&tC#!5!)lYTD+x@i zE$3#Yw1T8`yL{E>j}pZ`%<^MJ-$WTA_&o}g4%1$s zP_M3q#IAZ(sAWBfVv*V5VG^KhgTxR9ZsAVvUM}H;Ot5OQYKpGEcso(EqOdOu_R8q=02=iqdua^e7BsZZ7$3 zn0WcxTEgj~n~ICV{=-h3XJ4t>6ke@+(yrvR#+aw~e2n;85^1Awv7(qlzO5ve+LO&o zA&NFB$YdgSpN+xPKs_4I5N0TBZ#thD>bQ4MX1$beiH~j3|6;*LAPwtHmG9n2ZY_;b ztL6RVjp~vqJ+|kik%4RRevZWC`)HdnQ#Rz--<*1ZwkkkgBJ|pc&7n%}kbNy@00%lZt$rc@(#)b=QgS z?U+1i5Xyj!&v6XHh$vWMCw`fG;K__`&a&hPW%Cl1@@pPf96a7TYlB{!wf=`6B*Gh=*gzDFFtfdtf4WLP>oJX5p_eQf*?0(n;>0rKRn#jA1NPT5> zw(Ch}efF_Vsx!LN*Nfm)R-885lIF>3Dpl1luU&4&D&%ihW6_&T?XoTxYkAGMqQ>AA z%%f)2aYtXP^tsMx@^GNvf-h52=)8ng&*6X=C?gt8E5@G&7`oWFCTdgAE@#|`F0ZOy zxirfmw^2&xuG4bqTNQ@t;MDqc6V|*NX(BGZ=@OstuaXor(e-srr4OIkn!W#==VSxg zM~Rwtj7GbokS?#AMUFLA>f545ePkpxa$q~2afpihmhdb!&AteJ>^XxT`;Jbw7h(v8 zr=|AhA_EdSvOL2_8Tt1$mZ-Tt;Y-LZ-;zqR7*?3jRXsupI|N}k5=@%L6`RCwMRjVt zy8?Q{s%RK94{L8(0m(PBsb|aF$n3iz^kPkrZNH`xI>p1R`=bW?Vp@$x^k#N!_C>uJ z9vY4xvDg?HNk?Wd;WbXa5t}`QqB-l+PZ40EKk8nT!^s@hqlRmd)_;_Swf}LQ6?cD) zyC&>zAO-ArJ-rtrV-&ah1UoUK%7jTyZmTA-4>-ies$bk?`6)3ay<39CIifUPzb(Hc zV~T$DkRb82y?^{#jPcXjt8(50YUGs5zx3|Bshu%9 zy?IN-{DiMI7=vEUoc@^|HoG04uwl@}lIz@1i1W~4A#;iY-$Ln!rM90UOi%C9L6foO}LNLbz9U+ z-$y5XOq@7PF?aKd)Fb8TIyM!ApCd=64lFN(AXA^Xhauc)xCsKSJCBfD?)6hEz9Jw ziuf8c%$@U9+i-;76S>V@^z*!u=usm|h1)`-p*z<%{7bJon|hOt8K_2NIWU9_F4t`d z>do-au?Y=m4j9*=?Rbtwlc+(8u#teEN_z7`5`1#TRr>S@eIVlBFoyXs8 za9rA?@4V4Ux5xr}t28hjG_#k!4fa4@dPUy-%8 zWiHbW-K$AkK8L7{p?0DDUX3#@AM>yF41KG~(&0?QLEZ882t2Y_^UjhKyhAh6Arqos zXr<6`!FSV7JwkL4IQnT|PA`w&F@B!|!;qzApXUlNHe%|`y=l|rPuX;f^&XQhNl3*v zR^U7zhwqPbwl)HX9863P9x|eoRS>E4`6Q&Y%>v4rtO2^SS7#ezJo~9Oxot@G#b$( zO7ZR)wco%$_0EIGXlx608?o&z-n9*)X91h_Jn~?j059P z5S;x)Vw_L_dBcdI5P+VL0bSua32_tWLC5^RLka+VmjOUb!a?goC(=}N13Ln~_w$^vj{b2$-2abD`VaZ&vH>ZG zmFyn^(|?nX{{g#Q1x%wHU&lhZm)`tP?9a|8JCSsZBkd=`}GMGUP$JAOcf1RKyy z4f$n<{Y8!W&!)uZf`}3S-^O1M8F|=W=!d_IH-i5d;e%qZe|pMENCf^eI)wc;QUiAf z&w$#K|D+>>8lc*glk}q?Kaep30UU>*AlyTu#0@+g5+>FW6Tj_`PaHbKe6U}o#1=$$ zRWz}AUGT3>Ze*UAeiip*4s*i(UH3yQulO?xBFLWpF@75sg8fy5@yGV-AUXh{Bfql$0Ui%=p8x;= delta 20228 zcmV)9K*hh*+5^MR1F$OrldI+sv!!iA2nD4BB1t=w?R8Fn-BTM?6#w09HVexJQb;J2 zwt%)Z1WKw_w4|+VfoNzbl~53^I+x@&Y`g5@W&`~j`ruPv`l3%xUwr9|2-TVPO=tQS z`1PVQI-}!*ALGZ2Gmht8!bhio(=nNxd-t4s&hK|V_U6GqAKwG;4Bj#k#|!mn!3ik_ zrO22hPS)Xnl!?=Lu>lF3k(#q6&S9tl!x%A;HSm&&C|-`7nSuJ4$YE59^9IHYTre=s z5OKV6S@;YcdCxDW%RVnTBE97Eg$3cK^U9cEs4EFalzAW+j&65w*jsWPkC!g`UfCCw zO5Uyn!d0$&7ksg3d)3Ou8Q~X&8!)gO;h(f!J2=gMa6Y*UfyaXEnPLbJc_rf7l($`R zp*lY+{7F9Rkfu5B6}dCTeOo@)l;L2`t}t{Ciz~e91Up4$uyQV~Lk_Q01Ua1Ajn|?7 zh(@JJlxns@z=LXKXpXyOQDSIG=CATao_0l$zBG}`jE>5j3|=b901S-}n;D`-&!wP2 zUby9dV2&y~%3!Vsml30cP`ozA7it+NBv*I66}&78UY1jWdU6e`wOI9ivOL-|>AVJS zd+FTx$n~OF2yD+K7Hw47V%4E3dBjb{rFJ(2UcjAonz2oa>ngM0Rmmr7OP0~~IQG5tB7)^aJ|vBTnEa#V$ph32lSjAf7@}u^U7WSwm{qtIqY&UWXQswUCzHzlQ^ob3$K*Nwi~!@1h}u=~P0eD&9uZp#BM>Gwu2c z8t>mx-~&X^s-^J+>PY@fMg4^u^DB=xke(NrbKnJm~`@K z)tKx?dRdheQ#+ZIrjn|IHgL{efYm^G(G5|{Yl5%P%>!;7Qo+B>V*vx?>q zHd-H1(f;0{)yHdSaXhEcLd0BpK948WKQCQA$Ww;qzfem91PTBE2nYZG06_rJx=!pY z0{{TP1^@sw0F$BU9g|$>8Gn^k-BS`#6#rdB7uQ9JP*d|GH3L*o`_eR1G0H+EP}A&X zg&o|&U0RmZf2e2c0iB%bp{6f;=nrb9>D(0=L`RK>d(J)Qch3Etv*%t8{(k%fUHT13R$(*^aXr`KwP2FISW;9JPLTNdhRR}W>(T!9vWys0265KT8Ohz$+)B2{C z*5zdP$poVeO)15UQh)fSZX`>5s;)6~d3}*r@>@BmDQ56=5M^*?c-|v7FT#pR%UUWJ zHw{%w5y(LxQ%~q=hH4AHm{o|sGj7U>*RyiQs#U-=L#Ox5A_hl!2W?ve4DIIt8N|4r zGZIQz<$ZJ>xdNP@ga$N9c!);=9!r?P6A4cd5il!Z4)Y9+<$qO7<zVyx zaM3Kpls7pgOYnv53~yT5-dj2n$I^EnLsIj*FM?yJjK=1dR~ULOnzw`{eU-&ngiNKZ z$U-Qobk9)3$A7#yf}SJ%@gc3^Ez#(U^?OgcPev35f={=pADW0t32HlQDjUVKsoUl@ z)p?=ZlyvwM-~~fnY;Vnm^2KTNZ7r;ReF~iPdQ>W#4lLO8KZ&@dKc@#e-*It zdjy6nvlX7Hm;hL9D;9-6X--}j~&BVY%46YL69u9Lk=-;Ux z;fbbyP)h>@3IG5I2mk;8K>#EG$$sMx003AZ001EXlcDG%f2~>xcpJxcevbsPOK^EX z5+&$_WgQd`(2{kMmTZxtBuKnOkd!G|l2^czgau;Z#X=OFIF9YeiR~zMY$tJ?rf!?2 zZksrfoCuUf+e(kft=%^15%);j^hl4iO;6XolCb{_79c=Ew9~KpgxQ%lZ{BFl_{?3a(OokD-_p*s2p4=thZd+5vbk7D|t zMDx!o{fmcQq<>ZD-^BB6(fqq;-Vx1zc<4*?pC0-zfBJ9H{7*Sl|3IZ5dgwqdD=Vrf0R9D#O-KUw@nbM2YU|p^d9XwHPqQ3 z3ikGZt?M5BtlkpS?%&_pe<~C_h7ku# ziRy`|s;|HIK!0Z_bgJVZWS5GF!Mcv#o}SK*0cbci5bW;k9UM5-9qj4~hB`5`FNDP# ze`}b0{hfRF6=h&@$IQ`Dv5ys9rZw6!YUz=f(K2D_iG*Rbbje9rs$krsj~h%L^o9&8 z88zcfHHmrtXf7t_M(%@T_ifR5)ZW9?UcZ0^^Sw8pvT2CP)nP_pWOY|GZuF$aPaD>N zemZ6d|C?bwHl$loF?NV9dn}3|uUg1tf0$@4XxWdm-S@hUm0>eJ5*)YL?_?gYo=HC8>`XgH~*g|GL@~ z-mmZhg%2tmRQQm>hjYwPrqy!-f3s<>^H&uRLX&Y@KUZLLN{FdL^xE}gG(0ymHWdy0 zd?$$%@PumLagjPeGe9ayGqcu^;(^}6^jb4C3#%=9+HeZfASdJGON&8 znzuqCe3zU+$hw$n0T1C+Ot+1}oF~>6k5=KfrRU-j8`T7aPM8*U<1G*;%YkWeeNhP> zK^rpS5pi@>WCjkv*3M4lXl^r^f#PyAnNQqng;6w~keRZ=hA37*L>7qxLXJj{&;`*v zq0tBFL5&`wltvFzim7b@e-vByE@vPla<@hwqVpPkjGjOQ#%wzgNC@N-n^(9;<6gRo zVipt0*%_w5LVD+)tU^_v!bddj=a9w&JgD&yAJyntdP<{9^peJR@-Xl-TeR&GdW=YZ zX#+d*AuWGO$Ui2U;~L+^Cp5ZDX^q~XH{n-daI*}g#wYm{PRs>tf7keK)-^sYnlNK% z@QB8vJf?6|<9qmw#xY^{=NaZyL0sf2Ar6pm|ba)N154tRQV>5a2ItHD2^C;fQ~ z1H$Zt!uM)yaZ+QO5mr+8ti}_Z(D zPc-jC@%u+~I5X1ff44I-HGV(6DQvnQm7ZTk8h-#2{D5daD7^BZ=shHwhchca1m7-z zf`FA-Bl~eGKw;kGqW#hkzis*xx|KBiLML6P*O|&>{%L%kA7MIwbZ>u8u;+k(Fe!F) zaA2U%FEQ0$2&#VbtYP`}IGmj{!Z?!sv$!dgWX~->7Wogze{}FiP#RYBbV~39{CzP4 zh$@yPqj04^l~WiBphkr{(~92bK)5?&ghnsZRgFK)AJO+>V}n@KyK;ji2O?!+(PV`-)BtUS|e6A)l$Ol!y0!~rv3B>6KM ze?kC(1n9t72d*_|#x+vML(d0mY^ z$)5t86+xQdzT9nZ)j}Y;8TX-EvL)z19!{cScOGoN_;n#4jN*Ch`E}h@5dMKN%bdtu zOP3TqbeRtSzul_EWhOrxCqWmmioyUdmfDl@EML$~Qp)W9=e*E)l7{UZgNRt(wV;4c z%BU1-e{~DQjH_$1hyLsp+C6?I619@@B7Y2JWt-A}InL}|5`|Ge|LX3mFMeYcb5+=G zJU?*D=g2I$D0{K1e&gO0?)$Tj+F0biSNtud7Rw!Z&Ow6Pd3{jYAtmdP9K8x&Daf6r zd2T7ZT8n!m#3HtK_DukO3PQFe-y6#6kGG3qe@#KU$*D@|+IPbug4^(Nk2T?#nH29~VlocV&F|?(?;QMXbNHPL`9l1vojXh#7EY!d ze@sZnl_T(>@R%XMQbGTqnY1&#J^;AW(?ve0=p9KJ;+POszTeVE$K?$>@t%@*J|*~n zTPCb_qk!~Sa!x*E-E=HttVBK$)O^25Vq2y*3ZT(9pUrtyfs;h8IO5j7OCYlfgk!U> zS$7m!b9~;Kd@1u@+?L&F4$g?i&zfftf4^NtoN;{NG|Ii|35T^$+T#0LU9laC&j>5) zI~GbokrlJ=aqbb*8rSVPRu$R&4U@Z#Zlcw6jF?O+Cm-3ALjNogmCyt&r*kx!8{dcV z`|`%`$N2ud@dq$|pkVA3uVd(Y#T%J?KI}a4QiZ1nypPa_(S8J@K`J8`p5+aVf85kO zMSMw$c~ml%plul^|QWD^<}pT1?yFydAWj zc1oMJW+dlq+K{tpgWPV3>^&rHe-b@moeNaFS~}LGfQpir1;atKoT_s-~%O zn5U@f3RMf6N~KLzQcfGy&~92mw@Vwe%zDR$C-H-Z8sX-T(^JruadW9$S>2STnl#lO zZ4i6*&Tcj%xE;>!K!2YU?9VL8ZLXT0re~zGYWf6y5-UF?l`)+{|Jkgvf6}w$mY;N6 zxrbZJ8n4izG%ap*Pt%g&X{sBB;-yoxtjFh0ldsj)(CBkb(Q^2HMXTa-c~|N&)YpvDAg(yOZulm|0c)p6>f1fd2q}0NT-`|6J|t#8HBks@JAgD9L^Ovmdlb|=X&5zsHyx)i-9;mG z0(E~9wR{R`dNF?*f8|{CO66vW0$@K8-aBwBJw9(Pxlh3F!CDiwb>7p)V_RQdrC` zK+X)+FUZA`>*l%{7_SuN1NhBgj|G$DOtC^+BMM!d0k+f>V{ra}1}FVU1tIE zJOs+av=-PVe*#Icv;i-sA5eiXVMgL@x`B^PKZu+ zb_4xpPmMh}5ZjXju{|_}1S!GlopePa^po-gDft0ae;~4pbN<`}rkCkz#-AL6Qa5HU zz-@hLI?~4uRO}YG%wIOVjbzGM~#=hRI{Y zrH$UZ(sTk0$G=7=FJk50Vx?ZV(&yr0+^sGduG0cN5w8*iy^oF{`29G9AHXx?qXu|} zP);h!e_g0KS5dpje+h0P>eFboNIWJQ-=d9l>vl$mISo~}9exU)9em$2d6~sTJ zCTZ_UOuj*HI(B{=N_Pk9-3UWJ9zxM;nCOWmueg4b|J zT$6h{m@&xNn;QqhZ^+1K2*hv7y?Jp={FdCC56AyE&HbuE~%)* z`{ew8{VG0y530yuSj7k)Qt>cG6?{m+WfkjjMa5mX>c@xWhL8C1Q3W59pC6ZtpHT5h ze5wkc#%B~fA~`=RAxZumJ}-wasQ4njq~go?ih{4I*p9CW%<9YL_EsHf@W=4X#!XStb|ln30kc0mU*+yDdiEiXq)f z8T?q7uV*wKYiczU2|d{_jot0=5U4V0CQlPcZdhBqq32x6HWIsYqVfP*$F>neF^B9J z{YhT)q#hb=|>C#RYYaf;EG!wM5B5n>0NM+}GMIquWa$ilB z(tg&6rfrk_i@f*`6mm(ox1Ws~t~m<6&fw_%{l#t&xG7v1kiwaat?Ej0m0nQ9-cTIQ z+N?tPGNy$~*!*#3rPM8#5lO>t+PAlhYl3p-7Z7{SC2jp|&K~lF@)B*A*&5e>Q>ixN z_%<`0>~FU$$Ns53wjMpXQy+42Ucom6R)r^yYKf{_Cbj8CAyj+Jv=uen^qyIAzE((q zOal*yHuFp}ZtDFS_M%6_6OqKyU<)_jy!xmWme;hjvKfn(){0Ki*@DmM>;-}1_@k7+9 zrv@2B4L`%r75qZOFYzl4F+4@X5Kd`0fu}0?wT9o|w*qrK%<7WmI3DNWb{Ec2<(1N* zzbo|M82@hF9&Aaaj0CgBl6=3H!yg3dJ(#z$R;6rCq`#POu0emqp9Hjj{5+yb?#>nC zS;1d4{1t!G@OK&9f8d&if8rX;!=20vYmq=z!IppF-*Vq$3jU+var{@IAR)vQ zMU-j6C(0F3p$SF!nNK%3LG;vkPV7x5?O4LdEfQZ;YC@G-_>NO~O;ia@U~{XUOqzD6 z-=L8RhAyQh-sRr6#+%mX<|CktTZ=1;F_3$Yl@huiCJPcGg1T{>?Y?*o6oZ`SjJz&`R?PB&= zyC`j1Z+0all7ntrPM&3Kpc8jbSfp9SdeXwxi?n@hZAPy9FLbnjC zDQTgTYUhOp7xkEb(qMSs(`t)llXm!qz+MG)tSfo07L-p%fMPgS(DXxIY2zuvt=XPy zUM1I&bBpIyrq~6I9+1Uts*@QMmshhorsl+VrqaZ6Qd+lo9Nm~#=H^VgvGgvyB`ajv zrOP{(W*I|qUEUY06#3VOCly^U%=*b~rB`aksZOnRO{dX+Hp?{YMVslq02YoZpJGU@ zn0>CPm}kRS>Ao(9>mK=DaqmTZ>Xe|4uM%(e_10MVh!n|PC36=|w|GZ36c+Oc3z%&> zMZJhqUOQ*!JF9olGSA3+qvIVJzMkly;auB|Q)xX;2hGUmcl+6fhJ$3_(NE|M-0dFT zKjg8;D{?b`JoZXW+>VuG=E=tF95$*_Hefh)ztE&H3-g%?9Vn$zY1_=czMM>zq~g9) zQeWv8_MNdF;vC)e@VeQU!sU?%+y$K&|*pHhb z|Ca>tA&3ZeLSPqXQ&7cucivp%e0ScwhVwmn^J(yZ^P4wyj=iKb@mKJ-ym1&)E;%gw zI952s5cYG_Tm~G#6Zl(+J{%+$H;a3yR26AgM^F}7Is)HL4&}Q>QPDRHrP&wsW#B&$ z^p#&mWnWpKs;AEv(0VeMnnCqAxki$wN%DbF)N*H_xja}d_tph{jTuaDt{B0LW+kYQ zS}}@$nPi!j!R!ozL9Wbc_6PmTM=)1T<~3I?8^Qc$HK;a@VnJW9aukAN;HE%m7&nh% zVPDWcj9Z4WXcUVHv?PQ2akIB0z_FfQ4%5&ERAVV-VHxIQIo4nWImD3`!ktd+M)^ECOm|iygCqQ!LJ60MbQoon z^B{B_Bi9}z5k)^;ew17Wjx!tvoj!m;D3o;vua1Wq$Me+U1Wpp|0`-d{!EhuUIRYlX z`S!?0IZCW4(lR=76yd(cK*KN^N3fJW%#xPok;WZTO~rt1s6z*q(0pmsOcx3km4Neq zb<{CRl`p=mz_r=5s$%?>x&JN}CD)F;-&}tfXq})o|ZwoZ+mFJI~@AweO@=XYnL{&10&$ zt54=%Eqr?wta}WV3hoMZDHN*8M`zaHJ|_==1&x8K47S~i>2BmX>Byi{sy%`_F6qXy zyioU3tbsYqwQ+YYaB|QUS_UzPV)&xXidml(Q$339M6aQ!VeBZ5&dEHu>MWdKDod`X z{|}RcoA?AdY!u>?f1EFWSb2ODcNPEsvd1iw0n$)H7igPWY;!N+DulyALNRR;AR&YV zq@C;zn<29^>+CFndQfexN4@KndY@QDrPypj(Z>5gYrSvLR;@=p>mT-0MSX8(H`(1R zDKVeV{?2#(-uu4y`%TXM=b?uItinI$VFN5~lH9zI8=IRHH;#;d7NjK{krBd(grhQK zqZrQ96n<_;MNyi7(nUe3*(^Kchl!K1rnyb`Zsl2^-k4ekly zwM_cD5MIx+-XP7v3#nkgZ7I zJ>0xk!uvvae+VCc2;qYvd`LzUKFk{*VQD8Md{o9d+%MA!KPKVh5>86^gh0g+)mULz zQPmjGlQ-#xCa|F6uzEy|=vIX18wJXlCZ?yHHr*Cjl$+W5VA|0wv)4AJm`u%y^mexs z(`8H+wai0$JZ-B?Cs5mA+3`r+R%3=18L`!5QnMp{Uf-I3PfGmZVl_QO>Z-NtdeRAj zN>7=gn(;^v5twme2s%T0YQ;){<)yT=n<+;%45r(po4T__;I5k42n(H1YL+|eB_C?0 z)wO#C{H<1uyuPqQH?^*GVon^u~eHiVj7kjBgO&3T1q{nwH0GcajayAc0@A>k97D7 zPde=zkq)9I!BvH>JC@A3ueykKQ=vPy5byjRM~x1DcdAL3MZ%{foRaVWSvzHVO2TP@ z%X7|jBf4|&uoh+A^Lq5SsXA$!)NP$fkY@m8M>K8Qn(0JZ$%(A4ggtVPmA0dr=cHV0 znwX40v)zmuR*In1sX0SdOv0xXJcuy`+i{bEP1vkp3pdZhjS9A6n}SxfDcFGw$;wxy zU>v)D1eO#-bX!_CVw$aB0%sIFgtHXaCTm#1XL!B?pH=WMCKY^+o6qyw7w|;|U&5Ca zd<9>X@HGWr$2kSxz&9m4qTpM2RKd3~Dd9T`zKib(1e%hn?I`#@en3X$06@B{S>X>Q z{7Au%nd>L`sf3>?_&I)|;5>e*;8%D|!Q=RUwSwQU{@)@_m}%1t&0%)JA9>uekCC7! z@H+{=SMUe?QNfe=lY&3vFGO4dn1rZSD{aK8P0OiHo44!9YD%DL$D&R&352>eHD#GC zB=xU+;J@MT3ZBBGz|v{&b*D{7PiRv@*;jOgo$Tc0vn4BOFUE|(m9v6I;QC8U(Ol4f zv!#p5c40bD(V1RocQh(omYwsGYf+w;mR?*bp*Cu3s^jLaz=o2Qwq%W*QJ;J@TqNhm zHD{N~r}pwdqIs8^(2BEg`Zi$MCY6!Kni6Gq#!?pM#29icZ%N?Vno?!IxPF)GskR)@ zTyv>z1@)9?=R&e`>tM<<(vG%Eb%w})F={lbrRbtsNmo^T&R0<3G3HR2vuc}JZNqG8 z3px2TIo?&wTRN7dd5dG2hwPqXDMzEL+^5-g{spm%PUg`0G&PZD^=j64^s+2U%Y2Bgdv>#nm}fp7Li$wUtE$Q0&lN#`9dXoPh%6rnygx7k1wq^Gv*piR)4P{eeHmOfiuLswRF0yV76dPP8;d4zd1u1}7LOuCUDc`6SVH|38H9;`X`d&w z@;*rZ6Y%>s)7(FSWnIgEM=?CB3CpKUXz_>rSy5sFS7u2ouOfoR46Y`k4641&Ygl~P ze+JL-A?)|0UE7zlcmgY0+}-C2v;@L|Gq_G*6q|W;y`bl1s3lmWq=uA)gLF*KnyjL5 za00b`C;mH`l^n>RE`xg3M?czZ$ZnK*Y8y}Bww6GV=m?4QEM(z-l`FleFFS26P?*QI ziY+3AtEULUft(#a@<%-dgb(M2WT|T{jjJZGhT^fd&z+n)i*@}xx?&tROhSl*A|aCW zG4FRVbvLeY|A$e7)r9Xggr~LWp%43gG#ezm#|hsfg!1Er?>hbrt9D2ng*eA}TUp#>U@f7xp zf8v0+fLmQmAA(H!mti;d5Q0xqPW6&@Kgq7gUK#~S(th-jZ2?Ag7W*~garD!!z=*h) za%(^F9vAoE09owA)1neDz(JC79u2PK0BHjhqWAW0BTuM*w9*Xu0`^G=N$7qC3+CTQ zb~!@A-~}v}5S0*n2CauBH2n(){*4GxK_fOl2|84# z0ssIR1^@s7EtAm57L&m08k0_?4S(H2Eq)-Pd`nv@TMC*G#TZG9CPgnWP4I45hO)5z z$Zo6tBX79SM558eOE3IU#xt`^TS^TyP0!5Po%cEK*_quxe}DV}P{JbvapYxKPEaVw z@Ia<3I*M{!HIP6_#~OoC_4vLkUN&liVYGb2-*d}pST7t`Jf;f=+;Q8U*nbwj&#SZ| z6RdD~y=v{WJf~izReHFJ!F*NsTikWG4uyTJ(z@`rT<-hAXV}bMROiYKuWAJ*tPdV< zHic(}l!aaz)zP*Z`&4AC?9|2Uc5P31Z~309Ts3U&R=DTLJiMsa&P?lm+qNlT*vOvm zaG2`xCr;gIJ!P2hgA8b@LVspkhYnR7rh?)472!Dtj@W02W^?ZtQadefA8+$!*p$Il zCkv~^B10j2Ww>NTJ{G%xk_2oF0q8#(XP`9++8i2m{sbk@+ERUWGG)@(X|z3C$g<*>R4x3x}qZ!6SytILxyzM+nc>3VSl$6CjXC7n^eIJ zy-^8z@gm4b2Q+!Y*Y|8po*oNPhVgTE1|K=$8&ALnXkbp|Kex*epiboI=h7 zGECwQpk@-z)J!%Tp?@De`LN7$8s)uI{wuWK(6vv{q9=4A+T(Sx$7?DC-=lvFk>oR$ z9>FwK4R}__i;?ZvNng+7J)9V3C5Oawn7<$PzyHjLZ_>yfFz~w5=7j zt*u&W(N?Wp=z^`NBxowy+FDzeYHe$+yWJPNecFD0rA`0mzM07+c?kIX_=Wr4yZ794 z&++2nm2ybq=^o;rJQ$=P z&yca1(##6-Y(7((aFFNl+#ns`v!t1)adD8Q@O+_Ppm9lnOM`S5muXxcr0HA{q`SFN zdKSuCmAoy|cyW-z918LhUK*r2UM8Q*rCA}(%JFoJ&kpb^jjLsNb&%f6Yozm>0DrHQ z=ea?C2iF9+Rz~VX`gKBBAEY8)AK>%kxk2NGAg$z$8lNAeRag$4jnZtArb+m0mZ@6; z{7&iFs&TW%+XB2jz&oU4XOL?7UDC7!>3QCz@otUZEw2{@>3n`qkT&v#8ebHo&BA>n z8v$;Wk2YymYTO>A?QCk?5#&zpl7A)=q@B`pagcU%D8PH<8I@*bkYgMVa3aXve91Vr zI4LUG0Zz&DQW2;}1=#_tPKjGLr+zYu;vWYQbrN!y4<>$=RgJ?b-VT6Iw)nKYA3p?`Jt>ua_* zZo6<@L-V$+4Yk|1HEeFWa7)d$4NL`%7aNxvRZ%0}S=DS?k$C57rU`Wk;TN}e7}1m& z;C)Q~Xri;zw3uczCalh?PRnSInpHiP(cNuYRgG#8GXw33o_I82v@^|iBWzfg9+y?R z4ZEubBF0*y!g;RSge|!=n13|g>}`vtl95Zz^^vGq)7EAtlbejVp=7Ia<4}LX31H`6 z6NyLcwM_3Rc?-SXT9cEDUAlwGTbF1znI<(x;$~AS)@oYY3=E0~5^Y9whhatJJKgEE zyCU%1OxKkiUqkv}n`Iidxh|5lnO3=Ku+w?Mp&gOVlx5hFM0|CrqDpKcu4v00 zXDU5qR?w&&%UhAwlzeZuqD&JV_Hom$+P<{`B!#&o&0WTlesJ<>|P~)r6 z-8j0NY1v7wJa5b_tgOk(>mpWGs9~LTwfL?`w|v8vz=_!{(~=rr4Yy#hEfs}%a|E7S zGLlQFTl6r*G?5Yj%?v#y{Od}@I*4hW}8@9nT4(uHXn5K=9s#ZxNj&8P%wmqAS zZiO?AuhICU8hu&gk1akz%i1t?|c))l7%4 z5-TarU0yO)v6Cu}$kt+x)8GW7%}yCn1(k8hM9OM2RX~h4d%Mjx+iX`OfvAH?s2X<1 zQ?BYhK?_JH?SCFgs?j4@q&dGQj~-T_P4U;)p*TLH6`6zF7^STmQ1Pb1ybTG(AEWYEEB*! zW4BwL@Br=_{TeP##rH;_@tLl1mg@nZ8Mm#ztP_-hF|`U=tX@VWt-%A?93jDyVX`@= zUoxYxihl^QigK9M$5Sygo7z1}EN{Ch`-`?WlPZhGuI@mRKcVp_0oArdcVAAXVp>?@ zn!(&q2voHRCCab*OMba#mX34M=%R~zI4L2i& z>nhngDZ^;FFj{l^jB@L!46hX@=XKI-li{^ecz;!%4zFFqlh2mP?>vRcr<-Z>dY2Bb zvPxE2ecDLK4X5#GR*M&%wz`-dY*rcGiHS@FzEH??dW;^|>38&dogSbEb$Xdz(dm2i zuufOdM|AoSeORY{8qnz)z77kYR@Ew#uGi@*x>~0zX`jY7==>?(uk)w*MvXrs9|v^4 ziGOd_`Lld8YRI=h`(k1CIh}9eTcJX(hKTp(4K=R)i8_q2i z!V8L%3&QOQGZ~I2>@X@;+la)&M!XMX7Js(agru{D;rjGm8@3bS4rKDM*^6yC+817& zrR!UWDq~o<&8-)sTj#s^9-WVHzs>`E2h#;76e7KL5=$h)v9~9I&PVxPjqlTWkiW&W z@#Gqd>kLbnW_1s{%mU~8`It_hr`vUYfFIQP+b}72?U1r3(x!5IIMLxYHQZsqxqt0* zisQxc7J%E8CT8@7yNpZCaI0y?!?qFYmLeeB6S2D%7RS};z>003wonuc=N7nO)MwS<;(uguJd;qvQeDcB)1CEYTe?oHR$c*{aE&VV#ti8E z9ljCu%`m>Urs8%aW@hUU3A%?+6%1$J8p|^JBn9jIU3yXH@A1Pre_!4nfdlCUiHTrq zB%Y3AVekV~0Vk@UMxZ-$D)6;+#S$oaJS&$k*ZGHtHE?-U=f@b~`-A{~s(*WB#}sne zyqz(ff5cA;qo<_#@d%}|m7mT}i$%O*Pl>XhWXMKVa611~$Y#HF5vTFbbbf|^uJf~! zJB!BVn6wGX>Jq7FyNVo?xro6`og3~RE_A~k39C9R`R5lJKd1Ba;utNFTo^~ir|}Cq zzsN6X{Ibrk@T)ril7EHa9)G7k)cM!^8=Zg4ze7ptS`q2=Xa2p;e-KChk^hvj@R+hq z=hr0l{aM^RbF>pSkErLS<)-1>A+i5o#2tUt=^yk@o&UyP0t!!@{FxS$Lil8MXS(oou7Tdx zol3zdvDJAftKRShOAvI~>y<0scA+-XYNxE6dj;t?(kHYU*Rz(w1$I|}5eGNBst&>l zxJF#`IOPHq91jJR27mE3Xt>zr`k?eA*E?RItX=T6yS7wrj8fh0hAp)hIvmLP+Mto$EPObh9wIisL(->yE$6DA`{A|s>Sbu8vb6mwb_4A!K8QBZ# zdf>@17R&nCE$nL(2^%3`bZJiCtB`&si`naB{Bd$=wcfdk$_E6; zs1Zr7%ha~8r+?87M4ff=dqXTS8N$>V@kAW8Y1ENsYKhB*iHe4#SX#u*b=2_fkk(^F zY}6gt3{-69Wb&e%1U2#&b(;I-gsgYQ@KE}eOL_wmwT zXPSRXORmmva&~ChSmZ8ndvo?jsGNb-Dw{PXdXUx)$$yzOa%o)G&=7%U@8*sZV7fuw zLM9yyxn9eKN^-q5@LRR~~(k3gpfK?*(!Jp`KUL zKJ~ncuEz5W&|X6yMf)*)T@DUjJm-}S(73We3bquC&!H%ibQWZoN3*FIZ}aI|jDM6lJn0kkNh0+oGO>CSsq)mD$mK!r zb#y&?M4F=%Bn{8C<^42i6Pn3QW%tlTyyRDVL*9NWsP@U@jA}pnCxrZiG^M31J4&HV zgEYORe1K*&c~*GyC)2kA)xJV+-mNsVGUV&0nJc`7-dl$LS`qSj3ZdkzgG0Zn?5EiW zNw4>LtF@5UPiJ{= zqwyi%wzKrt9Hj(HZXp(JKmRFCHdN>LN_&`#>5R_dc}0JM+Z3qZafu2(UHJ^C?DS% zh50zm*QoAInlQqZ{WOq<<`8)LM1TG=c+l5Wc`q$sIzUqjO1$?|X^W?#`6!hgrSdy5 zQi05KD~2jZ4|(pTg?R*s3Yw2n)%QWPXcUnQEWT68AikJSB5oUP+Iyh$eA(j+J)m1)BHDwh8w4~ZwDIvP_CRz-%F56kK zTvG~`H@A4vv7))fSJ~VG)QZB@zCl{q67mhu*$*7f;?L1}KbE_Z#v|yaz|bMONug#b zo@WCywZO==DrEim4$`-wpMMY3E9pCUlk`)UwL={}q*A23Nx0^zx9_U3KCF@`{|gLr zHT)huoOOJjis6lev0a}B{nkHzyv{IaTY=skyg}&Qqjs)ToCkW3uKzc<; zyO-AHkrR9`R*ZJ;*TMe~41Na`{RT|~fENS8s~}n}-Zub*8Rsshe18=N8Yq|1_vsNR z0Lik7ZcIBofSKavysUGbp8L@w3YU{-2uV!K1jaFqetWgH+T~Uhs|qs@cR%gH+q%zn|(_JO%6E^@4i%9IjvAJV56; zd3*!%IC4HHS?ZXBRZ1JqQFyuKN>-U1u?`{|u6)q#hpD5Mo^bz&tH zXzr)xoyd=3;%!X_X{N(=2VSvL9Hn>lQ;T%$5)Xy3S7?K@8yw$Va6v!4M_`CYKV8^Q z7afK+g$S^#XuEpefF$C;a2HKQdmpSl2>acSo%0wd9s~rxO@GyZSuJ3-6EM06SlI(_ zTt+{@{3pTbQ3U8?@X3?l_Beq21on*|VPAL(!2L01{zQ4S8*tr;-RKF7N$?ee{wb{1 zMYBM48P;4%Tj^;~d$Hd6^i%p7d~*$GpP`?lZ$CWy3_YvB{!kVJ52>(5K?@B1LV>*y zsCq67_Ie5ghJUbbAmd~F)oE0#(eoN@)R0igYwr(ft6W5tfb#4KZpVo$n$TSsL^kE)|+6GC%+e^ON6JexJ1(RgVZI( zLBv7w#ecHH?2#QNf|n=*b}=Vr;s>Zml&g@B1k~%NZiK?qgLDbv$Z8oeHbBV%vQaIC zywa5l`3LAyiK(80G{K3ko{;vy!J$vdqP@}?P;a3C^0AjLz|L<$I*V-e48kW;Ozy)j zv@dJGCV-9TFBttBO{V`ru6`ZH`3H>mPdbPGmw(`OAHF!n)~vc$-)Y>}}q*=5a=J&T+;h(To%g=;-uHc;^UOW(@!EUT z32#VNR=r>WZ;MT_Bp~v-7-|(FV@#zG5kUJK^ILKwPAdM$MPB&Lg{X%fF7e%GE04SE z(253pWgqdTD{uQY97Jn8_ju9c)lbTEl|TF~`|Ye>eHa3r9%1p9{U5k8rw4 z%7>IAEzQq%_O3k*x%0+3sWW0FFndLv(0NItE89N*f;HBn`*m+9N1A$B#w77b)DEQq z#=l-jn&q4d^g87q9U?4!*vTW$6@ACy%$br>jZ8xfBrmU06r!r?ZhRvKMOv_pLOadO z_n^1jCqCxi?+O%q-pmdZtVUngG=1>1I z98J}DeJzyEz@S1b3CL?Q!EGE}52NGv=B8~^(oH$4DUK4q+2}V~d?iVLjyq?i{xUO_vQ`Ha|Ls*U!VDyUyAvz^5|9{!h`CfY)#R zBLrrqG-6#!tWf%GDH9o~MqiI*Ufng`_1w*SE77j-R@N*tRoQ#e{1#4SCL={2d#3y| zB~S86pv;o6Rb`v3hqs7riCb}ghq4QW<>C3qE**B#n$n|l)&nuMp9rRh!V;8nwdlmq zuRoT2XJ*FusKbT-Erl>Yv8aO%tl8GJOSkwjXS*U8`LmyZT4i z!tRe%rAcVOk-fA4^+8qHBgV}Z_P}}6hUvT}k2B@#VVMGUXM>dIJ*v2)hM~t;dWS^f zYTl0KqJ87HYi`AaTQ-YP^cnBF+`fqBX)h5$-|Ic!Fwd%d#x@l&6c z$Jd)TI=U_9YST~>k#dCdkjRavH_9cnt9^B=tHP#-TIY$nomT7n zWM21SH~d7R2mS=n0Ht|L^t3}&FQezZq;duZBVIZ)ayBtIWKd$k9CuZ}GsFB<@2nLU zW`@wj`+P{jMNQr{t1bO9Dx}3+QPKRALd7t7p|echE5~h7!?(ig)gZ=vwPC~hE}=Pc zEVpx1(v~R4J*cMoz1;F%lh72RYUc_UjkL2ZoQthTB2%R-K%j;0=9ktBB-svTl3n}` z{j;d0v{!;oC%^WrG~ig(?c6Q}clYVG*^0%xEb4C&p7(tYu(BaY6+eDNDDobG#;S=J zzE^$Z;a^-?w6`dPs)1)i zXKGa%yvl0lV$~kh5jn(Fv|?Qrt#0zPuupP25HMY^?CV-@rX6sKhF^*%h@uo`6fL_t zmjL^ku*iGkj*g!+-u7{jg)Ekn@Z@Z(2p|DxVw3b2BQIz8`F7qs?hPScuN~`lAl#rG zHSdm2kQ!ed?~updv~ruh_`q(W+og|aKsS)Sff@Y9i@l(!-?>d!AxbgiZE{(%s7Frq z|B#7n-zLOOcHp+9v0|ULwK1_6luk%XayjO$p2GE(7Y{FWCZ780 z7nFOeY08*)bh|9E>BZ=%@5%31uD}-k*7D^inva)qHc9m1@%OmfuU?u&l4sS?!)#ltHs>y+k+FktWBH z23`q4ou+ZMI8OIK0RGE+e;uQV2GOxS((C&2w7{m6h@z;VJ;>_l_vSJ6`Zb=o zq@X?689ur;(Hf_f&%$*_QQL!BFA}Rs{9P}NoazTIKX_5~@(Nt3#<7;2j8~#bm3f?d z)#_L!UD)TQmY^V2<1JGu(#MlH)GSA3tru8 z&Be$X4o>&T zFz3PIp>{{q{WoR7$tKC?)@qZYrFao}#%YAQYPpwi0kakb#FE;jHnofu#pH55J#njd zYc;rza}rAzn|*GrqmzI|;p%G=(?aQ=UET7(sc8r@5dYeHXf5y~pQ}h$ia6pbEaTet z#iPCli?~;}qOKRjpmjl5)oG)r6V? zHNE&;K}nn7)JfJl5puyZJ93N2P|kVy9~*~oHlJ62(mQOTXXxYnw<1(JvtqbPI7fs_ z<|nSUxS?(IS?CMOk%P~%ap=Plyu#}4V3b*($QC4I7ZY+g#=3>%CKQJizarNe+>m8h z*Huj0jALg{bye9M-dL*Fkww%5BRMLNfw1XqvS*nvQa@~kIGYPdWuH! zKL`r)AjOw|3fyoioKcN#aqr`OUSJxQ*u1=~fH^o3FdHtFIS;hMi!V`^7_2;4C5siKj4rKx+(MrSy?^zaADG@E+v>8vVHd z%Dg;Oh!!AraDyrw_%Y4~(1oxuf!@>uADzTNEoUPe!T@Z~vmvM@v<^Wa!(cR^`7Z^Y z@zDW{RB&}c5pl2_&MyideT3zo;y4nBgT+05hvL{l$QW8GNM)IqG@v<1Y7duE1pr;J zhv`djXrTnvM9G2!lS^!f1KWXqH3s&}UFsije|L}|unzi~D^g7Y%lwG_Y*S0Hh)*mB z1cE%^cUFlC1%S}+X(7@Fpni2K^kx}_`0w>RNC+H&a<%?xi5@_bWuZ}4RF$QHY5&I{ zsBte11hNk1ohTph6SoB;Ed#{C;OiHGkA~S(LotaJS!k>)*r+^^w1R?WdIHn$Wq?sK z8?e0sho<^~DguyQWdqKQvmpMPWne=>zZwGvY9$zuf{6f2qbR6R2#^Pp1T@A_(2Nkk zcT5%98UoyB;RU3b;J_GYH3q6HHKv4qlR5%JGO{1Wjg;do~nGG}N0Q_?v&=Dda;i \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,101 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd32c..53a6b238d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,91 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From ea08b5cbfb8393b736f67beeb5fe147265335ddd Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 3 Feb 2023 23:35:17 +0100 Subject: [PATCH 063/498] Fixing FE compilation errors module by module - auditlog: Semantic and logic mostly stable - collection: Semantic and logic very likely stable - competition: Semantic and logic highly unstable - judgement: Semantic and logic very likely stable (so far) Be aware that the primary goal is to get the FE compilation error free, not necessarily working (yet) --- .../admin-auditlog-overview.component.ts | 6 +- .../auditlog.datasource.ts | 4 +- .../media-item-builder-dialog.component.ts | 8 +- .../collection-list.component.ts | 8 +- .../collection-viewer.component.ts | 8 +- ...mpetition-builder-task-dialog.component.ts | 41 ++-- .../competition-form.builder.ts | 187 +++++++++--------- ...ompetition-builder-task-group.component.ts | 14 +- ...tion-builder-task-type-dialog.component.ts | 91 +++++---- ...mpetition-builder-team-dialog.component.ts | 2 +- .../competition-builder.component.html | 4 +- .../competition-builder.component.ts | 85 ++++---- .../competition-create-dialog.component.ts | 2 +- .../competition-list.component.ts | 45 +++-- .../competition-start-dialog.component.ts | 6 +- .../judgement-media-viewer.component.ts | 20 +- .../judgement/judgement-viewer.component.ts | 11 +- .../judgement-voting-viewer.component.ts | 10 +- frontend/src/app/model/user-group.model.ts | 5 +- 19 files changed, 284 insertions(+), 273 deletions(-) diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts index 3c1e09548..c60e63e24 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts +++ b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts @@ -50,8 +50,7 @@ export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy /* Initialize subscription for loading audit logs. */ this.pollingSub = timer(0, this.pollingFrequency) .pipe(switchMap((s) => this.logService - .apiV2AuditInfoGet())) - //.getApiV1AuditInfo())) + .getApiV2AuditInfo())) .subscribe((i: AuditLogInfo) => { this.length = i.size; if (this.paginator.pageIndex === 0) { @@ -82,6 +81,7 @@ export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy } public detailsOf(log: ApiAuditLogEntry): string { + // TODO new design probably asks for the description to be printed -- alone? switch (log.type) { case 'PREPARE_JUDGEMENT': return `Judgement preparation in competition ${log?.competitionId} | ${log?.description}.`; @@ -89,8 +89,6 @@ export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy return `Submission validation in competition ${log?.competitionId} for submission ${log?.submissionId} | ${log?.description}.`; case 'SUBMISSION_STATUS_OVERWRITE': return `Submission status overwrite in competition ${log?.competitionId} for submission ${log.submissionId} | ${log?.description}.`; - case 'type': - return `Log with tyhpe 'type'. This is potentially an erroneous state and should not happen (logId ${log.id}). | ${log?.description}.`; case 'COMPETITION_START': return `Competition ${log.competitionId} has been started by user ${log.userId} | ${log?.description}.`; case 'COMPETITION_END': diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts index dfaa1602c..4a8360892 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts +++ b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts @@ -40,8 +40,8 @@ export class AuditlogDatasource implements DataSource { refresh(pageIndex = 0, pageSize = 100) { this.loadingSubject.next(true); this.logService - //.getApiV1AuditLogListLimitWithLimitWithPage(pageSize, pageIndex) - .apiV2AuditLogListLimitLimitPageGet(pageSize, pageIndex) + //.getApiV2AuditLogListLimitWithLimitWithPage(pageSize, pageIndex) + .getApiV2AuditLogListLimitlimitpage(pageSize, pageIndex) .pipe( first(), catchError(() => of([])), diff --git a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts index bdfe98718..caaa65f96 100644 --- a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import {ApiMediaItem} from '../../../../../openapi'; +import {ApiMediaItem, ApiMediaType} from '../../../../../openapi'; export interface MediaItemBuilderData { item?: ApiMediaItem; @@ -16,7 +16,7 @@ export interface MediaItemBuilderData { export class MediaItemBuilderDialogComponent implements OnInit { form: FormGroup; - types = Object.values(ApiMediaItem.TypeEnum).sort((a, b) => a.localeCompare(b)); + types = Object.values(ApiMediaType).sort((a, b) => a.localeCompare(b)); constructor( public dialogRef: MatDialogRef, @@ -29,7 +29,7 @@ export class MediaItemBuilderDialogComponent implements OnInit { type: new FormControl({ value: data?.item?.type, disabled: this.isEditing() }, [Validators.required]), collectionId: new FormControl(data.collectionId), }); - if (data?.item?.type === ApiMediaItem.TypeEnum.VIDEO) { + if (data?.item?.type === ApiMediaType.VIDEO) { this.form.addControl('durationMs', new FormControl(data?.item?.durationMs, [Validators.required, Validators.min(1)])); this.form.addControl( 'fps', @@ -84,7 +84,7 @@ export class MediaItemBuilderDialogComponent implements OnInit { item.id = this.form.get('id').value; } /* only relevant for video */ - if (item.type === ApiMediaItem.TypeEnum.VIDEO) { + if (item.type === ApiMediaType.VIDEO) { item.durationMs = this.form.get('durationMs').value; item.fps = this.form.get('fps').value; } diff --git a/frontend/src/app/collection/collection-list/collection-list.component.ts b/frontend/src/app/collection/collection-list/collection-list.component.ts index bcec63c65..8f6025249 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.ts +++ b/frontend/src/app/collection/collection-list/collection-list.component.ts @@ -23,7 +23,7 @@ export class CollectionListComponent implements AfterViewInit { ) {} refresh() { - this.collectionService.apiV2CollectionListGet().subscribe( + this.collectionService.getApiV2CollectionList().subscribe( (results: ApiMediaCollection[]) => { this.collections = results; }, @@ -52,9 +52,9 @@ export class CollectionListComponent implements AfterViewInit { filter((r) => r != null), flatMap((r: ApiMediaCollection) => { if (id) { - return this.collectionService.apiV2CollectionPatch(r); + return this.collectionService.patchApiV2Collection(r); } else { - return this.collectionService.apiV2CollectionPost(r); + return this.collectionService.postApiV2Collection(r); } }) ) @@ -75,7 +75,7 @@ export class CollectionListComponent implements AfterViewInit { delete(id: string) { if (confirm(`Do you really want to delete collection with ID ${id}?`)) { - this.collectionService.apiV2CollectionCollectionIdDelete(id).subscribe( + this.collectionService.deleteApiV2CollectioncollectionId(id).subscribe( (r) => { this.refresh(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index c0d089403..6d90db1d2 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -72,7 +72,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { this.collection = this.refreshSubject.pipe( flatMap((s) => this.collectionId), switchMap((id) => - this.collectionService.apiV2CollectionCollectionIdGet(id).pipe( + this.collectionService.getApiV2CollectioncollectionId(id).pipe( retry(3), catchError((err, o) => { console.log(`[CollectionViewer.${id}] There was an error while loading the current collection ${err?.message}`); @@ -101,7 +101,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { delete(id: string) { if (confirm(`Do you really want to delete media item with ID ${id}?`)) { - this.collectionService.apiV2MediaItemMediaIdDelete(id).subscribe( + this.collectionService.deleteApiV2MediaItemmediaId(id).subscribe( (r) => { this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -138,9 +138,9 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { filter((r) => r != null), flatMap((r: ApiMediaItem) => { if (id) { - return this.collectionService.apiV2MediaitemPatch(r); + return this.collectionService.patchApiV2Mediaitem(r); } else { - return this.collectionService.apiV2MediaItemPost(r); + return this.collectionService.postApiV2MediaItem(r); } }) ) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index 46bb7cd46..66d9a6b23 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -1,17 +1,5 @@ import { Component, ElementRef, Inject, ViewChild } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog'; -import { - CollectionService, - ConfiguredOptionQueryComponentOption, - ConfiguredOptionTargetOption, - RestMediaCollection, - ApiMediaItem, - ApiTaskTemplate, - ApiTemporalPoint, - ApiTemporalRange, - ApiTaskGroup, - ApiTaskType, -} from '../../../../../openapi'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; import { filter, first } from 'rxjs/operators'; @@ -23,6 +11,16 @@ import { AdvancedBuilderDialogData, } from './advanced-builder-dialog/advanced-builder-dialog.component'; import { TimeUtilities } from '../../../utilities/time.utilities'; +import { + ApiMediaCollection, + ApiMediaItem, + ApiTargetOption, + ApiTaskGroup, + ApiTaskTemplate, + ApiTaskType, ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit, + CollectionService +} from '../../../../../openapi'; +import {ApiComponentOption} from '../../../../../openapi/model/apiComponentOption'; /** * Its expected that the taskGroup and taskType properties are correctly given @@ -43,7 +41,7 @@ export class CompetitionBuilderTaskDialogComponent { form: FormGroup; units = ['FRAME_NUMBER', 'SECONDS', 'MILLISECONDS', 'TIMECODE']; /** Data source for list of {@link MediaCollection}. Loaded upon construction of the dialog. */ - mediaCollectionSource: Observable; + mediaCollectionSource: Observable; /** The {@link CompetitionFormBuilder} used by this dialogue. */ builder: CompetitionFormBuilder; @ViewChild('videoPlayer', { static: false }) video: ElementRef; @@ -61,7 +59,7 @@ export class CompetitionBuilderTaskDialogComponent { ) { this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.data.task); this.form = this.builder.form; - this.mediaCollectionSource = this.collectionService.apiV2CollectionListGet(); + this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); } private static randInt(min: number, max: number): number { @@ -80,7 +78,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query target form component. */ - public addQueryTarget(targetType: ConfiguredOptionTargetOption.OptionEnum) { + public addQueryTarget(targetType: ApiTargetOption) { this.builder.addTargetForm(targetType); } @@ -96,7 +94,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query hint form component. */ - public addQueryComponent(componentType: ConfiguredOptionQueryComponentOption.OptionEnum, previous: number = null) { + public addQueryComponent(componentType: ApiComponentOption, previous: number = null) { this.builder.addComponentForm(componentType, previous); } @@ -150,7 +148,7 @@ export class CompetitionBuilderTaskDialogComponent { */ public pickRandomMediaItem(collectionId: string, target: FormControl) { this.collectionService - .apiV2CollectionCollectionIdRandomGet(collectionId) + .getApiV2CollectioncollectionIdRandom(collectionId) .pipe(first()) .subscribe((value) => { target.setValue(value); @@ -186,7 +184,7 @@ export class CompetitionBuilderTaskDialogComponent { */ let start = -1; let end = -1; - const unit = unitControl?.value ? (unitControl.value as ApiTemporalPoint.UnitEnum) : ApiTemporalPoint.UnitEnum.SECONDS; + const unit = unitControl?.value ? (unitControl.value as ApiTemporalUnit) : ApiTemporalUnit.SECONDS; if (startControl && startControl.value) { if (unitControl.value === 'TIMECODE') { start = TimeUtilities.timeCode2Milliseconds(startControl.value, mediaItem.fps) / 1000; @@ -224,7 +222,7 @@ export class CompetitionBuilderTaskDialogComponent { onRangeChange(range: ApiTemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { startControl?.setValue(range.start.value); endControl?.setValue(range.end.value); - unitControl?.setValue(ApiTemporalPoint.UnitEnum.SECONDS); + unitControl?.setValue(ApiTemporalUnit.SECONDS); console.log('Range updated'); } @@ -280,9 +278,10 @@ export class CompetitionBuilderTaskDialogComponent { .subscribe((r: Array) => { this.builder.removeTargetForm(0); const mediaCollectionId = this.builder.form.get('mediaCollection').value; - this.collectionService.apiV2CollectionCollectionIdResolvePost(mediaCollectionId, r).subscribe((items) => { + this.collectionService.postApiV2CollectioncollectionIdResolve(mediaCollectionId, r).subscribe((items) => { items.forEach((item) => { - const form = this.builder.addTargetForm(ConfiguredOptionTargetOption.OptionEnum.MULTIPLE_MEDIA_ITEMS); + // const form = this.builder.addTargetForm(ConfiguredOptionTargetOption.OptionEnum.MULTIPLE_MEDIA_ITEMS); + const form = this.builder.addTargetForm(ApiTargetOption.SINGLE_MEDIA_ITEM); // FIXME only to make compiler happy. obviously this is semantically not appropriate console.log(`Adding new mediaItem as target ${mediaCollectionId}/${item.name}`); form.get('mediaItem').setValue(item); }); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index a40a57028..e27e77e37 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -1,13 +1,19 @@ -import { - ApiMediaItem, - ApiTaskGroup, ApiTaskType, - CollectionService, -} from '../../../../../openapi'; import { AbstractControl, FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { filter, first, switchMap } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { RequireMatch } from './require-match'; import { TimeUtilities } from '../../../utilities/time.utilities'; +import { + ApiHint, + ApiHintOption, + ApiMediaItem, ApiTarget, + ApiTargetOption, + ApiTaskGroup, + ApiTaskTemplate, + ApiTaskType, ApiTemporalPoint, ApiTemporalRange, + CollectionService +} from '../../../../../openapi'; +import {ApiComponentOption} from '../../../../../openapi/model/apiComponentOption'; export class CompetitionFormBuilder { /** The default duration of a query hint. This is currently a hard-coded constant. */ @@ -25,13 +31,13 @@ export class CompetitionFormBuilder { * @param taskGroup The {@link TaskGroup} to create this {@link CompetitionFormBuilder} for. * @param taskType The {@link TaskType} to create this {@link CompetitionFormBuilder} for. * @param collectionService The {@link CollectionService} reference used to fetch data through the DRES API. - * @param data The {@link RestTaskDescription} to initialize the form with. + * @param data The {@link ApiTaskTemplate} to initialize the form with. */ constructor( private taskGroup: ApiTaskGroup, private taskType: ApiTaskType, private collectionService: CollectionService, - private data?: RestTaskDescription + private data?: ApiTaskTemplate ) { this.initializeForm(); } @@ -45,7 +51,7 @@ export class CompetitionFormBuilder { if (this?.data?.duration) { return this.data.duration; } else { - return this.taskType.taskDuration; + return this.taskType.duration; } } @@ -64,7 +70,7 @@ export class CompetitionFormBuilder { * @param type The {@link ConfiguredOptionQueryComponentType.OptionEnum} to add a {@link FormGroup} for. * @param afterIndex The {@link FormControl} to insert the new {@link FormControl} after. */ - public addComponentForm(type: ConfiguredOptionQueryComponentOption.OptionEnum, afterIndex: number = null) { + public addComponentForm(type: ApiHintOption, afterIndex: number = null) { const array = this.form.get('components') as FormArray; const newIndex = afterIndex ? afterIndex + 1 : array.length; let component = null; @@ -117,14 +123,15 @@ export class CompetitionFormBuilder { * * @param type The {@link TaskType.TargetTypeEnum} to add a {@link FormGroup} for. */ - public addTargetForm(type: ConfiguredOptionTargetOption.OptionEnum) { + public addTargetForm(type: ApiTargetOption) { const array = this.form.get('target') as FormArray; const newIndex = array.length; switch (type) { - case 'MULTIPLE_MEDIA_ITEMS': + // FIXME to make compiler happy. obviously this is semantically not appropriate + /*case 'MULTIPLE_MEDIA_ITEMS': const targetForm = this.singleMediaItemTargetForm(newIndex); array.push(targetForm); - return targetForm; + return targetForm;*/ case 'TEXT': const form = this.singleTextTargetForm(); array.push(form); @@ -159,16 +166,17 @@ export class CompetitionFormBuilder { } /** - * Assembles form data and returns a {@link RestTaskDescription}. + * Assembles form data and returns a {@link ApiTaskTemplate}. */ - public fetchFormData(): RestTaskDescription { + public fetchFormData(): ApiTaskTemplate { + // FIXME semantic check for the entire fetching const data = { name: this.form.get('name').value, taskGroup: this.taskGroup.name /* Cannot be edited! */, taskType: this.taskGroup.type /* Cannot be edited! */, duration: this.form.get('duration').value, - mediaCollectionId: this.form.get('mediaCollection').value, - components: (this.form.get('components') as FormArray).controls.map((c) => { + collectionId: this.form.get('mediaCollection').value, + hints: (this.form.get('components') as FormArray).controls.map((c) => { return { type: c.get('type').value, start: c.get('start').value, @@ -177,30 +185,28 @@ export class CompetitionFormBuilder { range: c.get('segment_start') && c.get('segment_end') ? ({ - start: { value: c.get('segment_start').value, unit: c.get('segment_time_unit').value } as RestTemporalPoint, - end: { value: c.get('segment_end').value, unit: c.get('segment_time_unit').value } as RestTemporalPoint, - } as RestTemporalRange) + start: { value: c.get('segment_start').value, unit: c.get('segment_time_unit').value } as ApiTemporalPoint, + end: { value: c.get('segment_end').value, unit: c.get('segment_time_unit').value } as ApiTemporalPoint, + } as ApiTemporalRange) : null, description: c.get('description') ? c.get('description').value : null, path: c.get('path') ? c.get('path').value : null, - } as RestTaskDescriptionComponent; + } as ApiHint; }), - target: { - type: this.taskType.targetType.option, - mediaItems: (this.form.get('target') as FormArray).controls.map((t) => { + targets: (this.form.get('target') as FormArray).controls.map((t) => { return { - mediaItem: t.get('mediaItem').value?.id || t.get('mediaItem').value, - temporalRange: + type: this.taskType.targetOption, + target: t.get('mediaItem').value?.id || t.get('mediaItem').value, + range: t.get('segment_start') && t.get('segment_start') ? ({ - start: { value: t.get('segment_start').value, unit: t.get('segment_time_unit').value } as RestTemporalPoint, - end: { value: t.get('segment_end').value, unit: t.get('segment_time_unit').value } as RestTemporalPoint, - } as RestTemporalRange) + start: { value: t.get('segment_start').value, unit: t.get('segment_time_unit').value } as ApiTemporalPoint, + end: { value: t.get('segment_end').value, unit: t.get('segment_time_unit').value } as ApiTemporalPoint, + } as ApiTemporalRange) : null, - } as RestTaskDescriptionTargetItem; - }), - } as RestTaskDescriptionTarget, - } as RestTaskDescription; + } as ApiTarget; + })as Array, + } as ApiTaskTemplate; /* Set ID of set. */ if (this.form.get('id').value) { @@ -238,7 +244,7 @@ export class CompetitionFormBuilder { id: new FormControl(this.data?.id), name: new FormControl(this.data?.name, [Validators.required]), duration: new FormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), - mediaCollection: new FormControl(this.data?.mediaCollectionId, [Validators.required]), + mediaCollection: new FormControl(this.data?.collectionId, [Validators.required]), }); this.form.addControl('target', this.formForTarget()); this.form.addControl('components', this.formForQueryComponents()); @@ -248,28 +254,28 @@ export class CompetitionFormBuilder { * Returns the target form for the given {TaskType} */ private formForTarget() { - switch (this.taskType.targetType.option) { + switch (this.taskType.targetOption) { case 'SINGLE_MEDIA_ITEM': - return new FormArray([this.singleMediaItemTargetForm(0, this.data?.target?.mediaItems[0])]); - case 'MULTIPLE_MEDIA_ITEMS': + return new FormArray([this.singleMediaItemTargetForm(0, this.data?.targets[0])]); // FIXME semantic check + // FIXME only compiler happiness, no semantic check + /*case 'MULTIPLE_MEDIA_ITEMS': const content: FormGroup[] = []; if (this.data?.target) { this.data?.target?.mediaItems.forEach((d, i) => content.push(this.singleMediaItemTargetForm(i, d))); } else { content.push(this.singleMediaItemTargetForm(0)); } - return new FormArray(content); + return new FormArray(content);*/ case 'SINGLE_MEDIA_SEGMENT': - return new FormArray([this.singleMediaSegmentTargetForm(this.data?.target?.mediaItems[0])]); + return new FormArray([this.singleMediaSegmentTargetForm(this.data?.targets[0])]); // FIXME semantic check case 'JUDGEMENT': return new FormArray([]); case 'VOTE': return new FormArray([]); case 'TEXT': const text: FormGroup[] = []; - if (this.data?.target) { - console.log(this.data?.target); - this.data?.target?.mediaItems.forEach((d, i) => text.push(this.singleTextTargetForm(d))); + if (this.data?.targets) { + this.data?.targets?.forEach((d, i) => text.push(this.singleTextTargetForm(d))); // FIXME semantic check } else { console.log('no target'); text.push(this.singleTextTargetForm()); @@ -284,7 +290,7 @@ export class CompetitionFormBuilder { * @param index Index of the FormControl * @param initialize The optional {RestTaskDescriptionTargetItem} containing the data to initialize the form with. */ - private singleMediaItemTargetForm(index: number, initialize?: RestTaskDescriptionTargetItem): FormGroup { + private singleMediaItemTargetForm(index: number, initialize?: ApiTarget): FormGroup { /* Prepare auto complete field. */ const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); this.dataSources.set( @@ -292,15 +298,15 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) + this.collectionService.getApiV2CollectioncollectionIdstartsWith(this.form.get('mediaCollection').value, s) ) ) ); /* Load media item from API. */ - if (initialize?.mediaItem && this.data?.mediaCollectionId) { + if (initialize?.target && this.data?.collectionId) { this.collectionService - .apiV2MediaItemMediaIdGet(initialize.mediaItem) + .getApiV2MediaItemmediaId(initialize?.target) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -315,7 +321,7 @@ export class CompetitionFormBuilder { * * @param initialize The optional {RestTaskDescriptionTargetItem} to initialize the form with. */ - private singleMediaSegmentTargetForm(initialize?: RestTaskDescriptionTargetItem) { + private singleMediaSegmentTargetForm(initialize?: ApiTarget) { /* Prepare auto complete field. */ const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); @@ -324,15 +330,15 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) + this.collectionService.getApiV2CollectioncollectionIdstartsWith(this.form.get('mediaCollection').value, s) ) ) ); /* Load media item from API. */ - if (initialize?.mediaItem && this.data.mediaCollectionId) { + if (initialize?.target && this.data.collectionId) { this.collectionService - .apiV2MediaItemMediaIdGet(initialize.mediaItem) + .getApiV2MediaItemmediaId(initialize.target) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -341,10 +347,10 @@ export class CompetitionFormBuilder { const formGroup = new FormGroup({ mediaItem: mediaItemFormControl, - segment_start: new FormControl(initialize?.temporalRange.start.value, [Validators.required]), - segment_end: new FormControl(initialize?.temporalRange.end.value, [Validators.required]), + segment_start: new FormControl(initialize?.range.start.value, [Validators.required]), + segment_end: new FormControl(initialize?.range.end.value, [Validators.required]), segment_time_unit: new FormControl( - initialize?.temporalRange.start.unit ? initialize?.temporalRange.start.unit : 'SECONDS', + initialize?.range.start.unit ? initialize?.range.start.unit : 'SECONDS', [Validators.required] ), }); @@ -361,12 +367,12 @@ export class CompetitionFormBuilder { return formGroup; } - private singleTextTargetForm(initialize?: RestTaskDescriptionTargetItem) { + private singleTextTargetForm(initialize?: ApiTarget) { const textFormControl = new FormControl(null, [Validators.required]); - console.log(initialize?.mediaItem); + console.log(initialize?.target); - textFormControl.setValue(initialize?.mediaItem); + textFormControl.setValue(initialize?.target); return new FormGroup({ mediaItem: textFormControl, @@ -379,24 +385,25 @@ export class CompetitionFormBuilder { private formForQueryComponents() { const array = []; if (this.data) { - for (const component of this.data.components) { - const index = this.data.components.indexOf(component); + for (const component of this.data.hints) { + const index = this.data.hints.indexOf(component); switch (component.type) { - case 'IMAGE_ITEM': + // FIXME handle external video / external image + case 'VIDEO': array.push(this.imageItemComponentForm(index, component)); break; - case 'VIDEO_ITEM_SEGMENT': + case 'IMAGE': array.push(this.videoItemComponentForm(index, component)); break; case 'TEXT': array.push(this.textItemComponentForm(index, component)); break; - case 'EXTERNAL_IMAGE': + /*case 'EXTERNAL_IMAGE': array.push(this.externalImageItemComponentForm(index, component)); break; case 'EXTERNAL_VIDEO': array.push(this.externalVideoItemComponentForm(index, component)); - break; + break;*/ } } } @@ -410,11 +417,11 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private imageItemComponentForm(index: number, initialize?: RestTaskDescriptionComponent): FormGroup { + private imageItemComponentForm(index: number, initialize?: ApiHint): FormGroup { const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); if ( !initialize?.mediaItem && - (this.taskType.targetType.option === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetType.option === 'SINGLE_MEDIA_ITEM') + (this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetOption === 'SINGLE_MEDIA_ITEM') ) { mediaItemFormControl.setValue((this.form.get('target') as FormArray).controls[0].get('mediaItem').value); } @@ -425,15 +432,15 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) + this.collectionService.getApiV2CollectioncollectionIdstartsWith(this.form.get('mediaCollection').value, s) ) ) ); /* Load media item from API. */ - if (initialize?.mediaItem && this.data?.mediaCollectionId) { + if (initialize?.mediaItem && this.data?.collectionId) { this.collectionService - .apiV2MediaItemMediaIdGet(initialize?.mediaItem) + .getApiV2MediaItemmediaId(initialize?.mediaItem) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -444,9 +451,9 @@ export class CompetitionFormBuilder { start: new FormControl(initialize?.start, [ Validators.required, Validators.min(0), - Validators.max(this.taskType.taskDuration), + Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.taskDuration)]), + end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), type: new FormControl('IMAGE_ITEM', [Validators.required]), mediaItem: mediaItemFormControl, }); @@ -459,12 +466,12 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private videoItemComponentForm(index: number, initialize?: RestTaskDescriptionComponent): FormGroup { + private videoItemComponentForm(index: number, initialize?: ApiHint): FormGroup { /* Initialize media item based on target. */ const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); if ( !initialize?.mediaItem && - (this.taskType.targetType.option === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetType.option === 'SINGLE_MEDIA_ITEM') + (this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetOption === 'SINGLE_MEDIA_ITEM') ) { mediaItemFormControl.setValue((this.form.get('target') as FormArray).controls[0].get('mediaItem').value); } @@ -475,15 +482,15 @@ export class CompetitionFormBuilder { mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => - this.collectionService.apiV2CollectionCollectionIdStartsWithGet(this.form.get('mediaCollection').value, s) + this.collectionService.getApiV2CollectioncollectionIdstartsWith(this.form.get('mediaCollection').value, s) ) ) ); /* Load media item from API. */ - if (initialize?.mediaItem && this.data?.mediaCollectionId) { + if (initialize?.mediaItem && this.data?.collectionId) { this.collectionService - .apiV2MediaItemMediaIdGet(initialize.mediaItem) + .getApiV2MediaItemmediaId(initialize.mediaItem) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s); @@ -495,12 +502,12 @@ export class CompetitionFormBuilder { start: new FormControl(initialize?.start, [ Validators.required, Validators.min(0), - Validators.max(this.taskType.taskDuration), + Validators.max(this.taskType.duration), ]), end: new FormControl(initialize?.end, [ Validators.required, Validators.min(0), - Validators.max(this.taskType.taskDuration), + Validators.max(this.taskType.duration), ]), type: new FormControl('VIDEO_ITEM_SEGMENT', [Validators.required]), mediaItem: mediaItemFormControl, @@ -515,20 +522,20 @@ export class CompetitionFormBuilder { /* Initialize start, end and time unit based on target. */ // fetch target time unit const targetTimeUnit = (this.form.get('target') as FormArray).controls[0].get('segment_time_unit').value; - if (targetTimeUnit && this.taskType.targetType.option === 'SINGLE_MEDIA_SEGMENT') { + if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { group.get('segment_time_unit').setValue(targetTimeUnit); } - if (!group.get('segment_start').value && this.taskType.targetType.option === 'SINGLE_MEDIA_SEGMENT') { + if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { group.get('segment_start').setValue((this.form.get('target') as FormArray).controls[0].get('segment_start').value); } - if (!group.get('segment_end').value && this.taskType.targetType.option === 'SINGLE_MEDIA_SEGMENT') { + if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { group.get('segment_end').setValue((this.form.get('target') as FormArray).controls[0].get('segment_end').value); } /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ - group.get('end').setValue(this.taskType.taskDuration); + group.get('end').setValue(this.taskType.duration); group .get('segment_start') @@ -549,14 +556,14 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private textItemComponentForm(index: number, initialize?: RestTaskDescriptionComponent): FormGroup { + private textItemComponentForm(index: number, initialize?: ApiHint): FormGroup { return new FormGroup({ start: new FormControl(initialize?.start, [ Validators.required, Validators.min(0), - Validators.max(this.taskType.taskDuration), + Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.taskDuration)]), + end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), type: new FormControl('TEXT', [Validators.required]), description: new FormControl(initialize?.description, [Validators.required]), }); @@ -568,7 +575,7 @@ export class CompetitionFormBuilder { * @param index The position of the new {@link FormGroup} (for data source). * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. */ - private externalImageItemComponentForm(index: number, initialize?: RestTaskDescriptionComponent): FormGroup { + private externalImageItemComponentForm(index: number, initialize?: ApiHint): FormGroup { /* Prepare form control. */ const pathFormControl = new FormControl(initialize?.path, [Validators.required]); @@ -577,7 +584,7 @@ export class CompetitionFormBuilder { `components.${index}.path`, pathFormControl.valueChanges.pipe( filter((s) => s.length >= 1), - switchMap((s) => this.collectionService.apiV2ExternalStartsWithGet(s)) + switchMap((s) => this.collectionService.getApiV2ExternalstartsWith(s)) ) ); @@ -585,9 +592,9 @@ export class CompetitionFormBuilder { start: new FormControl(initialize?.start, [ Validators.required, Validators.min(0), - Validators.max(this.taskType.taskDuration), + Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.taskDuration)]), + end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), type: new FormControl('EXTERNAL_IMAGE', [Validators.required]), path: pathFormControl, }); @@ -600,7 +607,7 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private externalVideoItemComponentForm(index: number, initialize?: RestTaskDescriptionComponent): FormGroup { + private externalVideoItemComponentForm(index: number, initialize?: ApiHint): FormGroup { /* Prepare form control. */ const pathFormControl = new FormControl(initialize?.path, [Validators.required]); @@ -609,7 +616,7 @@ export class CompetitionFormBuilder { `components.${index}.path`, pathFormControl.valueChanges.pipe( filter((s) => s.length >= 1), - switchMap((s) => this.collectionService.apiV2ExternalStartsWithGet(s)) + switchMap((s) => this.collectionService.getApiV2ExternalstartsWith(s)) ) ); @@ -617,9 +624,9 @@ export class CompetitionFormBuilder { start: new FormControl(initialize?.start, [ Validators.required, Validators.min(0), - Validators.max(this.taskType.taskDuration), + Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.taskDuration)]), + end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), type: new FormControl('EXTERNAL_VIDEO', [Validators.required]), path: pathFormControl, }); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts index 62e2b9bb4..62facaaa6 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts @@ -1,11 +1,11 @@ import { Component, Inject } from '@angular/core'; -import { TaskGroup, TaskType } from '../../../../../openapi'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormControl, FormGroup, Validators } from '@angular/forms'; +import {ApiTaskGroup, ApiTaskType} from '../../../../../openapi'; export interface CompetitionBuilderTaskGroupDialogData { - types: TaskType[]; - group?: TaskGroup; + types: ApiTaskType[]; + group?: ApiTaskGroup; } @Component({ @@ -14,7 +14,7 @@ export interface CompetitionBuilderTaskGroupDialogData { }) export class CompetitionBuilderTaskGroupDialogComponent { /** List of task types currently supported by the UI. */ - readonly supportedTaskTypes: TaskType[] = []; + readonly supportedTaskTypes: ApiTaskType[] = []; /** FromGroup for this dialog. */ form: FormGroup; @@ -36,8 +36,8 @@ export class CompetitionBuilderTaskGroupDialogComponent { fetchFormData() { return { name: this.form.get('name').value, - type: (this.form.get('type').value as TaskType).name, - } as TaskGroup; + type: (this.form.get('type').value as ApiTaskType).name, + } as ApiTaskGroup; } fileProvider = () => (this.fetchFormData()?.name ? this.fetchFormData().name : 'task-group-download.json'); @@ -45,7 +45,7 @@ export class CompetitionBuilderTaskGroupDialogComponent { downloadProvider = () => JSON.stringify(this.fetchFormData()); uploaded = (data: string) => { - const parsed = JSON.parse(data) as TaskGroup; + const parsed = JSON.parse(data) as ApiTaskGroup; this.data.group = parsed; this.init(); console.log('Loaded task group: ' + JSON.stringify(parsed)); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts index c3b7cf0e0..11ce27382 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts @@ -1,11 +1,8 @@ import { AfterViewInit, Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { - ApiTaskType - -} from '../../../../../openapi'; import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatCheckboxChange } from '@angular/material/checkbox'; +import {ApiHintOption, ApiScoreOption, ApiSubmissionOption, ApiTargetOption, ApiTaskOption, ApiTaskType} from '../../../../../openapi'; /** * Wrapper to be able to have an enum value boolean tuple @@ -28,22 +25,22 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV * Dynamically generated list of all target types. Since TargetType is an enum, values is required as this is the "underscore sensitive" * version. Object.keys() strips the underscores from the names. */ - targetTypes = Object.values(ConfiguredOptionTargetOption.OptionEnum).sort((a, b) => a.localeCompare(b)); // sorted alphabetically - componentTypes = Object.values(ConfiguredOptionQueryComponentOption.OptionEnum) + targetTypes = Object.values(ApiTargetOption).sort((a, b) => a.localeCompare(b)); // sorted alphabetically + componentTypes = Object.values(ApiHintOption) .sort((a, b) => a.localeCompare(b)) .map((v) => { - return { type: v, activated: false } as ActivatedType; + return { type: v, activated: false } as ActivatedType; }); - scoreTypes = Object.values(ConfiguredOptionScoringOption.OptionEnum).sort((a, b) => a.localeCompare(b)); - filterTypes = Object.values(ConfiguredOptionSubmissionFilterOption.OptionEnum) + scoreTypes = Object.values(ApiScoreOption).sort((a, b) => a.localeCompare(b)); + filterTypes = Object.values(ApiSubmissionOption) .sort((a, b) => a.localeCompare(b)) .map((v) => { - return { type: v, activated: false } as ActivatedType; + return { type: v, activated: false } as ActivatedType; }); - options = Object.values(ConfiguredOptionSimpleOption.OptionEnum) + options = Object.values(ApiTaskOption) .sort((a, b) => a.localeCompare(b)) .map((v) => { - return { type: v, activated: false } as ActivatedType; + return { type: v, activated: false } as ActivatedType; }); constructor( @@ -86,19 +83,19 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV // Loop over all enums this.componentTypes.forEach((ct) => { // if its in data, set to true to render it as checked - if (this.data?.components.find((p) => p.option === ct.type)) { + if (this.data?.hintOptions.find((p) => p === ct.type)) { ct.activated = true; } }); this.filterTypes.forEach((t) => { - if (this.data?.filter?.find((p) => p.option === t.type)) { + if (this.data?.submissionOptions?.find((p) => p === t.type)) { t.activated = true; } }); this.options.forEach((t) => { - if (this.data?.options?.find((p) => p.option === t.type)) { + if (this.data?.taskOptions?.find((p) => p === t.type)) { t.activated = true; } }); @@ -158,8 +155,14 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV } private init() { + + const parameters: Array<[string, string]> = []; /* Load all configuration parameters. */ - const parameters: Array<[string, string, string]> = []; + for (let configurationKey in this.data?.configuration) { + parameters.push([configurationKey, this?.data.configuration[configurationKey]]); + } + /* + --- Legacy. Keep to check validity if (this.data?.targetType?.parameters) { Object.keys(this.data?.targetType?.parameters).forEach((key) => { parameters.push([this.data.score.option, key, this.data.score.parameters[key]]); @@ -189,6 +192,7 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV parameters.push([domain.option, key, domain.parameters[key]]); }); }); + */ /* Prepare empty FormControl. */ this.form = new FormGroup({ @@ -196,31 +200,31 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV name: new FormControl(this.data?.name, [Validators.required, Validators.minLength(3)]), /* Default Duration. Required */ - defaultTaskDuration: new FormControl(this.data?.taskDuration, [Validators.required, Validators.min(1)]), + defaultTaskDuration: new FormControl(this.data?.duration, [Validators.required, Validators.min(1)]), /* Target Type. Required */ - target: new FormControl(this.data?.targetType?.option, [Validators.required]), + target: new FormControl(this.data?.targetOption, [Validators.required]), /* Components: Required, at least one */ - components: this.data?.components + components: this.data?.hintOptions ? new FormArray( - this.data?.components?.map((v) => new FormControl(v.option)), + this.data?.hintOptions?.map((v) => new FormControl(v)), [Validators.minLength(1)] ) : new FormArray([]), /* Scoring: Required */ - scoring: new FormControl(this.data?.score?.option, [Validators.required]), + scoring: new FormControl(this.data?.scoreOption, [Validators.required]), /* Submission Filters: Optional*/ - filters: this.data?.filter ? new FormArray(this.data.filter.map((v) => new FormControl(v.option))) : new FormArray([]), + filters: this.data?.submissionOptions ? new FormArray(this.data.submissionOptions.map((v) => new FormControl(v))) : new FormArray([]), /* Options: Optional */ - options: this.data?.options ? new FormArray(this.data.options.map((v) => new FormControl(v.option))) : new FormArray([]), + options: this.data?.taskOptions ? new FormArray(this.data.taskOptions.map((v) => new FormControl(v))) : new FormArray([]), /* Parameters: Optional */ parameters: new FormArray( - parameters.map((v) => new FormArray([new FormControl(v[0]), new FormControl(v[1]), new FormControl(v[2])])) + parameters.map((v) => new FormArray([new FormControl(v[0]), new FormControl(v[1])])) ), }); } @@ -231,25 +235,20 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV private fetchFromForm(): ApiTaskType { return { name: this.form.get('name').value, - taskDuration: this.form.get('defaultTaskDuration').value, - targetType: { - option: this.form.get('target').value, - parameters: this.fetchConfigurationParameters(this.form.get('scoring').value), - } as ConfiguredOptionTargetOption, - components: (this.form.get('components') as FormArray).controls.map((c) => { - return { option: c.value, parameters: this.fetchConfigurationParameters(c.value) }; - }) as Array, - score: { - option: this.form.get('scoring').value, - parameters: this.fetchConfigurationParameters(this.form.get('scoring').value), - } as ConfiguredOptionScoringOption, - filter: (this.form.get('filters') as FormArray).controls.map((c) => { - return { option: c.value, parameters: this.fetchConfigurationParameters(c.value) }; - }) as Array, - options: (this.form.get('options') as FormArray).controls.map((c) => { - return { option: c.value, parameters: this.fetchConfigurationParameters(c.value) }; - }) as Array, - } as TaskType; + duration: this.form.get('defaultTaskDuration').value, + targetOption: this.form.get('target').value as ApiTargetOption, + hintOptions: (this.form.get('components') as FormArray).controls.map((c) => { + return c.value as ApiHintOption; + }) as Array, + scoreOption: this.form.get('scoring').value as ApiScoreOption, + submissionOptions: (this.form.get('filters') as FormArray).controls.map((c) => { + return c.value as ApiSubmissionOption; + }) as Array, + taskOptions: (this.form.get('options') as FormArray).controls.map((c) => { + return c.value as ApiTaskOption; + }) as Array, + configuration: this.fetchConfigurationParameters() + } as ApiTaskType; } /** @@ -258,13 +257,11 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV * @param domain The domain to fetch the parameters for. * @private The object encoding the named paramters. */ - private fetchConfigurationParameters(domain: string): any { + private fetchConfigurationParameters(): any { const obj = {}; (this.form.get('parameters') as FormArray).controls.forEach((c) => { const cast = (c as FormArray).controls; - if (cast[0].value === domain) { - obj[cast[1].value] = cast[2].value; - } + obj[cast[0].value] = cast[1].value; }); return obj; } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index 811369147..5b8164099 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -56,7 +56,7 @@ export class CompetitionBuilderTeamDialogComponent { users: new FormControl(team?.users != null ? team.users : []), userInput: new FormControl(''), }); - this.availableUsers = this.userService.apiV2UserListGet() + this.availableUsers = this.userService.getApiV2UserList() //getApiV1UserList() .pipe( map((value) => { diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder.component.html index 417a7b36d..d9899eb64 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.html @@ -63,10 +63,10 @@

Task types - + + + @@ -59,8 +63,10 @@

grid_on + -

diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index 66d9a6b23..35b575761 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -12,6 +12,7 @@ import { } from './advanced-builder-dialog/advanced-builder-dialog.component'; import { TimeUtilities } from '../../../utilities/time.utilities'; import { + ApiHintOption, ApiMediaCollection, ApiMediaItem, ApiTargetOption, @@ -20,7 +21,6 @@ import { ApiTaskType, ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit, CollectionService } from '../../../../../openapi'; -import {ApiComponentOption} from '../../../../../openapi/model/apiComponentOption'; /** * Its expected that the taskGroup and taskType properties are correctly given @@ -78,7 +78,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query target form component. */ - public addQueryTarget(targetType: ApiTargetOption) { + public addQueryTarget(targetType: ApiTargetOption | 'MULTI' = 'MULTI') { this.builder.addTargetForm(targetType); } @@ -94,7 +94,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query hint form component. */ - public addQueryComponent(componentType: ApiComponentOption, previous: number = null) { + public addQueryComponent(componentType: ApiHintOption, previous: number = null) { this.builder.addComponentForm(componentType, previous); } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index e27e77e37..230d5250a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -13,7 +13,6 @@ import { ApiTaskType, ApiTemporalPoint, ApiTemporalRange, CollectionService } from '../../../../../openapi'; -import {ApiComponentOption} from '../../../../../openapi/model/apiComponentOption'; export class CompetitionFormBuilder { /** The default duration of a query hint. This is currently a hard-coded constant. */ @@ -123,15 +122,15 @@ export class CompetitionFormBuilder { * * @param type The {@link TaskType.TargetTypeEnum} to add a {@link FormGroup} for. */ - public addTargetForm(type: ApiTargetOption) { + public addTargetForm(type: ApiTargetOption | 'MULTI') { const array = this.form.get('target') as FormArray; const newIndex = array.length; switch (type) { // FIXME to make compiler happy. obviously this is semantically not appropriate - /*case 'MULTIPLE_MEDIA_ITEMS': + case 'MULTI': const targetForm = this.singleMediaItemTargetForm(newIndex); array.push(targetForm); - return targetForm;*/ + return targetForm; case 'TEXT': const form = this.singleTextTargetForm(); array.push(form); diff --git a/frontend/src/app/judgement/judgement-viewer.component.ts b/frontend/src/app/judgement/judgement-viewer.component.ts index 4c149dc59..be6732eed 100644 --- a/frontend/src/app/judgement/judgement-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-viewer.component.ts @@ -10,7 +10,6 @@ import {MatDialog} from '@angular/material/dialog'; import {JudgementDialogComponent} from './judgement-dialog/judgement-dialog.component'; import {JudgementDialogContent} from './judgement-dialog/judgement-dialog-content.model'; import {ApiJudgement, ApiJudgementRequest, ApiVerdictStatus, JudgementService} from '../../../openapi'; -import {ApiVerdict} from '../../../openapi/model/apiVerdict'; /** * This component subscribes to the websocket for submissions. @@ -224,7 +223,7 @@ export class JudgementViewerComponent implements AfterViewInit, OnDestroy { verdict: status, } as ApiJudgement; this.runId - .pipe(switchMap((runId) => this.judgementService.postApiV2RunrunIdJudge(runId, judgement)), + .pipe(switchMap((runId) => this.judgementService.postApiV2EvaluationevaluationIdJudge(runId, judgement)), catchError((err) => { const httperr = err as HttpErrorResponse; if (httperr) { diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index 4bf97a816..7a03ffa61 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -106,7 +106,7 @@ export class AbstractRunListComponent { } public nextTask(runId: string) { - this.runAdminService.postApiV2EvaluationAdminrunIdTaskNext(runId).subscribe( + this.runAdminService.postApiV2EvaluationAdminevaluationIdTaskNext(runId).subscribe( (r) => { this.update.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index cd3d57312..181686e88 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -60,7 +60,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { }); dialogRef.afterClosed().subscribe((result) => { if (result) { - this.runAdminService.postApiV2EvaluationAdminrunIdTerminate(runId).subscribe( + this.runAdminService.postApiV2EvaluationAdminevaluationIdTerminate(runId).subscribe( (r) => { this.update.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -112,7 +112,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { flatMap((t) => this.runService.getApiV2EvaluationInfoList()), map((runInfo) => runInfo.map((run) => - this.runAdminService.getApiV2RunAdminrunIdOverview(run.id).pipe( + this.runAdminService.getApiV2EvaluationAdminevaluationIdOverview(run.id).pipe( map((overview) => { return { id: run.id, diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index f6f222f28..2750d8ecc 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -68,7 +68,7 @@ export class RunAdminToolbarComponent implements OnInit { .subscribe((result) => { if (result) { const runId = this.runId.value; - this.runAdminService.postApiV2EvaluationAdminrunIdTerminate(runId).subscribe( + this.runAdminService.postApiV2EvaluationAdminevaluationIdTerminate(runId).subscribe( (r) => { this.update.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index d1d8bdbba..2721d31e0 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -138,7 +138,7 @@ export class RunAdminViewComponent { filter((q) => q != null) ), merge(timer(0, 1000), this.update).pipe( - switchMap((index) => this.runAdminService.getApiV2RunAdminrunIdOverview(runId)) + switchMap((index) => this.runAdminService.getApiV2EvaluationAdminevaluationIdOverview(runId)) ), ]) ), @@ -154,14 +154,14 @@ export class RunAdminViewComponent { this.teams = this.run.pipe( switchMap((runAndInfo) => { - return this.competitionService.getApiV2CompetitiontemplateIdTeamList(runAndInfo.info.templateId); + return this.competitionService.getApiV2TemplatetemplateIdTeamList(runAndInfo.info.templateId); }), shareReplay({ bufferSize: 1, refCount: true }) ); } public nextTask() { - this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminrunIdTaskNext(id))).subscribe( + this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminevaluationIdTaskNext(id))).subscribe( (r) => { this.update.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); @@ -238,7 +238,7 @@ export class RunAdminViewComponent { public adjustDuration(duration: number) { this.runId - .pipe(switchMap((id) => this.runAdminService.postApiV2RunAdminrunIdAdjustduration(id, duration))) + .pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminevaluationIdAdjustduration(id, duration))) .subscribe( (r) => { this.update.next(); diff --git a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts index 614fc154a..5f9d2c83a 100644 --- a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts +++ b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts @@ -65,7 +65,7 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { filter((q) => q != null) ), merge(timer(0, 1000), this.update).pipe( - switchMap((index) => this.runAdminService.getApiV2RunAdminrunIdOverview(runId)) + switchMap((index) => this.runAdminService.getApiV2EvaluationAdminevaluationIdOverview(runId)) ), ]) ), diff --git a/frontend/src/app/services/pipes/resolve-team.pipe.ts b/frontend/src/app/services/pipes/resolve-team.pipe.ts index 9766fc30a..cfa573967 100644 --- a/frontend/src/app/services/pipes/resolve-team.pipe.ts +++ b/frontend/src/app/services/pipes/resolve-team.pipe.ts @@ -1,15 +1,15 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { ApiTeam } from '../../../../openapi'; +import {ApiTeam, ApiTeamInfo} from '../../../../openapi'; @Pipe({ name: 'resolveTeam', }) export class ResolveTeamPipe implements PipeTransform { - transform(teamId: string, teams: ApiTeam[]): ApiTeam | null { + transform(teamId: string, teams: ApiTeamInfo[]): ApiTeamInfo | null { if (!teamId || !teams) { return null; } - const filtered = teams.filter((t) => t.uid === teamId); + const filtered = teams.filter((t) => t.id === teamId); return filtered.length > 0 ? filtered[0] : null; } } diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index f06c0977a..4c1997d2d 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -90,7 +90,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskHint = this.taskChanged.pipe( withLatestFrom(this.runId), switchMap(([task, runId]) => - this.runService.getApiV2RunevaluationIdHinttaskId(runId, task.id).pipe( + this.runService.getApiV2EvaluationevaluationIdHinttaskId(runId, task.id).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current query hint due to an error.', e); return of(null); @@ -104,7 +104,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskTarget = this.state.pipe( filter(s => s.taskRunStatus == ApiTaskStatus.ENDED), switchMap((s) => - this.runService.getApiV2RunevaluationIdHinttaskId(s.id, s.currentTask?.templateId).pipe( + this.runService.getApiV2EvaluationevaluationIdHinttaskId(s.id, s.currentTask?.templateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index 33595ef1d..0965fcb5b 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -108,7 +108,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /* Create source observable; list of all submissions. */ this.submissions = this.state.pipe( switchMap((st) => - this.evaluationService.getApiV2EvaluationevaluationIdSubmissionListtimestamp(st.id).pipe( + this.evaluationService.getApiV2EvaluationevaluationIdSubmissionList(st.id).pipe( retry(3), catchError((err, o) => { console.log(`[TeamsViewerComponent] Error while loading submissions: ${err?.message}.`); From d362558069316bda257c25f46b69402578c95310 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sun, 5 Feb 2023 14:20:42 +0100 Subject: [PATCH 076/498] Breakthrough: User management in FE confirmed --- .../types/collection/ApiMediaCollection.kt | 7 +- .../api/rest/types/evaluation/ApiAnswer.kt | 3 +- .../rest/types/evaluation/ApiAnswerType.kt | 8 +- .../dres/data/model/submissions/DbAnswer.kt | 6 +- .../dres/run/filter/AllSubmissionFilter.kt | 5 +- .../run/filter/TextualSubmissionFilter.kt | 5 +- doc/oas-client.json | 38 +- doc/oas.json | 38 +- frontend/package-lock.json | 17964 ++-------------- .../app/user/profile/profile.component.html | 6 +- 10 files changed, 2423 insertions(+), 15657 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt index 96b4abcb1..dcf894018 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaCollection.kt @@ -10,7 +10,12 @@ import dev.dres.data.model.media.DbMediaCollection * @author Ralph Gasser * @version 1.0 */ -data class ApiMediaCollection(val id: CollectionId, val name: String, val description: String? = null, val basePath: String? = null) { +data class ApiMediaCollection( + + val id: CollectionId? = null, // or we create a ApiCreateMediaCollection dto ? + val name: String, + val description: String? = null, + val basePath: String? = null) { companion object { /** * Generates a [ApiMediaCollection] from a [DbMediaCollection] and returns it. diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt index a3c6470bc..a8fe1e32a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt @@ -2,9 +2,10 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.submissions.Answer +import dev.dres.data.model.submissions.AnswerType data class ApiAnswer( - val type: ApiAnswerType, + override val type: AnswerType, override val item: ApiMediaItem?, override val text: String?, override val start: Long?, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt index 6d722a12d..dc38cfb25 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswerType /** @@ -9,9 +10,12 @@ import dev.dres.data.model.submissions.DbAnswerType * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiAnswerType { +enum class ApiAnswerType: AnswerType { ITEM, TEMPORAL, TEXT; + override fun eq(status: AnswerType.Type): Boolean { + return this.name == status.name + } /** * Converts this [ApiAnswerType] to a [DbAnswerType] representation. Requires an ongoing transaction. * @@ -22,4 +26,4 @@ enum class ApiAnswerType { TEMPORAL -> DbAnswerType.TEMPORAL TEXT -> DbAnswerType.TEXT } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt index 9afa5d068..15fcd3bda 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt @@ -15,7 +15,7 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { var answerSet: DbAnswerSet by xdParent(DbAnswerSet::answers) /** The [DbAnswerType] of this [DbAnswerSet]. */ - var type by xdLink1(DbAnswerType) + override var type by xdLink1(DbAnswerType) /** The [DbMediaItem] submitted. Only for [DbAnswerType.ITEM] or [DbAnswerType.TEMPORAL]. */ override var item by xdLink0_1(DbMediaItem) @@ -32,10 +32,10 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { fun toApi() = ApiAnswer( - type = this.type.toApi(), + type = this.type, item = this.item?.toApi(), start = this.start, end = this.end, text = this.text ) -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt index 227c8f54d..f25582323 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt @@ -1,6 +1,7 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission /** @@ -12,5 +13,5 @@ import dev.dres.data.model.submissions.DbSubmission object AllSubmissionFilter : SubmissionFilter { override val reason = "" //will never be relevant - override fun test(t: DbSubmission): Boolean = true -} \ No newline at end of file + override fun test(t: Submission): Boolean = true +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt index 2bdd88c07..d31ca4d8e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt @@ -2,6 +2,7 @@ package dev.dres.run.filter import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.Submission import kotlinx.dnq.query.asSequence /** @@ -14,6 +15,6 @@ class TextualSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include textual information (or is an empty submission)" - override fun test(submission: DbSubmission): Boolean - = submission.answerSets.asSequence().all { it.answers.asSequence().all { it.text != null && it.type == DbAnswerType.TEXT } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + override fun test(submission: Submission): Boolean + = submission.answerSets().all { it.answers().all { it.text != null && it.type == DbAnswerType.TEXT } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } diff --git a/doc/oas-client.json b/doc/oas-client.json index 62454c2f4..d36b674fe 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -3536,7 +3536,7 @@ "type" : "string" } }, - "required" : [ "id", "name" ] + "required" : [ "name" ] }, "ApiMediaItem" : { "type" : "object", @@ -3977,7 +3977,7 @@ "additionalProperties" : false, "properties" : { "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" + "$ref" : "#/components/schemas/AnswerType" }, "item" : { "$ref" : "#/components/schemas/ApiMediaItem" @@ -3992,6 +3992,9 @@ "end" : { "type" : "integer", "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" } }, "required" : [ "type" ] @@ -4012,10 +4015,6 @@ }, "required" : [ "status", "answers" ] }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -4749,6 +4748,28 @@ }, "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, "RunProperties" : { "type" : "object", "additionalProperties" : false, @@ -4769,6 +4790,11 @@ }, "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, + "AnswerType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, "RunManagerStatus" : { "type" : "string", "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] diff --git a/doc/oas.json b/doc/oas.json index e916000fb..220797e4b 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4881,7 +4881,7 @@ "type" : "string" } }, - "required" : [ "id", "name" ] + "required" : [ "name" ] }, "ApiMediaItem" : { "type" : "object", @@ -5322,7 +5322,7 @@ "additionalProperties" : false, "properties" : { "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" + "$ref" : "#/components/schemas/AnswerType" }, "item" : { "$ref" : "#/components/schemas/ApiMediaItem" @@ -5337,6 +5337,9 @@ "end" : { "type" : "integer", "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" } }, "required" : [ "type" ] @@ -5357,10 +5360,6 @@ }, "required" : [ "status", "answers" ] }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -6094,6 +6093,28 @@ }, "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, "RunProperties" : { "type" : "object", "additionalProperties" : false, @@ -6114,6 +6135,11 @@ }, "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, + "AnswerType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, "RunManagerStatus" : { "type" : "string", "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 40c7c52c1..372ada205 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,15502 +1,13 @@ { "name": "dres-frontend", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "dres-frontend", - "version": "1.0.0", - "hasInstallScript": true, - "dependencies": { - "@angular-slider/ngx-slider": "2.0.x", - "@angular/animations": "13.2.x", - "@angular/cdk": "13.2.x", - "@angular/common": "13.2.x", - "@angular/compiler": "13.2.x", - "@angular/core": "13.2.x", - "@angular/flex-layout": "^13.0.0-beta.38", - "@angular/forms": "13.2.x", - "@angular/material": "13.2.x", - "@angular/platform-browser": "13.2.x", - "@angular/platform-browser-dynamic": "13.2.x", - "@angular/router": "13.2.x", - "angularx-qrcode": "13.0.x", - "apexcharts": "3.33.x", - "ng-apexcharts": "1.7.x", - "ngx-color-picker": "12.0.x", - "rxjs": "6.5.x", - "tslib": "2.0.x", - "zone.js": "~0.11.4" - }, - "devDependencies": { - "@angular-devkit/build-angular": "13.2.x", - "@angular-eslint/builder": "13.2.0", - "@angular-eslint/eslint-plugin": "13.2.0", - "@angular-eslint/eslint-plugin-template": "13.2.0", - "@angular-eslint/schematics": "13.2.0", - "@angular-eslint/template-parser": "13.2.0", - "@angular/cli": "13.2.x", - "@angular/compiler-cli": "13.2.x", - "@angular/language-service": "13.2.x", - "@types/jasmine": "3.6.x", - "@types/jasminewd2": "2.0.x", - "@types/node": "12.11.x", - "@typescript-eslint/eslint-plugin": "5.17.0", - "@typescript-eslint/parser": "5.17.0", - "eslint": "^8.12.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", - "husky": "^7.0.0", - "jasmine-core": "3.8.x", - "jasmine-spec-reporter": "5.0.x", - "karma": "6.3.x", - "karma-chrome-launcher": "3.1.x", - "karma-coverage-istanbul-reporter": "3.0.x", - "karma-jasmine": "4.0.x", - "karma-jasmine-html-reporter": "1.5.x", - "lint-staged": "^12.3.7", - "prettier": "2.6.2", - "prettier-eslint": "^14.0.1", - "protractor": "7.0.x", - "stylelint": "^14.8.2", - "stylelint-config-html": "^1.0.0", - "stylelint-config-prettier": "^9.0.3", - "stylelint-config-prettier-scss": "^0.0.1", - "stylelint-config-scss": "^1.0.0-security", - "stylelint-config-standard": "^25.0.0", - "stylelint-config-standard-scss": "^3.0.0", - "ts-node": "8.3.x", - "typescript": "4.5.x" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.1.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/architect": { - "version": "0.1302.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "13.2.3", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/architect/node_modules/rxjs": { - "version": "6.6.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/architect/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular-devkit/build-angular": { - "version": "13.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "1.1.1", - "@angular-devkit/architect": "0.1302.3", - "@angular-devkit/build-webpack": "0.1302.3", - "@angular-devkit/core": "13.2.3", - "@babel/core": "7.16.12", - "@babel/generator": "7.16.8", - "@babel/helper-annotate-as-pure": "7.16.7", - "@babel/plugin-proposal-async-generator-functions": "7.16.8", - "@babel/plugin-transform-async-to-generator": "7.16.8", - "@babel/plugin-transform-runtime": "7.16.10", - "@babel/preset-env": "7.16.11", - "@babel/runtime": "7.16.7", - "@babel/template": "7.16.7", - "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.2.3", - "ansi-colors": "4.1.1", - "babel-loader": "8.2.3", - "babel-plugin-istanbul": "6.1.1", - "browserslist": "^4.9.1", - "cacache": "15.3.0", - "circular-dependency-plugin": "5.2.2", - "copy-webpack-plugin": "10.2.1", - "core-js": "3.20.3", - "critters": "0.0.16", - "css-loader": "6.5.1", - "esbuild-wasm": "0.14.14", - "glob": "7.2.0", - "https-proxy-agent": "5.0.0", - "inquirer": "8.2.0", - "jsonc-parser": "3.0.0", - "karma-source-map-support": "1.4.0", - "less": "4.1.2", - "less-loader": "10.2.0", - "license-webpack-plugin": "4.0.1", - "loader-utils": "3.2.0", - "mini-css-extract-plugin": "2.5.3", - "minimatch": "3.0.4", - "open": "8.4.0", - "ora": "5.4.1", - "parse5-html-rewriting-stream": "6.0.1", - "piscina": "3.2.0", - "postcss": "8.4.5", - "postcss-import": "14.0.2", - "postcss-loader": "6.2.1", - "postcss-preset-env": "7.2.3", - "regenerator-runtime": "0.13.9", - "resolve-url-loader": "5.0.0", - "rxjs": "6.6.7", - "sass": "1.49.0", - "sass-loader": "12.4.0", - "semver": "7.3.5", - "source-map-loader": "3.0.1", - "source-map-support": "0.5.21", - "stylus": "0.56.0", - "stylus-loader": "6.2.0", - "terser": "5.10.0", - "text-table": "0.2.0", - "tree-kill": "1.2.2", - "tslib": "2.3.1", - "webpack": "5.67.0", - "webpack-dev-middleware": "5.3.0", - "webpack-dev-server": "4.7.3", - "webpack-merge": "5.8.0", - "webpack-subresource-integrity": "5.1.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "optionalDependencies": { - "esbuild": "0.14.14" - }, - "peerDependencies": { - "@angular/compiler-cli": "^13.0.0", - "@angular/localize": "^13.0.0", - "@angular/service-worker": "^13.0.0", - "karma": "^6.3.0", - "ng-packagr": "^13.0.0", - "protractor": "^7.0.0", - "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.4.3 <4.6" - }, - "peerDependenciesMeta": { - "@angular/localize": { - "optional": true - }, - "@angular/service-worker": { - "optional": true - }, - "karma": { - "optional": true - }, - "ng-packagr": { - "optional": true - }, - "protractor": { - "optional": true - }, - "tailwindcss": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { - "version": "1.1.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.16.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { - "version": "3.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { - "version": "6.6.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.3.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1302.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/architect": "0.1302.3", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" - } - }, - "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { - "version": "6.6.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular-devkit/core": { - "version": "13.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "6.6.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/core/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular-devkit/schematics": { - "version": "13.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "13.2.3", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "6.6.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular-eslint/builder": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-13.2.0.tgz", - "integrity": "sha512-7V/bJwkMX+1OzxNUMxiOGdfdksjBmzCdPfxNEHXrT4la+vQQxg1oaIsgx74OkDqFTBv6NJO4PK8Flm9/QE4CDw==", - "dev": true, - "dependencies": { - "@nrwl/devkit": "13.1.3" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.2.0.tgz", - "integrity": "sha512-ZA9JPERpeSo+G/bmp8GS/WjBbYkPDVzN6IINHz9SVdv//LWE58yymFFjRabHJx46iAEOe8P0CoKduuJWtEvNrQ==", - "dev": true - }, - "node_modules/@angular-eslint/eslint-plugin": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-13.2.0.tgz", - "integrity": "sha512-8qscIBc4montFQ52+XfBk7qR675oXV8mvRpjDh3cTfIZCzV6CSbbbH2iLp/9egnn0Pgy2ZUIsWgcsbK4W3+4bw==", - "dev": true, - "dependencies": { - "@angular-eslint/utils": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-13.2.0.tgz", - "integrity": "sha512-Ej9bNGbpf8oaQGglPVWAQkBSIHhQv0FeJ/vVnB2fhHUoK9BbkGUYpjAFSSRlnMhl6ktPWKCX3yJiW+F4uIUTIw==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0", - "aria-query": "^4.2.2", - "axobject-query": "^2.2.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template/node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "node_modules/@angular-eslint/schematics": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", - "integrity": "sha512-wxpfZ1L8Twa9Jfwqis7fY6jZvORqAv1k+9m1P6+6/l8gIm/8RYwD44o9Qm2MGt7YClnVthpCDhvfprcoKq6zgA==", - "dev": true, - "dependencies": { - "@angular-eslint/eslint-plugin": "13.2.0", - "@angular-eslint/eslint-plugin-template": "13.2.0", - "ignore": "5.2.0", - "strip-json-comments": "3.1.1", - "tmp": "0.2.1" - }, - "peerDependencies": { - "@angular/cli": ">= 13.0.0 < 14.0.0" - } - }, - "node_modules/@angular-eslint/schematics/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/@angular-eslint/template-parser": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", - "integrity": "sha512-dv+bBFzvA4q/KiEDpO7BDUmB1EpVW0P0sX6OSs2eRzViEuXztkatQ4XOmgzuxP0+E1+zl/mK3F7uBDNXiaah9g==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "eslint-scope": "^5.1.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/utils": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-13.2.0.tgz", - "integrity": "sha512-ywkk+pVLaDLwzNKUcc/xWtJC4Bkm+qRrMOR8ZX3q84E5RTvIvc8IcPFBE4ey3lnGe+nE44OEGuLedCo9vn1Meg==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-slider/ngx-slider": { - "version": "2.0.4", - "license": "MIT", - "dependencies": { - "detect-passive-events": "^2.0.3", - "rxjs": "^6.5.2", - "tslib": "^1.9.0" - }, - "peerDependencies": { - "@angular/common": ">=6.1.0", - "@angular/core": ">=6.1.0", - "@angular/forms": ">=6.1.0" - } - }, - "node_modules/@angular-slider/ngx-slider/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, - "node_modules/@angular/animations": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/core": "13.2.2" - } - }, - "node_modules/@angular/animations/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/cdk": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "optionalDependencies": { - "parse5": "^5.0.0" - }, - "peerDependencies": { - "@angular/common": "^13.0.0 || ^14.0.0-0", - "@angular/core": "^13.0.0 || ^14.0.0-0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/cdk/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/cli": { - "version": "13.2.3", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/architect": "0.1302.3", - "@angular-devkit/core": "13.2.3", - "@angular-devkit/schematics": "13.2.3", - "@schematics/angular": "13.2.3", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.3.3", - "ini": "2.0.0", - "inquirer": "8.2.0", - "jsonc-parser": "3.0.0", - "npm-package-arg": "8.1.5", - "npm-pick-manifest": "6.1.1", - "open": "8.4.0", - "ora": "5.4.1", - "pacote": "12.0.3", - "resolve": "1.22.0", - "semver": "7.3.5", - "symbol-observable": "4.0.0", - "uuid": "8.3.2" - }, - "bin": { - "ng": "bin/ng.js" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/common": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/core": "13.2.2", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/common/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/compiler": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - } - }, - "node_modules/@angular/compiler-cli": { - "version": "13.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.8.6", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.11.0", - "magic-string": "^0.25.0", - "reflect-metadata": "^0.1.2", - "semver": "^7.0.0", - "sourcemap-codec": "^1.4.8", - "tslib": "^2.3.0", - "yargs": "^17.2.1" - }, - "bin": { - "ng-xi18n": "bundles/src/bin/ng_xi18n.js", - "ngc": "bundles/src/bin/ngc.js", - "ngcc": "bundles/ngcc/main-ngcc.js" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/compiler": "13.2.2", - "typescript": ">=4.4.2 <4.6" - } - }, - "node_modules/@angular/compiler-cli/node_modules/tslib": { - "version": "2.3.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/@angular/compiler/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/core": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4" - } - }, - "node_modules/@angular/core/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/flex-layout": { - "version": "13.0.0-beta.38", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/cdk": "^13.0.0", - "@angular/common": "^13.0.0", - "@angular/core": "^13.0.0", - "@angular/platform-browser": "^13.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/flex-layout/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/forms": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/common": "13.2.2", - "@angular/core": "13.2.2", - "@angular/platform-browser": "13.2.2", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/forms/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/language-service": { - "version": "13.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - } - }, - "node_modules/@angular/material": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/animations": "^13.0.0 || ^14.0.0-0", - "@angular/cdk": "13.2.2", - "@angular/common": "^13.0.0 || ^14.0.0-0", - "@angular/core": "^13.0.0 || ^14.0.0-0", - "@angular/forms": "^13.0.0 || ^14.0.0-0", - "@angular/platform-browser": "^13.0.0 || ^14.0.0-0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/material/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/platform-browser": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/animations": "13.2.2", - "@angular/common": "13.2.2", - "@angular/core": "13.2.2" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } - } - }, - "node_modules/@angular/platform-browser-dynamic": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/common": "13.2.2", - "@angular/compiler": "13.2.2", - "@angular/core": "13.2.2", - "@angular/platform-browser": "13.2.2" - } - }, - "node_modules/@angular/platform-browser-dynamic/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/platform-browser/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@angular/router": { - "version": "13.2.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "@angular/common": "13.2.2", - "@angular/core": "13.2.2", - "@angular/platform-browser": "13.2.2", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/router/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/@assemblyscript/loader": { - "version": "0.10.1", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.17.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.17.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.0.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.17.2", - "@babel/parser": "^7.17.0", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.17.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.17.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.17.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.17.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.17.0", - "dev": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.16.11", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.10", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-transform": "^0.14.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.16.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.16.11", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.0.tgz", - "integrity": "sha512-G5FaGZOWORq9zthDjIrjib5XlcddeqLbIiDO3YQsut6j7aGf76xn0umUC/pA6+nApk3hQJF4JzLzg5PCl6ewJg==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.20.2", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.16.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.17.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.17.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@cordobo/qrcode": { - "version": "1.5.0", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^17.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.1.0", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@gar/promisify": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@ngtools/webpack": { - "version": "13.2.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^13.0.0", - "typescript": ">=4.4.3 <4.6", - "webpack": "^5.30.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/git": { - "version": "2.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^1.3.2", - "lru-cache": "^6.0.0", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^6.1.1", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - } - }, - "node_modules/@npmcli/git/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/node-gyp": { - "version": "1.0.3", - "dev": true, - "license": "ISC" - }, - "node_modules/@npmcli/promise-spawn": { - "version": "1.3.2", - "dev": true, - "license": "ISC", - "dependencies": { - "infer-owner": "^1.0.4" - } - }, - "node_modules/@npmcli/run-script": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^1.0.2", - "@npmcli/promise-spawn": "^1.3.2", - "node-gyp": "^8.2.0", - "read-package-json-fast": "^2.0.1" - } - }, - "node_modules/@nrwl/cli": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.1.7.tgz", - "integrity": "sha512-HVvqYwefizcoVc4xrTgfIC8nfMA9cx5NiaIbZFDIZv12NsdalpA6a2BmmV8Zck+QQSGEHrhTZyt1AqDhA1t+6A==", - "dev": true, - "dependencies": { - "nx": "14.1.7" - } - }, - "node_modules/@nrwl/cli/node_modules/@nrwl/tao": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.1.7.tgz", - "integrity": "sha512-7jzPqEwElMiTKCZm2iuqC5aPIs+IwocWR6Tl1JhC1eK2PWskHOhdgTQM186lfXwPKWH4NJAE3eZFqtECnGDCJg==", - "dev": true, - "dependencies": { - "nx": "14.1.7" - }, - "bin": { - "tao": "index.js" - } - }, - "node_modules/@nrwl/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@nrwl/cli/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@nrwl/cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@nrwl/cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@nrwl/cli/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nrwl/cli/node_modules/glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@nrwl/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nrwl/cli/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@nrwl/cli/node_modules/nx": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/nx/-/nx-14.1.7.tgz", - "integrity": "sha512-Q10PVQ70TFg9HY6KQKJ316B42sVtxmFk/VUdC7ZHKxzDm/JeYf6OaDarQOUZaFZp/EL2brtEsrlWSMTWmk3r4w==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@nrwl/cli": "14.1.7", - "@nrwl/tao": "14.1.7", - "@parcel/watcher": "2.0.4", - "@swc-node/register": "^1.4.2", - "@swc/core": "^1.2.173", - "chalk": "4.1.0", - "chokidar": "^3.5.1", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^7.0.2", - "dotenv": "~10.0.0", - "enquirer": "~2.3.6", - "fast-glob": "3.2.7", - "figures": "3.2.0", - "flat": "^5.0.2", - "fs-extra": "^10.1.0", - "glob": "7.1.4", - "ignore": "^5.0.4", - "jsonc-parser": "3.0.0", - "minimatch": "3.0.4", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", - "semver": "7.3.4", - "string-width": "^4.2.3", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^3.9.0", - "tslib": "^2.3.0", - "v8-compile-cache": "2.3.0", - "yargs": "^17.4.0", - "yargs-parser": "21.0.1" - }, - "bin": { - "nx": "bin/nx.js" - } - }, - "node_modules/@nrwl/cli/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nrwl/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nrwl/cli/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/@nrwl/cli/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/@nrwl/devkit": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", - "integrity": "sha512-TAAsZJvVc/obeH0rZKY6miVhyM2GHGb8qIWp9MAIdLlXf4VDcNC7rxwb5OrGVSwuTTjqGYBGPUx0yEogOOJthA==", - "dev": true, - "dependencies": { - "@nrwl/tao": "13.1.3", - "ejs": "^3.1.5", - "ignore": "^5.0.4", - "rxjs": "^6.5.4", - "semver": "7.3.4", - "tslib": "^2.0.0" - } - }, - "node_modules/@nrwl/devkit/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nrwl/tao": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", - "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", - "dev": true, - "dependencies": { - "chalk": "4.1.0", - "enquirer": "~2.3.6", - "fs-extra": "^9.1.0", - "jsonc-parser": "3.0.0", - "nx": "13.1.3", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", - "semver": "7.3.4", - "tmp": "~0.2.1", - "tslib": "^2.0.0", - "yargs-parser": "20.0.0" - }, - "bin": { - "tao": "index.js" - } - }, - "node_modules/@nrwl/tao/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@nrwl/tao/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@nrwl/tao/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@nrwl/tao/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@nrwl/tao/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nrwl/tao/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nrwl/tao/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nrwl/tao/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nrwl/tao/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/@nrwl/tao/node_modules/yargs-parser": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", - "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", - "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@schematics/angular": { - "version": "13.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "13.2.3", - "@angular-devkit/schematics": "13.2.3", - "jsonc-parser": "3.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@socket.io/base64-arraybuffer": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/@swc-node/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.9.0.tgz", - "integrity": "sha512-vRnvsMtL9OxybA/Wun1ZhlDvB6MNs4Zujnina0VKdGk+yI6s87KUhdTcbAY6dQMZhQTLFiC1Lnv/BuwCKcCEug==", - "dev": true, - "dependencies": { - "@swc/core": "^1.2.172" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@swc-node/register": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.5.1.tgz", - "integrity": "sha512-6IL5s4QShKGs08qAeNou3rDA3gbp2WHk6fo0XnJXQn/aC9k6FnVBbj/thGOIEDtgNhC/DKpZT8tCY1LpQnOZFg==", - "dev": true, - "dependencies": { - "@swc-node/core": "^1.9.0", - "@swc-node/sourcemap-support": "^0.2.0", - "colorette": "^2.0.16", - "debug": "^4.3.4", - "pirates": "^4.0.5", - "tslib": "^2.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "typescript": ">= 4.3" - } - }, - "node_modules/@swc-node/register/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@swc-node/register/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/@swc-node/sourcemap-support": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.2.0.tgz", - "integrity": "sha512-FNrxdI6XMYfoNt81L8eFKEm1d8P82I1nPwS3MrnBGzZoMWB+seQhQK+iN6M5RreJxXbfZw5lF86LRjHEQeGMqg==", - "dev": true, - "dependencies": { - "source-map-support": "^0.5.21" - } - }, - "node_modules/@swc-node/sourcemap-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@swc-node/sourcemap-support/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/@swc/core": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.189.tgz", - "integrity": "sha512-S5cKX4ECMSfW78DLFgnlilJZgjrFRYwPslrrwpLl3gpwh+Qo72/Mhn71u7G/5xXW+T/xW5GwPccHfCk+k72uUg==", - "dev": true, - "bin": { - "swcx": "run_swcx.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-android-arm-eabi": "1.2.189", - "@swc/core-android-arm64": "1.2.189", - "@swc/core-darwin-arm64": "1.2.189", - "@swc/core-darwin-x64": "1.2.189", - "@swc/core-freebsd-x64": "1.2.189", - "@swc/core-linux-arm-gnueabihf": "1.2.189", - "@swc/core-linux-arm64-gnu": "1.2.189", - "@swc/core-linux-arm64-musl": "1.2.189", - "@swc/core-linux-x64-gnu": "1.2.189", - "@swc/core-linux-x64-musl": "1.2.189", - "@swc/core-win32-arm64-msvc": "1.2.189", - "@swc/core-win32-ia32-msvc": "1.2.189", - "@swc/core-win32-x64-msvc": "1.2.189" - } - }, - "node_modules/@swc/core-android-arm-eabi": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.189.tgz", - "integrity": "sha512-0kN3Le6QzFFz+Lc6a/tf/RkJXubWwWaHxF4c0bVm4AKIFf4nRlUCEqEkjdVaZvL92rpBMHaEEBuIIz3T8DqTTQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-android-arm64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.189.tgz", - "integrity": "sha512-smsb+YkDw2OKwg66Z63E/G4NlFApDbsiOPmZXFZbtZbNBD9v+wnk6WVA//XR1bdUI9VbzNKlMPKJxQTE685QDw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.189.tgz", - "integrity": "sha512-OGjZRkTulKirJMLYbW9etb59lA9ueDXVwYRVD9SrNh8tRMTf0Nq+SUT/C3LVhBBGC4KSdWOzBAYbDTTdsnY++Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.189.tgz", - "integrity": "sha512-BEcxnBHx51514Voe2dn/y1y5H9VNyw7Zpp9+mPekZqx5o/onPD5wZ1ZfAsPrA4UlvM3v16u6ITE/cLawJ/GdAQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-freebsd-x64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.189.tgz", - "integrity": "sha512-B6g2NWeh2iw6WPOaM19Uj3VE4se6alT265kWibLUshjcofRfnYT1lNhhkrF1D0EVnpC8I96I/xXNQo4Am9z4zQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.189.tgz", - "integrity": "sha512-6WhPG9pyN5AahQTVQk8MoN1I9Z/Ytfqizuie1wV7mW8FMNmMkiJvBekKtE6ftxu80Hqa34r86WfEwmJKm5MqYw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.189.tgz", - "integrity": "sha512-frJTGnxsDe7h2d7BtnBivOdOJTtabkQIIZmHd4JHqafcoekI6teyveIax17axLyimvWl278yTo3hf7ePagD/eg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.189.tgz", - "integrity": "sha512-27K38LoZpME5lojDJIUNo7zdTDwAKLm0BMQ7HXWcYOyiDAekhSidI+SrBWxCfLzfuClhFu6/VE3E7j32VFJsiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.189.tgz", - "integrity": "sha512-Ha5oJKOyQm9w7+e+WdRm4ypijzEmglWZGtgBR6vV6ViqqHcTBAU4nG87ex7y7AS9p+Cbc6EOSR9X1qIB8KxtbA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.189.tgz", - "integrity": "sha512-/p5yXa9HEzpVEuE4ivkW1IvwyYu6fT+L2OvVEs5oXIba80F0Wjy7InWqaa83gwrdMH+bXV6loG8LzZUZu/lpjA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.189.tgz", - "integrity": "sha512-o/1ueM6/sifNjYnO6NMEXB895spVfJs5oQIPxQG9vJ/4zWLw8YmAx+u1xJY+XGyK6gnroHt7yPiS87qWdbeF6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.189.tgz", - "integrity": "sha512-YDwRkzykaf+dw5Z7u189cC/Tttkn2NVV84hrGL3LbVuh7wT5PaDhZs4Yz4unZQSlPV12olmZWgNr/i27h5wlpg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.189.tgz", - "integrity": "sha512-Nge8Z/ZkAp5p5No50yBDpBG7+ZYaVWGSuwtPj6OJe7orzvDCEm9GgcVE6J9GEjbclSWlCH8B8lUe17GaKRZHbg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.12", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/eslint": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", - "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "0.0.50", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "4.17.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.28", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@types/http-proxy": { - "version": "1.17.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/jasmine": { - "version": "3.6.11", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jasminewd2": { - "version": "2.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/jasmine": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "12.11.7", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/prettier": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", - "integrity": "sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==", - "dev": true - }, - "node_modules/@types/q": { - "version": "0.0.32", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/retry": { - "version": "0.12.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/selenium-webdriver": { - "version": "3.0.19", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/serve-index": { - "version": "1.9.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.13.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.33", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", - "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.17.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "debug": "^4.3.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/abab": { - "version": "2.0.5", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/accepts": { - "version": "1.3.8", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/adm-zip": { - "version": "0.4.16", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.3.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/angularx-qrcode": { - "version": "13.0.3", - "license": "MIT", - "dependencies": { - "@cordobo/qrcode": "1.5.0", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/core": "^13.0.0" - } - }, - "node_modules/angularx-qrcode/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/apexcharts": { - "version": "3.33.1", - "license": "MIT", - "dependencies": { - "svg.draggable.js": "^2.2.2", - "svg.easing.js": "^2.0.0", - "svg.filter.js": "^2.0.2", - "svg.pathmorphing.js": "^0.1.3", - "svg.resize.js": "^1.4.3", - "svg.select.js": "^3.0.1" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/argparse/node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/array-union": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "2.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atob": { - "version": "2.1.2", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001297", - "fraction.js": "^4.1.2", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "dev": true, - "license": "MIT" - }, - "node_modules/babel-loader": { - "version": "8.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.21.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "dev": true, - "license": "MIT" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/blocking-proxy": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "blocking-proxy": "built/lib/bin.js" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/body-parser": { - "version": "1.19.1", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.1", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/bonjour": { - "version": "3.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.19.1", - "dev": true, - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/browserstack": { - "version": "1.6.1", - "dev": true, - "license": "MIT", - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, - "node_modules/browserstack/node_modules/agent-base": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/browserstack/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/browserstack/node_modules/https-proxy-agent": { - "version": "2.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/builtins": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "15.3.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001312", - "dev": true, - "license": "CC-BY-4.0", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "dev": true, - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "3.5.3", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/circular-dependency-plugin": { - "version": "5.2.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "webpack": ">=4.0.1" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "dependencies": { - "is-regexp": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", - "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.16", - "dev": true, - "license": "MIT" - }, - "node_modules/colors": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "dev": true, - "license": "MIT" - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/compressible": { - "version": "2.0.18", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "dev": true, - "license": "ISC" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/content-type": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.4.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/copy-anything": { - "version": "2.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "is-what": "^3.14.1" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "10.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.1", - "globby": "^12.0.2", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 12.20.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/array-union": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "12.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.9", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/core-js": { - "version": "3.20.3", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.21.0", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.19.1", - "semver": "7.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/core-js-pure": { - "version": "3.22.6", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.6.tgz", - "integrity": "sha512-u5yG2VL6NKXz9BZHr9RAm6eWD1DTNjG7jJnJgLGR+Im0whdPcPXqwqxd+dcUrZvpvPan5KMgn/3pI+Q/aGqPOA==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "dev": true, - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/critters": { - "version": "0.0.16", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" - } - }, - "node_modules/critters/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/critters/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/critters/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/critters/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/critters/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/critters/node_modules/parse5": { - "version": "6.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/critters/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, - "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-functions-list": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", - "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", - "dev": true, - "engines": { - "node": ">=12.22" - } - }, - "node_modules/css-has-pseudo": { - "version": "3.0.4", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-loader": { - "version": "6.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "dev": true, - "license": "CC0-1.0", - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "4.2.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "5.1.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssdb": { - "version": "5.1.0", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/custom-event": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/date-format": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/debug": { - "version": "4.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-equal": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defaults": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/del": { - "version": "2.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/depd": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-it": { - "version": "4.0.1", - "license": "MIT" - }, - "node_modules/detect-node": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-passive-events": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "detect-it": "^4.0.1" - } - }, - "node_modules/di": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/diff": { - "version": "4.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dijkstrajs": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/dns-packet": { - "version": "1.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-indexof": "^1.0.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.68", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encode-utf8": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", - "ws": "~8.2.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/base64-arraybuffer": "~1.0.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "2.2.0", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/errno": { - "version": "0.1.8", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "dev": true, - "license": "MIT" - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "dev": true, - "license": "MIT" - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/esbuild": { - "version": "0.14.14", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "optionalDependencies": { - "esbuild-android-arm64": "0.14.14", - "esbuild-darwin-64": "0.14.14", - "esbuild-darwin-arm64": "0.14.14", - "esbuild-freebsd-64": "0.14.14", - "esbuild-freebsd-arm64": "0.14.14", - "esbuild-linux-32": "0.14.14", - "esbuild-linux-64": "0.14.14", - "esbuild-linux-arm": "0.14.14", - "esbuild-linux-arm64": "0.14.14", - "esbuild-linux-mips64le": "0.14.14", - "esbuild-linux-ppc64le": "0.14.14", - "esbuild-linux-s390x": "0.14.14", - "esbuild-netbsd-64": "0.14.14", - "esbuild-openbsd-64": "0.14.14", - "esbuild-sunos-64": "0.14.14", - "esbuild-windows-32": "0.14.14", - "esbuild-windows-64": "0.14.14", - "esbuild-windows-arm64": "0.14.14" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.14", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/esbuild-wasm": { - "version": "0.14.14", - "dev": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", - "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, - "dependencies": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter-asyncresource": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "dependencies": { - "clone-regexp": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express": { - "version": "4.17.2", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.6", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/express/node_modules/cookie": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.5", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.14.8", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "dev": true, - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/gauge": { - "version": "4.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "ansi-regex": "^5.0.1", - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/globals": { - "version": "11.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/globjoin": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", - "dev": true - }, - "node_modules/graceful-fs": { - "version": "4.2.9", - "dev": true, - "license": "ISC" - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/har-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/hdr-histogram-js": { - "version": "2.0.3", - "dev": true, - "license": "BSD", - "dependencies": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "node_modules/hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.3.2", - "dev": true, - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "peer": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "1.8.1", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.5", - "dev": true, - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "dev": true, - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-walk": { - "version": "4.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/immutable": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer": { - "version": "8.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.2.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/rxjs": { - "version": "7.5.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/tslib": { - "version": "2.3.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/ip": { - "version": "1.1.5", - "dev": true, - "license": "MIT" - }, - "node_modules/ipaddr.js": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.8.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-cwd": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-in-cwd": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-path-inside": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-what": { - "version": "3.14.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/isbinaryfile": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/pify": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/semver": { - "version": "5.7.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.4", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jasmine": { - "version": "2.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" - } - }, - "node_modules/jasmine-core": { - "version": "3.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/jasmine-spec-reporter": { - "version": "5.0.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "colors": "1.4.0" - } - }, - "node_modules/jasmine/node_modules/jasmine-core": { - "version": "2.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/jasminewd2": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.9.x" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/json5": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/jsprim": { - "version": "1.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/jszip": { - "version": "3.7.1", - "dev": true, - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/karma": { - "version": "6.3.16", - "dev": true, - "license": "MIT", - "dependencies": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "colors": "1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.2.0", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/karma-chrome-launcher": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-coverage-istanbul-reporter": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^3.0.2", - "minimatch": "^3.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/mattlewis92" - } - }, - "node_modules/karma-jasmine": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "jasmine-core": "^3.6.0" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "karma": "*" - } - }, - "node_modules/karma-jasmine-html-reporter": { - "version": "1.5.4", - "dev": true, - "license": "MIT", - "peerDependencies": { - "jasmine-core": ">=3.5", - "karma": ">=0.9", - "karma-jasmine": ">=1.1" - } - }, - "node_modules/karma-source-map-support": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map-support": "^0.5.5" - } - }, - "node_modules/karma-source-map-support/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma-source-map-support/node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.9", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klona": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", - "dev": true - }, - "node_modules/less": { - "version": "4.1.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "copy-anything": "^2.0.1", - "parse-node-version": "^1.0.1", - "tslib": "^2.3.0" - }, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^2.5.2", - "source-map": "~0.6.0" - } - }, - "node_modules/less-loader": { - "version": "10.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "klona": "^2.0.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/less/node_modules/make-dir": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/less/node_modules/mime": { - "version": "1.6.0", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/less/node_modules/pify": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/less/node_modules/semver": { - "version": "5.7.1", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/less/node_modules/tslib": { - "version": "2.3.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/license-webpack-plugin": { - "version": "4.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "webpack-sources": "^3.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-sources": { - "optional": true - } - } - }, - "node_modules/lie": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/lint-staged": { - "version": "12.4.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz", - "integrity": "sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==", - "dev": true, - "dependencies": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.16", - "commander": "^8.3.0", - "debug": "^4.3.3", - "execa": "^5.1.1", - "lilconfig": "2.0.4", - "listr2": "^4.0.1", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.0", - "pidtree": "^0.5.0", - "string-argv": "^0.3.1", - "supports-color": "^9.2.1", - "yaml": "^1.10.2" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/lint-staged/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/lint-staged/node_modules/supports-color": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", - "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.5", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/listr2/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/listr2/node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/loader-runner": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-update/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log4js": { - "version": "6.4.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", - "rfdc": "^1.3.0", - "streamroller": "^3.0.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/loglevel": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", - "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } - }, - "node_modules/loglevel-colored-level-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", - "dev": true, - "dependencies": { - "chalk": "^1.1.3", - "loglevel": "^1.4.1" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/magic-string": { - "version": "0.25.7", - "dev": true, - "license": "MIT", - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC" - }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mathml-tag-names": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", - "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.4.1", - "dev": true, - "license": "Unlicense", - "dependencies": { - "fs-monkey": "1.0.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.51.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.34", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/minipass": { - "version": "3.1.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "0.5.5", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "6.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "dev": true, - "license": "ISC" - }, - "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/needle": { - "version": "2.9.1", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "node_modules/needle/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/ng-apexcharts": { - "version": "1.7.0", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": ">=13.0.0", - "@angular/core": ">=13.0.0", - "apexcharts": "^3.31.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/ngx-color-picker": { - "version": "12.0.0", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": ">=9.0.0", - "@angular/core": ">=9.0.0", - "@angular/forms": ">=9.0.0" - } - }, - "node_modules/ngx-color-picker/node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/nice-napi": { - "version": "1.0.2", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "!win32" - ], - "dependencies": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" - } - }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/node-forge": { - "version": "1.3.0", - "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/node-releases": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "5.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-bundled": { - "version": "1.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/npm-install-checks": { - "version": "4.0.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/npm-package-arg": { - "version": "8.1.5", - "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^4.0.1", - "semver": "^7.3.4", - "validate-npm-package-name": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-packlist": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.6", - "ignore-walk": "^4.0.1", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-pick-manifest": { - "version": "6.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^8.1.2", - "semver": "^7.3.4" - } - }, - "node_modules/npm-registry-fetch": { - "version": "12.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "make-fetch-happen": "^10.0.1", - "minipass": "^3.1.6", - "minipass-fetch": "^1.4.1", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^8.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.3.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "10.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.0", - "cacache": "^15.3.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.3.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.4.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.1.1", - "ssri": "^8.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "6.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.0", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/nth-check": { - "version": "2.0.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nx": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/nx/-/nx-13.1.3.tgz", - "integrity": "sha512-clM0NQhQKYkqcNz2E3uYRMLwhp2L/9dBhJhQi9XBX4IAyA2gWAomhRIlLm5Xxg3g4h1xwSpP3eJ5t89VikY8Pw==", - "dev": true, - "dependencies": { - "@nrwl/cli": "*" - }, - "bin": { - "nx": "bin/nx.js" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", - "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry/node_modules/retry": { - "version": "0.13.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pacote": { - "version": "12.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^2.1.0", - "@npmcli/installed-package-contents": "^1.0.6", - "@npmcli/promise-spawn": "^1.2.0", - "@npmcli/run-script": "^2.0.0", - "cacache": "^15.0.5", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.3", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^3.0.0", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^12.0.0", - "promise-retry": "^2.0.1", - "read-package-json-fast": "^2.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.1.0" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/pacote/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "dev": true, - "license": "(MIT AND Zlib)" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/parse5": { - "version": "5.1.1", - "license": "MIT", - "optional": true - }, - "node_modules/parse5-html-rewriting-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" - } - }, - "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { - "version": "6.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { - "version": "6.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/parse5-sax-parser": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-sax-parser/node_modules/parse5": { - "version": "6.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "dev": true, - "license": "(WTFPL OR MIT)" - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", - "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/piscina": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0" - }, - "optionalDependencies": { - "nice-napi": "^1.0.2" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/portfinder": { - "version": "1.0.28", - "dev": true, - "license": "MIT", - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/postcss": { - "version": "8.4.5", - "dev": true, - "license": "MIT", - "dependencies": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.2" - }, - "peerDependencies": { - "postcss": "^8.0.2" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.2", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.0.2", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-custom-media": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-custom-properties": { - "version": "12.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.2" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.4", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "3.0.5", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-env-function": { - "version": "4.0.5", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within": { - "version": "5.0.4", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "3.0.3", - "dev": true, - "license": "CC0-1.0", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-html": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.4.1.tgz", - "integrity": "sha512-OKihuWxPuBQrQeLNsavP7ytJ9IYNj/ViAXB2v7Qjh56LnfESKrkahKA9si4VfPN8xtz6oqUE6KdL0bTPrHJr6g==", - "dev": true, - "peer": true, - "dependencies": { - "htmlparser2": "^7.1.2", - "postcss": "^8.4.0", - "postcss-safe-parser": "^6.0.0" - }, - "engines": { - "node": "^12 || >=14" - } - }, - "node_modules/postcss-image-set-function": { - "version": "4.0.6", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-import": { - "version": "14.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.1.0", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-loader": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-logical": { - "version": "5.0.4", - "dev": true, - "license": "CC0-1.0", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", - "dev": true - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nesting": { - "version": "10.1.2", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.3", - "dev": true, - "license": "CC0-1.0", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "7.0.4", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.2.3", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "autoprefixer": "^10.4.2", - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001299", - "css-blank-pseudo": "^3.0.2", - "css-has-pseudo": "^3.0.3", - "css-prefers-color-scheme": "^6.0.2", - "cssdb": "^5.0.0", - "postcss-attribute-case-insensitive": "^5.0.0", - "postcss-color-functional-notation": "^4.2.1", - "postcss-color-hex-alpha": "^8.0.2", - "postcss-color-rebeccapurple": "^7.0.2", - "postcss-custom-media": "^8.0.0", - "postcss-custom-properties": "^12.1.2", - "postcss-custom-selectors": "^6.0.0", - "postcss-dir-pseudo-class": "^6.0.3", - "postcss-double-position-gradients": "^3.0.4", - "postcss-env-function": "^4.0.4", - "postcss-focus-visible": "^6.0.3", - "postcss-focus-within": "^5.0.3", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.2", - "postcss-image-set-function": "^4.0.4", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.0.3", - "postcss-logical": "^5.0.3", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.2", - "postcss-overflow-shorthand": "^3.0.2", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.3", - "postcss-pseudo-class-any-link": "^7.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^5.0.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.1", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", - "dev": true - }, - "node_modules/postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", - "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", - "dev": true, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", - "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-eslint": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-14.1.0.tgz", - "integrity": "sha512-K0TRVaAUXtI5xz1ZaVZfvGMmunDNyIGXFkE845hVl6FzSxzRN9E03YmK3IiapcRFv3w4PyAL25LIPsy2sRz2tw==", - "dev": true, - "dependencies": { - "@types/eslint": "^8.4.2", - "@types/prettier": "^2.6.0", - "@typescript-eslint/parser": "^5.10.0", - "common-tags": "^1.4.0", - "dlv": "^1.1.0", - "eslint": "^8.7.0", - "indent-string": "^4.0.0", - "lodash.merge": "^4.6.0", - "loglevel-colored-level-prefix": "^1.0.0", - "prettier": "^2.5.1", - "pretty-format": "^23.0.1", - "require-relative": "^0.8.7", - "typescript": "^4.5.4", - "vue-eslint-parser": "^8.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-format": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", - "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/protractor": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.1.7", - "yargs": "^15.3.1" - }, - "bin": { - "protractor": "bin/protractor", - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=10.13.x" - } - }, - "node_modules/protractor/node_modules/ansi-regex": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/ansi-styles": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/chalk": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/cliui": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/protractor/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/protractor/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/supports-color": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/protractor/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/y18n": { - "version": "4.0.3", - "dev": true, - "license": "ISC" - }, - "node_modules/protractor/node_modules/yargs": { - "version": "15.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/yargs-parser": { - "version": "18.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/psl": { - "version": "1.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.9.6", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.1", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "2.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/regenerate": { - "version": "1.4.2", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-parser": { - "version": "2.2.11", - "dev": true, - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.6.0", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.8.4", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/request": { - "version": "2.88.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", - "dev": true - }, - "node_modules/requires-port": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-url-loader": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/resolve-url-loader/node_modules/loader-utils": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "6.5.5", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/rxjs-for-await": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", - "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true, - "peerDependencies": { - "rxjs": "^6.0.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/sass": { - "version": "1.49.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/sass-loader": { - "version": "12.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/saucelabs": { - "version": "1.5.0", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/saucelabs/node_modules/agent-base": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/saucelabs/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/saucelabs/node_modules/https-proxy-agent": { - "version": "2.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/sax": { - "version": "1.2.4", - "dev": true, - "license": "ISC" - }, - "node_modules/schema-utils": { - "version": "2.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/select-hose": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/selenium-webdriver": { - "version": "3.6.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/selenium-webdriver/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.0.30", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/selfsigned": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "node-forge": "^1.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.3.5", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.17.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-static": { - "version": "1.14.2", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "dev": true, - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "node_modules/slash": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socket.io": { - "version": "4.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.3.3", - "dev": true, - "license": "MIT" - }, - "node_modules/socket.io-parser": { - "version": "4.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "dev": true, - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/socks": { - "version": "2.6.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ip": "^1.1.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "6.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/source-map": { - "version": "0.7.3", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/source-map-loader/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, - "node_modules/source-map-support": { - "version": "0.4.18", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "^0.5.6" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "dev": true, - "license": "MIT" - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "dev": true, - "bin": { - "specificity": "bin/specificity" - } - }, - "node_modules/sshpk": { - "version": "1.17.0", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ssri": { - "version": "8.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/streamroller": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-search": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", - "dev": true - }, - "node_modules/stylelint": { - "version": "14.8.3", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.3.tgz", - "integrity": "sha512-aLpskXwSgFEBYbFRKA/BfuyYMGuXNtn2t5GqoffNPSezvw97x/vVNWcZNF0+cwt+LBjfvyq9/MRE3OjInGRgNA==", - "dev": true, - "dependencies": { - "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", - "css-functions-list": "^3.0.1", - "debug": "^4.3.4", - "execall": "^2.0.0", - "fast-glob": "^3.2.11", - "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.2.0", - "ignore": "^5.2.0", - "import-lazy": "^4.0.0", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", - "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.13", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "specificity": "^0.4.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", - "supports-hyperlinks": "^2.2.0", - "svg-tags": "^1.0.0", - "table": "^6.8.0", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - } - }, - "node_modules/stylelint-config-html": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", - "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", - "dev": true, - "engines": { - "node": "^12 || >=14" - }, - "peerDependencies": { - "postcss-html": "^1.0.0", - "stylelint": ">=14.0.0" - } - }, - "node_modules/stylelint-config-prettier": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", - "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", - "dev": true, - "bin": { - "stylelint-config-prettier": "bin/check.js", - "stylelint-config-prettier-check": "bin/check.js" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "stylelint": ">=11.0.0" - } - }, - "node_modules/stylelint-config-prettier-scss": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-prettier-scss/-/stylelint-config-prettier-scss-0.0.1.tgz", - "integrity": "sha512-lBAYG9xYOh2LeWEPC/64xeUxwOTnQ8nDyBijQoWoJb10/bMGrUwnokpt8jegGck2Vbtxh6XGwH63z5qBcVHreQ==", - "dev": true, - "dependencies": { - "stylelint-config-prettier": ">=9.0.3" - }, - "bin": { - "stylelint-config-prettier-scss": "bin/check.js", - "stylelint-config-prettier-scss-check": "bin/check.js" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "stylelint": ">=11.0.0" - } - }, - "node_modules/stylelint-config-recommended": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", - "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.4.0" - } - }, - "node_modules/stylelint-config-recommended-scss": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", - "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", - "dev": true, - "dependencies": { - "postcss-scss": "^4.0.2", - "stylelint-config-recommended": "^6.0.0", - "stylelint-scss": "^4.0.0" - }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-scss": { - "version": "1.0.0-security", - "resolved": "https://registry.npmjs.org/stylelint-config-scss/-/stylelint-config-scss-1.0.0-security.tgz", - "integrity": "sha512-8Pgul2mNpzTeK2KCuyV5RcDC1BgzWzU7dCLVJWuxpkKgmxrMqCvjqgyosaKbAVZy2AiaMU0zfHBt7prg7/NaxA==", - "dev": true - }, - "node_modules/stylelint-config-standard": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", - "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", - "dev": true, - "dependencies": { - "stylelint-config-recommended": "^7.0.0" - }, - "peerDependencies": { - "stylelint": "^14.4.0" - } - }, - "node_modules/stylelint-config-standard-scss": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz", - "integrity": "sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==", - "dev": true, - "dependencies": { - "stylelint-config-recommended-scss": "^5.0.2", - "stylelint-config-standard": "^24.0.0" - }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-standard": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", - "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", - "dev": true, - "dependencies": { - "stylelint-config-recommended": "^6.0.0" - }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-scss": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", - "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0" - }, - "peerDependencies": { - "stylelint": "^14.5.1" - } - }, - "node_modules/stylelint/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true - }, - "node_modules/stylelint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/stylelint/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylelint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stylelint/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/stylelint/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylus": { - "version": "0.56.0", - "dev": true, - "license": "MIT", - "dependencies": { - "css": "^3.0.0", - "debug": "^4.3.2", - "glob": "^7.1.6", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus-loader": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.7", - "klona": "^2.0.4", - "normalize-path": "^3.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "stylus": ">=0.52.4", - "webpack": "^5.0.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "node_modules/svg.draggable.js": { - "version": "2.2.2", - "license": "MIT", - "dependencies": { - "svg.js": "^2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.easing.js": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "svg.js": ">=2.3.x" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.filter.js": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.js": { - "version": "2.7.1", - "license": "MIT" - }, - "node_modules/svg.pathmorphing.js": { - "version": "0.1.3", - "license": "MIT", - "dependencies": { - "svg.js": "^2.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.resize.js": { - "version": "1.4.3", - "license": "MIT", - "dependencies": { - "svg.js": "^2.6.5", - "svg.select.js": "^2.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.resize.js/node_modules/svg.select.js": { - "version": "2.1.2", - "license": "MIT", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.select.js": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "svg.js": "^2.6.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/table/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/table/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.1.11", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser": { - "version": "5.10.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "acorn": "^8.5.0" - }, - "peerDependenciesMeta": { - "acorn": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/terser/node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/through": { - "version": "2.3.8", - "dev": true, - "license": "MIT" - }, - "node_modules/thunky": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-node": { - "version": "8.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - }, - "peerDependencies": { - "typescript": ">=2.0" - } - }, - "node_modules/ts-node/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ts-node/node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.0.3", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "dev": true, - "license": "Unlicense" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-assert": { - "version": "1.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/typescript": { - "version": "4.5.5", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ua-parser-js": { - "version": "0.7.31", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "3.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "builtins": "^1.0.3" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/void-elements": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vue-eslint-parser": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", - "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", - "dev": true, - "dependencies": { - "debug": "^4.3.2", - "eslint-scope": "^7.0.0", - "eslint-visitor-keys": "^3.1.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/watchpack": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "dev": true, - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webdriver-js-extender": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager": { - "version": "12.1.8", - "dev": true, - "license": "MIT", - "dependencies": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - }, - "bin": { - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-regex": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-styles": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/chalk": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" - }, - "node_modules/webdriver-manager/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/webdriver-manager/node_modules/semver": { - "version": "5.7.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/webdriver-manager/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/supports-color": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/webpack": { - "version": "5.67.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.2.2", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.7.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/serve-index": "^1.9.1", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", - "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "portfinder": "^1.0.28", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/webpack-dev-server/node_modules/array-union": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/del": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/globby": { - "version": "11.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/is-path-cwd": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/is-path-inside": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/webpack-merge": { - "version": "5.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-subresource-integrity": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "typed-assert": "^1.0.8" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", - "webpack": "^5.12.0" - }, - "peerDependenciesMeta": { - "html-webpack-plugin": { - "optional": true - } - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/wide-align": { - "version": "1.1.5", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wildcard": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", - "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/ws": { - "version": "8.2.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml2js": { - "version": "0.4.23", - "dev": true, - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/zone.js": { - "version": "0.11.4", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - } - } - }, "dependencies": { "@ampproject/remapping": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.1.tgz", + "integrity": "sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA==", "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.0" @@ -15504,6 +15,8 @@ }, "@angular-devkit/architect": { "version": "0.1302.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.3.tgz", + "integrity": "sha512-0m8jMKrFfIqsYt33zTUwSmyekyfuS67hna08RQ6USjzWQSE3z4S8ulCUARSjM6AzdMblX+whfy56nJUpT17NSA==", "dev": true, "requires": { "@angular-devkit/core": "13.2.3", @@ -15512,6 +25,8 @@ "dependencies": { "rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -15519,12 +34,16 @@ }, "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } }, "@angular-devkit/build-angular": { "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.2.3.tgz", + "integrity": "sha512-cZ2gRcMRgW3t1WCeP+2D/wmr2M+BR/RICAh0wL9irIdypWAzIFt3Z2+2R/HmgAAxoEkdUMIfB9AnkYmwRVgFeA==", "dev": true, "requires": { "@ampproject/remapping": "1.1.1", @@ -15596,6 +115,8 @@ "dependencies": { "@ampproject/remapping": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", + "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -15604,6 +125,8 @@ }, "@babel/core": { "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", @@ -15625,12 +148,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/generator": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "requires": { "@babel/types": "^7.16.8", @@ -15640,6 +167,8 @@ }, "minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -15647,6 +176,8 @@ }, "rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -15654,16 +185,22 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } }, "source-map": { "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true }, "source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -15672,18 +209,24 @@ "dependencies": { "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "tslib": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } }, "@angular-devkit/build-webpack": { "version": "0.1302.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1302.3.tgz", + "integrity": "sha512-+JYH1lWU0UOjaWYxpoR2VLsdcb6nG9Gv+M1gH+kT0r2sAKOFaHnrksbOvca3EhDoaMa2b9LSGEE0OcSHWnN+eQ==", "dev": true, "requires": { "@angular-devkit/architect": "0.1302.3", @@ -15692,6 +235,8 @@ "dependencies": { "rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -15699,12 +244,16 @@ }, "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } }, "@angular-devkit/core": { "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz", + "integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==", "dev": true, "requires": { "ajv": "8.9.0", @@ -15717,6 +266,8 @@ "dependencies": { "rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -15724,12 +275,16 @@ }, "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } }, "@angular-devkit/schematics": { "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.3.tgz", + "integrity": "sha512-+dyC4iKV0huvpjiuz4uyjLNK3FsCIp/Ghv5lXvhG6yok/dCAubsJItJOxi6G16aVCzG/E9zbsDfm9fNMyVOkgQ==", "dev": true, "requires": { "@angular-devkit/core": "13.2.3", @@ -15741,6 +296,8 @@ "dependencies": { "rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -15748,6 +305,8 @@ }, "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } @@ -15853,6 +412,8 @@ }, "@angular-slider/ngx-slider": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@angular-slider/ngx-slider/-/ngx-slider-2.0.4.tgz", + "integrity": "sha512-EccMcGlb2bJJWikXZBjRwdWgRiYYmWd3UCgf8l3KAlyqPAxPVgxI73wqipp4/nZwidq53a9s3OB+KC79enfM2A==", "requires": { "detect-passive-events": "^2.0.3", "rxjs": "^6.5.2", @@ -15860,35 +421,47 @@ }, "dependencies": { "tslib": { - "version": "1.14.1" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" } } }, "@angular/animations": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.2.2.tgz", + "integrity": "sha512-qX8LAMuCJaueHBVyuwKtqunx96G0Dr26k7y5Z03VTcscYst4Ib4V2d4i5dwn3HS82DehFdO86cm3Hi2PqE/qww==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/cdk": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.2.2.tgz", + "integrity": "sha512-cT5DIaz+NI9IGb3X61Wh26+L6zdRcOXT1BP37iRbK2Qa2qM8/0VNeK6hrBBIblyoHKR/WUmRlS8XYf6mmArpZw==", "requires": { "parse5": "^5.0.0", "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/cli": { "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.3.tgz", + "integrity": "sha512-QsakxpdQuO67u4fQNuOASqabYUO9gJb/5CpUGpWbuBzru0/9CMEF1CtXoF4EoDiwa5sJMirz3SJMKhtzFlv1cQ==", "dev": true, "requires": { "@angular-devkit/architect": "0.1302.3", @@ -15914,28 +487,38 @@ }, "@angular/common": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.2.2.tgz", + "integrity": "sha512-56C/bheNLKtTCyQUZCiYtKbBIZN9jj6rjFILPtJCGls3cBCxp7t9tIdoLiQG/wVQRmaxdj1ioLT+sCWz7mLtQw==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/compiler": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.2.2.tgz", + "integrity": "sha512-XXQtB0/e7pR2LPrHmpEiTU72SX4xxHGy91vYWIj1JCjSn0fYF7vtHzSJPXDvkbnkNow/PXXzJJYaU1ctdMZPcA==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/compiler-cli": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.2.tgz", + "integrity": "sha512-tuOIcEEKVIht+mKrj0rtX3I8gc+ByPjzpCZhFQRggxM6xbKJIToO1zERbEGKrZ+sUJ6BB5KLvscDy+Pddy3b8w==", "dev": true, "requires": { "@babel/core": "^7.8.6", @@ -15952,97 +535,133 @@ "dependencies": { "tslib": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } }, "@angular/core": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.2.2.tgz", + "integrity": "sha512-zpctw0BxIVOsRFnckchK15SD1L8tzhf5GzwIDaM6+VylDQj1uYkm8mvAjJTQZyUuApomoFet2Rfj7XQPV+cNSQ==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/flex-layout": { "version": "13.0.0-beta.38", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-13.0.0-beta.38.tgz", + "integrity": "sha512-kcWb7CcoHbvw7fjo/knizWVmSSmvaTnr8v1ML6zOdxu1PK9UPPOcOS8RTm6fy61zoC2LABivP1/6Z2jF5XfpdQ==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/forms": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.2.2.tgz", + "integrity": "sha512-T61W4Ay9X9qhxjc6lLqpNFeHrGKwg2mqdsZ3zIm/c7oKo37mgl9TB5kkrtnS+205r3N2hF4ICnGFZ4a/egUP/g==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/language-service": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.2.2.tgz", + "integrity": "sha512-2P5+wRsbHgpI2rVeFwnsLWxyntUiw8kG9Tqh5BkVDqtQovbYtzFiaMkf5TFz/g938JBBgeRQzvXr1kQhEidAWQ==", "dev": true }, "@angular/material": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-13.2.2.tgz", + "integrity": "sha512-YAjPp2+/wuEOPfkAxdRVdbWHiK4P3DgMZa9qP/NizN2lTXNrftEfD09ZlPIFMZRnnExezJ2LnO7eyELpc1VSKg==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/platform-browser": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.2.2.tgz", + "integrity": "sha512-M7gWC8fFCPc/CRcHCzqe/j7WzwAUMeKt9vwlK633XnesHBoqZdYgbb3YHHc6WPVU0YI09Nb/Hm5sezEKmjUmPg==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/platform-browser-dynamic": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.2.2.tgz", + "integrity": "sha512-lj6xwat0StLp+ROFqXU62upwHQhlxaQi0djhrS+DGKUK0Xu9bkBeaSCfBFgS78jPm1SwL8Xztu9/vuDAHLRrqw==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@angular/router": { "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.2.2.tgz", + "integrity": "sha512-dt2b9/kGJAkmOqUmUD3aKlp4pGpdqLwB0zmhUYF3ktNEcQaPf4ZjWT/4jhy09gFL+TKOHG5OQW9GxBbhWI4bSg==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "@assemblyscript/loader": { "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, "@babel/code-frame": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "requires": { "@babel/highlight": "^7.16.7" @@ -16050,10 +669,14 @@ }, "@babel/compat-data": { "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", + "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", "dev": true }, "@babel/core": { "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", + "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", "dev": true, "requires": { "@ampproject/remapping": "^2.0.0", @@ -16075,12 +698,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/generator": { "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", + "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", "dev": true, "requires": { "@babel/types": "^7.17.0", @@ -16090,12 +717,16 @@ "dependencies": { "source-map": { "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true } } }, "@babel/helper-annotate-as-pure": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16103,6 +734,8 @@ }, "@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", "dev": true, "requires": { "@babel/helper-explode-assignable-expression": "^7.16.7", @@ -16111,6 +744,8 @@ }, "@babel/helper-compilation-targets": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", "dev": true, "requires": { "@babel/compat-data": "^7.16.4", @@ -16121,12 +756,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/helper-create-class-features-plugin": { "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz", + "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", @@ -16140,6 +779,8 @@ }, "@babel/helper-create-regexp-features-plugin": { "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", @@ -16148,6 +789,8 @@ }, "@babel/helper-define-polyfill-provider": { "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.13.0", @@ -16162,12 +805,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/helper-environment-visitor": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16175,6 +822,8 @@ }, "@babel/helper-explode-assignable-expression": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16182,6 +831,8 @@ }, "@babel/helper-function-name": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.7", @@ -16191,6 +842,8 @@ }, "@babel/helper-get-function-arity": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16198,6 +851,8 @@ }, "@babel/helper-hoist-variables": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16205,6 +860,8 @@ }, "@babel/helper-member-expression-to-functions": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", + "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16212,6 +869,8 @@ }, "@babel/helper-module-imports": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16219,6 +878,8 @@ }, "@babel/helper-module-transforms": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", @@ -16233,6 +894,8 @@ }, "@babel/helper-optimise-call-expression": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16240,10 +903,14 @@ }, "@babel/helper-plugin-utils": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", "dev": true }, "@babel/helper-remap-async-to-generator": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", @@ -16253,6 +920,8 @@ }, "@babel/helper-replace-supers": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", @@ -16264,6 +933,8 @@ }, "@babel/helper-simple-access": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16271,6 +942,8 @@ }, "@babel/helper-skip-transparent-expression-wrappers": { "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", "dev": true, "requires": { "@babel/types": "^7.16.0" @@ -16278,6 +951,8 @@ }, "@babel/helper-split-export-declaration": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", "dev": true, "requires": { "@babel/types": "^7.16.7" @@ -16285,14 +960,20 @@ }, "@babel/helper-validator-identifier": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true }, "@babel/helper-validator-option": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", "dev": true }, "@babel/helper-wrap-function": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", "dev": true, "requires": { "@babel/helper-function-name": "^7.16.7", @@ -16303,6 +984,8 @@ }, "@babel/helpers": { "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", + "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", "dev": true, "requires": { "@babel/template": "^7.16.7", @@ -16312,6 +995,8 @@ }, "@babel/highlight": { "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -16321,10 +1006,14 @@ }, "@babel/parser": { "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16332,6 +1021,8 @@ }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16341,6 +1032,8 @@ }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16350,6 +1043,8 @@ }, "@babel/plugin-proposal-class-properties": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.16.7", @@ -16358,6 +1053,8 @@ }, "@babel/plugin-proposal-class-static-block": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", + "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.16.7", @@ -16367,6 +1064,8 @@ }, "@babel/plugin-proposal-dynamic-import": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16375,6 +1074,8 @@ }, "@babel/plugin-proposal-export-namespace-from": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16383,6 +1084,8 @@ }, "@babel/plugin-proposal-json-strings": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16391,6 +1094,8 @@ }, "@babel/plugin-proposal-logical-assignment-operators": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16399,6 +1104,8 @@ }, "@babel/plugin-proposal-nullish-coalescing-operator": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16407,6 +1114,8 @@ }, "@babel/plugin-proposal-numeric-separator": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16415,6 +1124,8 @@ }, "@babel/plugin-proposal-object-rest-spread": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", + "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", "dev": true, "requires": { "@babel/compat-data": "^7.16.4", @@ -16426,6 +1137,8 @@ }, "@babel/plugin-proposal-optional-catch-binding": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16434,6 +1147,8 @@ }, "@babel/plugin-proposal-optional-chaining": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16443,6 +1158,8 @@ }, "@babel/plugin-proposal-private-methods": { "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.16.10", @@ -16451,6 +1168,8 @@ }, "@babel/plugin-proposal-private-property-in-object": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", @@ -16461,6 +1180,8 @@ }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.7", @@ -16469,6 +1190,8 @@ }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16476,6 +1199,8 @@ }, "@babel/plugin-syntax-class-properties": { "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.12.13" @@ -16483,6 +1208,8 @@ }, "@babel/plugin-syntax-class-static-block": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" @@ -16490,6 +1217,8 @@ }, "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16497,6 +1226,8 @@ }, "@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.3" @@ -16504,6 +1235,8 @@ }, "@babel/plugin-syntax-json-strings": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16511,6 +1244,8 @@ }, "@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -16518,6 +1253,8 @@ }, "@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16525,6 +1262,8 @@ }, "@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -16532,6 +1271,8 @@ }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16539,6 +1280,8 @@ }, "@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16546,6 +1289,8 @@ }, "@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -16553,6 +1298,8 @@ }, "@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" @@ -16560,6 +1307,8 @@ }, "@babel/plugin-syntax-top-level-await": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" @@ -16567,6 +1316,8 @@ }, "@babel/plugin-transform-arrow-functions": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16574,6 +1325,8 @@ }, "@babel/plugin-transform-async-to-generator": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.16.7", @@ -16583,6 +1336,8 @@ }, "@babel/plugin-transform-block-scoped-functions": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16590,6 +1345,8 @@ }, "@babel/plugin-transform-block-scoping": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16597,6 +1354,8 @@ }, "@babel/plugin-transform-classes": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", @@ -16611,6 +1370,8 @@ }, "@babel/plugin-transform-computed-properties": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16618,6 +1379,8 @@ }, "@babel/plugin-transform-destructuring": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", + "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16625,6 +1388,8 @@ }, "@babel/plugin-transform-dotall-regex": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.7", @@ -16633,6 +1398,8 @@ }, "@babel/plugin-transform-duplicate-keys": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16640,6 +1407,8 @@ }, "@babel/plugin-transform-exponentiation-operator": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", "dev": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", @@ -16648,6 +1417,8 @@ }, "@babel/plugin-transform-for-of": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16655,6 +1426,8 @@ }, "@babel/plugin-transform-function-name": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.16.7", @@ -16664,6 +1437,8 @@ }, "@babel/plugin-transform-literals": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16671,6 +1446,8 @@ }, "@babel/plugin-transform-member-expression-literals": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16678,6 +1455,8 @@ }, "@babel/plugin-transform-modules-amd": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.7", @@ -16687,6 +1466,8 @@ }, "@babel/plugin-transform-modules-commonjs": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", + "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.7", @@ -16697,6 +1478,8 @@ }, "@babel/plugin-transform-modules-systemjs": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", + "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.16.7", @@ -16708,6 +1491,8 @@ }, "@babel/plugin-transform-modules-umd": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.7", @@ -16716,6 +1501,8 @@ }, "@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.7" @@ -16723,6 +1510,8 @@ }, "@babel/plugin-transform-new-target": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16730,6 +1519,8 @@ }, "@babel/plugin-transform-object-super": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16738,6 +1529,8 @@ }, "@babel/plugin-transform-parameters": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16745,6 +1538,8 @@ }, "@babel/plugin-transform-property-literals": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16752,6 +1547,8 @@ }, "@babel/plugin-transform-regenerator": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" @@ -16759,6 +1556,8 @@ }, "@babel/plugin-transform-reserved-words": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16766,6 +1565,8 @@ }, "@babel/plugin-transform-runtime": { "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", + "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.16.7", @@ -16778,12 +1579,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/plugin-transform-shorthand-properties": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16791,6 +1596,8 @@ }, "@babel/plugin-transform-spread": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7", @@ -16799,6 +1606,8 @@ }, "@babel/plugin-transform-sticky-regex": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16806,6 +1615,8 @@ }, "@babel/plugin-transform-template-literals": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16813,6 +1624,8 @@ }, "@babel/plugin-transform-typeof-symbol": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16820,6 +1633,8 @@ }, "@babel/plugin-transform-unicode-escapes": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -16827,6 +1642,8 @@ }, "@babel/plugin-transform-unicode-regex": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.7", @@ -16835,6 +1652,8 @@ }, "@babel/preset-env": { "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", "dev": true, "requires": { "@babel/compat-data": "^7.16.8", @@ -16915,12 +1734,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/preset-modules": { "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -16932,6 +1755,8 @@ }, "@babel/runtime": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", + "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" @@ -16949,6 +1774,8 @@ }, "@babel/template": { "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", @@ -16958,6 +1785,8 @@ }, "@babel/traverse": { "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", + "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", @@ -16974,6 +1803,8 @@ }, "@babel/types": { "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -16982,6 +1813,8 @@ }, "@cordobo/qrcode": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@cordobo/qrcode/-/qrcode-1.5.0.tgz", + "integrity": "sha512-aZ5n3MYw10t4v68EGvRGE1DL7iWfAiTUy4MSZRoqjHTRYdjX40sYgJf48NZa6zZeXVuJOEB/1Ni9KzS+C/EC0w==", "requires": { "dijkstrajs": "^1.0.1", "encode-utf8": "^1.0.3", @@ -16991,6 +1824,8 @@ }, "@csstools/postcss-progressive-custom-properties": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.1.0.tgz", + "integrity": "sha512-DO76V3295AqhjJZvgeaDP5GAGAat4g6wYfF8X+1n+76MpJat8ffY5bCJ9eSUqFY71nImxXgaDTRYJcRnA9oo7g==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -16998,6 +1833,8 @@ }, "@discoveryjs/json-ext": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", "dev": true }, "@eslint/eslintrc": { @@ -17069,6 +1906,8 @@ }, "@gar/promisify": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", + "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", "dev": true }, "@humanwhocodes/config-array": { @@ -17090,6 +1929,8 @@ }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", @@ -17101,18 +1942,26 @@ }, "@istanbuljs/schema": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jridgewell/resolve-uri": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", "dev": true }, "@jridgewell/sourcemap-codec": { "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -17121,11 +1970,14 @@ }, "@ngtools/webpack": { "version": "13.2.3", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.3.tgz", + "integrity": "sha512-wooUZiV92QyoeFxkhqIwH/cfiAAAn+l8fEEuaaEIfJtpjpbShvvlboEVsqb28soeGiFJfLcmsZM3mUFgsG4QBQ==", + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", @@ -17134,10 +1986,14 @@ }, "@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", @@ -17146,6 +2002,8 @@ }, "@npmcli/fs": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "dev": true, "requires": { "@gar/promisify": "^1.0.1", @@ -17154,6 +2012,8 @@ }, "@npmcli/git": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", "dev": true, "requires": { "@npmcli/promise-spawn": "^1.3.2", @@ -17168,10 +2028,14 @@ "dependencies": { "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, "which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -17181,6 +2045,8 @@ }, "@npmcli/installed-package-contents": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", "dev": true, "requires": { "npm-bundled": "^1.1.1", @@ -17189,6 +2055,8 @@ }, "@npmcli/move-file": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "dev": true, "requires": { "mkdirp": "^1.0.4", @@ -17197,16 +2065,22 @@ "dependencies": { "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true } } }, "@npmcli/node-gyp": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", "dev": true }, "@npmcli/promise-spawn": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", "dev": true, "requires": { "infer-owner": "^1.0.4" @@ -17214,6 +2088,8 @@ }, "@npmcli/run-script": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", "dev": true, "requires": { "@npmcli/node-gyp": "^1.0.2", @@ -17535,6 +2411,8 @@ }, "@schematics/angular": { "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.3.tgz", + "integrity": "sha512-jloooGC7eco9AKxlIMMqFRptJYzZ0jNRBStWOp2dCISg6rmOKqpxbsHLtYFQIT1PnlomSxtKDAgYGQMDi9zhXw==", "dev": true, "requires": { "@angular-devkit/core": "13.2.3", @@ -17544,6 +2422,8 @@ }, "@socket.io/base64-arraybuffer": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", "dev": true }, "@swc-node/core": { @@ -17727,10 +2607,14 @@ }, "@tootallnate/once": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, "@types/body-parser": { "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dev": true, "requires": { "@types/connect": "*", @@ -17739,6 +2623,8 @@ }, "@types/bonjour": { "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", "dev": true, "requires": { "@types/node": "*" @@ -17746,10 +2632,14 @@ }, "@types/component-emitter": { "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", "dev": true }, "@types/connect": { "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dev": true, "requires": { "@types/node": "*" @@ -17757,6 +2647,8 @@ }, "@types/connect-history-api-fallback": { "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", "dev": true, "requires": { "@types/express-serve-static-core": "*", @@ -17765,10 +2657,14 @@ }, "@types/cookie": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", "dev": true }, "@types/cors": { "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, "@types/eslint": { @@ -17783,6 +2679,8 @@ }, "@types/eslint-scope": { "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "dev": true, "requires": { "@types/eslint": "*", @@ -17791,10 +2689,14 @@ }, "@types/estree": { "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, "@types/express": { "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", "dev": true, "requires": { "@types/body-parser": "*", @@ -17805,6 +2707,8 @@ }, "@types/express-serve-static-core": { "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", "dev": true, "requires": { "@types/node": "*", @@ -17814,6 +2718,8 @@ }, "@types/http-proxy": { "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", "dev": true, "requires": { "@types/node": "*" @@ -17821,10 +2727,14 @@ }, "@types/jasmine": { "version": "3.6.11", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz", + "integrity": "sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==", "dev": true }, "@types/jasminewd2": { "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", + "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", "dev": true, "requires": { "@types/jasmine": "*" @@ -17832,6 +2742,8 @@ }, "@types/json-schema": { "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/json5": { @@ -17842,6 +2754,8 @@ }, "@types/mime": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, "@types/minimist": { @@ -17852,6 +2766,8 @@ }, "@types/node": { "version": "12.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.7.tgz", + "integrity": "sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==", "dev": true }, "@types/normalize-package-data": { @@ -17862,6 +2778,8 @@ }, "@types/parse-json": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, "@types/prettier": { @@ -17872,26 +2790,38 @@ }, "@types/q": { "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", "dev": true }, "@types/qs": { "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "dev": true }, "@types/range-parser": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, "@types/retry": { "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", "dev": true }, "@types/selenium-webdriver": { "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.19.tgz", + "integrity": "sha512-OFUilxQg+rWL2FMxtmIgCkUDlJB6pskkpvmew7yeXfzzsOBb5rc+y2+DjHm+r3r1ZPPcJefK3DveNSYWGiy68g==", "dev": true }, "@types/serve-index": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", "dev": true, "requires": { "@types/express": "*" @@ -17899,6 +2829,8 @@ }, "@types/serve-static": { "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", "dev": true, "requires": { "@types/mime": "^1", @@ -17907,6 +2839,8 @@ }, "@types/sockjs": { "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", "dev": true, "requires": { "@types/node": "*" @@ -17914,6 +2848,8 @@ }, "@types/ws": { "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", "dev": true, "requires": { "@types/node": "*" @@ -18102,6 +3038,8 @@ }, "@webassemblyjs/ast": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.1", @@ -18110,18 +3048,26 @@ }, "@webassemblyjs/floating-point-hex-parser": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "@webassemblyjs/helper-buffer": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "@webassemblyjs/helper-numbers": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "requires": { "@webassemblyjs/floating-point-hex-parser": "1.11.1", @@ -18131,10 +3077,14 @@ }, "@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "@webassemblyjs/helper-wasm-section": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -18145,6 +3095,8 @@ }, "@webassemblyjs/ieee754": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" @@ -18152,6 +3104,8 @@ }, "@webassemblyjs/leb128": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" @@ -18159,10 +3113,14 @@ }, "@webassemblyjs/utf8": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "@webassemblyjs/wasm-edit": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -18177,6 +3135,8 @@ }, "@webassemblyjs/wasm-gen": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -18188,6 +3148,8 @@ }, "@webassemblyjs/wasm-opt": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -18198,6 +3160,8 @@ }, "@webassemblyjs/wasm-parser": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -18210,6 +3174,8 @@ }, "@webassemblyjs/wast-printer": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.11.1", @@ -18218,26 +3184,38 @@ }, "@xtuc/ieee754": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, "@xtuc/long": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, "@yarnpkg/lockfile": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true }, "abab": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, "abbrev": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "accepts": { "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "requires": { "mime-types": "~2.1.34", @@ -18252,18 +3230,20 @@ }, "acorn-import-assertions": { "version": "1.8.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "adjust-sourcemap-loader": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, "requires": { "loader-utils": "^2.0.0", @@ -18272,6 +3252,8 @@ "dependencies": { "loader-utils": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -18283,10 +3265,14 @@ }, "adm-zip": { "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", "dev": true }, "agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "requires": { "debug": "4" @@ -18294,6 +3280,8 @@ }, "agentkeepalive": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz", + "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==", "dev": true, "requires": { "debug": "^4.1.0", @@ -18303,6 +3291,8 @@ }, "aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -18311,6 +3301,8 @@ }, "ajv": { "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -18321,6 +3313,8 @@ }, "ajv-formats": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "requires": { "ajv": "^8.0.0" @@ -18328,6 +3322,8 @@ }, "ajv-keywords": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.3" @@ -18335,22 +3331,30 @@ }, "angularx-qrcode": { "version": "13.0.3", + "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-13.0.3.tgz", + "integrity": "sha512-KzalrRy2MczdApSq29k2TBOml6taQ7NqdgZDNgPSQBd5/27+l3xHkOkhhDOmGruSyCcT2KdB8qeOT/6K48c+xw==", "requires": { "@cordobo/qrcode": "1.5.0", "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "ansi-colors": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { "type-fest": "^0.21.3" @@ -18358,13 +3362,19 @@ }, "ansi-html-community": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true }, "ansi-regex": { - "version": "5.0.1" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -18372,6 +3382,8 @@ }, "anymatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -18380,6 +3392,8 @@ }, "apexcharts": { "version": "3.33.1", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.33.1.tgz", + "integrity": "sha512-5aVzrgJefd8EH4w7oRmuOhA3+cxJxQg27cYg3ANVGvPCOB4AY3mVVNtFHRFaIq7bv8ws4GRaA9MWfzoWQw3MPQ==", "requires": { "svg.draggable.js": "^2.2.2", "svg.easing.js": "^2.0.0", @@ -18391,10 +3405,14 @@ }, "aproba": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "dev": true }, "are-we-there-yet": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", + "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", "dev": true, "requires": { "delegates": "^1.0.0", @@ -18403,10 +3421,14 @@ }, "arg": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -18414,16 +3436,22 @@ "dependencies": { "sprintf-js": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true } } }, "array-flatten": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, "array-union": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "requires": { "array-uniq": "^1.0.1" @@ -18431,14 +3459,20 @@ }, "array-uniq": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true }, "arrify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, "asn1": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -18446,6 +3480,8 @@ }, "assert-plus": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "astral-regex": { @@ -18456,6 +3492,8 @@ }, "async": { "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -18463,6 +3501,8 @@ }, "asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "at-least-node": { @@ -18473,10 +3513,14 @@ }, "atob": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "autoprefixer": { "version": "10.4.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", + "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", "dev": true, "requires": { "browserslist": "^4.19.1", @@ -18489,14 +3533,20 @@ }, "aws-sign2": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, "babel-loader": { "version": "8.2.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", + "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", @@ -18507,6 +3557,8 @@ "dependencies": { "json5": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -18514,6 +3566,8 @@ }, "loader-utils": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -18525,6 +3579,8 @@ }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", "dev": true, "requires": { "object.assign": "^4.1.0" @@ -18532,6 +3588,8 @@ }, "babel-plugin-istanbul": { "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -18543,6 +3601,8 @@ }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", "dev": true, "requires": { "@babel/compat-data": "^7.13.11", @@ -18552,12 +3612,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "babel-plugin-polyfill-corejs3": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", "dev": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.3.1", @@ -18566,6 +3630,8 @@ }, "babel-plugin-polyfill-regenerator": { "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", "dev": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.3.1" @@ -18573,22 +3639,32 @@ }, "balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, "base64id": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true }, "batch": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { "tweetnacl": "^0.14.3" @@ -18596,14 +3672,20 @@ }, "big.js": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, "binary-extensions": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "bl": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "requires": { "buffer": "^5.5.0", @@ -18613,6 +3695,8 @@ }, "blocking-proxy": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -18620,6 +3704,8 @@ }, "body-parser": { "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", "dev": true, "requires": { "bytes": "3.1.1", @@ -18636,6 +3722,8 @@ "dependencies": { "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -18643,12 +3731,16 @@ }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } }, "bonjour": { "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", "dev": true, "requires": { "array-flatten": "^2.1.0", @@ -18661,10 +3753,14 @@ }, "boolbase": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, "brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -18673,6 +3769,8 @@ }, "braces": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -18680,6 +3778,8 @@ }, "browserslist": { "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "requires": { "caniuse-lite": "^1.0.30001286", @@ -18691,6 +3791,8 @@ }, "browserstack": { "version": "1.6.1", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", + "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", "dev": true, "requires": { "https-proxy-agent": "^2.2.1" @@ -18698,6 +3800,8 @@ "dependencies": { "agent-base": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -18705,6 +3809,8 @@ }, "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -18712,6 +3818,8 @@ }, "https-proxy-agent": { "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", @@ -18722,6 +3830,8 @@ }, "buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { "base64-js": "^1.3.1", @@ -18730,22 +3840,32 @@ }, "buffer-from": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "buffer-indexof": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, "builtins": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "dev": true }, "bytes": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "dev": true }, "cacache": { "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", "dev": true, "requires": { "@npmcli/fs": "^1.0.0", @@ -18770,12 +3890,16 @@ "dependencies": { "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true } } }, "call-bind": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -18784,10 +3908,14 @@ }, "callsites": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelcase-keys": { @@ -18802,15 +3930,21 @@ } }, "caniuse-lite": { - "version": "1.0.30001312", + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", "dev": true }, "caseless": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -18820,10 +3954,14 @@ }, "chardet": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chokidar": { "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -18838,23 +3976,32 @@ }, "chownr": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true }, "chrome-trace-event": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, "circular-dependency-plugin": { "version": "5.2.2", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true }, "clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { "restore-cursor": "^3.1.0" @@ -18862,6 +4009,8 @@ }, "cli-spinners": { "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true }, "cli-truncate": { @@ -18910,10 +4059,14 @@ }, "cli-width": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, "cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -18922,10 +4075,14 @@ }, "clone": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, "clone-deep": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { "is-plain-object": "^2.0.4", @@ -18944,6 +4101,8 @@ }, "color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" @@ -18951,10 +4110,14 @@ }, "color-name": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "color-support": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, "colord": { @@ -18965,14 +4128,20 @@ }, "colorette": { "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, "colors": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -18980,6 +4149,8 @@ }, "commander": { "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "common-tags": { @@ -18990,14 +4161,20 @@ }, "commondir": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "component-emitter": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "compressible": { "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "requires": { "mime-db": ">= 1.43.0 < 2" @@ -19005,6 +4182,8 @@ }, "compression": { "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, "requires": { "accepts": "~1.3.5", @@ -19018,10 +4197,14 @@ "dependencies": { "bytes": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true }, "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -19029,16 +4212,22 @@ }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } }, "concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "connect": { "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { "debug": "2.6.9", @@ -19049,6 +4238,8 @@ "dependencies": { "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -19056,20 +4247,28 @@ }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } }, "connect-history-api-fallback": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, "console-control-strings": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, "content-disposition": { "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "requires": { "safe-buffer": "5.2.1" @@ -19077,16 +4276,22 @@ "dependencies": { "safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true } } }, "content-type": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "convert-source-map": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -19094,14 +4299,20 @@ }, "cookie": { "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true }, "cookie-signature": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, "copy-anything": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, "requires": { "is-what": "^3.14.1" @@ -19109,6 +4320,8 @@ }, "copy-webpack-plugin": { "version": "10.2.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.1.tgz", + "integrity": "sha512-nr81NhCAIpAWXGCK5thrKmfCQ6GDY0L5RN0U+BnIn/7Us55+UCex5ANNsNKmIVtDRnk0Ecf+/kzp9SUVrrBMLg==", "dev": true, "requires": { "fast-glob": "^3.2.7", @@ -19121,10 +4334,14 @@ "dependencies": { "array-union": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true }, "glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { "is-glob": "^4.0.3" @@ -19132,6 +4349,8 @@ }, "globby": { "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", "dev": true, "requires": { "array-union": "^3.0.1", @@ -19144,6 +4363,8 @@ }, "schema-utils": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -19156,10 +4377,14 @@ }, "core-js": { "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", "dev": true }, "core-js-compat": { "version": "3.21.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz", + "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==", "dev": true, "requires": { "browserslist": "^4.19.1", @@ -19168,6 +4393,8 @@ "dependencies": { "semver": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", "dev": true } } @@ -19180,10 +4407,14 @@ }, "core-util-is": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, "cors": { "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, "requires": { "object-assign": "^4", @@ -19192,6 +4423,8 @@ }, "cosmiconfig": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", @@ -19203,6 +4436,8 @@ }, "critters": { "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -19215,6 +4450,8 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -19222,6 +4459,8 @@ }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -19230,6 +4469,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -19237,18 +4478,26 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "parse5": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -19258,6 +4507,8 @@ }, "cross-spawn": { "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -19267,6 +4518,8 @@ "dependencies": { "which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -19276,6 +4529,8 @@ }, "css": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", "dev": true, "requires": { "inherits": "^2.0.4", @@ -19285,12 +4540,16 @@ "dependencies": { "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "css-blank-pseudo": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.9" @@ -19304,6 +4563,8 @@ }, "css-has-pseudo": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.9" @@ -19311,6 +4572,8 @@ }, "css-loader": { "version": "6.5.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", + "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", "dev": true, "requires": { "icss-utils": "^5.1.0", @@ -19325,11 +4588,14 @@ }, "css-prefers-color-scheme": { "version": "6.0.3", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true }, "css-select": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", "dev": true, "requires": { "boolbase": "^1.0.0", @@ -19341,22 +4607,32 @@ }, "css-what": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", "dev": true }, "cssdb": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", + "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", "dev": true }, "cssesc": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, "custom-event": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, "dashdash": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -19364,10 +4640,14 @@ }, "date-format": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", + "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", "dev": true }, "debug": { "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -19375,6 +4655,8 @@ }, "decamelize": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "decamelize-keys": { @@ -19397,10 +4679,14 @@ }, "decode-uri-component": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", "dev": true }, "deep-equal": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", "dev": true, "requires": { "is-arguments": "^1.0.4", @@ -19419,6 +4705,8 @@ }, "default-gateway": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, "requires": { "execa": "^5.0.0" @@ -19426,6 +4714,8 @@ }, "defaults": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", "dev": true, "requires": { "clone": "^1.0.2" @@ -19433,10 +4723,14 @@ }, "define-lazy-prop": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true }, "define-properties": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -19444,6 +4738,8 @@ }, "del": { "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", "dev": true, "requires": { "globby": "^5.0.0", @@ -19457,6 +4753,8 @@ "dependencies": { "rimraf": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -19466,50 +4764,74 @@ }, "delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "delegates": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, "depd": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true }, "dependency-graph": { "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", "dev": true }, "destroy": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", "dev": true }, "detect-it": { - "version": "4.0.1" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz", + "integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug==" }, "detect-node": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, "detect-passive-events": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-passive-events/-/detect-passive-events-2.0.3.tgz", + "integrity": "sha512-QN/1X65Axis6a9D8qg8Py9cwY/fkWAmAH/edTbmLMcv4m5dboLJ7LcAi8CfaCON2tjk904KwKX/HTdsHC6yeRg==", "requires": { "detect-it": "^4.0.1" } }, "di": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, "diff": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, "dijkstrajs": { - "version": "1.0.2" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" }, "dir-glob": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { "path-type": "^4.0.0" @@ -19523,10 +4845,14 @@ }, "dns-equal": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", "dev": true }, "dns-packet": { "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "dev": true, "requires": { "ip": "^1.1.0", @@ -19535,6 +4861,8 @@ }, "dns-txt": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", "dev": true, "requires": { "buffer-indexof": "^1.0.0" @@ -19551,6 +4879,8 @@ }, "dom-serialize": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", "dev": true, "requires": { "custom-event": "~1.0.0", @@ -19561,6 +4891,8 @@ }, "dom-serializer": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", "dev": true, "requires": { "domelementtype": "^2.0.1", @@ -19570,10 +4902,14 @@ }, "domelementtype": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", "dev": true }, "domhandler": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", "dev": true, "requires": { "domelementtype": "^2.2.0" @@ -19581,6 +4917,8 @@ }, "domutils": { "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, "requires": { "dom-serializer": "^1.0.1", @@ -19602,6 +4940,8 @@ }, "ecc-jsbn": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { "jsbn": "~0.1.0", @@ -19610,6 +4950,8 @@ }, "ee-first": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, "ejs": { @@ -19623,24 +4965,36 @@ }, "electron-to-chromium": { "version": "1.4.68", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", + "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==", "dev": true }, "emoji-regex": { - "version": "8.0.0" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "emojis-list": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, "encode-utf8": { - "version": "1.0.3" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" }, "encodeurl": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true }, "encoding": { "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "optional": true, "requires": { @@ -19649,6 +5003,8 @@ "dependencies": { "iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "optional": true, "requires": { @@ -19668,6 +5024,8 @@ }, "engine.io": { "version": "6.1.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", + "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -19684,6 +5042,8 @@ }, "engine.io-parser": { "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", "dev": true, "requires": { "@socket.io/base64-arraybuffer": "~1.0.2" @@ -19691,6 +5051,8 @@ }, "enhanced-resolve": { "version": "5.9.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", + "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -19708,22 +5070,32 @@ }, "ent": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, "entities": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true }, "env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true }, "err-code": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "dev": true }, "errno": { "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, "optional": true, "requires": { @@ -19732,6 +5104,8 @@ }, "error-ex": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -19739,14 +5113,20 @@ }, "es-module-lexer": { "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "es6-promise": { "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", "dev": true, "requires": { "es6-promise": "^4.0.3" @@ -19754,6 +5134,8 @@ }, "esbuild": { "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.14.tgz", + "integrity": "sha512-aiK4ddv+uui0k52OqSHu4xxu+SzOim7Rlz4i25pMEiC8rlnGU0HJ9r+ZMfdWL5bzifg+nhnn7x4NSWTeehYblg==", "dev": true, "optional": true, "requires": { @@ -19777,24 +5159,153 @@ "esbuild-windows-arm64": "0.14.14" } }, - "esbuild-darwin-64": { + "esbuild-android-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.14.tgz", + "integrity": "sha512-be/Uw6DdpQiPfula1J4bdmA+wtZ6T3BRCZsDMFB5X+k0Gp8TIh9UvmAcqvKNnbRAafSaXG3jPCeXxDKqnc8hFQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.14.tgz", + "integrity": "sha512-BEexYmjWafcISK8cT6O98E3TfcLuZL8DKuubry6G54n2+bD4GkoRD6HYUOnCkfl2p7jodA+s4369IjSFSWjtHg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.14.tgz", + "integrity": "sha512-tnBKm41pDOB1GtZ8q/w26gZlLLRzVmP8fdsduYjvM+yFD7E2DLG4KbPAqFMWm4Md9B+DitBglP57FY7AznxbTg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.14.tgz", + "integrity": "sha512-Q9Rx6sgArOHalQtNwAaIzJ6dnQ8A+I7f/RsQsdkS3JrdzmnlFo8JEVofTmwVQLoIop7OKUqIVOGP4PoQcwfVMA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.14.tgz", + "integrity": "sha512-TJvq0OpLM7BkTczlyPIphcvnwrQwQDG1HqxzoYePWn26SMUAlt6wrLnEvxdbXAvNvDLVzG83kA+JimjK7aRNBA==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.14.tgz", + "integrity": "sha512-h/CrK9Baimt5VRbu8gqibWV7e1P9l+mkanQgyOgv0Ng3jHT1NVFC9e6rb1zbDdaJVmuhWX5xVliUA5bDDCcJeg==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.14.tgz", + "integrity": "sha512-IC+wAiIg/egp5OhQp4W44D9PcBOH1b621iRn1OXmlLzij9a/6BGr9NMIL4CRwz4j2kp3WNZu5sT473tYdynOuQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.14.tgz", + "integrity": "sha512-gxpOaHOPwp7zSmcKYsHrtxabScMqaTzfSQioAMUaB047YiMuDBzqVcKBG8OuESrYkGrL9DDljXr/mQNg7pbdaQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.14.tgz", + "integrity": "sha512-6QVul3RI4M5/VxVIRF/I5F+7BaxzR3DfNGoqEVSCZqUbgzHExPn+LXr5ly1C7af2Kw4AHpo+wDqx8A4ziP9avw==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.14.tgz", + "integrity": "sha512-4Jl5/+xoINKbA4cesH3f4R+q0vltAztZ6Jm8YycS8lNhN1pgZJBDxWfI6HUMIAdkKlIpR1PIkA9aXQgZ8sxFAg==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.14.tgz", + "integrity": "sha512-BitW37GxeebKxqYNl4SVuSdnIJAzH830Lr6Mkq3pBHXtzQay0vK+IeOR/Ele1GtNVJ+/f8wYM53tcThkv5SC5w==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.14.tgz", + "integrity": "sha512-vLj6p76HOZG3wfuTr5MyO3qW5iu8YdhUNxuY+tx846rPo7GcKtYSPMusQjeVEfZlJpSYoR+yrNBBxq+qVF9zrw==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.14.tgz", + "integrity": "sha512-fn8looXPQhpVqUyCBWUuPjesH+yGIyfbIQrLKG05rr1Kgm3rZD/gaYrd3Wpmf5syVZx70pKZPvdHp8OTA+y7cQ==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.14.tgz", + "integrity": "sha512-HdAnJ399pPff3SKbd8g+P4o5znseni5u5n5rJ6Z7ouqOdgbOwHe2ofZbMow17WMdNtz1IyOZk2Wo9Ve6/lZ4Rg==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.14.tgz", + "integrity": "sha512-bmDHa99ulsGnYlh/xjBEfxoGuC8CEG5OWvlgD+pF7bKKiVTbtxqVCvOGEZeoDXB+ja6AvHIbPxrEE32J+m5nqQ==", "dev": true, "optional": true }, "esbuild-wasm": { "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.14.tgz", + "integrity": "sha512-qTjK4MWnYtQHCMGg2qDUqeFYXfVvYq5qJkQTIsOV4VZCknoYePVaDTG9ygEB9Ct0kc0DWs7IrS6Ja+GjY62Kzw==", "dev": true }, + "esbuild-windows-32": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.14.tgz", + "integrity": "sha512-6tVooQcxJCNenPp5GHZBs/RLu31q4B+BuF4MEoRxswT+Eq2JGF0ZWDRQwNKB8QVIo3t6Svc5wNGez+CwKNQjBg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.14.tgz", + "integrity": "sha512-kl3BdPXh0/RD/dad41dtzj2itMUR4C6nQbXQCyYHHo4zoUoeIXhpCrSl7BAW1nv5EFL8stT1V+TQVXGZca5A2A==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.14.tgz", + "integrity": "sha512-dCm1wTOm6HIisLanmybvRKvaXZZo4yEVrHh1dY0v582GThXJOzuXGja1HIQgV09RpSHYRL3m4KoUBL00l6SWEg==", + "dev": true, + "optional": true + }, "escalade": { - "version": "3.1.1" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "eslint": { @@ -19974,8 +5485,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-prettier": { "version": "4.0.0", @@ -19988,6 +5498,8 @@ }, "eslint-scope": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -20030,6 +5542,8 @@ }, "esprima": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esquery": { @@ -20051,6 +5565,8 @@ }, "esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { "estraverse": "^5.2.0" @@ -20058,36 +5574,52 @@ "dependencies": { "estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "estraverse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "etag": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, "eventemitter-asyncresource": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", "dev": true }, "eventemitter3": { "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, "events": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "execa": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { "cross-spawn": "^7.0.3", @@ -20112,10 +5644,14 @@ }, "exit": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, "express": { "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", "dev": true, "requires": { "accepts": "~1.3.7", @@ -20152,14 +5688,20 @@ "dependencies": { "array-flatten": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "cookie": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "dev": true }, "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -20167,20 +5709,28 @@ }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true } } }, "extend": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "external-editor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { "chardet": "^0.7.0", @@ -20190,10 +5740,14 @@ }, "extsprintf": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-diff": { @@ -20204,6 +5758,8 @@ }, "fast-glob": { "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -20215,6 +5771,8 @@ }, "fast-json-stable-stringify": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { @@ -20231,6 +5789,8 @@ }, "fastq": { "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -20238,6 +5798,8 @@ }, "faye-websocket": { "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, "requires": { "websocket-driver": ">=0.5.1" @@ -20245,6 +5807,8 @@ }, "figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -20290,6 +5854,8 @@ }, "fill-range": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -20297,6 +5863,8 @@ }, "finalhandler": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "requires": { "debug": "2.6.9", @@ -20310,6 +5878,8 @@ "dependencies": { "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -20317,12 +5887,16 @@ }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } }, "find-cache-dir": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "requires": { "commondir": "^1.0.1", @@ -20332,6 +5906,8 @@ }, "find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -20356,18 +5932,26 @@ }, "flatted": { "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "follow-redirects": { "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", "dev": true }, "forever-agent": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -20377,14 +5961,20 @@ }, "forwarded": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true }, "fraction.js": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz", + "integrity": "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==", "dev": true }, "fresh": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, "fs-constants": { @@ -20406,6 +5996,8 @@ }, "fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -20413,19 +6005,27 @@ }, "fs-monkey": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", "dev": true }, "fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, "function-bind": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "functional-red-black-tree": { @@ -20436,6 +6036,8 @@ }, "gauge": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", "dev": true, "requires": { "ansi-regex": "^5.0.1", @@ -20451,13 +6053,19 @@ }, "gensync": { "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { - "version": "2.0.5" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -20467,6 +6075,8 @@ }, "get-package-type": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, "get-stdin": { @@ -20477,10 +6087,14 @@ }, "get-stream": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "getpass": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -20488,6 +6102,8 @@ }, "glob": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -20500,6 +6116,8 @@ }, "glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -20507,6 +6125,8 @@ }, "glob-to-regexp": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, "global-modules": { @@ -20539,10 +6159,14 @@ }, "globals": { "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "globby": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", "dev": true, "requires": { "array-union": "^1.0.1", @@ -20561,18 +6185,26 @@ }, "graceful-fs": { "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "handle-thing": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, "har-schema": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true }, "har-validator": { "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { "ajv": "^6.12.3", @@ -20581,6 +6213,8 @@ "dependencies": { "ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -20591,6 +6225,8 @@ }, "json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true } } @@ -20603,6 +6239,8 @@ }, "has": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -20610,6 +6248,8 @@ }, "has-ansi": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -20617,20 +6257,28 @@ "dependencies": { "ansi-regex": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true } } }, "has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "has-symbols": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, "has-tostringtag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "requires": { "has-symbols": "^1.0.2" @@ -20638,10 +6286,14 @@ }, "has-unicode": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, "hdr-histogram-js": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", "dev": true, "requires": { "@assemblyscript/loader": "^0.10.1", @@ -20651,10 +6303,14 @@ }, "hdr-histogram-percentiles-obj": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", "dev": true }, "hosted-git-info": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -20662,6 +6318,8 @@ }, "hpack.js": { "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -20672,6 +6330,8 @@ "dependencies": { "readable-stream": { "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -20685,6 +6345,8 @@ }, "string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -20694,10 +6356,14 @@ }, "html-entities": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", "dev": true }, "html-escaper": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "html-tags": { @@ -20706,38 +6372,22 @@ "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "dev": true }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "peer": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - }, - "dependencies": { - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "peer": true - } - } - }, "http-cache-semantics": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, "http-deceiver": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true }, "http-errors": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, "requires": { "depd": "~1.1.2", @@ -20749,10 +6399,14 @@ }, "http-parser-js": { "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", "dev": true }, "http-proxy": { "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -20762,6 +6416,8 @@ }, "http-proxy-agent": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, "requires": { "@tootallnate/once": "1", @@ -20771,6 +6427,8 @@ }, "http-proxy-middleware": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz", + "integrity": "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==", "dev": true, "requires": { "@types/http-proxy": "^1.17.8", @@ -20782,6 +6440,8 @@ }, "http-signature": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -20791,6 +6451,8 @@ }, "https-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "dev": true, "requires": { "agent-base": "6", @@ -20799,10 +6461,14 @@ }, "human-signals": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "humanize-ms": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, "requires": { "ms": "^2.0.0" @@ -20816,6 +6482,8 @@ }, "iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -20823,19 +6491,26 @@ }, "icss-utils": { "version": "5.1.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true }, "ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "ignore-walk": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", "dev": true, "requires": { "minimatch": "^3.0.4" @@ -20843,19 +6518,27 @@ }, "image-size": { "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", "dev": true, "optional": true }, "immediate": { "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, "immutable": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", "dev": true }, "import-fresh": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -20864,6 +6547,8 @@ "dependencies": { "resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true } } @@ -20876,18 +6561,26 @@ }, "imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "infer-owner": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "dev": true }, "inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -20896,14 +6589,20 @@ }, "inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, "inquirer": { "version": "8.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", + "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -20924,6 +6623,8 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -20931,6 +6632,8 @@ }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -20939,6 +6642,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -20946,14 +6651,20 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "rxjs": { "version": "7.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", + "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -20961,6 +6672,8 @@ }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -20968,20 +6681,28 @@ }, "tslib": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } }, "ip": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA==", "dev": true }, "ipaddr.js": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, "is-arguments": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -20990,10 +6711,14 @@ }, "is-arrayish": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-binary-path": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -21001,6 +6726,8 @@ }, "is-core-module": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -21008,6 +6735,8 @@ }, "is-date-object": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -21015,17 +6744,25 @@ }, "is-docker": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, "is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { - "version": "3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -21033,22 +6770,32 @@ }, "is-interactive": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, "is-lambda": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, "is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-path-cwd": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", "dev": true }, "is-path-in-cwd": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { "is-path-inside": "^1.0.0" @@ -21056,6 +6803,8 @@ }, "is-path-inside": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", "dev": true, "requires": { "path-is-inside": "^1.0.1" @@ -21063,10 +6812,14 @@ }, "is-plain-obj": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true }, "is-plain-object": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -21074,6 +6827,8 @@ }, "is-regex": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -21088,22 +6843,32 @@ }, "is-stream": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "is-what": { "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", "dev": true }, "is-wsl": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "requires": { "is-docker": "^2.0.0" @@ -21111,30 +6876,44 @@ }, "isarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "isbinaryfile": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", + "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", "dev": true }, "isexe": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, "isstream": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "istanbul-lib-coverage": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, "istanbul-lib-instrument": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", "dev": true, "requires": { "@babel/core": "^7.12.3", @@ -21146,12 +6925,16 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "istanbul-lib-report": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", @@ -21161,10 +6944,14 @@ "dependencies": { "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -21174,6 +6961,8 @@ }, "istanbul-lib-source-maps": { "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", "dev": true, "requires": { "debug": "^4.1.1", @@ -21185,10 +6974,14 @@ "dependencies": { "istanbul-lib-coverage": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, "make-dir": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "requires": { "pify": "^4.0.1", @@ -21197,10 +6990,14 @@ }, "pify": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, "rimraf": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -21208,16 +7005,22 @@ }, "semver": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "istanbul-reports": { "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -21295,6 +7098,8 @@ }, "jasmine": { "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", "dev": true, "requires": { "exit": "^0.1.2", @@ -21304,16 +7109,22 @@ "dependencies": { "jasmine-core": { "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", "dev": true } } }, "jasmine-core": { "version": "3.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.8.0.tgz", + "integrity": "sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg==", "dev": true }, "jasmine-spec-reporter": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", + "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", "dev": true, "requires": { "colors": "1.4.0" @@ -21321,10 +7132,14 @@ }, "jasminewd2": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", "dev": true }, "jest-worker": { "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "requires": { "@types/node": "*", @@ -21334,10 +7149,14 @@ "dependencies": { "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -21347,10 +7166,14 @@ }, "js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -21359,26 +7182,38 @@ }, "jsbn": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, "jsesc": { "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json-parse-better-errors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "json-parse-even-better-errors": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -21389,10 +7224,14 @@ }, "json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json5": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -21400,10 +7239,14 @@ }, "jsonc-parser": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, "jsonfile": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", @@ -21412,10 +7255,14 @@ }, "jsonparse": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, "jsprim": { "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, "requires": { "assert-plus": "1.0.0", @@ -21426,6 +7273,8 @@ }, "jszip": { "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", + "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", "dev": true, "requires": { "lie": "~3.3.0", @@ -21436,6 +7285,8 @@ "dependencies": { "readable-stream": { "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -21449,6 +7300,8 @@ }, "string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -21458,6 +7311,8 @@ }, "karma": { "version": "6.3.16", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", + "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", "dev": true, "requires": { "body-parser": "^1.19.0", @@ -21488,10 +7343,14 @@ "dependencies": { "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "tmp": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { "rimraf": "^3.0.0" @@ -21499,6 +7358,8 @@ }, "yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -21512,12 +7373,16 @@ }, "yargs-parser": { "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true } } }, "karma-chrome-launcher": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", "dev": true, "requires": { "which": "^1.2.1" @@ -21525,6 +7390,8 @@ }, "karma-coverage-istanbul-reporter": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", @@ -21536,6 +7403,8 @@ }, "karma-jasmine": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", + "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", "dev": true, "requires": { "jasmine-core": "^3.6.0" @@ -21543,11 +7412,14 @@ }, "karma-jasmine-html-reporter": { "version": "1.5.4", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", + "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", + "dev": true }, "karma-source-map-support": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, "requires": { "source-map-support": "^0.5.5" @@ -21555,10 +7427,14 @@ "dependencies": { "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -21569,10 +7445,14 @@ }, "kind-of": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "klona": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", "dev": true }, "known-css-properties": { @@ -21583,6 +7463,8 @@ }, "less": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", + "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", "dev": true, "requires": { "copy-anything": "^2.0.1", @@ -21599,6 +7481,8 @@ "dependencies": { "make-dir": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "optional": true, "requires": { @@ -21608,32 +7492,44 @@ }, "mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, "optional": true }, "pify": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "optional": true }, "semver": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "optional": true }, "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true }, "tslib": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } }, "less-loader": { "version": "10.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz", + "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==", "dev": true, "requires": { "klona": "^2.0.4" @@ -21651,6 +7547,8 @@ }, "license-webpack-plugin": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.1.tgz", + "integrity": "sha512-SQum9mg3BgnY5BK+2KYl4W7pk9b26Q8tW2lTsO6tidD0/Ds9ksdXvp3ip2s9LqDjj5gtBMyWRfOPZptWj4PfCg==", "dev": true, "requires": { "webpack-sources": "^3.0.0" @@ -21658,6 +7556,8 @@ }, "lie": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "requires": { "immediate": "~3.0.5" @@ -21671,6 +7571,8 @@ }, "lines-and-columns": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "lint-staged": { @@ -21789,14 +7691,20 @@ }, "loader-runner": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", "dev": true }, "loader-utils": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", "dev": true }, "locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -21804,10 +7712,14 @@ }, "lodash": { "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.debounce": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, "lodash.merge": { @@ -21824,6 +7736,8 @@ }, "log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -21832,6 +7746,8 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -21839,6 +7755,8 @@ }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -21847,6 +7765,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -21854,14 +7774,20 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -21931,6 +7857,8 @@ }, "log4js": { "version": "6.4.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", + "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", "dev": true, "requires": { "date-format": "^4.0.3", @@ -22000,6 +7928,8 @@ }, "lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -22007,6 +7937,8 @@ }, "magic-string": { "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", "dev": true, "requires": { "sourcemap-codec": "^1.4.4" @@ -22014,6 +7946,8 @@ }, "make-dir": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { "semver": "^6.0.0" @@ -22021,16 +7955,22 @@ "dependencies": { "semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "make-error": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "make-fetch-happen": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", "dev": true, "requires": { "agentkeepalive": "^4.1.3", @@ -22065,10 +8005,14 @@ }, "media-typer": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, "memfs": { "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "dev": true, "requires": { "fs-monkey": "1.0.3" @@ -22110,18 +8054,26 @@ }, "merge-descriptors": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "merge-stream": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "methods": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "micromatch": { @@ -22136,14 +8088,20 @@ }, "mime": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true }, "mime-db": { "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true }, "mime-types": { "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "requires": { "mime-db": "1.51.0" @@ -22151,6 +8109,8 @@ }, "mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "min-indent": { @@ -22161,6 +8121,8 @@ }, "mini-css-extract-plugin": { "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", + "integrity": "sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==", "dev": true, "requires": { "schema-utils": "^4.0.0" @@ -22168,6 +8130,8 @@ "dependencies": { "schema-utils": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -22180,6 +8144,8 @@ }, "minimalistic-assert": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, "minimatch": { @@ -22218,6 +8184,8 @@ }, "minipass": { "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -22225,6 +8193,8 @@ }, "minipass-collect": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -22232,6 +8202,8 @@ }, "minipass-fetch": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", "dev": true, "requires": { "encoding": "^0.1.12", @@ -22242,6 +8214,8 @@ }, "minipass-flush": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -22249,6 +8223,8 @@ }, "minipass-json-stream": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", "dev": true, "requires": { "jsonparse": "^1.3.1", @@ -22257,6 +8233,8 @@ }, "minipass-pipeline": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -22264,6 +8242,8 @@ }, "minipass-sized": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -22271,6 +8251,8 @@ }, "minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { "minipass": "^3.0.0", @@ -22279,6 +8261,8 @@ }, "mkdirp": { "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -22286,10 +8270,14 @@ }, "ms": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "multicast-dns": { "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "requires": { "dns-packet": "^1.3.1", @@ -22298,10 +8286,14 @@ }, "multicast-dns-service-types": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true }, "mute-stream": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, "nanoid": { @@ -22318,6 +8310,8 @@ }, "needle": { "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", "dev": true, "optional": true, "requires": { @@ -22328,6 +8322,8 @@ "dependencies": { "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "optional": true, "requires": { @@ -22338,31 +8334,43 @@ }, "negotiator": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "ng-apexcharts": { "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.0.tgz", + "integrity": "sha512-HSLPHrSH4CYn6crU8RsbZzuecKoXZ7a8i0cGdq8yDZ9DwaEauiJl5WWCWKajHLbwloVEdqoAsKN04TcvIP0ulQ==", "requires": { "tslib": "^2.0.0" } }, "ngx-color-picker": { "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.0.tgz", + "integrity": "sha512-SY5KoZka/uq2MNhUAKfJXQjjS2TFvKDJHbsCxfnjKjS/VHx8VVeTJpnt5wuuewzRzLxfOm5y2Fw8/HTPEPtRkA==", "requires": { "tslib": "^2.3.0" }, "dependencies": { "tslib": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" } } }, "nice-napi": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, "optional": true, "requires": { @@ -22372,14 +8380,20 @@ }, "node-addon-api": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true }, "node-forge": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", + "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", "dev": true }, "node-gyp": { "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", "dev": true, "requires": { "env-paths": "^2.2.0", @@ -22396,6 +8410,8 @@ "dependencies": { "which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -22405,14 +8421,20 @@ }, "node-gyp-build": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", "dev": true }, "node-releases": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", "dev": true }, "nopt": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dev": true, "requires": { "abbrev": "1" @@ -22432,14 +8454,20 @@ }, "normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "normalize-range": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, "npm-bundled": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -22447,6 +8475,8 @@ }, "npm-install-checks": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", "dev": true, "requires": { "semver": "^7.1.1" @@ -22454,10 +8484,14 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true }, "npm-package-arg": { "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", "dev": true, "requires": { "hosted-git-info": "^4.0.1", @@ -22467,6 +8501,8 @@ }, "npm-packlist": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", "dev": true, "requires": { "glob": "^7.1.6", @@ -22477,6 +8513,8 @@ }, "npm-pick-manifest": { "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", "dev": true, "requires": { "npm-install-checks": "^4.0.0", @@ -22487,6 +8525,8 @@ }, "npm-registry-fetch": { "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", "dev": true, "requires": { "make-fetch-happen": "^10.0.1", @@ -22499,10 +8539,14 @@ "dependencies": { "@tootallnate/once": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, "http-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "requires": { "@tootallnate/once": "2", @@ -22512,10 +8556,14 @@ }, "lru-cache": { "version": "7.3.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.3.1.tgz", + "integrity": "sha512-nX1x4qUrKqwbIAhv4s9et4FIUVzNOpeY07bsjGUy8gwJrXH/wScImSQqXErmo/b2jZY2r0mohbLA9zVj7u1cNw==", "dev": true }, "make-fetch-happen": { "version": "10.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.0.2.tgz", + "integrity": "sha512-JSFLK53NJP22FL/eAGOyKsWbc2G3v+toPMD7Dq9PJKQCvK0i3t8hGkKxe+3YZzwYa+c0kxRHu7uxH3fvO+rsaA==", "dev": true, "requires": { "agentkeepalive": "^4.2.0", @@ -22540,6 +8588,8 @@ }, "npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -22547,6 +8597,8 @@ }, "npmlog": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", + "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", "dev": true, "requires": { "are-we-there-yet": "^3.0.0", @@ -22557,6 +8609,8 @@ }, "nth-check": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "dev": true, "requires": { "boolbase": "^1.0.0" @@ -22573,10 +8627,14 @@ }, "oauth-sign": { "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, "object-assign": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-inspect": { @@ -22587,6 +8645,8 @@ }, "object-is": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -22595,10 +8655,14 @@ }, "object-keys": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, "object.assign": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { "call-bind": "^1.0.0", @@ -22609,10 +8673,14 @@ }, "obuf": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, "on-finished": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, "requires": { "ee-first": "1.1.1" @@ -22620,10 +8688,14 @@ }, "on-headers": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true }, "once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -22631,6 +8703,8 @@ }, "onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -22638,6 +8712,8 @@ }, "open": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "requires": { "define-lazy-prop": "^2.0.0", @@ -22661,6 +8737,8 @@ }, "ora": { "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "requires": { "bl": "^4.1.0", @@ -22676,6 +8754,8 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -22683,6 +8763,8 @@ }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -22691,6 +8773,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -22698,14 +8782,20 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -22715,10 +8805,14 @@ }, "os-tmpdir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, "p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -22726,6 +8820,8 @@ }, "p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -22733,6 +8829,8 @@ }, "p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { "aggregate-error": "^3.0.0" @@ -22740,6 +8838,8 @@ }, "p-retry": { "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", "dev": true, "requires": { "@types/retry": "^0.12.0", @@ -22748,16 +8848,22 @@ "dependencies": { "retry": { "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true } } }, "p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "pacote": { "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", "dev": true, "requires": { "@npmcli/git": "^2.1.0", @@ -22783,16 +8889,22 @@ "dependencies": { "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true } } }, "pako": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, "parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" @@ -22800,6 +8912,8 @@ }, "parse-json": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -22810,14 +8924,20 @@ }, "parse-node-version": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true }, "parse5": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "optional": true }, "parse5-html-rewriting-stream": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", "dev": true, "requires": { "parse5": "^6.0.1", @@ -22826,12 +8946,16 @@ "dependencies": { "parse5": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true } } }, "parse5-htmlparser2-tree-adapter": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, "requires": { "parse5": "^6.0.1" @@ -22839,12 +8963,16 @@ "dependencies": { "parse5": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true } } }, "parse5-sax-parser": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", "dev": true, "requires": { "parse5": "^6.0.1" @@ -22852,52 +8980,76 @@ "dependencies": { "parse5": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true } } }, "parseurl": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, "path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-is-inside": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-to-regexp": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, "path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "performance-now": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "picocolors": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pidtree": { @@ -22908,14 +9060,20 @@ }, "pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "pinkie": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true }, "pinkie-promise": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "requires": { "pinkie": "^2.0.0" @@ -22929,6 +9087,8 @@ }, "piscina": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", "dev": true, "requires": { "eventemitter-asyncresource": "^1.0.0", @@ -22939,16 +9099,22 @@ }, "pkg-dir": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { "find-up": "^4.0.0" } }, "pngjs": { - "version": "5.0.0" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" }, "portfinder": { "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", "dev": true, "requires": { "async": "^2.6.2", @@ -22958,6 +9124,8 @@ "dependencies": { "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -22967,6 +9135,8 @@ }, "postcss": { "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", "dev": true, "requires": { "nanoid": "^3.1.30", @@ -22976,6 +9146,8 @@ }, "postcss-attribute-case-insensitive": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", + "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.2" @@ -22983,6 +9155,8 @@ }, "postcss-color-functional-notation": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.2.tgz", + "integrity": "sha512-DXVtwUhIk4f49KK5EGuEdgx4Gnyj6+t2jBSEmxvpIK9QI40tWrpS2Pua8Q7iIZWBrki2QOaeUdEaLPPa91K0RQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -22990,6 +9164,8 @@ }, "postcss-color-hex-alpha": { "version": "8.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz", + "integrity": "sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -22997,6 +9173,8 @@ }, "postcss-color-rebeccapurple": { "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", + "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -23004,11 +9182,14 @@ }, "postcss-custom-media": { "version": "8.0.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", + "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", + "dev": true }, "postcss-custom-properties": { "version": "12.1.4", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.4.tgz", + "integrity": "sha512-i6AytuTCoDLJkWN/MtAIGriJz3j7UX6bV7Z5t+KgFz+dwZS15/mlTJY1S0kRizlk6ba0V8u8hN50Fz5Nm7tdZw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -23016,6 +9197,8 @@ }, "postcss-custom-selectors": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", + "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.4" @@ -23023,6 +9206,8 @@ }, "postcss-dir-pseudo-class": { "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz", + "integrity": "sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.9" @@ -23030,6 +9215,8 @@ }, "postcss-double-position-gradients": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.5.tgz", + "integrity": "sha512-XiZzvdxLOWZwtt/1GgHJYGoD9scog/DD/yI5dcvPrXNdNDEv7T53/6tL7ikl+EM3jcerII5/XIQzd1UHOdTi2w==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -23037,6 +9224,8 @@ }, "postcss-env-function": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.5.tgz", + "integrity": "sha512-gPUJc71ji9XKyl0WSzAalBeEA/89kU+XpffpPxSaaaZ1c48OL36r1Ep5R6+9XAPkIiDlSvVAwP4io12q/vTcvA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -23044,6 +9233,8 @@ }, "postcss-focus-visible": { "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.9" @@ -23051,6 +9242,8 @@ }, "postcss-focus-within": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.9" @@ -23058,28 +9251,20 @@ }, "postcss-font-variant": { "version": "5.0.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true }, "postcss-gap-properties": { "version": "3.0.3", - "dev": true, - "requires": {} - }, - "postcss-html": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.4.1.tgz", - "integrity": "sha512-OKihuWxPuBQrQeLNsavP7ytJ9IYNj/ViAXB2v7Qjh56LnfESKrkahKA9si4VfPN8xtz6oqUE6KdL0bTPrHJr6g==", - "dev": true, - "peer": true, - "requires": { - "htmlparser2": "^7.1.2", - "postcss": "^8.4.0", - "postcss-safe-parser": "^6.0.0" - } + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", + "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", + "dev": true }, "postcss-image-set-function": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz", + "integrity": "sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -23087,6 +9272,8 @@ }, "postcss-import": { "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", "dev": true, "requires": { "postcss-value-parser": "^4.0.0", @@ -23096,11 +9283,14 @@ }, "postcss-initial": { "version": "4.0.1", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true }, "postcss-lab-function": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.1.0.tgz", + "integrity": "sha512-59uHN/2wRaOd7whDyeaJ82E0kncIEeJkwcmvXFPNus8v1YMhtv2IUo9OtOAncn7sifZVMRsyoPlhxwckTjn4cQ==", "dev": true, "requires": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", @@ -23109,6 +9299,8 @@ }, "postcss-loader": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", "dev": true, "requires": { "cosmiconfig": "^7.0.0", @@ -23118,13 +9310,15 @@ }, "postcss-logical": { "version": "5.0.4", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true }, "postcss-media-minmax": { "version": "5.0.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true }, "postcss-media-query-parser": { "version": "0.2.3", @@ -23134,11 +9328,14 @@ }, "postcss-modules-extract-imports": { "version": "3.0.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", "dev": true, "requires": { "icss-utils": "^5.0.0", @@ -23148,6 +9345,8 @@ }, "postcss-modules-scope": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.4" @@ -23155,6 +9354,8 @@ }, "postcss-modules-values": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, "requires": { "icss-utils": "^5.0.0" @@ -23162,6 +9363,8 @@ }, "postcss-nesting": { "version": "10.1.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz", + "integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.8" @@ -23169,16 +9372,20 @@ }, "postcss-overflow-shorthand": { "version": "3.0.3", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", + "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", + "dev": true }, "postcss-page-break": { "version": "3.0.4", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true }, "postcss-place": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.4.tgz", + "integrity": "sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -23186,6 +9393,8 @@ }, "postcss-preset-env": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", + "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", "dev": true, "requires": { "autoprefixer": "^10.4.2", @@ -23225,6 +9434,8 @@ }, "postcss-pseudo-class-any-link": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.1.tgz", + "integrity": "sha512-JRoLFvPEX/1YTPxRxp1JO4WxBVXJYrSY7NHeak5LImwJ+VobFMwYDQHvfTXEpcn+7fYIeGkC29zYFhFWIZD8fg==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.9" @@ -23232,8 +9443,9 @@ }, "postcss-replace-overflow-wrap": { "version": "4.0.0", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true }, "postcss-resolve-nested-selector": { "version": "0.1.1", @@ -23245,18 +9457,18 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-scss": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-selector-not": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", + "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", "dev": true, "requires": { "balanced-match": "^1.0.0" @@ -23274,6 +9486,8 @@ }, "postcss-value-parser": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "prelude-ls": { @@ -23321,6 +9535,8 @@ }, "pretty-bytes": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, "pretty-format": { @@ -23343,14 +9559,20 @@ }, "process-nextick-args": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "promise-inflight": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, "promise-retry": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "requires": { "err-code": "^2.0.2", @@ -23359,6 +9581,8 @@ }, "protractor": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", + "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", "dev": true, "requires": { "@types/q": "^0.0.32", @@ -23380,14 +9604,20 @@ "dependencies": { "ansi-regex": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -23399,6 +9629,8 @@ }, "cliui": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", @@ -23408,10 +9640,14 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -23421,6 +9657,8 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -23428,10 +9666,14 @@ }, "color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "strip-ansi": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -23439,10 +9681,14 @@ }, "supports-color": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true }, "wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -23452,10 +9698,14 @@ "dependencies": { "ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -23463,6 +9713,8 @@ }, "strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -23472,10 +9724,14 @@ }, "y18n": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -23493,6 +9749,8 @@ }, "yargs-parser": { "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -23503,6 +9761,8 @@ }, "proxy-addr": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "requires": { "forwarded": "0.2.0", @@ -23511,37 +9771,53 @@ "dependencies": { "ipaddr.js": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true } } }, "prr": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true, "optional": true }, "psl": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, "punycode": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "q": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", "dev": true }, "qjobs": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", "dev": true }, "qs": { "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true }, "queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "quick-lru": { @@ -23552,6 +9828,8 @@ }, "randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { "safe-buffer": "^5.1.0" @@ -23559,10 +9837,14 @@ }, "range-parser": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true }, "raw-body": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "dev": true, "requires": { "bytes": "3.1.1", @@ -23573,6 +9855,8 @@ }, "read-cache": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, "requires": { "pify": "^2.3.0" @@ -23580,6 +9864,8 @@ }, "read-package-json-fast": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", "dev": true, "requires": { "json-parse-even-better-errors": "^2.3.0", @@ -23651,6 +9937,8 @@ }, "readable-stream": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -23660,6 +9948,8 @@ }, "readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -23677,14 +9967,20 @@ }, "reflect-metadata": { "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", "dev": true }, "regenerate": { "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, "regenerate-unicode-properties": { "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", "dev": true, "requires": { "regenerate": "^1.4.2" @@ -23692,10 +9988,14 @@ }, "regenerator-runtime": { "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", "dev": true }, "regenerator-transform": { "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" @@ -23703,10 +10003,14 @@ }, "regex-parser": { "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", "dev": true }, "regexp.prototype.flags": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -23721,6 +10025,8 @@ }, "regexpu-core": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", "dev": true, "requires": { "regenerate": "^1.4.2", @@ -23733,10 +10039,14 @@ }, "regjsgen": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", "dev": true }, "regjsparser": { "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -23744,12 +10054,16 @@ "dependencies": { "jsesc": { "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true } } }, "request": { "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -23776,23 +10090,33 @@ "dependencies": { "qs": { "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, "uuid": { "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true } } }, "require-directory": { - "version": "2.1.1" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "require-from-string": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, "require-main-filename": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "require-relative": { @@ -23803,10 +10127,14 @@ }, "requires-port": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "resolve": { "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { "is-core-module": "^2.8.1", @@ -23816,10 +10144,14 @@ }, "resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "resolve-url-loader": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, "requires": { "adjust-sourcemap-loader": "^4.0.0", @@ -23831,6 +10163,8 @@ "dependencies": { "loader-utils": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -23840,12 +10174,16 @@ }, "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { "onetime": "^5.1.0", @@ -23854,18 +10192,26 @@ }, "retry": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true }, "reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "rfdc": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, "rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -23873,10 +10219,14 @@ }, "run-async": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true }, "run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { "queue-microtask": "^1.2.2" @@ -23884,12 +10234,16 @@ }, "rxjs": { "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", "requires": { "tslib": "^1.9.0" }, "dependencies": { "tslib": { - "version": "1.14.1" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" } } }, @@ -23897,19 +10251,24 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true, - "requires": {} + "dev": true }, "safe-buffer": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "sass": { "version": "1.49.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.0.tgz", + "integrity": "sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -23919,6 +10278,8 @@ }, "sass-loader": { "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", "dev": true, "requires": { "klona": "^2.0.4", @@ -23927,6 +10288,8 @@ }, "saucelabs": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", "dev": true, "requires": { "https-proxy-agent": "^2.2.1" @@ -23934,6 +10297,8 @@ "dependencies": { "agent-base": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -23941,6 +10306,8 @@ }, "debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -23948,6 +10315,8 @@ }, "https-proxy-agent": { "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", @@ -23958,10 +10327,14 @@ }, "sax": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "schema-utils": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { "@types/json-schema": "^7.0.5", @@ -23971,6 +10344,8 @@ "dependencies": { "ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -23981,21 +10356,28 @@ }, "ajv-keywords": { "version": "3.5.2", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true } } }, "select-hose": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true }, "selenium-webdriver": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", "dev": true, "requires": { "jszip": "^3.1.3", @@ -24006,6 +10388,8 @@ "dependencies": { "rimraf": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -24013,6 +10397,8 @@ }, "tmp": { "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", "dev": true, "requires": { "os-tmpdir": "~1.0.1" @@ -24022,6 +10408,8 @@ }, "selfsigned": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", "dev": true, "requires": { "node-forge": "^1.2.0" @@ -24029,6 +10417,8 @@ }, "semver": { "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -24036,6 +10426,8 @@ }, "send": { "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", "dev": true, "requires": { "debug": "2.6.9", @@ -24055,6 +10447,8 @@ "dependencies": { "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -24062,22 +10456,30 @@ "dependencies": { "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } }, "mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, "ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, "serialize-javascript": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -24085,6 +10487,8 @@ }, "serve-index": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -24098,6 +10502,8 @@ "dependencies": { "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -24105,6 +10511,8 @@ }, "http-errors": { "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "requires": { "depd": "~1.1.2", @@ -24115,20 +10523,28 @@ }, "inherits": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "setprototypeof": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true } } }, "serve-static": { "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", "dev": true, "requires": { "encodeurl": "~1.0.2", @@ -24139,18 +10555,26 @@ }, "set-blocking": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "set-immediate-shim": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", "dev": true }, "setprototypeof": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "shallow-clone": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -24158,6 +10582,8 @@ }, "shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" @@ -24165,14 +10591,20 @@ }, "shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "slash": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true }, "slice-ansi": { @@ -24201,10 +10633,14 @@ }, "smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, "socket.io": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", + "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -24217,10 +10653,14 @@ }, "socket.io-adapter": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", "dev": true }, "socket.io-parser": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", "dev": true, "requires": { "@types/component-emitter": "^1.2.10", @@ -24230,6 +10670,8 @@ }, "sockjs": { "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "requires": { "faye-websocket": "^0.11.3", @@ -24239,6 +10681,8 @@ }, "socks": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "dev": true, "requires": { "ip": "^1.1.5", @@ -24247,6 +10691,8 @@ }, "socks-proxy-agent": { "version": "6.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", + "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", "dev": true, "requires": { "agent-base": "^6.0.2", @@ -24256,14 +10702,20 @@ }, "source-map": { "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true }, "source-map-js": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, "source-map-loader": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", "dev": true, "requires": { "abab": "^2.0.5", @@ -24273,6 +10725,8 @@ "dependencies": { "iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -24282,6 +10736,8 @@ }, "source-map-resolve": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", "dev": true, "requires": { "atob": "^2.1.2", @@ -24290,6 +10746,8 @@ }, "source-map-support": { "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { "source-map": "^0.5.6" @@ -24297,12 +10755,16 @@ "dependencies": { "source-map": { "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true } } }, "sourcemap-codec": { "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, "spdx-correct": { @@ -24339,6 +10801,8 @@ }, "spdy": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, "requires": { "debug": "^4.1.0", @@ -24350,6 +10814,8 @@ }, "spdy-transport": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "requires": { "debug": "^4.1.0", @@ -24368,6 +10834,8 @@ }, "sshpk": { "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -24383,6 +10851,8 @@ }, "ssri": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "dev": true, "requires": { "minipass": "^3.1.1" @@ -24390,10 +10860,14 @@ }, "statuses": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true }, "streamroller": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", + "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", "dev": true, "requires": { "date-format": "^4.0.3", @@ -24401,19 +10875,6 @@ "fs-extra": "^10.0.0" } }, - "string_decoder": { - "version": "1.3.0", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "dev": true - } - } - }, "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -24422,14 +10883,35 @@ }, "string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, "strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { "ansi-regex": "^5.0.1" } @@ -24442,6 +10924,8 @@ }, "strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-indent": { @@ -24577,15 +11061,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-prettier": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-prettier-scss": { "version": "0.0.1", @@ -24600,8 +11082,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-recommended-scss": { "version": "5.0.2", @@ -24618,8 +11099,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -24652,8 +11132,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-standard": { "version": "24.0.0", @@ -24681,6 +11160,8 @@ }, "stylus": { "version": "0.56.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", + "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", "dev": true, "requires": { "css": "^3.0.0", @@ -24693,6 +11174,8 @@ }, "stylus-loader": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.2.0.tgz", + "integrity": "sha512-5dsDc7qVQGRoc6pvCL20eYgRUxepZ9FpeK28XhdXaIPP6kXr6nI1zAAKFQgP5OBkOfKaURp4WUpJzspg1f01Gg==", "dev": true, "requires": { "fast-glob": "^3.2.7", @@ -24702,6 +11185,8 @@ }, "supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -24736,6 +11221,8 @@ }, "supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, "svg-tags": { @@ -24746,33 +11233,45 @@ }, "svg.draggable.js": { "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", "requires": { "svg.js": "^2.0.1" } }, "svg.easing.js": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", "requires": { "svg.js": ">=2.3.x" } }, "svg.filter.js": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", "requires": { "svg.js": "^2.2.5" } }, "svg.js": { - "version": "2.7.1" + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" }, "svg.pathmorphing.js": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", "requires": { "svg.js": "^2.4.0" } }, "svg.resize.js": { "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", "requires": { "svg.js": "^2.6.5", "svg.select.js": "^2.1.2" @@ -24780,6 +11279,8 @@ "dependencies": { "svg.select.js": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", "requires": { "svg.js": "^2.2.5" } @@ -24788,12 +11289,16 @@ }, "svg.select.js": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", "requires": { "svg.js": "^2.6.5" } }, "symbol-observable": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", "dev": true }, "table": { @@ -24848,10 +11353,14 @@ }, "tapable": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, "tar": { "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -24864,6 +11373,8 @@ "dependencies": { "mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true } } @@ -24883,6 +11394,8 @@ }, "terser": { "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "dev": true, "requires": { "commander": "^2.20.0", @@ -24892,6 +11405,8 @@ "dependencies": { "source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -24900,6 +11415,8 @@ "dependencies": { "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -24908,6 +11425,8 @@ }, "terser-webpack-plugin": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", "dev": true, "requires": { "jest-worker": "^27.4.5", @@ -24919,6 +11438,8 @@ "dependencies": { "ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -24929,15 +11450,20 @@ }, "ajv-keywords": { "version": "3.5.2", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "schema-utils": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -24947,12 +11473,16 @@ }, "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "test-exclude": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { "@istanbuljs/schema": "^0.1.2", @@ -24962,18 +11492,26 @@ }, "text-table": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "thunky": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, "tmp": { "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -24981,10 +11519,14 @@ }, "to-fast-properties": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" @@ -24992,10 +11534,14 @@ }, "toidentifier": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, "tough-cookie": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { "psl": "^1.1.28", @@ -25004,6 +11550,8 @@ }, "tree-kill": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "trim-newlines": { @@ -25014,6 +11562,8 @@ }, "ts-node": { "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", "dev": true, "requires": { "arg": "^4.1.0", @@ -25025,10 +11575,14 @@ "dependencies": { "source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -25061,10 +11615,14 @@ } }, "tslib": { - "version": "2.0.3" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" }, "tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -25072,6 +11630,8 @@ }, "tweetnacl": { "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "type-check": { @@ -25085,10 +11645,14 @@ }, "type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "type-is": { "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", @@ -25097,22 +11661,32 @@ }, "typed-assert": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.8.tgz", + "integrity": "sha512-5NkbXZUlmCE73Fs7gvkp1XXJWHYetPkg60QnQ2NXQmBYNFxbBr2zA8GCtaH4K2s2WhOmSlgiSTmrjrcm5tnM5g==", "dev": true }, "typescript": { "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true }, "ua-parser-js": { "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", "dev": true }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true }, "unicode-match-property-ecmascript": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^2.0.0", @@ -25121,14 +11695,20 @@ }, "unicode-match-property-value-ecmascript": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", "dev": true }, "unicode-property-aliases-ecmascript": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", "dev": true }, "unique-filename": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { "unique-slug": "^2.0.0" @@ -25136,6 +11716,8 @@ }, "unique-slug": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -25143,14 +11725,20 @@ }, "universalify": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, "unpipe": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, "uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -25158,14 +11746,20 @@ }, "util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "utils-merge": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, "uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "v8-compile-cache": { @@ -25186,6 +11780,8 @@ }, "validate-npm-package-name": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", "dev": true, "requires": { "builtins": "^1.0.3" @@ -25193,10 +11789,14 @@ }, "vary": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, "verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -25206,6 +11806,8 @@ }, "void-elements": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", "dev": true }, "vue-eslint-parser": { @@ -25243,6 +11845,8 @@ }, "watchpack": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -25251,6 +11855,8 @@ }, "wbuf": { "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "requires": { "minimalistic-assert": "^1.0.0" @@ -25258,6 +11864,8 @@ }, "wcwidth": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "requires": { "defaults": "^1.0.3" @@ -25265,6 +11873,8 @@ }, "webdriver-js-extender": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", "dev": true, "requires": { "@types/selenium-webdriver": "^3.0.0", @@ -25273,6 +11883,8 @@ }, "webdriver-manager": { "version": "12.1.8", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz", + "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==", "dev": true, "requires": { "adm-zip": "^0.4.9", @@ -25290,14 +11902,20 @@ "dependencies": { "ansi-regex": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -25309,10 +11927,14 @@ }, "ini": { "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "rimraf": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -25320,10 +11942,14 @@ }, "semver": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "strip-ansi": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -25331,12 +11957,16 @@ }, "supports-color": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } }, "webpack": { "version": "5.67.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", + "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.0", @@ -25367,6 +11997,8 @@ "dependencies": { "ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -25377,15 +12009,20 @@ }, "ajv-keywords": { "version": "3.5.2", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "schema-utils": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -25397,6 +12034,8 @@ }, "webpack-dev-middleware": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", + "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", "dev": true, "requires": { "colorette": "^2.0.10", @@ -25408,6 +12047,8 @@ "dependencies": { "schema-utils": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -25420,6 +12061,8 @@ }, "webpack-dev-server": { "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", "dev": true, "requires": { "@types/bonjour": "^3.5.9", @@ -25455,14 +12098,20 @@ "dependencies": { "ansi-regex": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, "array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "del": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", "dev": true, "requires": { "globby": "^11.0.1", @@ -25477,6 +12126,8 @@ }, "globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -25489,14 +12140,20 @@ }, "is-path-cwd": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, "is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, "schema-utils": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -25507,10 +12164,14 @@ }, "slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "strip-ansi": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { "ansi-regex": "^6.0.1" @@ -25520,6 +12181,8 @@ }, "webpack-merge": { "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { "clone-deep": "^4.0.1", @@ -25528,10 +12191,14 @@ }, "webpack-sources": { "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, "webpack-subresource-integrity": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, "requires": { "typed-assert": "^1.0.8" @@ -25539,6 +12206,8 @@ }, "websocket-driver": { "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, "requires": { "http-parser-js": ">=0.5.1", @@ -25548,10 +12217,14 @@ }, "websocket-extensions": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, "which": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -25559,10 +12232,14 @@ }, "which-module": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "wide-align": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, "requires": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -25570,6 +12247,8 @@ }, "wildcard": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, "word-wrap": { @@ -25580,6 +12259,8 @@ }, "wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -25588,23 +12269,31 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { "color-convert": "^2.0.1" } }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, "wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "write-file-atomic": { @@ -25619,11 +12308,14 @@ }, "ws": { "version": "8.2.3", - "dev": true, - "requires": {} + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true }, "xml2js": { "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", "dev": true, "requires": { "sax": ">=0.6.0", @@ -25632,17 +12324,25 @@ }, "xmlbuilder": { "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true }, "y18n": { - "version": "5.0.8" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yaml": { "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true }, "yargs": { @@ -25666,10 +12366,14 @@ }, "yn": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, "zone.js": { "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", "requires": { "tslib": "^2.0.0" } diff --git a/frontend/src/app/user/profile/profile.component.html b/frontend/src/app/user/profile/profile.component.html index c55e5eb56..8661c02a7 100644 --- a/frontend/src/app/user/profile/profile.component.html +++ b/frontend/src/app/user/profile/profile.component.html @@ -4,9 +4,7 @@
person
- - {{ (user | async)?.username }} - + {{(user | async)?.username}} User Profile
- diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 81715833d..9ed756d0c 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -231,7 +231,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat public back() { if (this.checkDirty()) { - this.routerService.navigate(['/competition/list']); + this.routerService.navigate(['/template/list']); } } diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts index 7512e21c2..79a274ff3 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ b/frontend/src/app/competition/competition-list/competition-list.component.ts @@ -93,7 +93,7 @@ export class CompetitionListComponent implements AfterViewInit { } public edit(competitionId: string) { - this.routerService.navigate(['/competition/builder', competitionId]); + this.routerService.navigate(['/template/builder', competitionId]); } public delete(competitionId: string) { diff --git a/frontend/src/app/judgement/judgement-voting-viewer.component.ts b/frontend/src/app/judgement/judgement-voting-viewer.component.ts index f8c6646cb..ca8e7e191 100644 --- a/frontend/src/app/judgement/judgement-voting-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-voting-viewer.component.ts @@ -67,7 +67,7 @@ export class JudgementVotingViewerComponent implements OnInit, OnDestroy { if (httpErr.status === 404) { const snack = this.snackBar.open(`Invalid runId: ${runId}`, null, { duration: 2000 }); snack.afterDismissed().subscribe(() => { - this.router.navigate(['/run/list']); + this.router.navigate(['/evaluation/list']); }); } } diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index df59fc725..c0da5da01 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -49,7 +49,7 @@ export class AbstractRunListComponent { public navigateToViewer(runId: string) { /* TODO: Setup depends on type of competition run. */ this.router.navigate([ - '/run/viewer', + '/evaluation/viewer', runId, { center: 'player', @@ -82,7 +82,7 @@ export class AbstractRunListComponent { * Navigates to admin viewer (for admins). */ public navigateToAdmin(runId: string, async: boolean = false) { - this.router.navigate([`/run/admin${async ? '/async' : ''}`, runId]); + this.router.navigate([`/evaluation/admin${async ? '/async' : ''}`, runId]); } /** @@ -91,7 +91,7 @@ export class AbstractRunListComponent { * @param runId ID of the run to navigate to. */ public navigateToScoreHistory(runId: string) { - this.router.navigate(['/run/scores', runId]); + this.router.navigate(['/evaluation/scores', runId]); } public downloadScores(runId: string) { diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index b2208b47c..1e94a7f5b 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -119,7 +119,7 @@ export class RunAdminToolbarComponent implements OnInit { */ public navigateToAdmin() { const runId = this.runId.value; - this.router.navigate(['/run/admin', runId]); + this.router.navigate(['/evaluation/admin', runId]); } /** @@ -129,7 +129,7 @@ export class RunAdminToolbarComponent implements OnInit { */ public navigateToScoreHistory() { const runId = this.runId.value; - this.router.navigate(['/run/scores', runId]); + this.router.navigate(['/evaluation/scores', runId]); } public downloadScores(runId: string) { diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index f813f495d..dd75dbe33 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -66,7 +66,7 @@ export class RunAdminViewComponent { duration: 5000, }); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), @@ -131,7 +131,7 @@ export class RunAdminViewComponent { duration: 5000, }); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), diff --git a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts index b817c7d62..843c05df5 100644 --- a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts +++ b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts @@ -58,7 +58,7 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { ); this.snackBar.open(`There was an error while loading information in the current run: ${err?.message}`); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), diff --git a/frontend/src/app/run/score-history/run-score-history.component.ts b/frontend/src/app/run/score-history/run-score-history.component.ts index 5045633b0..6dbbb1caf 100644 --- a/frontend/src/app/run/score-history/run-score-history.component.ts +++ b/frontend/src/app/run/score-history/run-score-history.component.ts @@ -126,7 +126,7 @@ export class RunScoreHistoryComponent { `[ScoreHistoryComponent] There was an error while loading information in the current run: ${err?.message}` ); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), @@ -157,7 +157,7 @@ export class RunScoreHistoryComponent { `[ScoreHistoryComponent] There was an error while loading information in the current run: ${err?.message}` ); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of([]); }) @@ -182,7 +182,7 @@ export class RunScoreHistoryComponent { catchError((err, o) => { console.log(`[ScoreHistoryComponent] There was an error while loading scores for run: ${err?.message}`); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index 4fcb12cf8..33c6d862a 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -143,7 +143,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { duration: 5000, }); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), @@ -208,7 +208,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { duration: 5000, }); if (err.status === 404) { - this.router.navigate(['/competition/list']); + this.router.navigate(['/template/list']); } return of(null); }), From 97ac0d9e80e22602ec225a9b72c543bedda96310 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 8 Feb 2023 16:38:37 +0100 Subject: [PATCH 098/498] Fixed some issues that prevented evaluation creation and start --- .../admin/CreateEvaluationHandler.kt | 6 +- .../UpdateEvaluationTemplateHandler.kt | 6 +- .../rest/types/evaluation/ApiEvaluation.kt | 3 +- .../dres/data/model/run/AbstractEvaluation.kt | 2 +- .../dev/dres/data/model/run/DbEvaluation.kt | 11 +- .../data/model/template/task/DbTaskGroup.kt | 3 +- .../dres/data/model/template/team/DbTeam.kt | 2 +- frontend/package-lock.json | 16880 +++++++++++++++- frontend/yarn.lock | 2366 +-- 9 files changed, 17944 insertions(+), 1335 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 4f10c006c..77ed6deca 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -66,7 +66,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs } /* Prepare run manager. */ - val evaluation = this.store.transactional { tx -> + val evaluationId = this.store.transactional { tx -> val template = DbEvaluationTemplate.query(DbEvaluationTemplate::id eq message.templateId).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID ${message.templateId} not found.'", ctx) /* ensure that only one synchronous run of a competition is happening at any given time */ @@ -116,10 +116,10 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs ApiEvaluationType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) ApiEvaluationType.NON_INTERACTIVE -> TODO() }, this.store) - evaluation + evaluation.id } /* Schedule newly created run manager. */ - return SuccessStatus("Evaluation '${message.name}' was started and is running with ID ${evaluation.id}.") + return SuccessStatus("Evaluation '${message.name}' was started and is running with ID $evaluationId.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 50a87825d..84ff75bec 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -118,7 +118,7 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C val t = if (task.id != null) { existing.tasks.filter { it.id eq task.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown task ${task.id} for evaluation ${apiValue.id}.", ctx) } else { - DbTaskTemplate.new { this.id = UUID.randomUUID().toString() } + DbTaskTemplate.new() } t.name = task.name t.duration = task.duration @@ -166,7 +166,7 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C val t = if (team.id != null) { existing.teams.filter { it.id eq team.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team ${team.id} for evaluation ${apiValue.id}.", ctx) } else { - DbTeam.new { this.id = UUID.randomUUID().toString() } + DbTeam.new() } t.name = team.name ?: throw ErrorStatusException(404, "Team name must be specified.", ctx) t.color = team.color ?: throw ErrorStatusException(404, "Team colour must be specified.", ctx) @@ -187,7 +187,7 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C val t = if (teamGroup.id != null) { existing.teamGroups.filter { it.id eq teamGroup.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team groum ${teamGroup.id} for evaluation ${apiValue.id}.", ctx) } else { - DbTeamGroup.new { this.id = UUID.randomUUID().toString() } + DbTeamGroup.new() } t.teams.clear() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt index a20380f5b..615a9fa98 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt @@ -16,7 +16,8 @@ data class ApiEvaluation( val name: String, val type: ApiEvaluationType, val template: ApiEvaluationTemplate, - val started: Long, + val created: Long, + val started: Long?, val ended: Long?, val tasks: List ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 08830c8fe..df1518cfc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -44,7 +44,7 @@ abstract class AbstractEvaluation(evaluation: DbEvaluation): EvaluationRun { get() = DbEvaluation.findById(this.xdId) /** Timestamp of when this [AbstractEvaluation] was started. */ - override var started: Long + override var started: Long? get() = this.evaluation.started protected set(value) { this.evaluation.started = value diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 8aa117015..343bca644 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -19,6 +19,11 @@ typealias EvaluationId = String class DbEvaluation(entity: Entity) : PersistentEntity(entity) { companion object : XdNaturalEntityType() + override fun constructor() { + super.constructor() + created = System.currentTimeMillis() + } + /** The [EvaluationId] of this [DbEvaluation]. */ var evaluationId: EvaluationId get() = this.id @@ -33,8 +38,11 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity) { /** The [DbEvaluationTemplate] backing this [DbEvaluation]. */ var template by xdLink1(DbEvaluationTemplate) + /** Timestamp of when this [DbEvaluation] was created. */ + var created by xdRequiredLongProp() + /** Timestamp of when this [DbEvaluation] started. */ - var started by xdRequiredLongProp() + var started by xdNullableLongProp() /** Timestamp of when this [DbEvaluation] ended. */ var ended by xdNullableLongProp() @@ -66,6 +74,7 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity) { name = this.name, type = this.type.toApi(), template = this.template.toApi(), + created = this.created, started = this.started, ended = this.ended, tasks = this.tasks.asSequence().map { it.toApi() }.toList() diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt index 1642ecd24..e025b505e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.competition.tasks.ApiTaskGroup import dev.dres.data.model.template.DbEvaluationTemplate import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.link.OnDeletePolicy /** * A [DbTaskGroup] allows the user to specify common traits among a group of [Task]s. @@ -24,7 +25,7 @@ class DbTaskGroup(entity: Entity) : XdEntity(entity) { var name: String by xdRequiredStringProp(unique = false, trimmed = false) /** The [DbTaskType] this [DbTaskGroup] belongs to.*/ - var type by xdLink1(DbTaskType) + var type by xdLink1(DbTaskType, onTargetDelete = OnDeletePolicy.CASCADE) /** The [DbEvaluationTemplate] this [DbTaskGroup] belongs to. */ var evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::taskGroups) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt index 34af4e0cc..c5cf38357 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt @@ -44,7 +44,7 @@ class DbTeam(entity: Entity) : PersistentEntity(entity), Team { val evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::teams) /** The [DbTeamGroup] this [DbTeam] belongs to (or null if not assigned to a group). */ - var group: DbTeamGroup? by xdLink0_1(DbTeamGroup::teams) + var group: DbTeamGroup? by xdLink0_1(DbTeamGroup::teams) //FIXME a team should be able to belong to arbitrarily many team groups /** The [DbUser]s that belong to this [DbTeam]. */ val users by xdLink0_N(DbUser, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 372ada205..dc7d5901d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,8 +1,16764 @@ { "name": "dres-frontend", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "dres-frontend", + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "@angular-slider/ngx-slider": "2.0.x", + "@angular/animations": "13.2.x", + "@angular/cdk": "13.2.x", + "@angular/common": "13.2.x", + "@angular/compiler": "13.2.x", + "@angular/core": "13.2.x", + "@angular/flex-layout": "^13.0.0-beta.38", + "@angular/forms": "13.2.x", + "@angular/material": "13.2.x", + "@angular/platform-browser": "13.2.x", + "@angular/platform-browser-dynamic": "13.2.x", + "@angular/router": "13.2.x", + "angularx-qrcode": "13.0.x", + "apexcharts": "3.33.x", + "ng-apexcharts": "1.7.x", + "ngx-color-picker": "12.0.x", + "rxjs": "6.5.x", + "tslib": "2.0.x", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "13.2.x", + "@angular-eslint/builder": "13.2.0", + "@angular-eslint/eslint-plugin": "13.2.0", + "@angular-eslint/eslint-plugin-template": "13.2.0", + "@angular-eslint/schematics": "13.2.0", + "@angular-eslint/template-parser": "13.2.0", + "@angular/cli": "13.2.x", + "@angular/compiler-cli": "13.2.x", + "@angular/language-service": "13.2.x", + "@types/jasmine": "3.6.x", + "@types/jasminewd2": "2.0.x", + "@types/node": "12.11.x", + "@typescript-eslint/eslint-plugin": "5.17.0", + "@typescript-eslint/parser": "5.17.0", + "eslint": "^8.12.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.0.0", + "husky": "^7.0.0", + "jasmine-core": "3.8.x", + "jasmine-spec-reporter": "5.0.x", + "karma": "6.3.x", + "karma-chrome-launcher": "3.1.x", + "karma-coverage-istanbul-reporter": "3.0.x", + "karma-jasmine": "4.0.x", + "karma-jasmine-html-reporter": "1.5.x", + "lint-staged": "^12.3.7", + "prettier": "2.6.2", + "prettier-eslint": "^14.0.1", + "protractor": "7.0.x", + "stylelint": "^14.8.2", + "stylelint-config-html": "^1.0.0", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-prettier-scss": "^0.0.1", + "stylelint-config-scss": "^1.0.0-security", + "stylelint-config-standard": "^25.0.0", + "stylelint-config-standard-scss": "^3.0.0", + "ts-node": "8.3.x", + "typescript": "4.5.x" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.1.tgz", + "integrity": "sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1302.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.3.tgz", + "integrity": "sha512-0m8jMKrFfIqsYt33zTUwSmyekyfuS67hna08RQ6USjzWQSE3z4S8ulCUARSjM6AzdMblX+whfy56nJUpT17NSA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.2.3", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.2.3.tgz", + "integrity": "sha512-cZ2gRcMRgW3t1WCeP+2D/wmr2M+BR/RICAh0wL9irIdypWAzIFt3Z2+2R/HmgAAxoEkdUMIfB9AnkYmwRVgFeA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "1.1.1", + "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/build-webpack": "0.1302.3", + "@angular-devkit/core": "13.2.3", + "@babel/core": "7.16.12", + "@babel/generator": "7.16.8", + "@babel/helper-annotate-as-pure": "7.16.7", + "@babel/plugin-proposal-async-generator-functions": "7.16.8", + "@babel/plugin-transform-async-to-generator": "7.16.8", + "@babel/plugin-transform-runtime": "7.16.10", + "@babel/preset-env": "7.16.11", + "@babel/runtime": "7.16.7", + "@babel/template": "7.16.7", + "@discoveryjs/json-ext": "0.5.6", + "@ngtools/webpack": "13.2.3", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.3", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "15.3.0", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "10.2.1", + "core-js": "3.20.3", + "critters": "0.0.16", + "css-loader": "6.5.1", + "esbuild-wasm": "0.14.14", + "glob": "7.2.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.2", + "less-loader": "10.2.0", + "license-webpack-plugin": "4.0.1", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.5.3", + "minimatch": "3.0.4", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.5", + "postcss-import": "14.0.2", + "postcss-loader": "6.2.1", + "postcss-preset-env": "7.2.3", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.49.0", + "sass-loader": "12.4.0", + "semver": "7.3.5", + "source-map-loader": "3.0.1", + "source-map-support": "0.5.21", + "stylus": "0.56.0", + "stylus-loader": "6.2.0", + "terser": "5.10.0", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.1", + "webpack": "5.67.0", + "webpack-dev-middleware": "5.3.0", + "webpack-dev-server": "4.7.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.14.14" + }, + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0", + "@angular/localize": "^13.0.0", + "@angular/service-worker": "^13.0.0", + "karma": "^6.3.0", + "ng-packagr": "^13.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.4.3 <4.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", + "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "sourcemap-codec": "1.4.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1302.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1302.3.tgz", + "integrity": "sha512-+JYH1lWU0UOjaWYxpoR2VLsdcb6nG9Gv+M1gH+kT0r2sAKOFaHnrksbOvca3EhDoaMa2b9LSGEE0OcSHWnN+eQ==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1302.3", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/core": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz", + "integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==", + "dev": true, + "dependencies": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/schematics": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.3.tgz", + "integrity": "sha512-+dyC4iKV0huvpjiuz4uyjLNK3FsCIp/Ghv5lXvhG6yok/dCAubsJItJOxi6G16aVCzG/E9zbsDfm9fNMyVOkgQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.2.3", + "jsonc-parser": "3.0.0", + "magic-string": "0.25.7", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-eslint/builder": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-13.2.0.tgz", + "integrity": "sha512-7V/bJwkMX+1OzxNUMxiOGdfdksjBmzCdPfxNEHXrT4la+vQQxg1oaIsgx74OkDqFTBv6NJO4PK8Flm9/QE4CDw==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "13.1.3" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.2.0.tgz", + "integrity": "sha512-ZA9JPERpeSo+G/bmp8GS/WjBbYkPDVzN6IINHz9SVdv//LWE58yymFFjRabHJx46iAEOe8P0CoKduuJWtEvNrQ==", + "dev": true + }, + "node_modules/@angular-eslint/eslint-plugin": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-13.2.0.tgz", + "integrity": "sha512-8qscIBc4montFQ52+XfBk7qR675oXV8mvRpjDh3cTfIZCzV6CSbbbH2iLp/9egnn0Pgy2ZUIsWgcsbK4W3+4bw==", + "dev": true, + "dependencies": { + "@angular-eslint/utils": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-13.2.0.tgz", + "integrity": "sha512-Ej9bNGbpf8oaQGglPVWAQkBSIHhQv0FeJ/vVnB2fhHUoK9BbkGUYpjAFSSRlnMhl6ktPWKCX3yJiW+F4uIUTIw==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0", + "aria-query": "^4.2.2", + "axobject-query": "^2.2.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "node_modules/@angular-eslint/schematics": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", + "integrity": "sha512-wxpfZ1L8Twa9Jfwqis7fY6jZvORqAv1k+9m1P6+6/l8gIm/8RYwD44o9Qm2MGt7YClnVthpCDhvfprcoKq6zgA==", + "dev": true, + "dependencies": { + "@angular-eslint/eslint-plugin": "13.2.0", + "@angular-eslint/eslint-plugin-template": "13.2.0", + "ignore": "5.2.0", + "strip-json-comments": "3.1.1", + "tmp": "0.2.1" + }, + "peerDependencies": { + "@angular/cli": ">= 13.0.0 < 14.0.0" + } + }, + "node_modules/@angular-eslint/schematics/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@angular-eslint/template-parser": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", + "integrity": "sha512-dv+bBFzvA4q/KiEDpO7BDUmB1EpVW0P0sX6OSs2eRzViEuXztkatQ4XOmgzuxP0+E1+zl/mK3F7uBDNXiaah9g==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "eslint-scope": "^5.1.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/utils": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-13.2.0.tgz", + "integrity": "sha512-ywkk+pVLaDLwzNKUcc/xWtJC4Bkm+qRrMOR8ZX3q84E5RTvIvc8IcPFBE4ey3lnGe+nE44OEGuLedCo9vn1Meg==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "13.2.0", + "@typescript-eslint/experimental-utils": "5.17.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-slider/ngx-slider": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@angular-slider/ngx-slider/-/ngx-slider-2.0.4.tgz", + "integrity": "sha512-EccMcGlb2bJJWikXZBjRwdWgRiYYmWd3UCgf8l3KAlyqPAxPVgxI73wqipp4/nZwidq53a9s3OB+KC79enfM2A==", + "dependencies": { + "detect-passive-events": "^2.0.3", + "rxjs": "^6.5.2", + "tslib": "^1.9.0" + }, + "peerDependencies": { + "@angular/common": ">=6.1.0", + "@angular/core": ">=6.1.0", + "@angular/forms": ">=6.1.0" + } + }, + "node_modules/@angular-slider/ngx-slider/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@angular/animations": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.2.2.tgz", + "integrity": "sha512-qX8LAMuCJaueHBVyuwKtqunx96G0Dr26k7y5Z03VTcscYst4Ib4V2d4i5dwn3HS82DehFdO86cm3Hi2PqE/qww==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "13.2.2" + } + }, + "node_modules/@angular/animations/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/cdk": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.2.2.tgz", + "integrity": "sha512-cT5DIaz+NI9IGb3X61Wh26+L6zdRcOXT1BP37iRbK2Qa2qM8/0VNeK6hrBBIblyoHKR/WUmRlS8XYf6mmArpZw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^13.0.0 || ^14.0.0-0", + "@angular/core": "^13.0.0 || ^14.0.0-0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cdk/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/cli": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.3.tgz", + "integrity": "sha512-QsakxpdQuO67u4fQNuOASqabYUO9gJb/5CpUGpWbuBzru0/9CMEF1CtXoF4EoDiwa5sJMirz3SJMKhtzFlv1cQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/core": "13.2.3", + "@angular-devkit/schematics": "13.2.3", + "@schematics/angular": "13.2.3", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.3", + "ini": "2.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "npm-package-arg": "8.1.5", + "npm-pick-manifest": "6.1.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "12.0.3", + "resolve": "1.22.0", + "semver": "7.3.5", + "symbol-observable": "4.0.0", + "uuid": "8.3.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.2.2.tgz", + "integrity": "sha512-56C/bheNLKtTCyQUZCiYtKbBIZN9jj6rjFILPtJCGls3cBCxp7t9tIdoLiQG/wVQRmaxdj1ioLT+sCWz7mLtQw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "13.2.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/common/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/compiler": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.2.2.tgz", + "integrity": "sha512-XXQtB0/e7pR2LPrHmpEiTU72SX4xxHGy91vYWIj1JCjSn0fYF7vtHzSJPXDvkbnkNow/PXXzJJYaU1ctdMZPcA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.2.tgz", + "integrity": "sha512-tuOIcEEKVIht+mKrj0rtX3I8gc+ByPjzpCZhFQRggxM6xbKJIToO1zERbEGKrZ+sUJ6BB5KLvscDy+Pddy3b8w==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.6", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "13.2.2", + "typescript": ">=4.4.2 <4.6" + } + }, + "node_modules/@angular/compiler-cli/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@angular/compiler/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/core": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.2.2.tgz", + "integrity": "sha512-zpctw0BxIVOsRFnckchK15SD1L8tzhf5GzwIDaM6+VylDQj1uYkm8mvAjJTQZyUuApomoFet2Rfj7XQPV+cNSQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/core/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/flex-layout": { + "version": "13.0.0-beta.38", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-13.0.0-beta.38.tgz", + "integrity": "sha512-kcWb7CcoHbvw7fjo/knizWVmSSmvaTnr8v1ML6zOdxu1PK9UPPOcOS8RTm6fy61zoC2LABivP1/6Z2jF5XfpdQ==", + "deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^13.0.0", + "@angular/common": "^13.0.0", + "@angular/core": "^13.0.0", + "@angular/platform-browser": "^13.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/flex-layout/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/forms": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.2.2.tgz", + "integrity": "sha512-T61W4Ay9X9qhxjc6lLqpNFeHrGKwg2mqdsZ3zIm/c7oKo37mgl9TB5kkrtnS+205r3N2hF4ICnGFZ4a/egUP/g==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.2.2", + "@angular/core": "13.2.2", + "@angular/platform-browser": "13.2.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/forms/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/language-service": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.2.2.tgz", + "integrity": "sha512-2P5+wRsbHgpI2rVeFwnsLWxyntUiw8kG9Tqh5BkVDqtQovbYtzFiaMkf5TFz/g938JBBgeRQzvXr1kQhEidAWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + } + }, + "node_modules/@angular/material": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-13.2.2.tgz", + "integrity": "sha512-YAjPp2+/wuEOPfkAxdRVdbWHiK4P3DgMZa9qP/NizN2lTXNrftEfD09ZlPIFMZRnnExezJ2LnO7eyELpc1VSKg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^13.0.0 || ^14.0.0-0", + "@angular/cdk": "13.2.2", + "@angular/common": "^13.0.0 || ^14.0.0-0", + "@angular/core": "^13.0.0 || ^14.0.0-0", + "@angular/forms": "^13.0.0 || ^14.0.0-0", + "@angular/platform-browser": "^13.0.0 || ^14.0.0-0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/material/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/platform-browser": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.2.2.tgz", + "integrity": "sha512-M7gWC8fFCPc/CRcHCzqe/j7WzwAUMeKt9vwlK633XnesHBoqZdYgbb3YHHc6WPVU0YI09Nb/Hm5sezEKmjUmPg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "13.2.2", + "@angular/common": "13.2.2", + "@angular/core": "13.2.2" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.2.2.tgz", + "integrity": "sha512-lj6xwat0StLp+ROFqXU62upwHQhlxaQi0djhrS+DGKUK0Xu9bkBeaSCfBFgS78jPm1SwL8Xztu9/vuDAHLRrqw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.2.2", + "@angular/compiler": "13.2.2", + "@angular/core": "13.2.2", + "@angular/platform-browser": "13.2.2" + } + }, + "node_modules/@angular/platform-browser-dynamic/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/platform-browser/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@angular/router": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.2.2.tgz", + "integrity": "sha512-dt2b9/kGJAkmOqUmUD3aKlp4pGpdqLwB0zmhUYF3ktNEcQaPf4ZjWT/4jhy09gFL+TKOHG5OQW9GxBbhWI4bSg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.2.2", + "@angular/core": "13.2.2", + "@angular/platform-browser": "13.2.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/router/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", + "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", + "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.0.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.17.2", + "@babel/parser": "^7.17.0", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", + "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz", + "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", + "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", + "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", + "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", + "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", + "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", + "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", + "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", + "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", + "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.0.tgz", + "integrity": "sha512-G5FaGZOWORq9zthDjIrjib5XlcddeqLbIiDO3YQsut6j7aGf76xn0umUC/pA6+nApk3hQJF4JzLzg5PCl6ewJg==", + "dev": true, + "dependencies": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", + "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.0", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cordobo/qrcode": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@cordobo/qrcode/-/qrcode-1.5.0.tgz", + "integrity": "sha512-aZ5n3MYw10t4v68EGvRGE1DL7iWfAiTUy4MSZRoqjHTRYdjX40sYgJf48NZa6zZeXVuJOEB/1Ni9KzS+C/EC0w==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^17.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.1.0.tgz", + "integrity": "sha512-DO76V3295AqhjJZvgeaDP5GAGAat4g6wYfF8X+1n+76MpJat8ffY5bCJ9eSUqFY71nImxXgaDTRYJcRnA9oo7g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", + "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "dev": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@ngtools/webpack": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.3.tgz", + "integrity": "sha512-wooUZiV92QyoeFxkhqIwH/cfiAAAn+l8fEEuaaEIfJtpjpbShvvlboEVsqb28soeGiFJfLcmsZM3mUFgsG4QBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0", + "typescript": ">=4.4.3 <4.6", + "webpack": "^5.30.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/@npmcli/git/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/@nrwl/cli": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.1.7.tgz", + "integrity": "sha512-HVvqYwefizcoVc4xrTgfIC8nfMA9cx5NiaIbZFDIZv12NsdalpA6a2BmmV8Zck+QQSGEHrhTZyt1AqDhA1t+6A==", + "dev": true, + "dependencies": { + "nx": "14.1.7" + } + }, + "node_modules/@nrwl/cli/node_modules/@nrwl/tao": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.1.7.tgz", + "integrity": "sha512-7jzPqEwElMiTKCZm2iuqC5aPIs+IwocWR6Tl1JhC1eK2PWskHOhdgTQM186lfXwPKWH4NJAE3eZFqtECnGDCJg==", + "dev": true, + "dependencies": { + "nx": "14.1.7" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nrwl/cli/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@nrwl/cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nrwl/cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@nrwl/cli/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/cli/node_modules/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@nrwl/cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/cli/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@nrwl/cli/node_modules/nx": { + "version": "14.1.7", + "resolved": "https://registry.npmjs.org/nx/-/nx-14.1.7.tgz", + "integrity": "sha512-Q10PVQ70TFg9HY6KQKJ316B42sVtxmFk/VUdC7ZHKxzDm/JeYf6OaDarQOUZaFZp/EL2brtEsrlWSMTWmk3r4w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nrwl/cli": "14.1.7", + "@nrwl/tao": "14.1.7", + "@parcel/watcher": "2.0.4", + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173", + "chalk": "4.1.0", + "chokidar": "^3.5.1", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^7.0.2", + "dotenv": "~10.0.0", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^10.1.0", + "glob": "7.1.4", + "ignore": "^5.0.4", + "jsonc-parser": "3.0.0", + "minimatch": "3.0.4", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "string-width": "^4.2.3", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^3.9.0", + "tslib": "^2.3.0", + "v8-compile-cache": "2.3.0", + "yargs": "^17.4.0", + "yargs-parser": "21.0.1" + }, + "bin": { + "nx": "bin/nx.js" + } + }, + "node_modules/@nrwl/cli/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/cli/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@nrwl/cli/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@nrwl/devkit": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", + "integrity": "sha512-TAAsZJvVc/obeH0rZKY6miVhyM2GHGb8qIWp9MAIdLlXf4VDcNC7rxwb5OrGVSwuTTjqGYBGPUx0yEogOOJthA==", + "dev": true, + "dependencies": { + "@nrwl/tao": "13.1.3", + "ejs": "^3.1.5", + "ignore": "^5.0.4", + "rxjs": "^6.5.4", + "semver": "7.3.4", + "tslib": "^2.0.0" + } + }, + "node_modules/@nrwl/devkit/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", + "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", + "dev": true, + "dependencies": { + "chalk": "4.1.0", + "enquirer": "~2.3.6", + "fs-extra": "^9.1.0", + "jsonc-parser": "3.0.0", + "nx": "13.1.3", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "tmp": "~0.2.1", + "tslib": "^2.0.0", + "yargs-parser": "20.0.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/tao/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@nrwl/tao/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@nrwl/tao/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@nrwl/tao/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@nrwl/tao/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/tao/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nrwl/tao/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@nrwl/tao/node_modules/yargs-parser": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", + "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", + "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@schematics/angular": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.3.tgz", + "integrity": "sha512-jloooGC7eco9AKxlIMMqFRptJYzZ0jNRBStWOp2dCISg6rmOKqpxbsHLtYFQIT1PnlomSxtKDAgYGQMDi9zhXw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.2.3", + "@angular-devkit/schematics": "13.2.3", + "jsonc-parser": "3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/@swc-node/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.9.0.tgz", + "integrity": "sha512-vRnvsMtL9OxybA/Wun1ZhlDvB6MNs4Zujnina0VKdGk+yI6s87KUhdTcbAY6dQMZhQTLFiC1Lnv/BuwCKcCEug==", + "dev": true, + "dependencies": { + "@swc/core": "^1.2.172" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@swc-node/register": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.5.1.tgz", + "integrity": "sha512-6IL5s4QShKGs08qAeNou3rDA3gbp2WHk6fo0XnJXQn/aC9k6FnVBbj/thGOIEDtgNhC/DKpZT8tCY1LpQnOZFg==", + "dev": true, + "dependencies": { + "@swc-node/core": "^1.9.0", + "@swc-node/sourcemap-support": "^0.2.0", + "colorette": "^2.0.16", + "debug": "^4.3.4", + "pirates": "^4.0.5", + "tslib": "^2.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "typescript": ">= 4.3" + } + }, + "node_modules/@swc-node/register/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@swc-node/register/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@swc-node/sourcemap-support": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.2.0.tgz", + "integrity": "sha512-FNrxdI6XMYfoNt81L8eFKEm1d8P82I1nPwS3MrnBGzZoMWB+seQhQK+iN6M5RreJxXbfZw5lF86LRjHEQeGMqg==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.21" + } + }, + "node_modules/@swc-node/sourcemap-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@swc-node/sourcemap-support/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@swc/core": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.189.tgz", + "integrity": "sha512-S5cKX4ECMSfW78DLFgnlilJZgjrFRYwPslrrwpLl3gpwh+Qo72/Mhn71u7G/5xXW+T/xW5GwPccHfCk+k72uUg==", + "dev": true, + "bin": { + "swcx": "run_swcx.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-android-arm-eabi": "1.2.189", + "@swc/core-android-arm64": "1.2.189", + "@swc/core-darwin-arm64": "1.2.189", + "@swc/core-darwin-x64": "1.2.189", + "@swc/core-freebsd-x64": "1.2.189", + "@swc/core-linux-arm-gnueabihf": "1.2.189", + "@swc/core-linux-arm64-gnu": "1.2.189", + "@swc/core-linux-arm64-musl": "1.2.189", + "@swc/core-linux-x64-gnu": "1.2.189", + "@swc/core-linux-x64-musl": "1.2.189", + "@swc/core-win32-arm64-msvc": "1.2.189", + "@swc/core-win32-ia32-msvc": "1.2.189", + "@swc/core-win32-x64-msvc": "1.2.189" + } + }, + "node_modules/@swc/core-android-arm-eabi": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.189.tgz", + "integrity": "sha512-0kN3Le6QzFFz+Lc6a/tf/RkJXubWwWaHxF4c0bVm4AKIFf4nRlUCEqEkjdVaZvL92rpBMHaEEBuIIz3T8DqTTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-android-arm64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.189.tgz", + "integrity": "sha512-smsb+YkDw2OKwg66Z63E/G4NlFApDbsiOPmZXFZbtZbNBD9v+wnk6WVA//XR1bdUI9VbzNKlMPKJxQTE685QDw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.189.tgz", + "integrity": "sha512-OGjZRkTulKirJMLYbW9etb59lA9ueDXVwYRVD9SrNh8tRMTf0Nq+SUT/C3LVhBBGC4KSdWOzBAYbDTTdsnY++Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.189.tgz", + "integrity": "sha512-BEcxnBHx51514Voe2dn/y1y5H9VNyw7Zpp9+mPekZqx5o/onPD5wZ1ZfAsPrA4UlvM3v16u6ITE/cLawJ/GdAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-freebsd-x64": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.189.tgz", + "integrity": "sha512-B6g2NWeh2iw6WPOaM19Uj3VE4se6alT265kWibLUshjcofRfnYT1lNhhkrF1D0EVnpC8I96I/xXNQo4Am9z4zQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.189.tgz", + "integrity": "sha512-6WhPG9pyN5AahQTVQk8MoN1I9Z/Ytfqizuie1wV7mW8FMNmMkiJvBekKtE6ftxu80Hqa34r86WfEwmJKm5MqYw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.189.tgz", + "integrity": "sha512-frJTGnxsDe7h2d7BtnBivOdOJTtabkQIIZmHd4JHqafcoekI6teyveIax17axLyimvWl278yTo3hf7ePagD/eg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.189.tgz", + "integrity": "sha512-27K38LoZpME5lojDJIUNo7zdTDwAKLm0BMQ7HXWcYOyiDAekhSidI+SrBWxCfLzfuClhFu6/VE3E7j32VFJsiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.189.tgz", + "integrity": "sha512-Ha5oJKOyQm9w7+e+WdRm4ypijzEmglWZGtgBR6vV6ViqqHcTBAU4nG87ex7y7AS9p+Cbc6EOSR9X1qIB8KxtbA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.189.tgz", + "integrity": "sha512-/p5yXa9HEzpVEuE4ivkW1IvwyYu6fT+L2OvVEs5oXIba80F0Wjy7InWqaa83gwrdMH+bXV6loG8LzZUZu/lpjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.189.tgz", + "integrity": "sha512-o/1ueM6/sifNjYnO6NMEXB895spVfJs5oQIPxQG9vJ/4zWLw8YmAx+u1xJY+XGyK6gnroHt7yPiS87qWdbeF6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.189.tgz", + "integrity": "sha512-YDwRkzykaf+dw5Z7u189cC/Tttkn2NVV84hrGL3LbVuh7wT5PaDhZs4Yz4unZQSlPV12olmZWgNr/i27h5wlpg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.2.189", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.189.tgz", + "integrity": "sha512-Nge8Z/ZkAp5p5No50yBDpBG7+ZYaVWGSuwtPj6OJe7orzvDCEm9GgcVE6J9GEjbclSWlCH8B8lUe17GaKRZHbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", + "dev": true + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz", + "integrity": "sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==", + "dev": true + }, + "node_modules/@types/jasminewd2": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", + "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", + "dev": true, + "dependencies": { + "@types/jasmine": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.7.tgz", + "integrity": "sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", + "integrity": "sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==", + "dev": true + }, + "node_modules/@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "node_modules/@types/selenium-webdriver": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.19.tgz", + "integrity": "sha512-OFUilxQg+rWL2FMxtmIgCkUDlJB6pskkpvmew7yeXfzzsOBb5rc+y2+DjHm+r3r1ZPPcJefK3DveNSYWGiy68g==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", + "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz", + "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/angularx-qrcode": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-13.0.3.tgz", + "integrity": "sha512-KzalrRy2MczdApSq29k2TBOml6taQ7NqdgZDNgPSQBd5/27+l3xHkOkhhDOmGruSyCcT2KdB8qeOT/6K48c+xw==", + "dependencies": { + "@cordobo/qrcode": "1.5.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": "^13.0.0" + } + }, + "node_modules/angularx-qrcode/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/apexcharts": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.33.1.tgz", + "integrity": "sha512-5aVzrgJefd8EH4w7oRmuOhA3+cxJxQg27cYg3ANVGvPCOB4AY3mVVNtFHRFaIq7bv8ws4GRaA9MWfzoWQw3MPQ==", + "dependencies": { + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", + "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", + "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001297", + "fraction.js": "^4.1.2", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "node_modules/babel-loader": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", + "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "blocking-proxy": "built/lib/bin.js" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dev": true, + "dependencies": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/browserstack": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", + "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + } + }, + "node_modules/browserstack/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/browserstack/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/browserstack/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "dependencies": { + "is-regexp": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.1.tgz", + "integrity": "sha512-nr81NhCAIpAWXGCK5thrKmfCQ6GDY0L5RN0U+BnIn/7Us55+UCex5ANNsNKmIVtDRnk0Ecf+/kzp9SUVrrBMLg==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz", + "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==", + "dev": true, + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.22.6", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.6.tgz", + "integrity": "sha512-u5yG2VL6NKXz9BZHr9RAm6eWD1DTNjG7jJnJgLGR+Im0whdPcPXqwqxd+dcUrZvpvPan5KMgn/3pI+Q/aGqPOA==", + "deprecated": "core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure.", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/critters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/critters/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/critters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-functions-list": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", + "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", + "dev": true, + "engines": { + "node": ">=12.22" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", + "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssdb": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", + "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", + "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", + "dev": true, + "dependencies": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, + "node_modules/detect-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz", + "integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug==" + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/detect-passive-events": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-passive-events/-/detect-passive-events-2.0.3.tgz", + "integrity": "sha512-QN/1X65Axis6a9D8qg8Py9cwY/fkWAmAH/edTbmLMcv4m5dboLJ7LcAi8CfaCON2tjk904KwKX/HTdsHC6yeRg==", + "dependencies": { + "detect-it": "^4.0.1" + } + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.68", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", + "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", + "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", + "dev": true, + "dependencies": { + "@socket.io/base64-arraybuffer": "~1.0.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", + "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dev": true, + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/esbuild": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.14.tgz", + "integrity": "sha512-aiK4ddv+uui0k52OqSHu4xxu+SzOim7Rlz4i25pMEiC8rlnGU0HJ9r+ZMfdWL5bzifg+nhnn7x4NSWTeehYblg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.14.14", + "esbuild-darwin-64": "0.14.14", + "esbuild-darwin-arm64": "0.14.14", + "esbuild-freebsd-64": "0.14.14", + "esbuild-freebsd-arm64": "0.14.14", + "esbuild-linux-32": "0.14.14", + "esbuild-linux-64": "0.14.14", + "esbuild-linux-arm": "0.14.14", + "esbuild-linux-arm64": "0.14.14", + "esbuild-linux-mips64le": "0.14.14", + "esbuild-linux-ppc64le": "0.14.14", + "esbuild-linux-s390x": "0.14.14", + "esbuild-netbsd-64": "0.14.14", + "esbuild-openbsd-64": "0.14.14", + "esbuild-sunos-64": "0.14.14", + "esbuild-windows-32": "0.14.14", + "esbuild-windows-64": "0.14.14", + "esbuild-windows-arm64": "0.14.14" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.14.tgz", + "integrity": "sha512-be/Uw6DdpQiPfula1J4bdmA+wtZ6T3BRCZsDMFB5X+k0Gp8TIh9UvmAcqvKNnbRAafSaXG3jPCeXxDKqnc8hFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.14.tgz", + "integrity": "sha512-BEexYmjWafcISK8cT6O98E3TfcLuZL8DKuubry6G54n2+bD4GkoRD6HYUOnCkfl2p7jodA+s4369IjSFSWjtHg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.14.tgz", + "integrity": "sha512-tnBKm41pDOB1GtZ8q/w26gZlLLRzVmP8fdsduYjvM+yFD7E2DLG4KbPAqFMWm4Md9B+DitBglP57FY7AznxbTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.14.tgz", + "integrity": "sha512-Q9Rx6sgArOHalQtNwAaIzJ6dnQ8A+I7f/RsQsdkS3JrdzmnlFo8JEVofTmwVQLoIop7OKUqIVOGP4PoQcwfVMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.14.tgz", + "integrity": "sha512-TJvq0OpLM7BkTczlyPIphcvnwrQwQDG1HqxzoYePWn26SMUAlt6wrLnEvxdbXAvNvDLVzG83kA+JimjK7aRNBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.14.tgz", + "integrity": "sha512-h/CrK9Baimt5VRbu8gqibWV7e1P9l+mkanQgyOgv0Ng3jHT1NVFC9e6rb1zbDdaJVmuhWX5xVliUA5bDDCcJeg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.14.tgz", + "integrity": "sha512-IC+wAiIg/egp5OhQp4W44D9PcBOH1b621iRn1OXmlLzij9a/6BGr9NMIL4CRwz4j2kp3WNZu5sT473tYdynOuQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.14.tgz", + "integrity": "sha512-gxpOaHOPwp7zSmcKYsHrtxabScMqaTzfSQioAMUaB047YiMuDBzqVcKBG8OuESrYkGrL9DDljXr/mQNg7pbdaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.14.tgz", + "integrity": "sha512-6QVul3RI4M5/VxVIRF/I5F+7BaxzR3DfNGoqEVSCZqUbgzHExPn+LXr5ly1C7af2Kw4AHpo+wDqx8A4ziP9avw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.14.tgz", + "integrity": "sha512-4Jl5/+xoINKbA4cesH3f4R+q0vltAztZ6Jm8YycS8lNhN1pgZJBDxWfI6HUMIAdkKlIpR1PIkA9aXQgZ8sxFAg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.14.tgz", + "integrity": "sha512-BitW37GxeebKxqYNl4SVuSdnIJAzH830Lr6Mkq3pBHXtzQay0vK+IeOR/Ele1GtNVJ+/f8wYM53tcThkv5SC5w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.14.tgz", + "integrity": "sha512-vLj6p76HOZG3wfuTr5MyO3qW5iu8YdhUNxuY+tx846rPo7GcKtYSPMusQjeVEfZlJpSYoR+yrNBBxq+qVF9zrw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.14.tgz", + "integrity": "sha512-fn8looXPQhpVqUyCBWUuPjesH+yGIyfbIQrLKG05rr1Kgm3rZD/gaYrd3Wpmf5syVZx70pKZPvdHp8OTA+y7cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.14.tgz", + "integrity": "sha512-HdAnJ399pPff3SKbd8g+P4o5znseni5u5n5rJ6Z7ouqOdgbOwHe2ofZbMow17WMdNtz1IyOZk2Wo9Ve6/lZ4Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.14.tgz", + "integrity": "sha512-bmDHa99ulsGnYlh/xjBEfxoGuC8CEG5OWvlgD+pF7bKKiVTbtxqVCvOGEZeoDXB+ja6AvHIbPxrEE32J+m5nqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-wasm": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.14.tgz", + "integrity": "sha512-qTjK4MWnYtQHCMGg2qDUqeFYXfVvYq5qJkQTIsOV4VZCknoYePVaDTG9ygEB9Ct0kc0DWs7IrS6Ja+GjY62Kzw==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.14.tgz", + "integrity": "sha512-6tVooQcxJCNenPp5GHZBs/RLu31q4B+BuF4MEoRxswT+Eq2JGF0ZWDRQwNKB8QVIo3t6Svc5wNGez+CwKNQjBg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.14.tgz", + "integrity": "sha512-kl3BdPXh0/RD/dad41dtzj2itMUR4C6nQbXQCyYHHo4zoUoeIXhpCrSl7BAW1nv5EFL8stT1V+TQVXGZca5A2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.14", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.14.tgz", + "integrity": "sha512-dCm1wTOm6HIisLanmybvRKvaXZZo4yEVrHh1dY0v582GThXJOzuXGja1HIQgV09RpSHYRL3m4KoUBL00l6SWEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", + "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "dev": true, + "dependencies": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "dependencies": { + "clone-regexp": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz", + "integrity": "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz", + "integrity": "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", + "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/rxjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", + "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", + "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", + "dev": true, + "dependencies": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.8.0.tgz", + "integrity": "sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg==", + "dev": true + }, + "node_modules/jasmine-spec-reporter": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", + "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "dev": true, + "dependencies": { + "colors": "1.4.0" + } + }, + "node_modules/jasmine/node_modules/jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", + "dev": true + }, + "node_modules/jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", + "dev": true, + "engines": { + "node": ">= 6.9.x" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jszip": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", + "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/karma": { + "version": "6.3.16", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", + "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", + "dev": true, + "dependencies": { + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "colors": "1.4.0", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.2.0", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/mattlewis92" + } + }, + "node_modules/karma-jasmine": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", + "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", + "dev": true, + "dependencies": { + "jasmine-core": "^3.6.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "karma": "*" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", + "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", + "dev": true, + "peerDependencies": { + "jasmine-core": ">=3.5", + "karma": ">=0.9", + "karma-jasmine": ">=1.1" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma-source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma-source-map-support/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/known-css-properties": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", + "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "dev": true + }, + "node_modules/less": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", + "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz", + "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.1.tgz", + "integrity": "sha512-SQum9mg3BgnY5BK+2KYl4W7pk9b26Q8tW2lTsO6tidD0/Ds9ksdXvp3ip2s9LqDjj5gtBMyWRfOPZptWj4PfCg==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz", + "integrity": "sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/lint-staged/node_modules/supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/listr2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/listr2/node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log4js": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", + "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", + "dev": true, + "dependencies": { + "date-format": "^4.0.3", + "debug": "^4.3.3", + "flatted": "^3.2.4", + "rfdc": "^1.3.0", + "streamroller": "^3.0.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dev": true, + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", + "integrity": "sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/ng-apexcharts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.0.tgz", + "integrity": "sha512-HSLPHrSH4CYn6crU8RsbZzuecKoXZ7a8i0cGdq8yDZ9DwaEauiJl5WWCWKajHLbwloVEdqoAsKN04TcvIP0ulQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@angular/core": ">=13.0.0", + "apexcharts": "^3.31.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/ngx-color-picker": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.0.tgz", + "integrity": "sha512-SY5KoZka/uq2MNhUAKfJXQjjS2TFvKDJHbsCxfnjKjS/VHx8VVeTJpnt5wuuewzRzLxfOm5y2Fw8/HTPEPtRkA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=9.0.0", + "@angular/core": ">=9.0.0", + "@angular/forms": ">=9.0.0" + } + }, + "node_modules/ngx-color-picker/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true + }, + "node_modules/node-forge": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", + "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.3.1.tgz", + "integrity": "sha512-nX1x4qUrKqwbIAhv4s9et4FIUVzNOpeY07bsjGUy8gwJrXH/wScImSQqXErmo/b2jZY2r0mohbLA9zVj7u1cNw==", + "deprecated": "Please update to latest patch version to fix memory leak https://github.com/isaacs/node-lru-cache/issues/227", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.0.2.tgz", + "integrity": "sha512-JSFLK53NJP22FL/eAGOyKsWbc2G3v+toPMD7Dq9PJKQCvK0i3t8hGkKxe+3YZzwYa+c0kxRHu7uxH3fvO+rsaA==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.0", + "cacache": "^15.3.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.3.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.4.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.1.1", + "ssri": "^8.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", + "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nx": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/nx/-/nx-13.1.3.tgz", + "integrity": "sha512-clM0NQhQKYkqcNz2E3uYRMLwhp2L/9dBhJhQi9XBX4IAyA2gWAomhRIlLm5Xxg3g4h1xwSpP3eJ5t89VikY8Pw==", + "dev": true, + "dependencies": { + "@nrwl/cli": "*" + }, + "bin": { + "nx": "bin/nx.js" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", + "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/pacote/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/postcss": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", + "dev": true, + "dependencies": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", + "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.2" + }, + "peerDependencies": { + "postcss": "^8.0.2" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.2.tgz", + "integrity": "sha512-DXVtwUhIk4f49KK5EGuEdgx4Gnyj6+t2jBSEmxvpIK9QI40tWrpS2Pua8Q7iIZWBrki2QOaeUdEaLPPa91K0RQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz", + "integrity": "sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", + "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", + "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.4.tgz", + "integrity": "sha512-i6AytuTCoDLJkWN/MtAIGriJz3j7UX6bV7Z5t+KgFz+dwZS15/mlTJY1S0kRizlk6ba0V8u8hN50Fz5Nm7tdZw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", + "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.2" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz", + "integrity": "sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.5.tgz", + "integrity": "sha512-XiZzvdxLOWZwtt/1GgHJYGoD9scog/DD/yI5dcvPrXNdNDEv7T53/6tL7ikl+EM3jcerII5/XIQzd1UHOdTi2w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.5.tgz", + "integrity": "sha512-gPUJc71ji9XKyl0WSzAalBeEA/89kU+XpffpPxSaaaZ1c48OL36r1Ep5R6+9XAPkIiDlSvVAwP4io12q/vTcvA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", + "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz", + "integrity": "sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.1.0.tgz", + "integrity": "sha512-59uHN/2wRaOd7whDyeaJ82E0kncIEeJkwcmvXFPNus8v1YMhtv2IUo9OtOAncn7sifZVMRsyoPlhxwckTjn4cQ==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz", + "integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.8" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", + "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.4.tgz", + "integrity": "sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", + "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", + "dev": true, + "dependencies": { + "autoprefixer": "^10.4.2", + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001299", + "css-blank-pseudo": "^3.0.2", + "css-has-pseudo": "^3.0.3", + "css-prefers-color-scheme": "^6.0.2", + "cssdb": "^5.0.0", + "postcss-attribute-case-insensitive": "^5.0.0", + "postcss-color-functional-notation": "^4.2.1", + "postcss-color-hex-alpha": "^8.0.2", + "postcss-color-rebeccapurple": "^7.0.2", + "postcss-custom-media": "^8.0.0", + "postcss-custom-properties": "^12.1.2", + "postcss-custom-selectors": "^6.0.0", + "postcss-dir-pseudo-class": "^6.0.3", + "postcss-double-position-gradients": "^3.0.4", + "postcss-env-function": "^4.0.4", + "postcss-focus-visible": "^6.0.3", + "postcss-focus-within": "^5.0.3", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.2", + "postcss-image-set-function": "^4.0.4", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.0.3", + "postcss-logical": "^5.0.3", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.2", + "postcss-overflow-shorthand": "^3.0.2", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.3", + "postcss-pseudo-class-any-link": "^7.0.2", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^5.0.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.1.tgz", + "integrity": "sha512-JRoLFvPEX/1YTPxRxp1JO4WxBVXJYrSY7NHeak5LImwJ+VobFMwYDQHvfTXEpcn+7fYIeGkC29zYFhFWIZD8fg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", + "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", + "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-eslint": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-14.1.0.tgz", + "integrity": "sha512-K0TRVaAUXtI5xz1ZaVZfvGMmunDNyIGXFkE845hVl6FzSxzRN9E03YmK3IiapcRFv3w4PyAL25LIPsy2sRz2tw==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.4.2", + "@types/prettier": "^2.6.0", + "@typescript-eslint/parser": "^5.10.0", + "common-tags": "^1.4.0", + "dlv": "^1.1.0", + "eslint": "^8.7.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^2.5.1", + "pretty-format": "^23.0.1", + "require-relative": "^0.8.7", + "typescript": "^4.5.4", + "vue-eslint-parser": "^8.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protractor": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", + "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", + "deprecated": "We have news to share - Protractor is deprecated and will reach end-of-life by Summer 2023. To learn more and find out about other options please refer to this post on the Angular blog. Thank you for using and contributing to Protractor. https://goo.gle/state-of-e2e-in-angular", + "dev": true, + "dependencies": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.1.7", + "yargs": "^15.3.1" + }, + "bin": { + "protractor": "bin/protractor", + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=10.13.x" + } + }, + "node_modules/protractor/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/protractor/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/protractor/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/protractor/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dev": true, + "dependencies": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/rxjs-for-await": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", + "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", + "dev": true, + "peerDependencies": { + "rxjs": "^6.0.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.0.tgz", + "integrity": "sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/saucelabs/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/saucelabs/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/saucelabs/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "dependencies": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/selenium-webdriver/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/selfsigned": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "dev": true, + "dependencies": { + "node-forge": "^1.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", + "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", + "dev": true + }, + "node_modules/socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "dev": true, + "dependencies": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "dev": true, + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", + "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true, + "bin": { + "specificity": "bin/specificity" + } + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamroller": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", + "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", + "dev": true, + "dependencies": { + "date-format": "^4.0.3", + "debug": "^4.1.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, + "node_modules/stylelint": { + "version": "14.8.3", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.3.tgz", + "integrity": "sha512-aLpskXwSgFEBYbFRKA/BfuyYMGuXNtn2t5GqoffNPSezvw97x/vVNWcZNF0+cwt+LBjfvyq9/MRE3OjInGRgNA==", + "dev": true, + "dependencies": { + "balanced-match": "^2.0.0", + "colord": "^2.9.2", + "cosmiconfig": "^7.0.1", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", + "execall": "^2.0.0", + "fast-glob": "^3.2.11", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.0", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.25.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.13", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", + "svg-tags": "^1.0.0", + "table": "^6.8.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-html": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", + "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", + "dev": true, + "engines": { + "node": "^12 || >=14" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-prettier": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", + "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", + "dev": true, + "bin": { + "stylelint-config-prettier": "bin/check.js", + "stylelint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">=11.0.0" + } + }, + "node_modules/stylelint-config-prettier-scss": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier-scss/-/stylelint-config-prettier-scss-0.0.1.tgz", + "integrity": "sha512-lBAYG9xYOh2LeWEPC/64xeUxwOTnQ8nDyBijQoWoJb10/bMGrUwnokpt8jegGck2Vbtxh6XGwH63z5qBcVHreQ==", + "dev": true, + "dependencies": { + "stylelint-config-prettier": ">=9.0.3" + }, + "bin": { + "stylelint-config-prettier-scss": "bin/check.js", + "stylelint-config-prettier-scss-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">=11.0.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-recommended-scss": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", + "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", + "dev": true, + "dependencies": { + "postcss-scss": "^4.0.2", + "stylelint-config-recommended": "^6.0.0", + "stylelint-scss": "^4.0.0" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", + "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-scss": { + "version": "1.0.0-security", + "resolved": "https://registry.npmjs.org/stylelint-config-scss/-/stylelint-config-scss-1.0.0-security.tgz", + "integrity": "sha512-8Pgul2mNpzTeK2KCuyV5RcDC1BgzWzU7dCLVJWuxpkKgmxrMqCvjqgyosaKbAVZy2AiaMU0zfHBt7prg7/NaxA==", + "dev": true + }, + "node_modules/stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^7.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-standard-scss": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz", + "integrity": "sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==", + "dev": true, + "dependencies": { + "stylelint-config-recommended-scss": "^5.0.2", + "stylelint-config-standard": "^24.0.0" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-recommended": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", + "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-standard": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", + "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^6.0.0" + }, + "peerDependencies": { + "stylelint": "^14.0.0" + } + }, + "node_modules/stylelint-scss": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", + "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0" + }, + "peerDependencies": { + "stylelint": "^14.5.1" + } + }, + "node_modules/stylelint/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylelint/node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/stylelint/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylus": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", + "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", + "dev": true, + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.2.0.tgz", + "integrity": "sha512-5dsDc7qVQGRoc6pvCL20eYgRUxepZ9FpeK28XhdXaIPP6kXr6nI1zAAKFQgP5OBkOfKaURp4WUpJzspg1f01Gg==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", + "dependencies": { + "svg.js": ">=2.3.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "node_modules/svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "dependencies": { + "svg.js": "^2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/table/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/table/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "acorn": "^8.5.0" + }, + "peerDependenciesMeta": { + "acorn": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "dev": true, + "dependencies": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/terser/node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-node": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + }, + "peerDependencies": { + "typescript": ">=2.0" + } + }, + "node_modules/ts-node/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ts-node/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.8.tgz", + "integrity": "sha512-5NkbXZUlmCE73Fs7gvkp1XXJWHYetPkg60QnQ2NXQmBYNFxbBr2zA8GCtaH4K2s2WhOmSlgiSTmrjrcm5tnM5g==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "dependencies": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "dependencies": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz", + "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==", + "dev": true, + "dependencies": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "bin": { + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/webdriver-manager/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/webdriver-manager/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/webdriver-manager/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack": { + "version": "5.67.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", + "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", + "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/webpack-dev-server/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "dependencies": { + "tslib": "^2.0.0" + } + } + }, "dependencies": { "@ampproject/remapping": { "version": "2.1.1", @@ -1972,7 +18728,8 @@ "version": "13.2.3", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.3.tgz", "integrity": "sha512-wooUZiV92QyoeFxkhqIwH/cfiAAAn+l8fEEuaaEIfJtpjpbShvvlboEVsqb28soeGiFJfLcmsZM3mUFgsG4QBQ==", - "dev": true + "dev": true, + "requires": {} }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -3232,13 +19989,15 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -3990,7 +20749,8 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true + "dev": true, + "requires": {} }, "clean-stack": { "version": "2.2.0", @@ -4590,7 +21350,8 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true + "dev": true, + "requires": {} }, "css-select": { "version": "4.2.1", @@ -5485,7 +22246,8 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-prettier": { "version": "4.0.0", @@ -6493,7 +23255,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -7414,7 +24177,8 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", - "dev": true + "dev": true, + "requires": {} }, "karma-source-map-support": { "version": "1.4.0", @@ -9184,7 +25948,8 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true + "dev": true, + "requires": {} }, "postcss-custom-properties": { "version": "12.1.4", @@ -9253,13 +26018,15 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true + "dev": true, + "requires": {} }, "postcss-gap-properties": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-image-set-function": { "version": "4.0.6", @@ -9285,7 +26052,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-lab-function": { "version": "4.1.0", @@ -9312,13 +26080,15 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true + "dev": true, + "requires": {} }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-media-query-parser": { "version": "0.2.3", @@ -9330,7 +26100,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -9374,13 +26145,15 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-place": { "version": "7.0.4", @@ -9445,7 +26218,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-resolve-nested-selector": { "version": "0.1.1", @@ -9457,13 +26231,15 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-scss": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-selector-not": { "version": "5.0.0", @@ -10251,7 +27027,8 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true + "dev": true, + "requires": {} }, "safe-buffer": { "version": "5.1.2", @@ -10358,7 +27135,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -10875,22 +27653,6 @@ "fs-extra": "^10.0.0" } }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -10908,6 +27670,22 @@ } } }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11061,13 +27839,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", - "dev": true + "dev": true, + "requires": {} }, "stylelint-config-prettier": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", - "dev": true + "dev": true, + "requires": {} }, "stylelint-config-prettier-scss": { "version": "0.0.1", @@ -11082,7 +27862,8 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", - "dev": true + "dev": true, + "requires": {} }, "stylelint-config-recommended-scss": { "version": "5.0.2", @@ -11099,7 +27880,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -11132,7 +27914,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true + "dev": true, + "requires": {} }, "stylelint-config-standard": { "version": "24.0.0", @@ -11452,7 +28235,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -12011,7 +28795,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -12310,7 +29095,8 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true + "dev": true, + "requires": {} }, "xml2js": { "version": "0.4.23", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ab7ca3b2d..0a9285394 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -15,34 +15,33 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.1.0": - version: 2.2.0 - resolution: "@ampproject/remapping@npm:2.2.0" +"@ampproject/remapping@npm:^2.0.0": + version: 2.1.1 + resolution: "@ampproject/remapping@npm:2.1.1" dependencies: - "@jridgewell/gen-mapping": ^0.1.0 - "@jridgewell/trace-mapping": ^0.3.9 - checksum: d74d170d06468913921d72430259424b7e4c826b5a7d39ff839a29d547efb97dc577caa8ba3fb5cf023624e9af9d09651afc3d4112a45e2050328abc9b3a2292 + "@jridgewell/trace-mapping": ^0.3.0 + checksum: cc5cf29833e2b8bdf420821a6e027a35cf6a48605df64ae5095b55cb722581b236554fc8f920138e6da3f7a99ec99273b07ebe2e2bb98b6a4a8d8e33648cac77 languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1302.6": - version: 0.1302.6 - resolution: "@angular-devkit/architect@npm:0.1302.6" +"@angular-devkit/architect@npm:0.1302.3": + version: 0.1302.3 + resolution: "@angular-devkit/architect@npm:0.1302.3" dependencies: - "@angular-devkit/core": 13.2.6 + "@angular-devkit/core": 13.2.3 rxjs: 6.6.7 - checksum: d6f1b706bc56bfc8c1ab4c25bff52714728895ac58ea52c6ad7d0ffa73e55326982f7f024ab2bb14cb7ba5a330b05e797df004f4404995a22c26163d106e9a42 + checksum: c9b85a71b5765bd319ff474825a18a510bfb0aeb8f632cdab258dc8fbba9a79b9db5c1802312f9b446614190c283d6527474dd30ca83405ce429ed0c559a847e languageName: node linkType: hard "@angular-devkit/build-angular@npm:13.2.x": - version: 13.2.6 - resolution: "@angular-devkit/build-angular@npm:13.2.6" + version: 13.2.3 + resolution: "@angular-devkit/build-angular@npm:13.2.3" dependencies: "@ampproject/remapping": 1.1.1 - "@angular-devkit/architect": 0.1302.6 - "@angular-devkit/build-webpack": 0.1302.6 - "@angular-devkit/core": 13.2.6 + "@angular-devkit/architect": 0.1302.3 + "@angular-devkit/build-webpack": 0.1302.3 + "@angular-devkit/core": 13.2.3 "@babel/core": 7.16.12 "@babel/generator": 7.16.8 "@babel/helper-annotate-as-pure": 7.16.7 @@ -53,7 +52,7 @@ __metadata: "@babel/runtime": 7.16.7 "@babel/template": 7.16.7 "@discoveryjs/json-ext": 0.5.6 - "@ngtools/webpack": 13.2.6 + "@ngtools/webpack": 13.2.3 ansi-colors: 4.1.1 babel-loader: 8.2.3 babel-plugin-istanbul: 6.1.1 @@ -64,8 +63,8 @@ __metadata: core-js: 3.20.3 critters: 0.0.16 css-loader: 6.5.1 - esbuild: 0.14.22 - esbuild-wasm: 0.14.22 + esbuild: 0.14.14 + esbuild-wasm: 0.14.14 glob: 7.2.0 https-proxy-agent: 5.0.0 inquirer: 8.2.0 @@ -73,7 +72,7 @@ __metadata: karma-source-map-support: 1.4.0 less: 4.1.2 less-loader: 10.2.0 - license-webpack-plugin: 4.0.2 + license-webpack-plugin: 4.0.1 loader-utils: 3.2.0 mini-css-extract-plugin: 2.5.3 minimatch: 3.0.4 @@ -95,7 +94,7 @@ __metadata: source-map-support: 0.5.21 stylus: 0.56.0 stylus-loader: 6.2.0 - terser: 5.11.0 + terser: 5.10.0 text-table: 0.2.0 tree-kill: 1.2.2 tslib: 2.3.1 @@ -129,26 +128,26 @@ __metadata: optional: true tailwindcss: optional: true - checksum: f5b041a64661bcb845d9f91d19397fa9f14760c4904266974c6d4a7c9db3360ae7f76e2dd1332a85e68fc3d1d0051964a40fc2fdfbfef5ff912a29b67ffca978 + checksum: 4138cfbfa5e68984073294a69e6fb6b58df9712c42deba6aae75767e072df2a4e4e165b0d1590d2e72cdeea4e0eb77e87db336a8a579d6a403a0bd3ebdbb7729 languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1302.6": - version: 0.1302.6 - resolution: "@angular-devkit/build-webpack@npm:0.1302.6" +"@angular-devkit/build-webpack@npm:0.1302.3": + version: 0.1302.3 + resolution: "@angular-devkit/build-webpack@npm:0.1302.3" dependencies: - "@angular-devkit/architect": 0.1302.6 + "@angular-devkit/architect": 0.1302.3 rxjs: 6.6.7 peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^4.0.0 - checksum: 9c8f7737211eeb5240a2abd0caf6a61a19065829d2b75f47faa9dd2ed48c117ad0572641701cddabc264201ec3c4c2037c139a4efb8514c991704f4ac030e0df + checksum: 827f35b1186093efda0be38e7b5b44634f59ba9203a6ba21dd9701af0cc487f55cf2bafc0a6729ac1534226cd9e9c97d9f35297517ac73304f67f19f9e74d6e6 languageName: node linkType: hard -"@angular-devkit/core@npm:13.2.6": - version: 13.2.6 - resolution: "@angular-devkit/core@npm:13.2.6" +"@angular-devkit/core@npm:13.2.3": + version: 13.2.3 + resolution: "@angular-devkit/core@npm:13.2.3" dependencies: ajv: 8.9.0 ajv-formats: 2.1.1 @@ -161,20 +160,20 @@ __metadata: peerDependenciesMeta: chokidar: optional: true - checksum: ec8589dbd7b2b1c444229be9915c6a1f5ddb3ee26dcc43612f930125d93c7b2be1aabba09e0ba46008883908cd908c7883e67d534313d979cb3ad86b30e06019 + checksum: 523671d26806046aa2e81aac45dbd2a18afdd4a8803ad9bdf5883e818357dcbd31174a1439fb1c7dc1b1dbe5ce885b385cb03dae6e7034c87b3d4f58b25a5c88 languageName: node linkType: hard -"@angular-devkit/schematics@npm:13.2.6": - version: 13.2.6 - resolution: "@angular-devkit/schematics@npm:13.2.6" +"@angular-devkit/schematics@npm:13.2.3": + version: 13.2.3 + resolution: "@angular-devkit/schematics@npm:13.2.3" dependencies: - "@angular-devkit/core": 13.2.6 + "@angular-devkit/core": 13.2.3 jsonc-parser: 3.0.0 magic-string: 0.25.7 ora: 5.4.1 rxjs: 6.6.7 - checksum: 0aeade80952a08ddf461f6e3b8fb3643089e5691c5aecb3b330610a197cdb3b0b2f2032a131128d9355146ad6a46a10af2e986dd5eac7d93a7e89374e772a6da + checksum: 98748b5a3456798b7e32362b8b67ece9c00757d154a2d95275a18989713bd66174bff158bd3db8b3b50f054c9f2d33e418e79067992bd3a6e1205c5bb9bac0f2 languageName: node linkType: hard @@ -282,19 +281,19 @@ __metadata: linkType: hard "@angular/animations@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/animations@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/animations@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.7 - checksum: e444c814cdbfd2c1360e9f7f01d853baeccf17551eba178fd30b5c29b8e95386fe7d315ccc951b8f0c5d2cfa04091f5ecffecde71f6a2a3137c711218d4cae58 + "@angular/core": 13.2.2 + checksum: 8b976929ca0db1ccb7a81e1769506edb7ceab16406bfdd936c30da51513b34234ecda8ad3d5a3d57b6e5cd39b4b3369990ce21cbbbe19d3f810eac7072e3e5f4 languageName: node linkType: hard "@angular/cdk@npm:13.2.x": - version: 13.2.6 - resolution: "@angular/cdk@npm:13.2.6" + version: 13.2.2 + resolution: "@angular/cdk@npm:13.2.2" dependencies: parse5: ^5.0.0 tslib: ^2.3.0 @@ -305,18 +304,18 @@ __metadata: dependenciesMeta: parse5: optional: true - checksum: 8cce0c7568bf7d83ec1b8f6be171f061531c9c0c681b94967f6345f33e7febf58b71266d1414ef71e16808229de03c3fa835cb032e043a494c398fffc62f7d24 + checksum: 940b3e9d43bc425cdf0b8211ea00601e0a5ca98fe3bb8dca6981ec0738690c56dddf34c79fb71890d0064643cccd1e18956506aefabf24c9729aa7a624741619 languageName: node linkType: hard "@angular/cli@npm:13.2.x": - version: 13.2.6 - resolution: "@angular/cli@npm:13.2.6" + version: 13.2.3 + resolution: "@angular/cli@npm:13.2.3" dependencies: - "@angular-devkit/architect": 0.1302.6 - "@angular-devkit/core": 13.2.6 - "@angular-devkit/schematics": 13.2.6 - "@schematics/angular": 13.2.6 + "@angular-devkit/architect": 0.1302.3 + "@angular-devkit/core": 13.2.3 + "@angular-devkit/schematics": 13.2.3 + "@schematics/angular": 13.2.3 "@yarnpkg/lockfile": 1.1.0 ansi-colors: 4.1.1 debug: 4.3.3 @@ -334,65 +333,65 @@ __metadata: uuid: 8.3.2 bin: ng: bin/ng.js - checksum: 66765d8ead86045949dc3b8fe45577e9ae705354975da4e6bfdce51654102f313dde667f67ae2106cd79e810bec44192e4cca2f75a19522fac3561314a10d84c + checksum: b979a704ac248f6ee11d2b2bd57a86504be440afa8f07747b908080e32848d22aa220d3ca2580c50e4ec62e613d61a332ae5ee8fc15f4d3acea34fefa2585936 languageName: node linkType: hard "@angular/common@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/common@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/common@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.7 + "@angular/core": 13.2.2 rxjs: ^6.5.3 || ^7.4.0 - checksum: dbf2c324b3e3a549312d5d196da02cb07854fc75c7db5afeeb5efd7d432dff0d9539b097a1f150a2e8e8291cc85f0405a3e5cf7ec7a888a86f6f66a2213e269e + checksum: c7a6eadf9b31c6bcffc5072f8c2159c1624fc87a2056f092d7c2bc2ab8b925c8ee9a099262a1dae7bead451a29ad5cea5ed42688a2c9f6c0b85c1302ed5dafd7 languageName: node linkType: hard "@angular/compiler-cli@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/compiler-cli@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/compiler-cli@npm:13.2.2" dependencies: - "@babel/core": ^7.17.2 + "@babel/core": ^7.8.6 chokidar: ^3.0.0 convert-source-map: ^1.5.1 dependency-graph: ^0.11.0 - magic-string: ^0.26.0 + magic-string: ^0.25.0 reflect-metadata: ^0.1.2 semver: ^7.0.0 sourcemap-codec: ^1.4.8 tslib: ^2.3.0 yargs: ^17.2.1 peerDependencies: - "@angular/compiler": 13.2.7 + "@angular/compiler": 13.2.2 typescript: ">=4.4.2 <4.6" bin: ng-xi18n: bundles/src/bin/ng_xi18n.js ngc: bundles/src/bin/ngc.js ngcc: bundles/ngcc/main-ngcc.js - checksum: 9c7f207285174bd2e5bbb52363eadbaf143bc1582dca44fa941fc8c29cefd580173a8aa9444f152dc9b96bd8a267415890cd4509a08b786211a14a3f85cf1254 + checksum: ade19505c9ca98a1c68f266532eed47798ba979612bbfca2be3d16a23a6b6af4aeeaad6fe000bfcdb8fdaf7cf48de3192ebf39dc38707a2ce0112d7d8c6b4918 languageName: node linkType: hard "@angular/compiler@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/compiler@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/compiler@npm:13.2.2" dependencies: tslib: ^2.3.0 - checksum: 6d63ede056a8625d237e9895c2c532009b90b4a3b0ed232a84cea5a6005021c4cd91746fe38398af72c8b6794107386bba56e1b1c65163635bde5f325fb67674 + checksum: 338a0da25cbbf47bf09ed617fd948fa3565c39b971f92718ce119c14f287ee1191de898e0c9adeee774754c203f130e1ba87132353a5084b3ae69012a326e988 languageName: node linkType: hard "@angular/core@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/core@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/core@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.11.4 - checksum: 7601052ae8c2206a6c26a1e1f92077cc2cf2c84aeed2562b68b3f45066adac1fb581622b175475e4979ca5aee5ce3bc9c9d5cb95325ca61b6546c5366d10adcc + checksum: 801240fe14c4dbf4d1c5fb80418ecf067b8cf2741b5a3fb2a29c4e11aa2494c853a593a28f35c270fbc88979095bfbf0c985983e0cfec7ba1d5b52957d2538d5 languageName: node linkType: hard @@ -412,84 +411,84 @@ __metadata: linkType: hard "@angular/forms@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/forms@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/forms@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.7 - "@angular/core": 13.2.7 - "@angular/platform-browser": 13.2.7 + "@angular/common": 13.2.2 + "@angular/core": 13.2.2 + "@angular/platform-browser": 13.2.2 rxjs: ^6.5.3 || ^7.4.0 - checksum: bea0ae8de61695b341846982821a7463ee9cded9f8afb1e3fd1c344935898682ce73743adfdadc4d2be075cb77fab2af2b1fe4ab4c5ec3cc8bd371c71c99876b + checksum: bba5e568453db12a0857d0be1d7840e0f3d6755816dafc4320318ec38663e1b7e261291ef56fd8a08f5e595164fca6890d72d589e5064bd308b09a3a3a103896 languageName: node linkType: hard "@angular/language-service@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/language-service@npm:13.2.7" - checksum: 2054f85287c822143ba891659148dfa5934b8abe2186d5b8a7544d6e890e7cec7d8afaaeddd76c004cc4e4b45414b24d4902ef176aa1dfca9b28c0598246968f + version: 13.2.2 + resolution: "@angular/language-service@npm:13.2.2" + checksum: 2eca2dd49dae04c57637e1f9f5f5da528d98823f31a899e8466974bae5e1b4ad6ea9b397094cda26667605f934948baf4dffed180f746f3d4ad3be7d430ebd6a languageName: node linkType: hard "@angular/material@npm:13.2.x": - version: 13.2.6 - resolution: "@angular/material@npm:13.2.6" + version: 13.2.2 + resolution: "@angular/material@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: "@angular/animations": ^13.0.0 || ^14.0.0-0 - "@angular/cdk": 13.2.6 + "@angular/cdk": 13.2.2 "@angular/common": ^13.0.0 || ^14.0.0-0 "@angular/core": ^13.0.0 || ^14.0.0-0 "@angular/forms": ^13.0.0 || ^14.0.0-0 "@angular/platform-browser": ^13.0.0 || ^14.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - checksum: 0287d8b966271cf1cd4587859ec59124a2019c542bffc561251238221e4a31b9fb2dfe57c1e47263b8c195aa66175ae0f8bc4373c66db11299793ff1aee406a7 + checksum: 0a6ab51fb588d2911f770b2abfdb7e1d55c28441b83e5650c29b0056855e553dcc364fd40a398fb06ab28f922cfdbc6257ad8822a3ec70f5a252c1e91a4d9355 languageName: node linkType: hard "@angular/platform-browser-dynamic@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/platform-browser-dynamic@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/platform-browser-dynamic@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.7 - "@angular/compiler": 13.2.7 - "@angular/core": 13.2.7 - "@angular/platform-browser": 13.2.7 - checksum: f8423844f0c63d618b8861e2911163c266ce972fb9ebcc8e0bb49d8a176e9abedb5019a9b300100d7d7f054784c343410dd80ac7f1e62946910e5484513cec7c + "@angular/common": 13.2.2 + "@angular/compiler": 13.2.2 + "@angular/core": 13.2.2 + "@angular/platform-browser": 13.2.2 + checksum: 6dbf65da1460d855b623a4d1c10468241c0f0f5efcd3aab4f3b150d262c8f58e6a96f8e48732aacd9d8a930512c751af7d693c71d8c01a6cd8d2a21361540be7 languageName: node linkType: hard "@angular/platform-browser@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/platform-browser@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/platform-browser@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/animations": 13.2.7 - "@angular/common": 13.2.7 - "@angular/core": 13.2.7 + "@angular/animations": 13.2.2 + "@angular/common": 13.2.2 + "@angular/core": 13.2.2 peerDependenciesMeta: "@angular/animations": optional: true - checksum: 92c9020c6cd0b3e61e251fff6d86d8173e8af26c95b11f82548a81afc5b305507bd366b2a107d7596477b9d82a1b39397bdd20cf39f5779902ccbbd1dba6a816 + checksum: 22472d8dba4151654d413b9d3e65d56ab15867007e6b838261ca46e48ac0794d770fea57149ccdbf8d973d787afcead0a4dd73121c0fd36a377ca38fa7e305ad languageName: node linkType: hard "@angular/router@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/router@npm:13.2.7" + version: 13.2.2 + resolution: "@angular/router@npm:13.2.2" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.7 - "@angular/core": 13.2.7 - "@angular/platform-browser": 13.2.7 + "@angular/common": 13.2.2 + "@angular/core": 13.2.2 + "@angular/platform-browser": 13.2.2 rxjs: ^6.5.3 || ^7.4.0 - checksum: ac3403e65a6eb420b040179e0ccb927744b654b0c335f58b7b0e144f53b0af5d5273284fdc143265c17a872680b94be304b61d031910f19375bafadd8b1389af + checksum: de28b31d2552b0551b3bc70f1e5a9e46effe7a421ece8c72a3708d0d0d53394feee4ca362f842d19fc909c08cfeeae3e8e36028e3b9c05274c7ffaf0480578f5 languageName: node linkType: hard @@ -509,10 +508,10 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.16.8, @babel/compat-data@npm:^7.17.10": - version: 7.17.10 - resolution: "@babel/compat-data@npm:7.17.10" - checksum: e85051087cd4690de5061909a2dd2d7f8b6434a3c2e30be6c119758db2027ae1845bcd75a81127423dd568b706ac6994a1a3d7d701069a23bf5cfe900728290b +"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.16.4, @babel/compat-data@npm:^7.16.8": + version: 7.17.0 + resolution: "@babel/compat-data@npm:7.17.0" + checksum: fe5afaf529d107a223cd5937dace248464b6df1e9f4ea4031a5723e9571b46a4db1c4ff226bac6351148b1bc02ba1b39cb142662cd235aa99c1dda77882f8c9d languageName: node linkType: hard @@ -539,30 +538,30 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.12.3, @babel/core@npm:^7.17.2": - version: 7.18.0 - resolution: "@babel/core@npm:7.18.0" +"@babel/core@npm:^7.12.3, @babel/core@npm:^7.8.6": + version: 7.17.2 + resolution: "@babel/core@npm:7.17.2" dependencies: - "@ampproject/remapping": ^2.1.0 + "@ampproject/remapping": ^2.0.0 "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.18.0 - "@babel/helper-compilation-targets": ^7.17.10 - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helpers": ^7.18.0 - "@babel/parser": ^7.18.0 + "@babel/generator": ^7.17.0 + "@babel/helper-compilation-targets": ^7.16.7 + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helpers": ^7.17.2 + "@babel/parser": ^7.17.0 "@babel/template": ^7.16.7 - "@babel/traverse": ^7.18.0 - "@babel/types": ^7.18.0 + "@babel/traverse": ^7.17.0 + "@babel/types": ^7.17.0 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.2.1 + json5: ^2.1.2 semver: ^6.3.0 - checksum: 350b7724a48c80b76f8af11e3cac1ad8ec9021325389f5ae20c713b10d4359c5e60aa7e71a309a3e1893826c46e72eef5df4978eb63eaabc403e8cc4ce5e94fc + checksum: 68ab3459f41b41feb5cb263937f15e418e1c46998d482d1b6dfe34f78064765466cfd5b10205c22fb16b69dbd1d46e7a3c26c067884ca4eb514b3dac1e09a57f languageName: node linkType: hard -"@babel/generator@npm:7.16.8": +"@babel/generator@npm:7.16.8, @babel/generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/generator@npm:7.16.8" dependencies: @@ -573,14 +572,14 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.16.8, @babel/generator@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/generator@npm:7.18.0" +"@babel/generator@npm:^7.17.0": + version: 7.17.0 + resolution: "@babel/generator@npm:7.17.0" dependencies: - "@babel/types": ^7.18.0 - "@jridgewell/gen-mapping": ^0.3.0 + "@babel/types": ^7.17.0 jsesc: ^2.5.1 - checksum: 0854b21d94f99e3ac68249a9bbaa0c3a914a600c69c12fffa4a01377d89282174a67e619654e401be4c791414a1d5e825671f089f1c2407694a494dcfab8b06c + source-map: ^0.5.0 + checksum: 2987dbebb484727a227f1ce3db90810320986cfb3ffd23e6d1d87f75bbd8e7871b5bc44252822d4d5f048a2d872a5702b2a9bf7bab7e07f087d7f306f0ea6c0a languageName: node linkType: hard @@ -603,46 +602,46 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.7, @babel/helper-compilation-targets@npm:^7.17.10": - version: 7.17.10 - resolution: "@babel/helper-compilation-targets@npm:7.17.10" +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-compilation-targets@npm:7.16.7" dependencies: - "@babel/compat-data": ^7.17.10 + "@babel/compat-data": ^7.16.4 "@babel/helper-validator-option": ^7.16.7 - browserslist: ^4.20.2 + browserslist: ^4.17.5 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 5f547c7ebd372e90fa72c2aaea867e7193166e9f469dec5acde4f0e18a78b80bdca8e02a0f641f3e998be984fb5b802c729a9034faaee8b1a9ef6670cb76f120 + checksum: 7238aaee78c011a42fb5ca92e5eff098752f7b314c2111d7bb9cdd58792fcab1b9c819b59f6a0851dc210dc09dc06b30d130a23982753e70eb3111bc65204842 languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.17.12, @babel/helper-create-class-features-plugin@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/helper-create-class-features-plugin@npm:7.18.0" +"@babel/helper-create-class-features-plugin@npm:^7.16.10, @babel/helper-create-class-features-plugin@npm:^7.16.7": + version: 7.17.1 + resolution: "@babel/helper-create-class-features-plugin@npm:7.17.1" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.17.9 - "@babel/helper-member-expression-to-functions": ^7.17.7 + "@babel/helper-function-name": ^7.16.7 + "@babel/helper-member-expression-to-functions": ^7.16.7 "@babel/helper-optimise-call-expression": ^7.16.7 "@babel/helper-replace-supers": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0 - checksum: 9a6ef175350f1cf87abe7a738e8c9b603da7fcdb153c74e49af509183f8705278020baddb62a12c7f9ca059487fef97d75a4adea6a1446598ad9901d010e4296 + checksum: fb791071dcaa664640d7f1d041772c6b57a8a456720bf7cb21aa055845fad98c644cc7707f03aa94abe8720d19a7c69fd5984fe02fe57b7e99a69f77aa501fc8 languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.16.7, @babel/helper-create-regexp-features-plugin@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.12" +"@babel/helper-create-regexp-features-plugin@npm:^7.16.7": + version: 7.17.0 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.0" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 regexpu-core: ^5.0.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: fe49d26b0f6c58d4c1748a4d0e98b343882b428e6db43c4ba5e0aa7ff2296b3a557f0a88de9f000599bb95640a6c47c0b0c9a952b58c11f61aabb06bcc304329 + checksum: eb66d9241544c705e9ce96d2d122b595ef52d926e6e031653e09af8a01050bd9d7e7fee168bf33a863342774d7d6a8cc7e8e9e5a45b955e9c01121c7a2d51708 languageName: node linkType: hard @@ -682,13 +681,23 @@ __metadata: languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.16.7, @babel/helper-function-name@npm:^7.17.9": - version: 7.17.9 - resolution: "@babel/helper-function-name@npm:7.17.9" +"@babel/helper-function-name@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-function-name@npm:7.16.7" dependencies: + "@babel/helper-get-function-arity": ^7.16.7 "@babel/template": ^7.16.7 - "@babel/types": ^7.17.0 - checksum: a59b2e5af56d8f43b9b0019939a43774754beb7cb01a211809ca8031c71890999d07739e955343135ec566c4d8ff725435f1f60fb0af3bb546837c1f9f84f496 + "@babel/types": ^7.16.7 + checksum: fc77cbe7b10cfa2a262d7a37dca575c037f20419dfe0c5d9317f589599ca24beb5f5c1057748011159149eaec47fe32338c6c6412376fcded68200df470161e1 + languageName: node + linkType: hard + +"@babel/helper-get-function-arity@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-get-function-arity@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: 25d969fb207ff2ad5f57a90d118f6c42d56a0171022e200aaa919ba7dc95ae7f92ec71cdea6c63ef3629a0dc962ab4c78e09ca2b437185ab44539193f796e0c3 languageName: node linkType: hard @@ -701,12 +710,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.16.7, @babel/helper-member-expression-to-functions@npm:^7.17.7": - version: 7.17.7 - resolution: "@babel/helper-member-expression-to-functions@npm:7.17.7" +"@babel/helper-member-expression-to-functions@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.16.7" dependencies: - "@babel/types": ^7.17.0 - checksum: 70f361bab627396c714c3938e94a569cb0da522179328477cdbc4318e4003c2666387ad4931d6bd5de103338c667c9e4bbe3e917fc8c527b3f3eb6175b888b7d + "@babel/types": ^7.16.7 + checksum: e275378022278a7e7974a3f65566690f1804ac88c5f4e848725cf936f61cd1e2557e88cfb6cb4fea92ae5a95ad89d78dbccc9a53715d4363f84c9fd109272c18 languageName: node linkType: hard @@ -719,19 +728,19 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/helper-module-transforms@npm:7.18.0" +"@babel/helper-module-transforms@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-module-transforms@npm:7.16.7" dependencies: "@babel/helper-environment-visitor": ^7.16.7 "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-simple-access": ^7.17.7 + "@babel/helper-simple-access": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 "@babel/helper-validator-identifier": ^7.16.7 "@babel/template": ^7.16.7 - "@babel/traverse": ^7.18.0 - "@babel/types": ^7.18.0 - checksum: 824c3967c08d75bb36adc18c31dcafebcd495b75b723e2e17c6185e88daf5c6db62a6a75d9f791b5f38618a349e7cb32503e715a1b9a4e8bad4d0f43e3e6b523 + "@babel/traverse": ^7.16.7 + "@babel/types": ^7.16.7 + checksum: 6e930ce776c979f299cdbeaf80187f4ab086d75287b96ecc1c6896d392fcb561065f0d6219fc06fa79b4ceb4bbdc1a9847da8099aba9b077d0a9e583500fb673 languageName: node linkType: hard @@ -744,10 +753,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.17.12, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.17.12 - resolution: "@babel/helper-plugin-utils@npm:7.17.12" - checksum: 4813cf0ddb0f143de032cb88d4207024a2334951db330f8216d6fa253ea320c02c9b2667429ef1a34b5e95d4cfbd085f6cb72d418999751c31d0baf2422cc61d +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.16.7 + resolution: "@babel/helper-plugin-utils@npm:7.16.7" + checksum: d08dd86554a186c2538547cd537552e4029f704994a9201d41d82015c10ed7f58f9036e8d1527c3760f042409163269d308b0b3706589039c5f1884619c6d4ce languageName: node linkType: hard @@ -775,12 +784,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.17.7": - version: 7.17.7 - resolution: "@babel/helper-simple-access@npm:7.17.7" +"@babel/helper-simple-access@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-simple-access@npm:7.16.7" dependencies: - "@babel/types": ^7.17.0 - checksum: 58a9bfd054720024f6ff47fbb113c96061dc2bd31a5e5285756bd3c2e83918c6926900e00150d0fb175d899494fe7d69bf2a8b278c32ef6f6bea8d032e6a3831 + "@babel/types": ^7.16.7 + checksum: 8d22c46c5ec2ead0686c4d5a3d1d12b5190c59be676bfe0d9d89df62b437b51d1a3df2ccfb8a77dded2e585176ebf12986accb6d45a18cff229eef3b10344f4b languageName: node linkType: hard @@ -802,6 +811,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.19.4": + version: 7.19.4 + resolution: "@babel/helper-string-parser@npm:7.19.4" + checksum: b2f8a3920b30dfac81ec282ac4ad9598ea170648f8254b10f475abe6d944808fb006aab325d3eb5a8ad3bea8dfa888cfa6ef471050dae5748497c110ec060943 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-validator-identifier@npm:7.16.7" @@ -809,6 +825,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.19.1": + version: 7.19.1 + resolution: "@babel/helper-validator-identifier@npm:7.19.1" + checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-validator-option@npm:7.16.7" @@ -828,62 +851,62 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/helpers@npm:7.18.0" +"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.17.2": + version: 7.17.2 + resolution: "@babel/helpers@npm:7.17.2" dependencies: "@babel/template": ^7.16.7 - "@babel/traverse": ^7.18.0 - "@babel/types": ^7.18.0 - checksum: 3f41631c0797b052cc22337ee56290700fe7db7bc06b847fcdf2c0043cddc35861855a1acc4c948397838675d2dc694f4fb1b102d1c7eb484ea01e9029916b55 + "@babel/traverse": ^7.17.0 + "@babel/types": ^7.17.0 + checksum: 5fa06bbf59636314fb4098bb2e70cf488e0fb6989553438abab90356357b79976102ac129fb16fc8186893c79e0809de1d90e3304426d6fcdb1750da2b6dff9d languageName: node linkType: hard "@babel/highlight@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/highlight@npm:7.17.12" + version: 7.16.10 + resolution: "@babel/highlight@npm:7.16.10" dependencies: "@babel/helper-validator-identifier": ^7.16.7 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 841a11aa353113bcce662b47085085a379251bf8b09054e37e1e082da1bf0d59355a556192a6b5e9ee98e8ee6f1f2831ac42510633c5e7043e3744dda2d6b9d6 + checksum: 1f1bdd752a90844f4efc22166a46303fb651ba0fd75a06daba3ebae2575ab3edc1da9827c279872a3aaf305f50a18473c5fa1966752726a2b253065fd4c0745e languageName: node linkType: hard -"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/parser@npm:7.18.0" +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.0": + version: 7.17.0 + resolution: "@babel/parser@npm:7.17.0" bin: parser: ./bin/babel-parser.js - checksum: 253b5828bf4a0b443301baedc5993d6f7f35aa0d81cf8f2f2f53940904b7067eab7bd2380aee4b3be1d8efd5ae1008eb0fad19bde28f5fbc213c0fdf9a414466 + checksum: d0ac5ffba0b234dde516f867edf5da5d92d6f841592b370ae3244cd7c8f27a7f5e3e3d4e90ca9c15ea58bc46823f1643f3f75b6eb9a9f676ae16e8b2365e922a languageName: node linkType: hard "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0 - checksum: 6ef739b3a2b0ac0b22b60ff472c118163ceb8d414dd08c8186cc563fddc2be62ad4d8681e02074a1c7f0056a72e7146493a85d12ded02e50904b0009ed85d8bf + checksum: bbb0f82a4cf297bdbb9110eea570addd4b883fd1b61535558d849822b087aa340fe4e9c31f8a39b087595c8310b58d0f5548d6be0b72c410abefb23a5734b7bc languageName: node linkType: hard "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 - "@babel/plugin-proposal-optional-chaining": ^7.17.12 + "@babel/plugin-proposal-optional-chaining": ^7.16.7 peerDependencies: "@babel/core": ^7.13.0 - checksum: 68520a8f26e56bc8d90c22133537a9819e82598e3c82007f30bdaf8898b0e12a7bfa0cd3044aca35a7f362fd6bc04e4cd8052a571fc2eb40ad8f1cf24e0fc45f + checksum: 81b372651a7d886a06596b02df7fb65ea90265a8bd60c9f0d5c1777590a598e6cccbdc3239033ee0719abf904813e69577eeb0ed5960b40e07978df023b17a6a languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:7.16.8": +"@babel/plugin-proposal-async-generator-functions@npm:7.16.8, @babel/plugin-proposal-async-generator-functions@npm:^7.16.8": version: 7.16.8 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.8" dependencies: @@ -896,41 +919,28 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:^7.16.8": - version: 7.17.12 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.17.12" - dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-remap-async-to-generator": ^7.16.8 - "@babel/plugin-syntax-async-generators": ^7.8.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 16a3c7f68a27031b4973b7c64ca009873c91b91afd7b3a4694ec7f1c6d8e91a6ee142eafd950113810fae122faa1031de71140333b2b1bd03d5367b1a05b1d91 - languageName: node - linkType: hard - "@babel/plugin-proposal-class-properties@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-proposal-class-properties@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" dependencies: - "@babel/helper-create-class-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 884df6a4617a18cdc2a630096b2a10954bcc94757c893bb01abd6702fdc73343ca5c611f4884c4634e0608f5e86c3093ea6b973ce00bf21b248ba54de92c837d + checksum: 3977e841e17b45b47be749b9a5b67b9e8b25ff0840f9fdad3f00cbcb35db4f5ff15f074939fe19b01207a29688c432cc2c682351959350834d62920b7881f803 languageName: node linkType: hard "@babel/plugin-proposal-class-static-block@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.16.7" dependencies: - "@babel/helper-create-class-features-plugin": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-class-static-block": ^7.14.5 peerDependencies: "@babel/core": ^7.12.0 - checksum: 70fd622fd7c62cca2aa99c70532766340a5c30105e35cb3f1187b450580d43adc78b3fcb1142ed339bcfccf84be95ea03407adf467331b318ce6874432736c89 + checksum: 3b95b5137e089f0be17de667299ea2e28867b6310ab94219a5a89ac7675824e69f316d31930586142b9f432122ef3b98eb05fffdffae01b5587019ce9aab4ef3 languageName: node linkType: hard @@ -947,50 +957,50 @@ __metadata: linkType: hard "@babel/plugin-proposal-export-namespace-from@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 41c9cd4c0a5629b65725d2554867c15b199f534cea5538bd1ae379c0d13e7206d8590e23b23cb05a8b243e33e6eb88c1de3fd03a55cdbc6d4cf8634a6bebe43d + checksum: 5016079a5305c1c130fea587b42cdce501574739cfefa5b63469dbc1f32d436df0ff42fabf04089fe8b6a00f4ea7563869e944744b457e186c677995983cb166 languageName: node linkType: hard "@babel/plugin-proposal-json-strings@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-proposal-json-strings@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-json-strings@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-json-strings": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8ed4ee3fbc28e44fac17c48bd95b5b8c3ffc852053a9fffd36ab498ec0b0ba069b8b2f5658edc18332748948433b9d3e1e376f564a1d65cb54592ba9943be09b + checksum: ea6487918f8d88322ac2a4e5273be6163b0d84a34330c31cee346e23525299de3b4f753bc987951300a79f55b8f4b1971b24d04c0cdfcb7ceb4d636975c215e8 languageName: node linkType: hard "@babel/plugin-proposal-logical-assignment-operators@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0d48451836219b7beeca4be22a8aeb4a177a4944be4727afb94a4a11f201dde8b0b186dd2ad65b537d61e9af3fa1afda734f7096bec8602debd76d07aa342e21 + checksum: c4cf18e10f900d40eaa471c4adce4805e67bd845f997a4b9d5653eced4e653187b9950843b2bf7eab6c0c3e753aba222b1d38888e3e14e013f87295c5b014f19 languageName: node linkType: hard "@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7881d8005d0d4e17a94f3bfbfa4a0d8af016d2f62ed90912fabb8c5f8f0cc0a15fd412f09c230984c40b5c893086987d403c73198ef388ffcb3726ff72efc009 + checksum: bfafc2701697b5c763dbbb65dd97b56979bfb0922e35be27733699a837aeff22316313ddfdd0fb45129efa3f86617219b77110d05338bc4dca4385d8ce83dd19 languageName: node linkType: hard @@ -1007,17 +1017,17 @@ __metadata: linkType: hard "@babel/plugin-proposal-object-rest-spread@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.16.7" dependencies: - "@babel/compat-data": ^7.17.10 - "@babel/helper-compilation-targets": ^7.17.10 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/compat-data": ^7.16.4 + "@babel/helper-compilation-targets": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-transform-parameters": ^7.17.12 + "@babel/plugin-transform-parameters": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b49bcf9a6b11fd8b6a1d4962a64f3c846a63f8340eca9824c907f75bfcff7422ca35b135607fc3ef2d4e7e77ce6b6d955b772dc3c1c39f7ed24a0d8a560ec78 + checksum: 2d3740e4df6d3f51d57862100c45c000104571aa98b7f798fdfc05ae0c12b9e7cc9b55f4a28612d626e29f3369a1481a0ee8a0241b23508b9d3da00c55f99d41 languageName: node linkType: hard @@ -1033,54 +1043,54 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.16.7, @babel/plugin-proposal-optional-chaining@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-proposal-optional-chaining@npm:7.17.12" +"@babel/plugin-proposal-optional-chaining@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a27b220573441a0ad3eecf8ddcb249556a64de45add236791d76cfa164a8fd34181857528fa7d21d03d6b004e7c043bd929cce068e611ee1ac72aaf4d397aa12 + checksum: e4a6c1ac7e6817b92a673ea52ab0b7dc1fb39d29fb0820cd414e10ae2cd132bd186b4238dcca881a29fc38fe9d38ed24fc111ba22ca20086481682d343f4f130 languageName: node linkType: hard "@babel/plugin-proposal-private-methods@npm:^7.16.11": - version: 7.17.12 - resolution: "@babel/plugin-proposal-private-methods@npm:7.17.12" + version: 7.16.11 + resolution: "@babel/plugin-proposal-private-methods@npm:7.16.11" dependencies: - "@babel/helper-create-class-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.16.10 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a1e5bd6a0a541af55d133d7bcf51ff8eb4ac7417a30f518c2f38107d7d033a3d5b7128ea5b3a910b458d7ceb296179b6ff9d972be60d1c686113d25fede8bed3 + checksum: b333e5aa91c265bb394a57b5f4ae1a34fc8ee73a8d75506b12df258d8b5342107cbd9261f95e606bd3264a5b023db77f1f95be30c2e526683916c57f793f7943 languageName: node linkType: hard "@babel/plugin-proposal-private-property-in-object@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.16.7" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-create-class-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 056cb77994b2ee367301cdf8c5b7ed71faf26d60859bbba1368b342977481b0884712a1b97fbd9b091750162923d0265bf901119d46002775aa66e4a9f30f411 + checksum: 666d668f51d8c01aaf0dd87b27a83fc0392884d2c8e9d8e17b3b7011c0d348865dee94b44dc2d7070726e58e3b579728dc2588aaa8140d563f7390743ee90f0a languageName: node linkType: hard "@babel/plugin-proposal-unicode-property-regex@npm:^7.16.7, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": - version: 7.17.12 - resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.16.7" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-regexp-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0e4194510415ed11849f1617fcb32d996df746ba93cd05ebbabecb63cfc02c0e97b585c97da3dcf68acdd3c8b71cfae964abe5d5baba6bd3977a475d9225ad9e + checksum: 2b8a33713d456183f0b7d011011e7bd932c08cc06216399a7b2015ab39284b511993dc10a89bbb15d1d728e6a2ef42ca08c3202619aa148cbd48052422ea3995 languageName: node linkType: hard @@ -1239,17 +1249,17 @@ __metadata: linkType: hard "@babel/plugin-transform-arrow-functions@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 48f99e74f523641696d5d9fb3f5f02497eca2e97bc0e9b8230a47f388e37dc5fd84b8b29e9f5a0c82d63403f7ba5f085a28e26939678f6e917d5c01afd884b50 + checksum: 2a6aa982c6fc80f4de7ccd973507ce5464fab129987cb6661136a7b9b6a020c2b329b912cbc46a68d39b5a18451ba833dcc8d1ca8d615597fec98624ac2add54 languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.16.8": +"@babel/plugin-transform-async-to-generator@npm:7.16.8, @babel/plugin-transform-async-to-generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.8" dependencies: @@ -1262,19 +1272,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.16.8": - version: 7.17.12 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.17.12" - dependencies: - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-remap-async-to-generator": ^7.16.8 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 052dd56eb3b10bc31f5aaced0f75fc7307713f74049ccfb91cd087bebfc890a6d462b59445c5299faaca9030814172cac290c941c76b731a38dcb267377c9187 - languageName: node - linkType: hard - "@babel/plugin-transform-block-scoped-functions@npm:^7.16.7": version: 7.16.7 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.16.7" @@ -1287,53 +1284,53 @@ __metadata: linkType: hard "@babel/plugin-transform-block-scoping@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-block-scoping@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-block-scoping@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ea3d4d88e38367d62a1029d204c5cc0ac410b00779179c8507448001c64784bf8e34c6fa57f23d8b95a835541a2fc67d1076650b1efc99c78f699de354472e49 + checksum: f93b5441af573fc274655f1707aeb4f67a43e926b58f56d89cc35a27877ae0bf198648603cbc19f442579489138f93c3838905895f109aa356996dbc3ed97a68 languageName: node linkType: hard "@babel/plugin-transform-classes@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-classes@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-classes@npm:7.16.7" dependencies: "@babel/helper-annotate-as-pure": ^7.16.7 "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.17.9 + "@babel/helper-function-name": ^7.16.7 "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/helper-replace-supers": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0127b1cc432373965edf28cbfd9e85df5bc77e974ceb80ba32691e050e8fb6792f207d1941529c81d1b9e7a6e82da26ecc445f6f547f0ad5076cd2b27adc18ac + checksum: 791526a1bf3c4659b94d619536e3181d3ad54887d50539066628c6e695789a3bb264dc1fbc8540169d62a222f623df54defb490c1811ae63bad1e3557d6b3bb0 languageName: node linkType: hard "@babel/plugin-transform-computed-properties@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-computed-properties@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5d05418617e0967bec4818556b7febb6f8c40813e32035f0bd6b7dbd7b9d63e9ab7c7c8fd7bd05bab2a599dad58e7b69957d9559b41079d112c219bbc3649aa1 + checksum: 28b17f7cfe643f45920b76dc040cab40d4e54eccf5074fba2658c484feacda9b4885b3854ffaf26292189783fdecc97211519c61831b6708fcbf739cfbcbf31c languageName: node linkType: hard "@babel/plugin-transform-destructuring@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-transform-destructuring@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-transform-destructuring@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d85d60737c3b05c4db71bc94270e952122d360bd6ebf91b5f98cf16fb8564558b615d115354fe0ef41e2aae9c4540e6e16144284d881ecaef687693736cd2a79 + checksum: d1c2e15e7be2a7c57ac8ec4df06fbb706c7ecc872ab7bc2193606e6d6a01929b6d5a1bb41540e41180e42a5ce0e70dce22e7896cb6578dd581d554f77780971b languageName: node linkType: hard @@ -1350,13 +1347,13 @@ __metadata: linkType: hard "@babel/plugin-transform-duplicate-keys@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fb6ad550538830b0dc5b1b547734359f2d782209570e9d61fe9b84a6929af570fcc38ab579a67ee7cd6a832147db91a527f4cceb1248974f006fe815980816bb + checksum: b96f6e9f7b33a91ad0eb6b793e4da58b7a0108b58269109f391d57078d26e043b3872c95429b491894ae6400e72e44d9b744c9b112b8433c99e6969b767e30ed languageName: node linkType: hard @@ -1373,13 +1370,13 @@ __metadata: linkType: hard "@babel/plugin-transform-for-of@npm:^7.16.7": - version: 7.18.1 - resolution: "@babel/plugin-transform-for-of@npm:7.18.1" + version: 7.16.7 + resolution: "@babel/plugin-transform-for-of@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: cdc6e1f1170218cc6ac5b26b4b8f011ec5c36666101e00e0061aaa5772969b093bad5b2af8ce908c184126d5bb0c26b89dd4debb96b2375aba2e20e427a623a8 + checksum: 35c9264ee4bef814818123d70afe8b2f0a85753a0a9dc7b73f93a71cadc5d7de852f1a3e300a7c69a491705805704611de1e2ccceb5686f7828d6bca2e5a7306 languageName: node linkType: hard @@ -1397,13 +1394,13 @@ __metadata: linkType: hard "@babel/plugin-transform-literals@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-literals@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-literals@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 09280fc1ed23b81deafd4fcd7a35d6c0944668de2317f14c1b8b78c5c201f71a063bb8d174d2fc97d86df480ff23104c8919d3aacf19f33c2b5ada584203bf1c + checksum: a9565d999fc7a72a391ef843cf66028c38ca858537c7014d9ea8ea587a59e5f952d9754bdcca6ca0446e84653e297d417d4faedccb9e4221af1aa30f25d918e0 languageName: node linkType: hard @@ -1419,79 +1416,78 @@ __metadata: linkType: hard "@babel/plugin-transform-modules-amd@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-amd@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-transform-modules-amd@npm:7.16.7" dependencies: - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bed3ff5cd81f236981360fc4a6fd2262685c1202772c657ce3ab95b7930437f8fa22361021b481c977b6f47988dfcc07c7782a1c91b90d3a5552c91401f4631a + checksum: 9ac251ee96183b10cf9b4ec8f9e8d52e14ec186a56103f6c07d0c69e99faa60391f6bac67da733412975e487bd36adb403e2fc99bae6b785bf1413e9d928bc71 languageName: node linkType: hard "@babel/plugin-transform-modules-commonjs@npm:^7.16.8": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.18.0" + version: 7.16.8 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.16.8" dependencies: - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 - "@babel/helper-simple-access": ^7.17.7 + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-simple-access": ^7.16.7 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: debb8c952b689def0d3f02d2944b8d031650adcad042277f91c4d137d96c4de1796576d2791fc55217c19004947a37f031c9870d830861075d33d279fe02dda8 + checksum: c0ac00f5457e12cac7825b14725b6fc787bef78945181469ff79f07ef0fd7df021cb00fe1d3a9f35fc9bc92ae59e6e3fc9075a70b627dfe10e00d0907892aace languageName: node linkType: hard "@babel/plugin-transform-modules-systemjs@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.16.7" dependencies: "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/helper-validator-identifier": ^7.16.7 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 80fccfc546aab76238d3f4aeb454f61ed885670578f1ab6dc063bba5b5d4cbdf821439ac6ca8bc24449eed752359600b47be717196103d2eabba06de1bf3f732 + checksum: 2e50ae45a725eeafac5a9d30e07a5e17ab8dcf62c3528cf4efe444fc6f12cd3c4e42e911a9aa37abab169687a98b29a4418eeafcf2031f9917162ac36105cb1b languageName: node linkType: hard "@babel/plugin-transform-modules-umd@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-transform-modules-umd@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-transform-modules-umd@npm:7.16.7" dependencies: - "@babel/helper-module-transforms": ^7.18.0 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4081a79cfd4c6fda785c2137f9f2721e35c06a9d2f23c304172838d12e9317a24d3cb5b652a9db61e58319b370c57b1b44991429efe709679f98e114d98597fb + checksum: d1433f8b0e0b3c9f892aa530f08fe3ba653a5e51fe1ed6034ac7d45d4d6f22c3ba99186b72e41ad9ce5d8dcf964104c3da2419f15fcdcf5ba05c5fda3ea2cefc languageName: node linkType: hard "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.16.8": - version: 7.17.12 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.17.12" + version: 7.16.8 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.16.8" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.17.12 - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-create-regexp-features-plugin": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0 - checksum: cff9d91d0abd87871da6574583e79093ed75d5faecea45b6a13350ba243b1a595d349a6e7d906f5dfdf6c69c643cba9df662c3d01eaa187c5b1a01cb5838e848 + checksum: 73e149f5ff690f5b8e3764a881e8e5240f12f394256e7d5217705d0cbeae074c3faff394783190fe1a41f9fc5a53b960b6021158b7e5174391b5fc38f4ba047a languageName: node linkType: hard "@babel/plugin-transform-new-target@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-new-target@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-new-target@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bec26350fa49c9a9431d23b4ff234f8eb60554b8cdffca432a94038406aae5701014f343568c0e0cc8afae6f95d492f6bae0d0e2c101c1a484fb20eec75b2c07 + checksum: 7410c3e68abc835f87a98d40269e65fb1a05c131decbb6721a80ed49a01bd0c53abb6b8f7f52d5055815509022790e1accca32e975c02f2231ac3cf13d8af768 languageName: node linkType: hard @@ -1507,14 +1503,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.16.7, @babel/plugin-transform-parameters@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-parameters@npm:7.17.12" +"@babel/plugin-transform-parameters@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/plugin-transform-parameters@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d9ed5ec61dc460835bade8fa710b42ec9f207bd448ead7e8abd46b87db0afedbb3f51284700fd2a6892fdf6544ec9b949c505c6542c5ba0a41ca4e8749af00f0 + checksum: 4d6904376db82d0b35f0a6cce08f630daf8608d94e903d6c7aff5bd742b251651bd1f88cdf9f16cad98aba5fc7c61da8635199364865fad6367d5ae37cf56cc1 languageName: node linkType: hard @@ -1530,25 +1526,24 @@ __metadata: linkType: hard "@babel/plugin-transform-regenerator@npm:^7.16.7": - version: 7.18.0 - resolution: "@babel/plugin-transform-regenerator@npm:7.18.0" + version: 7.16.7 + resolution: "@babel/plugin-transform-regenerator@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 - regenerator-transform: ^0.15.0 + regenerator-transform: ^0.14.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ebacf2bbe9e2fb6f2bd7996e19b41bfc9848628950ae06a1a832802a0b8e32a32003c6b89318da6ca521f79045c91324dcb4c97247ed56f86fa58d7401a7316f + checksum: 12b1f9a4f324027af69f49522fbe7feea2ac53285ca5c7e27a70de09f56c74938bfda8b09ac06e57fa1207e441f00efb7adbc462afc9be5e8abd0c2a07715e01 languageName: node linkType: hard "@babel/plugin-transform-reserved-words@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-reserved-words@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-reserved-words@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d8a617cb79ca5852ac2736a9f81c15a3b0760919720c3b9069a864e2288006ebcaab557dbb36a3eba936defd6699f82e3bf894915925aa9185f5d9bcbf3b29fd + checksum: 00218a646e99a97c1f10b77c41c178ca1b91d0e6cf18dd4ca3c59b8a5ad721db04ef508f49be4cd0dcca7742490dbb145307b706a2dbea1917d5e5f7ba2f31b7 languageName: node linkType: hard @@ -1580,14 +1575,14 @@ __metadata: linkType: hard "@babel/plugin-transform-spread@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-spread@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-spread@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3a95e4f163d598c0efc9d983e5ce3e8716998dd2af62af8102b11cb8d6383c71b74c7106adbce73cda6e48d3d3e927627847d36d76c2eb688cd0e2e07f67fb51 + checksum: 6e961af1a70586bb72dd85e8296cee857c5dadd73225fccd0fe261c0d98652a82d69c65f3e9dc31ce019a12e9677262678479b96bd2d9140ddf6514618362828 languageName: node linkType: hard @@ -1603,24 +1598,24 @@ __metadata: linkType: hard "@babel/plugin-transform-template-literals@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-template-literals@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-template-literals@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fec220cea6e7bcd7720c65245e628cdf8e8276379e8ee041e49217b5ebb426911cb738d5b66afa5b1c7d17fc8dbe76d8041dbbce442925d83f08fb510f90507e + checksum: b55a519dd8b957247ebad3cab21918af5adca4f6e6c87819501cfe3d4d4bccda25bc296c7dfc8a30909b4ad905902aeb9d55ad955cb9f5cbc74b42dab32baa18 languageName: node linkType: hard "@babel/plugin-transform-typeof-symbol@npm:^7.16.7": - version: 7.17.12 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.17.12" + version: 7.16.7 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.16.7" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/helper-plugin-utils": ^7.16.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e30bd03c8abc1b095f8b2a10289df6850e3bc3cd0aea1cbc29050aa3b421cbb77d0428b0cd012333632a7a930dc8301cd888e762b2dd601e7dc5dac50f4140c9 + checksum: 739a8c439dacbd9af62cfbfa0a7cbc3f220849e5fc774e5ef708a09186689a724c41a1d11323e7d36588d24f5481c8b702c86ff7be8da2e2fed69bed0175f625 languageName: node linkType: hard @@ -1756,7 +1751,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.16.7": +"@babel/runtime@npm:7.16.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": version: 7.16.7 resolution: "@babel/runtime@npm:7.16.7" dependencies: @@ -1765,15 +1760,6 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": - version: 7.18.0 - resolution: "@babel/runtime@npm:7.18.0" - dependencies: - regenerator-runtime: ^0.13.4 - checksum: 9d0caa5fe690623fb6c5df6fb3b3581d227b55ef9f7c35eba0da83d10aa756669a81fe521ac4dbc007e5790716bac40ebe71ff098e2d1a9599dd696a282a3e95 - languageName: node - linkType: hard - "@babel/template@npm:7.16.7, @babel/template@npm:^7.16.7": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" @@ -1785,38 +1771,42 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.18.0": - version: 7.18.0 - resolution: "@babel/traverse@npm:7.18.0" +"@babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.17.0": + version: 7.17.0 + resolution: "@babel/traverse@npm:7.17.0" dependencies: "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.18.0 + "@babel/generator": ^7.17.0 "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.17.9 + "@babel/helper-function-name": ^7.16.7 "@babel/helper-hoist-variables": ^7.16.7 "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.18.0 - "@babel/types": ^7.18.0 + "@babel/parser": ^7.17.0 + "@babel/types": ^7.17.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: b80b49ba5cead42c4b09bdfbe926d94179f884d35319a0a3ab5a798c85f16102a7342799fac928b3041337ea2c3f5194f17c4a08f611a474de6eea719b640dd4 + checksum: 9b7de053d8a29453fd7b9614a028d8dc811817f02948eaee02093274b67927a1cfb0513b521bc4a9328c9b6e5b021fd343b358c3526bbb6ee61ec078d4110c0c languageName: node linkType: hard -"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.18.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.18.0 - resolution: "@babel/types@npm:7.18.0" +"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.4.4": + version: 7.17.0 + resolution: "@babel/types@npm:7.17.0" dependencies: "@babel/helper-validator-identifier": ^7.16.7 to-fast-properties: ^2.0.0 - checksum: 151485f94c929171fd6539430c0ae519e8bb67fbc0d856b285328f5e6ecbaf4237b52d7a581b413f5e7b6268d31a4db6ca9bc01372b284b2966aa473fc902f27 + checksum: 12e5a287986fe557188e87b2c5202223f1dc83d9239a196ab936fdb9f8c1eb0be717ff19f934b5fad4e29a75586d5798f74bed209bccea1c20376b9952056f0e languageName: node linkType: hard -"@colors/colors@npm:1.5.0": - version: 1.5.0 - resolution: "@colors/colors@npm:1.5.0" - checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 +"@babel/types@npm:^7.8.3": + version: 7.20.7 + resolution: "@babel/types@npm:7.20.7" + dependencies: + "@babel/helper-string-parser": ^7.19.4 + "@babel/helper-validator-identifier": ^7.19.1 + to-fast-properties: ^2.0.0 + checksum: b39af241f0b72bba67fd6d0d23914f6faec8c0eba8015c181cbd5ea92e59fc91a52a1ab490d3520c7dbd19ddb9ebb76c476308f6388764f16d8201e37fae6811 languageName: node linkType: hard @@ -1835,23 +1825,13 @@ __metadata: linkType: hard "@csstools/postcss-progressive-custom-properties@npm:^1.1.0": - version: 1.3.0 - resolution: "@csstools/postcss-progressive-custom-properties@npm:1.3.0" + version: 1.1.0 + resolution: "@csstools/postcss-progressive-custom-properties@npm:1.1.0" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.3 - checksum: e281845fde5b8a80d06ec20147bd74e96a9351bebbec5e5c3a6fb37ea30a597ff84172601786a8a270662f58f708b4a3bf8d822d6318023def9773d2f6589962 - languageName: node - linkType: hard - -"@csstools/selector-specificity@npm:1.0.0": - version: 1.0.0 - resolution: "@csstools/selector-specificity@npm:1.0.0" - peerDependencies: - postcss: ^8.3 - postcss-selector-parser: ^6.0.10 - checksum: cfdabe0df7d9d5e1e0b23c68d4f39afd33520d3196a4c9b9dc52942d14f32b594bd227a91cc7e933b3191306b447ca231b00b5f3ad3c2676958f3b364725a0a0 + checksum: d8bb27f1fd83d3b5f3740cd9c85063c63178c765acf4af59cef62f5af90c46f49e5048a37e274f614cfd6b37c4d653a43b67ffb391fde306829997936bc76a24 languageName: node linkType: hard @@ -1879,7 +1859,14 @@ __metadata: languageName: node linkType: hard -"@gar/promisify@npm:^1.0.1, @gar/promisify@npm:^1.1.3": +"@gar/promisify@npm:^1.0.1": + version: 1.1.2 + resolution: "@gar/promisify@npm:1.1.2" + checksum: d05081e0887a49c178b75ee3067bd6ee086f73c154d121b854fb2e044e8a89cb1cbb6de3a0dd93a519b80f0531fda68b099dd7256205f7fbb3490324342f2217 + languageName: node + linkType: hard + +"@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 @@ -1924,66 +1911,38 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.1.0": - version: 0.1.1 - resolution: "@jridgewell/gen-mapping@npm:0.1.1" - dependencies: - "@jridgewell/set-array": ^1.0.0 - "@jridgewell/sourcemap-codec": ^1.4.10 - checksum: 3bcc21fe786de6ffbf35c399a174faab05eb23ce6a03e8769569de28abbf4facc2db36a9ddb0150545ae23a8d35a7cf7237b2aa9e9356a7c626fb4698287d5cc - languageName: node - linkType: hard - -"@jridgewell/gen-mapping@npm:^0.3.0": - version: 0.3.1 - resolution: "@jridgewell/gen-mapping@npm:0.3.1" - dependencies: - "@jridgewell/set-array": ^1.0.0 - "@jridgewell/sourcemap-codec": ^1.4.10 - "@jridgewell/trace-mapping": ^0.3.9 - checksum: e9e7bb3335dea9e60872089761d4e8e089597360cdb1af90370e9d53b7d67232c1e0a3ab65fbfef4fc785745193fbc56bff9f3a6cab6c6ce3f15e12b4191f86b - languageName: node - linkType: hard - "@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.7 - resolution: "@jridgewell/resolve-uri@npm:3.0.7" - checksum: 94f454f4cef8f0acaad85745fd3ca6cd0d62ef731cf9f952ecb89b8b2ce5e20998cd52be31311cedc5fa5b28b1708a15f3ad9df0fe1447ee4f42959b036c4b5b - languageName: node - linkType: hard - -"@jridgewell/set-array@npm:^1.0.0": - version: 1.1.1 - resolution: "@jridgewell/set-array@npm:1.1.1" - checksum: cc5d91e0381c347e3edee4ca90b3c292df9e6e55f29acbe0dd97de8651b4730e9ab761406fd572effa79972a0edc55647b627f8c72315e276d959508853d9bf2 + version: 3.0.5 + resolution: "@jridgewell/resolve-uri@npm:3.0.5" + checksum: 1ee652b693da7979ac4007926cc3f0a32b657ffeb913e111f44e5b67153d94a2f28a1d560101cc0cf8087625468293a69a00f634a2914e1a6d0817ba2039a913 languageName: node linkType: hard "@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.13 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.13" - checksum: f14449096f60a5f921262322fef65ce0bbbfb778080b3b20212080bcefdeba621c43a58c27065bd536ecb4cc767b18eb9c45f15b6b98a4970139572b60603a1c + version: 1.4.11 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.11" + checksum: 3b2afaf8400fb07a36db60e901fcce6a746cdec587310ee9035939d89878e57b2dec8173b0b8f63176f647efa352294049a53c49739098eb907ff81fec2547c8 languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.13 - resolution: "@jridgewell/trace-mapping@npm:0.3.13" +"@jridgewell/trace-mapping@npm:^0.3.0": + version: 0.3.4 + resolution: "@jridgewell/trace-mapping@npm:0.3.4" dependencies: "@jridgewell/resolve-uri": ^3.0.3 "@jridgewell/sourcemap-codec": ^1.4.10 - checksum: e38254e830472248ca10a6ed1ae75af5e8514f0680245a5e7b53bc3c030fd8691d4d3115d80595b45d3badead68269769ed47ecbbdd67db1343a11f05700e75a + checksum: ab8bce84bbbc8c34f3ba8325ed926f8f2d3098983c10442a80c55764c4eb6e47d5b92d8ff20a0dd868c3e76a3535651fd8a0138182c290dbfc8396195685c37b languageName: node linkType: hard -"@ngtools/webpack@npm:13.2.6": - version: 13.2.6 - resolution: "@ngtools/webpack@npm:13.2.6" +"@ngtools/webpack@npm:13.2.3": + version: 13.2.3 + resolution: "@ngtools/webpack@npm:13.2.3" peerDependencies: "@angular/compiler-cli": ^13.0.0 typescript: ">=4.4.3 <4.6" webpack: ^5.30.0 - checksum: 9dfc5d89393bb90b56fd78fb08da536a88ab23f43d772493cf78c8b9aad94f4b859cfd770aa520c3776a261444a999550467803bc074b69b66a7c3c7dc4170d6 + checksum: 1d37be74c4f79b0f021f768e62bd670e7e4e800af835f51700cb00fc6753edefc0f20befe1760bb0c9c54f04203f90bdb2984a6bdc5d4a6c554f502a1c00b4d4 languageName: node linkType: hard @@ -2025,12 +1984,12 @@ __metadata: linkType: hard "@npmcli/fs@npm:^2.1.0": - version: 2.1.0 - resolution: "@npmcli/fs@npm:2.1.0" + version: 2.1.2 + resolution: "@npmcli/fs@npm:2.1.2" dependencies: "@gar/promisify": ^1.1.3 semver: ^7.3.5 - checksum: 6ec6d678af6da49f9dac50cd882d7f661934dd278972ffbaacde40d9eaa2871292d634000a0cca9510f6fc29855fbd4af433e1adbff90a524ec3eaf140f1219b + checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 languageName: node linkType: hard @@ -2073,12 +2032,12 @@ __metadata: linkType: hard "@npmcli/move-file@npm:^2.0.0": - version: 2.0.0 - resolution: "@npmcli/move-file@npm:2.0.0" + version: 2.0.1 + resolution: "@npmcli/move-file@npm:2.0.1" dependencies: mkdirp: ^1.0.4 rimraf: ^3.0.2 - checksum: 1388777b507b0c592d53f41b9d182e1a8de7763bc625fc07999b8edbc22325f074e5b3ec90af79c89d6987fdb2325bc66d59f483258543c14a43661621f841b0 + checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380 languageName: node linkType: hard @@ -2176,14 +2135,21 @@ __metadata: languageName: node linkType: hard -"@schematics/angular@npm:13.2.6": - version: 13.2.6 - resolution: "@schematics/angular@npm:13.2.6" +"@schematics/angular@npm:13.2.3": + version: 13.2.3 + resolution: "@schematics/angular@npm:13.2.3" dependencies: - "@angular-devkit/core": 13.2.6 - "@angular-devkit/schematics": 13.2.6 + "@angular-devkit/core": 13.2.3 + "@angular-devkit/schematics": 13.2.3 jsonc-parser: 3.0.0 - checksum: f4f74d1d3ceee65374f49419ef5240a8aa8fb8df2eadf89f2f6af3bfc38b7d2b688bedb65e69200cfafceffbce790dfbb0855f1e0d408b330284c6e88da5dbc7 + checksum: eab01246d34fa7a9af89017a95a921a3ec2c38d23e5e0efa3d33475030d91ff076fc1bfb68e282e116360e4c57080538facca9a596b00fb9c66cea6ab41ca26e + languageName: node + linkType: hard + +"@socket.io/base64-arraybuffer@npm:~1.0.2": + version: 1.0.2 + resolution: "@socket.io/base64-arraybuffer@npm:1.0.2" + checksum: fa3e58c7581643d0557969cd3bece20e198596df77968ff29ede6be329d488e65104bef900e68a67f39d8855abfa59baa2b08d96fb856504bd01cbdd8f52249c languageName: node linkType: hard @@ -2455,14 +2421,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*": - version: 0.0.51 - resolution: "@types/estree@npm:0.0.51" - checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 - languageName: node - linkType: hard - -"@types/estree@npm:^0.0.50": +"@types/estree@npm:*, @types/estree@npm:^0.0.50": version: 0.0.50 resolution: "@types/estree@npm:0.0.50" checksum: 9a2b6a4a8c117f34d08fbda5e8f69b1dfb109f7d149b60b00fd7a9fb6ac545c078bc590aa4ec2f0a256d680cf72c88b3b28b60c326ee38a7bc8ee1ee95624922 @@ -2493,22 +2452,15 @@ __metadata: linkType: hard "@types/http-proxy@npm:^1.17.8": - version: 1.17.9 - resolution: "@types/http-proxy@npm:1.17.9" + version: 1.17.8 + resolution: "@types/http-proxy@npm:1.17.8" dependencies: "@types/node": "*" - checksum: 7a6746d00729b2a9fe9f9dd3453430b099931df879ec8f7a7b5f07b1795f6d99b0512640c45a67390b1e4bacb9401e36824952aeeaf089feba8627a063cf8e00 - languageName: node - linkType: hard - -"@types/jasmine@npm:*": - version: 4.0.3 - resolution: "@types/jasmine@npm:4.0.3" - checksum: 9d2af9ddb5750667bc9a3b439542fc9457c6eac2ab2c74801f598b03b9bc5cee1338fa8e75e6804dff1856e64539e8b0e3962ed8664a702f362d3db62eb9cd3a + checksum: 3b3d683498267096c8aca03652702243b1e087bc20e77a9abe74fdbee1c89c8283ee41c47d245cda2f422483b01980d70a1030b92a8ff24b280e0aa868241a8b languageName: node linkType: hard -"@types/jasmine@npm:3.6.x": +"@types/jasmine@npm:*, @types/jasmine@npm:3.6.x": version: 3.6.11 resolution: "@types/jasmine@npm:3.6.11" checksum: ccb4b749dc43b9ccb4365f36b14bdba8aac5ad7fdd00cc693695064acfbddb6b32fd2d59accd7e70b8f3a1eba69b49e8afa263e96f2b29aed565d0b911efe6c4 @@ -2525,9 +2477,9 @@ __metadata: linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": - version: 7.0.11 - resolution: "@types/json-schema@npm:7.0.11" - checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d + version: 7.0.9 + resolution: "@types/json-schema@npm:7.0.9" + checksum: 259d0e25f11a21ba5c708f7ea47196bd396e379fddb79c76f9f4f62c945879dc21657904914313ec2754e443c5018ea8372362f323f30e0792897fdb2098a705 languageName: node linkType: hard @@ -2552,14 +2504,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=10.0.0": - version: 17.0.35 - resolution: "@types/node@npm:17.0.35" - checksum: 7a24946ae7fd20267ed92466384f594e448bfb151081158d565cc635d406ecb29ea8fb85fcd2a1f71efccf26fb5bd3c6f509bde56077eb8b832b847a6664bc62 - languageName: node - linkType: hard - -"@types/node@npm:12.11.x": +"@types/node@npm:*, @types/node@npm:12.11.x, @types/node@npm:>=10.0.0": version: 12.11.7 resolution: "@types/node@npm:12.11.7" checksum: 7e60bb298fe80f85e3c4b26592d74983452f2eedc7a4bd65157c2059160685ff8ef26f670768ca4d20e41cd7195be2892bea9b483215faf8da1a737fd180e635 @@ -2608,17 +2553,17 @@ __metadata: languageName: node linkType: hard -"@types/retry@npm:0.12.0": - version: 0.12.0 - resolution: "@types/retry@npm:0.12.0" - checksum: 61a072c7639f6e8126588bf1eb1ce8835f2cb9c2aba795c4491cf6310e013267b0c8488039857c261c387e9728c1b43205099223f160bb6a76b4374f741b5603 +"@types/retry@npm:^0.12.0": + version: 0.12.1 + resolution: "@types/retry@npm:0.12.1" + checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c languageName: node linkType: hard "@types/selenium-webdriver@npm:^3.0.0": - version: 3.0.20 - resolution: "@types/selenium-webdriver@npm:3.0.20" - checksum: 6f96abc30b0dc00b833f3dd69dde561984f4d5aa5ad8b234e222d301e74be898b5acc01d4810bb012743ce4b1ae902605365900b3f7ad04c4f522d6e663c9fff + version: 3.0.19 + resolution: "@types/selenium-webdriver@npm:3.0.19" + checksum: 81d5a4da6a73ad1bc53ae36de0a981919f21e9863777fb16198fd2c963040476d782fc0f41263f902b60ae07d49adacad87b86c0eff67a10b2e047b829462498 languageName: node linkType: hard @@ -2651,11 +2596,11 @@ __metadata: linkType: hard "@types/ws@npm:^8.2.2": - version: 8.5.3 - resolution: "@types/ws@npm:8.5.3" + version: 8.2.2 + resolution: "@types/ws@npm:8.2.2" dependencies: "@types/node": "*" - checksum: 0ce46f850d41383fcdc2149bcacc86d7232fa7a233f903d2246dff86e31701a02f8566f40af5f8b56d1834779255c04ec6ec78660fe0f9b2a69cf3d71937e4ae + checksum: 308957864b9a5a0378ac82f1b084fa31b1bbe85106fb0d84ed2b392e4829404f21ab6ab2c1eb782d556e59cd33d57c75ad2d0cedc4b9b9d0ca3b2311bc915578 languageName: node linkType: hard @@ -2960,20 +2905,20 @@ __metadata: linkType: hard "abab@npm:^2.0.5": - version: 2.0.6 - resolution: "abab@npm:2.0.6" - checksum: 6ffc1af4ff315066c62600123990d87551ceb0aafa01e6539da77b0f5987ac7019466780bf480f1787576d4385e3690c81ccc37cfda12819bf510b8ab47e5a3e + version: 2.0.5 + resolution: "abab@npm:2.0.5" + checksum: 0ec951b46d5418c2c2f923021ec193eaebdb4e802ffd5506286781b454be722a13a8430f98085cd3e204918401d9130ec6cc8f5ae19be315b3a0e857d83196e1 languageName: node linkType: hard -"abbrev@npm:1": +"abbrev@npm:1, abbrev@npm:^1.0.0": version: 1.1.1 resolution: "abbrev@npm:1.1.1" checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 languageName: node linkType: hard -"accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.8": +"accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.7": version: 1.3.8 resolution: "accepts@npm:1.3.8" dependencies: @@ -3045,7 +2990,18 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": +"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.0": + version: 4.2.0 + resolution: "agentkeepalive@npm:4.2.0" + dependencies: + debug: ^4.1.0 + depd: ^1.1.2 + humanize-ms: ^1.2.1 + checksum: 89806f83ceebbcaabf6bd581a8dce4870910fd2a11f66df8f505b4cd4ce4ca5ab9e6eec8d11ce8531a6b60f6748b75b0775e0e2fa33871503ef00d535418a19a + languageName: node + linkType: hard + +"agentkeepalive@npm:^4.2.1": version: 4.2.1 resolution: "agentkeepalive@npm:4.2.1" dependencies: @@ -3100,7 +3056,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.9.0": +"ajv@npm:8.9.0, ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": version: 8.9.0 resolution: "ajv@npm:8.9.0" dependencies: @@ -3124,44 +3080,25 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": - version: 8.11.0 - resolution: "ajv@npm:8.11.0" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef - languageName: node - linkType: hard - "angularx-qrcode@npm:13.0.x": - version: 13.0.15 - resolution: "angularx-qrcode@npm:13.0.15" + version: 13.0.3 + resolution: "angularx-qrcode@npm:13.0.3" dependencies: "@cordobo/qrcode": 1.5.0 tslib: ^2.3.0 peerDependencies: "@angular/core": ^13.0.0 - checksum: 53cdb02a041d9fb489f83c798b0be119a0331bd67d295bda112622a8eb435ba9fc151544f1c0e524659c15a5223bc76ca75f41b3fd2cfd9ea12de2c39f27f744 + checksum: e36ae1afc87a2ce3c5d25845f3c940956dfca8a4d0e53e33f5d085046e96bd5390288183e04aae9968597177d45b7138fe1a409c8be8b0a9743aea9543c7b173 languageName: node linkType: hard -"ansi-colors@npm:4.1.1": +"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 languageName: node linkType: hard -"ansi-colors@npm:^4.1.1": - version: 4.1.3 - resolution: "ansi-colors@npm:4.1.3" - checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -3251,8 +3188,8 @@ __metadata: linkType: hard "apexcharts@npm:3.33.x": - version: 3.33.2 - resolution: "apexcharts@npm:3.33.2" + version: 3.33.1 + resolution: "apexcharts@npm:3.33.1" dependencies: svg.draggable.js: ^2.2.2 svg.easing.js: ^2.0.0 @@ -3260,7 +3197,7 @@ __metadata: svg.pathmorphing.js: ^0.1.3 svg.resize.js: ^1.4.3 svg.select.js: ^3.0.1 - checksum: 094cae1017e84d8519f4d800c020e10957601ce7edc421f2e666717e73c10016ee69ad8407363e648265fd2bf9f0fd4d509408001ffd82d8d6531072144c2d03 + checksum: 15f3fe73cb249ce33a8588333c282873046b114430f0a0e9350dd0cdbbc0cd6ad61142e7ce311d064b3385f26c2f3f996c7da333b4f55a2bf495c6081c6e2409 languageName: node linkType: hard @@ -3389,11 +3326,11 @@ __metadata: linkType: hard "async@npm:^2.6.2": - version: 2.6.4 - resolution: "async@npm:2.6.4" + version: 2.6.3 + resolution: "async@npm:2.6.3" dependencies: lodash: ^4.17.14 - checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 + checksum: 5e5561ff8fca807e88738533d620488ac03a5c43fce6c937451f7e35f943d33ad06c24af3f681a48cca3d2b0002b3118faff0a128dc89438a9bf0226f712c499 languageName: node linkType: hard @@ -3428,12 +3365,12 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.2": - version: 10.4.7 - resolution: "autoprefixer@npm:10.4.7" + version: 10.4.2 + resolution: "autoprefixer@npm:10.4.2" dependencies: - browserslist: ^4.20.3 - caniuse-lite: ^1.0.30001335 - fraction.js: ^4.2.0 + browserslist: ^4.19.1 + caniuse-lite: ^1.0.30001297 + fraction.js: ^4.1.2 normalize-range: ^0.1.2 picocolors: ^1.0.0 postcss-value-parser: ^4.2.0 @@ -3441,7 +3378,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 0e55d0d19806c672ec0c79cc23c27cf77e90edf2600670735266ba33ec5294458f404baaa2f7cd4cfe359cf7a97b3c86f01886bdbdc129a4f2f76ca5977a91af + checksum: dbd13e641eaa7d7e3121769c22cc439222f1a9d0371a583d12300849de7287ece1e793767ff9902842dbfd56c4b7c19ed9fe1947c9f343ba2f4f3519dbddfdef languageName: node linkType: hard @@ -3619,23 +3556,21 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.0, body-parser@npm:^1.19.0": - version: 1.20.0 - resolution: "body-parser@npm:1.20.0" +"body-parser@npm:1.19.1, body-parser@npm:^1.19.0": + version: 1.19.1 + resolution: "body-parser@npm:1.19.1" dependencies: - bytes: 3.1.2 + bytes: 3.1.1 content-type: ~1.0.4 debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 + depd: ~1.1.2 + http-errors: 1.8.1 iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.10.3 - raw-body: 2.5.1 + on-finished: ~2.3.0 + qs: 6.9.6 + raw-body: 2.4.2 type-is: ~1.6.18 - unpipe: 1.0.0 - checksum: 12fffdeac82fe20dddcab7074215d5156e7d02a69ae90cbe9fee1ca3efa2f28ef52097cbea76685ee0a1509c71d85abd0056a08e612c09077cad6277a644cf88 + checksum: 9197a300a6580b8723c7b6b1e22cebd5ba47cd4a6fd45c153350efcde79293869ddee8d17d95fb52724812d649d89d62775faab072608d3243a0cbb00582234e languageName: node linkType: hard @@ -3688,18 +3623,18 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.19.1, browserslist@npm:^4.20.2, browserslist@npm:^4.20.3, browserslist@npm:^4.9.1": - version: 4.20.3 - resolution: "browserslist@npm:4.20.3" +"browserslist@npm:^4.14.5, browserslist@npm:^4.17.5, browserslist@npm:^4.19.1, browserslist@npm:^4.9.1": + version: 4.19.1 + resolution: "browserslist@npm:4.19.1" dependencies: - caniuse-lite: ^1.0.30001332 - electron-to-chromium: ^1.4.118 + caniuse-lite: ^1.0.30001286 + electron-to-chromium: ^1.4.17 escalade: ^3.1.1 - node-releases: ^2.0.3 + node-releases: ^2.0.1 picocolors: ^1.0.0 bin: browserslist: cli.js - checksum: 1e4b719ac2ca0fe235218a606e8b8ef16b8809e0973b924158c39fbc435a0b0fe43437ea52dd6ef5ad2efcb83fcb07431244e472270177814217f7c563651f7d + checksum: c0777fd483691638fd6801e16c9d809e1d65f6d2b06db2e806654be51045cbab1452a89841a2c5caea2cbe19d621b4f1d391cffbb24512aa33280039ab345875 languageName: node linkType: hard @@ -3750,14 +3685,14 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.2": - version: 3.1.2 - resolution: "bytes@npm:3.1.2" - checksum: e4bcd3948d289c5127591fbedf10c0b639ccbf00243504e4e127374a15c3bc8eed0d28d4aaab08ff6f1cf2abc0cce6ba3085ed32f4f90e82a5683ce0014e1b6e +"bytes@npm:3.1.1": + version: 3.1.1 + resolution: "bytes@npm:3.1.1" + checksum: 949ab99a385d6acf4d2c69f1afc618615dc905936e0b0b9aa94a9e94d722baaba44d6a0851536585a0892ae4d462b5a270ccb1b04c774640742cbde5538ca328 languageName: node linkType: hard -"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0": +"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0, cacache@npm:^15.3.0": version: 15.3.0 resolution: "cacache@npm:15.3.0" dependencies: @@ -3784,8 +3719,8 @@ __metadata: linkType: hard "cacache@npm:^16.1.0": - version: 16.1.0 - resolution: "cacache@npm:16.1.0" + version: 16.1.3 + resolution: "cacache@npm:16.1.3" dependencies: "@npmcli/fs": ^2.1.0 "@npmcli/move-file": ^2.0.0 @@ -3804,8 +3739,8 @@ __metadata: rimraf: ^3.0.2 ssri: ^9.0.0 tar: ^6.1.11 - unique-filename: ^1.1.1 - checksum: ddfcf92f079f24ccecef4e2ca1e4428443787b61429b921803b020fd0f33d9ac829ac47837b74b40868d8ae4f1b2ed82e164cdaa5508fbd790eee005a9d88469 + unique-filename: ^2.0.0 + checksum: d91409e6e57d7d9a3a25e5dcc589c84e75b178ae8ea7de05cbf6b783f77a5fae938f6e8fda6f5257ed70000be27a681e1e44829251bfffe4c10216002f8f14e6 languageName: node linkType: hard @@ -3844,10 +3779,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001299, caniuse-lite@npm:^1.0.30001332, caniuse-lite@npm:^1.0.30001335": - version: 1.0.30001342 - resolution: "caniuse-lite@npm:1.0.30001342" - checksum: 9ad47aec82e85017c59aaa0acee8027d910a715c7481cf66c9b4e296f3d10cc5d96df86d3c3033326b0110f5792b3f117a1dc935e3299abf2139fa345bb326f1 +"caniuse-lite@npm:^1.0.30001286, caniuse-lite@npm:^1.0.30001297, caniuse-lite@npm:^1.0.30001299": + version: 1.0.30001450 + resolution: "caniuse-lite@npm:1.0.30001450" + checksum: 511b360bfc907b2e437699364cf96b83507bc45043926450056642332bcd6f65a1e72540c828534ae15e0ac906e3e9af46cb2bb84458dd580bc31478e9dce282 languageName: node linkType: hard @@ -4082,7 +4017,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": +"color-support@npm:^1.1.2": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -4206,7 +4141,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed @@ -4245,10 +4180,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 +"cookie@npm:0.4.1": + version: 0.4.1 + resolution: "cookie@npm:0.4.1" + checksum: bd7c47f5d94ab70ccdfe8210cde7d725880d2fcda06d8e375afbdd82de0c8d3b73541996e9ce57d35f67f672c4ee6d60208adec06b3c5fc94cebb85196084cf8 languageName: node linkType: hard @@ -4285,12 +4220,12 @@ __metadata: linkType: hard "core-js-compat@npm:^3.20.2, core-js-compat@npm:^3.21.0": - version: 3.22.6 - resolution: "core-js-compat@npm:3.22.6" + version: 3.21.0 + resolution: "core-js-compat@npm:3.21.0" dependencies: - browserslist: ^4.20.3 + browserslist: ^4.19.1 semver: 7.0.0 - checksum: 6b83b87abeb04c08b54bdc6a6756ad6d1503aeadebc598a162bfe1044a31183864bb3016f16c839d40235545c009dc8aea6421ada0d2e18b5fdf93c6059eb380 + checksum: 7914d2f8a2f7c1b400e1c04c7560f4c96028bf23cec3cea6063ba594e38023cccbd38ad88af41c5d6b65450e97a989eb37598f609e3f7fbc6ebc1856d4195cbb languageName: node linkType: hard @@ -4308,20 +4243,13 @@ __metadata: languageName: node linkType: hard -"core-util-is@npm:1.0.2": +"core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab languageName: node linkType: hard -"core-util-is@npm:~1.0.0": - version: 1.0.3 - resolution: "core-util-is@npm:1.0.3" - checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 - languageName: node - linkType: hard - "cors@npm:~2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" @@ -4433,22 +4361,22 @@ __metadata: linkType: hard "css-select@npm:^4.2.0": - version: 4.3.0 - resolution: "css-select@npm:4.3.0" + version: 4.2.1 + resolution: "css-select@npm:4.2.1" dependencies: boolbase: ^1.0.0 - css-what: ^6.0.1 - domhandler: ^4.3.1 + css-what: ^5.1.0 + domhandler: ^4.3.0 domutils: ^2.8.0 nth-check: ^2.0.1 - checksum: d6202736839194dd7f910320032e7cfc40372f025e4bf21ca5bf6eb0a33264f322f50ba9c0adc35dadd342d3d6fae5ca244779a4873afbfa76561e343f2058e0 + checksum: 6617193ec7c332217204c4ea371d332c6845603fda415e36032e7e9e18206d7c368a14e3c57532082314d2689955b01122aa1097c1c52b6c1cab7ad90970d3c6 languageName: node linkType: hard -"css-what@npm:^6.0.1": - version: 6.1.0 - resolution: "css-what@npm:6.1.0" - checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe +"css-what@npm:^5.1.0": + version: 5.1.0 + resolution: "css-what@npm:5.1.0" + checksum: 0b75d1bac95c885c168573c85744a6c6843d8c33345f54f717218b37ea6296b0e99bb12105930ea170fd4a921990392a7c790c16c585c1d8960c49e2b7ec39f7 languageName: node linkType: hard @@ -4495,10 +4423,10 @@ __metadata: languageName: node linkType: hard -"date-format@npm:^4.0.10": - version: 4.0.10 - resolution: "date-format@npm:4.0.10" - checksum: 677c8e022a5efcead2c869d18b3b1a3b61df6c2c464545ec91e68ea890088dae7704e5822009af420ea8bb26325ffce01be599cc9f087879ea4aefc70a964c7b +"date-format@npm:^4.0.3": + version: 4.0.3 + resolution: "date-format@npm:4.0.3" + checksum: 8ae4d9de3532010169a89bc7b079342051ba3ec88552636aa677bfb53e8eb15113af8394679aea7d41367dc8bb6e9865da17f21ac2802202180b09d6e3f2339e languageName: node linkType: hard @@ -4511,19 +4439,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - -"debug@npm:4.3.3": +"debug@npm:4, debug@npm:4.3.3, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.3 resolution: "debug@npm:4.3.3" dependencies: @@ -4544,6 +4460,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.0 resolution: "decamelize-keys@npm:1.1.0" @@ -4615,12 +4543,11 @@ __metadata: linkType: hard "define-properties@npm:^1.1.3": - version: 1.1.4 - resolution: "define-properties@npm:1.1.4" + version: 1.1.3 + resolution: "define-properties@npm:1.1.3" dependencies: - has-property-descriptors: ^1.0.0 - object-keys: ^1.1.1 - checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b + object-keys: ^1.0.12 + checksum: da80dba55d0cd76a5a7ab71ef6ea0ebcb7b941f803793e4e0257b384cb772038faa0c31659d244e82c4342edef841c1a1212580006a05a5068ee48223d787317 languageName: node linkType: hard @@ -4640,8 +4567,8 @@ __metadata: linkType: hard "del@npm:^6.0.0": - version: 6.1.0 - resolution: "del@npm:6.1.0" + version: 6.0.0 + resolution: "del@npm:6.0.0" dependencies: globby: ^11.0.1 graceful-fs: ^4.2.4 @@ -4651,7 +4578,7 @@ __metadata: p-map: ^4.0.0 rimraf: ^3.0.2 slash: ^3.0.0 - checksum: 150ecf2617e0468df931df60d309dc46c7256e53baa82f6b368a2b6d71be062781602fa9336ebd31d6a2df8e19d8f07c29c5f7512385db6b95d00187ec5ee9e1 + checksum: 5742891627e91aaf62385714025233f4664da28bc55b6ab825649dcdea4691fed3cf329a2b1913fd2d2612e693e99e08a03c84cac7f36ef54bacac9390520192 languageName: node linkType: hard @@ -4669,13 +4596,6 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0": - version: 2.0.0 - resolution: "depd@npm:2.0.0" - checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a - languageName: node - linkType: hard - "depd@npm:^1.1.2, depd@npm:~1.1.2": version: 1.1.2 resolution: "depd@npm:1.1.2" @@ -4690,10 +4610,10 @@ __metadata: languageName: node linkType: hard -"destroy@npm:1.2.0": - version: 1.2.0 - resolution: "destroy@npm:1.2.0" - checksum: 0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38 +"destroy@npm:~1.0.4": + version: 1.0.4 + resolution: "destroy@npm:1.0.4" + checksum: da9ab4961dc61677c709da0c25ef01733042614453924d65636a7db37308fef8a24cd1e07172e61173d471ca175371295fbc984b0af5b2b4ff47cd57bd784c03 languageName: node linkType: hard @@ -4805,29 +4725,29 @@ __metadata: linkType: hard "dom-serializer@npm:^1.0.1": - version: 1.4.1 - resolution: "dom-serializer@npm:1.4.1" + version: 1.3.2 + resolution: "dom-serializer@npm:1.3.2" dependencies: domelementtype: ^2.0.1 domhandler: ^4.2.0 entities: ^2.0.0 - checksum: fbb0b01f87a8a2d18e6e5a388ad0f7ec4a5c05c06d219377da1abc7bb0f674d804f4a8a94e3f71ff15f6cb7dcfc75704a54b261db672b9b3ab03da6b758b0b22 + checksum: bff48714944d67b160db71ba244fb0f3fe72e77ef2ec8414e2eeb56f2d926e404a13456b8b83a5392e217ba47dec2ec0c368801b31481813e94d185276c3e964 languageName: node linkType: hard "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": - version: 2.3.0 - resolution: "domelementtype@npm:2.3.0" - checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 + version: 2.2.0 + resolution: "domelementtype@npm:2.2.0" + checksum: 24cb386198640cd58aa36f8c987f2ea61859929106d06ffcc8f547e70cb2ed82a6dc56dcb8252b21fba1f1ea07df6e4356d60bfe57f77114ca1aed6828362629 languageName: node linkType: hard -"domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": - version: 4.3.1 - resolution: "domhandler@npm:4.3.1" +"domhandler@npm:^4.2.0, domhandler@npm:^4.3.0": + version: 4.3.0 + resolution: "domhandler@npm:4.3.0" dependencies: domelementtype: ^2.2.0 - checksum: 4c665ceed016e1911bf7d1dadc09dc888090b64dee7851cccd2fcf5442747ec39c647bb1cb8c8919f8bbdd0f0c625a6bafeeed4b2d656bbecdbae893f43ffaaa + checksum: d2a2dbf40dd99abf936b65ad83c6b530afdb3605a87cad37a11b5d9220e68423ebef1b86c89e0f6d93ffaf315cc327cf1a988652e7a9a95cce539e3984f4c64d languageName: node linkType: hard @@ -4948,10 +4868,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.118": - version: 1.4.137 - resolution: "electron-to-chromium@npm:1.4.137" - checksum: 639d7b94906efafcf363519c3698eecc44be46755a6a5cdc9088954329978866cc93fbd57e08b97290599b68d5226243d21de9fa50be416b8a5d3fa8fd42c3a0 +"electron-to-chromium@npm:^1.4.17": + version: 1.4.68 + resolution: "electron-to-chromium@npm:1.4.68" + checksum: d7654d07ab7c504a0683cf29715db227bdbd3e397ad3a41bad3d1e35e9f837447be2bc5dea54b3350d51be5c9c7b79756dcbe44903fbee5949d67d783e788acb languageName: node linkType: hard @@ -5008,16 +4928,18 @@ __metadata: languageName: node linkType: hard -"engine.io-parser@npm:~5.0.3": - version: 5.0.4 - resolution: "engine.io-parser@npm:5.0.4" - checksum: d4ad0cef6ff63c350e35696da9bb3dbd180f67b56e93e90375010cc40393e6c0639b780d5680807e1d93a7e2e3d7b4a1c3b27cf75db28eb8cbf605bc1497da03 +"engine.io-parser@npm:~5.0.0": + version: 5.0.3 + resolution: "engine.io-parser@npm:5.0.3" + dependencies: + "@socket.io/base64-arraybuffer": ~1.0.2 + checksum: 88d664420a441dd02db17d110f7bbbd9efe971747918150bf666b82ee138df596a2f5038f461c8a01864c83af67cb202548364e4174543f8c0bf5f4776ca6e0d languageName: node linkType: hard -"engine.io@npm:~6.2.0": - version: 6.2.0 - resolution: "engine.io@npm:6.2.0" +"engine.io@npm:~6.1.0": + version: 6.1.2 + resolution: "engine.io@npm:6.1.2" dependencies: "@types/cookie": ^0.4.1 "@types/cors": ^2.8.12 @@ -5027,19 +4949,19 @@ __metadata: cookie: ~0.4.1 cors: ~2.8.5 debug: ~4.3.1 - engine.io-parser: ~5.0.3 + engine.io-parser: ~5.0.0 ws: ~8.2.3 - checksum: cc485c5ba2e0c4f6ca02dcafd192b22f9dad89d01dc815005298780d3fb910db4cebab4696e8615290c473c2eeb259e8bee2a1fb7ab594d9c80f9f3485771911 + checksum: bd98d6ce2b1e868e8ff0f65d7667a885b90bce62065d851ea0394a00c86686925be824ab91237151222d6a5acfd5610634f36966fa9b4c502e7cf362fbdf974a languageName: node linkType: hard "enhanced-resolve@npm:^5.8.3": - version: 5.9.3 - resolution: "enhanced-resolve@npm:5.9.3" + version: 5.9.0 + resolution: "enhanced-resolve@npm:5.9.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 64c2dbbdd608d1a4df93b6e60786c603a1faf3b2e66dfd051d62cf4cfaeeb5e800166183685587208d62e9f7afff3f78f3d5978e32cd80125ba0c83b59a79d78 + checksum: 06435f52670229eb7fd8d92ea2d988a275f9a1b8c08b9023ba45767c6ed844bc87aa9c9c7e6d044eef99ad73fc3b066b78101163696e5c53121909e5bf8efe8b languageName: node linkType: hard @@ -5123,171 +5045,163 @@ __metadata: languageName: node linkType: hard -"esbuild-android-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-android-arm64@npm:0.14.22" +"esbuild-android-arm64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-android-arm64@npm:0.14.14" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-darwin-64@npm:0.14.22" +"esbuild-darwin-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-darwin-64@npm:0.14.14" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-darwin-arm64@npm:0.14.22" +"esbuild-darwin-arm64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-darwin-arm64@npm:0.14.14" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-freebsd-64@npm:0.14.22" +"esbuild-freebsd-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-freebsd-64@npm:0.14.14" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-freebsd-arm64@npm:0.14.22" +"esbuild-freebsd-arm64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-freebsd-arm64@npm:0.14.14" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-32@npm:0.14.22" +"esbuild-linux-32@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-32@npm:0.14.14" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-64@npm:0.14.22" +"esbuild-linux-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-64@npm:0.14.14" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-arm64@npm:0.14.22" +"esbuild-linux-arm64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-arm64@npm:0.14.14" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-arm@npm:0.14.22" +"esbuild-linux-arm@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-arm@npm:0.14.14" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-mips64le@npm:0.14.22" +"esbuild-linux-mips64le@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-mips64le@npm:0.14.14" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-ppc64le@npm:0.14.22" +"esbuild-linux-ppc64le@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-ppc64le@npm:0.14.14" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-riscv64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-riscv64@npm:0.14.22" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - -"esbuild-linux-s390x@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-s390x@npm:0.14.22" +"esbuild-linux-s390x@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-linux-s390x@npm:0.14.14" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-netbsd-64@npm:0.14.22" +"esbuild-netbsd-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-netbsd-64@npm:0.14.14" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-openbsd-64@npm:0.14.22" +"esbuild-openbsd-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-openbsd-64@npm:0.14.14" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-sunos-64@npm:0.14.22" +"esbuild-sunos-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-sunos-64@npm:0.14.14" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-wasm@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-wasm@npm:0.14.22" +"esbuild-wasm@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-wasm@npm:0.14.14" bin: esbuild: bin/esbuild - checksum: 56a75d428e086440126132bb465f64ad33e40fd9ecf91dbdd4fe5b1e16ca65b30d8cec8f90eb538c99ad0c5f57dcb15df809cbe626ed6ea5106d723de985a53b + checksum: 047703c94952561da3e8ab58dbadf3581e0a71dfbd866eef6c2f89b58315aab90b7d0087b1b99882c5698a18fe1fa0103cdc5ab292751a039fc0cee14f0432b1 languageName: node linkType: hard -"esbuild-windows-32@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-windows-32@npm:0.14.22" +"esbuild-windows-32@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-windows-32@npm:0.14.14" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-windows-64@npm:0.14.22" +"esbuild-windows-64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-windows-64@npm:0.14.14" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-windows-arm64@npm:0.14.22" +"esbuild-windows-arm64@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild-windows-arm64@npm:0.14.14" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"esbuild@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild@npm:0.14.22" - dependencies: - esbuild-android-arm64: 0.14.22 - esbuild-darwin-64: 0.14.22 - esbuild-darwin-arm64: 0.14.22 - esbuild-freebsd-64: 0.14.22 - esbuild-freebsd-arm64: 0.14.22 - esbuild-linux-32: 0.14.22 - esbuild-linux-64: 0.14.22 - esbuild-linux-arm: 0.14.22 - esbuild-linux-arm64: 0.14.22 - esbuild-linux-mips64le: 0.14.22 - esbuild-linux-ppc64le: 0.14.22 - esbuild-linux-riscv64: 0.14.22 - esbuild-linux-s390x: 0.14.22 - esbuild-netbsd-64: 0.14.22 - esbuild-openbsd-64: 0.14.22 - esbuild-sunos-64: 0.14.22 - esbuild-windows-32: 0.14.22 - esbuild-windows-64: 0.14.22 - esbuild-windows-arm64: 0.14.22 +"esbuild@npm:0.14.14": + version: 0.14.14 + resolution: "esbuild@npm:0.14.14" + dependencies: + esbuild-android-arm64: 0.14.14 + esbuild-darwin-64: 0.14.14 + esbuild-darwin-arm64: 0.14.14 + esbuild-freebsd-64: 0.14.14 + esbuild-freebsd-arm64: 0.14.14 + esbuild-linux-32: 0.14.14 + esbuild-linux-64: 0.14.14 + esbuild-linux-arm: 0.14.14 + esbuild-linux-arm64: 0.14.14 + esbuild-linux-mips64le: 0.14.14 + esbuild-linux-ppc64le: 0.14.14 + esbuild-linux-s390x: 0.14.14 + esbuild-netbsd-64: 0.14.14 + esbuild-openbsd-64: 0.14.14 + esbuild-sunos-64: 0.14.14 + esbuild-windows-32: 0.14.14 + esbuild-windows-64: 0.14.14 + esbuild-windows-arm64: 0.14.14 dependenciesMeta: esbuild-android-arm64: optional: true @@ -5311,8 +5225,6 @@ __metadata: optional: true esbuild-linux-ppc64le: optional: true - esbuild-linux-riscv64: - optional: true esbuild-linux-s390x: optional: true esbuild-netbsd-64: @@ -5329,7 +5241,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 8b99a61203b289ff0ad2be1dfc78420d3f838384ddef6a302203e5a9d8e4913f2c152d58dd775be9ab15ce6c77ea9588934c0af27b25806be48d56472e661676 + checksum: af09b271777ee6e63900cd7fbbe6a8845992454f97febba514d29cc91c2b23c385d9450badf6a538f7c78714421c21121ec9859c086b78aebf748a52d9fa456a languageName: node linkType: hard @@ -5599,41 +5511,40 @@ __metadata: linkType: hard "express@npm:^4.17.1": - version: 4.18.1 - resolution: "express@npm:4.18.1" + version: 4.17.2 + resolution: "express@npm:4.17.2" dependencies: - accepts: ~1.3.8 + accepts: ~1.3.7 array-flatten: 1.1.1 - body-parser: 1.20.0 + body-parser: 1.19.1 content-disposition: 0.5.4 content-type: ~1.0.4 - cookie: 0.5.0 + cookie: 0.4.1 cookie-signature: 1.0.6 debug: 2.6.9 - depd: 2.0.0 + depd: ~1.1.2 encodeurl: ~1.0.2 escape-html: ~1.0.3 etag: ~1.8.1 - finalhandler: 1.2.0 + finalhandler: ~1.1.2 fresh: 0.5.2 - http-errors: 2.0.0 merge-descriptors: 1.0.1 methods: ~1.1.2 - on-finished: 2.4.1 + on-finished: ~2.3.0 parseurl: ~1.3.3 path-to-regexp: 0.1.7 proxy-addr: ~2.0.7 - qs: 6.10.3 + qs: 6.9.6 range-parser: ~1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.17.2 + serve-static: 1.14.2 setprototypeof: 1.2.0 - statuses: 2.0.1 + statuses: ~1.5.0 type-is: ~1.6.18 utils-merge: 1.0.1 vary: ~1.1.2 - checksum: c3d44c92e48226ef32ec978becfedb0ecf0ca21316bfd33674b3c5d20459840584f2325726a4f17f33d9c99f769636f728982d1c5433a5b6fe6eb95b8cf0c854 + checksum: 1535d56d20e65a1a39b5f056c025dd635290a744478ac69cc47633aeb4b2ce51458f8eb4080cfb7ba47c853ba5cfd794d404cff822a25127f1556b726ec3914a languageName: node linkType: hard @@ -5655,20 +5566,13 @@ __metadata: languageName: node linkType: hard -"extsprintf@npm:1.3.0": +"extsprintf@npm:1.3.0, extsprintf@npm:^1.2.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 languageName: node linkType: hard -"extsprintf@npm:^1.2.0": - version: 1.4.1 - resolution: "extsprintf@npm:1.4.1" - checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 - languageName: node - linkType: hard - "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -5784,7 +5688,7 @@ __metadata: languageName: node linkType: hard -"finalhandler@npm:1.1.2": +"finalhandler@npm:1.1.2, finalhandler@npm:~1.1.2": version: 1.1.2 resolution: "finalhandler@npm:1.1.2" dependencies: @@ -5799,21 +5703,6 @@ __metadata: languageName: node linkType: hard -"finalhandler@npm:1.2.0": - version: 1.2.0 - resolution: "finalhandler@npm:1.2.0" - dependencies: - debug: 2.6.9 - encodeurl: ~1.0.2 - escape-html: ~1.0.3 - on-finished: 2.4.1 - parseurl: ~1.3.3 - statuses: 2.0.1 - unpipe: ~1.0.0 - checksum: 92effbfd32e22a7dff2994acedbd9bcc3aa646a3e919ea6a53238090e87097f8ef07cced90aa2cc421abdf993aefbdd5b00104d55c7c5479a8d00ed105b45716 - languageName: node - linkType: hard - "find-cache-dir@npm:^3.3.1": version: 3.3.2 resolution: "find-cache-dir@npm:3.3.2" @@ -5854,7 +5743,7 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.1.0, flatted@npm:^3.2.5": +"flatted@npm:^3.1.0, flatted@npm:^3.2.4": version: 3.2.5 resolution: "flatted@npm:3.2.5" checksum: 3c436e9695ccca29620b4be5671dd72e5dd0a7500e0856611b7ca9bd8169f177f408c3b9abfa78dfe1493ee2d873e2c119080a8a9bee4e1a186a9e60ca6c89f1 @@ -5862,12 +5751,12 @@ __metadata: linkType: hard "follow-redirects@npm:^1.0.0": - version: 1.15.0 - resolution: "follow-redirects@npm:1.15.0" + version: 1.14.8 + resolution: "follow-redirects@npm:1.14.8" peerDependenciesMeta: debug: optional: true - checksum: eaec81c3e0ae57aae2422e38ad3539d0e7279b3a63f9681eeea319bb683dea67502c4e097136b8ce9721542b4e236e092b6b49e34e326cdd7733c274f0a3f378 + checksum: 40c67899c2e3149a27e8b6498a338ff27f39fe138fde8d7f0756cb44b073ba0bfec3d52af28f20c5bdd67263d564d0d8d7b5efefd431de95c18c42f7b4aef457 languageName: node linkType: hard @@ -5896,10 +5785,10 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.2.0": - version: 4.2.0 - resolution: "fraction.js@npm:4.2.0" - checksum: 8c76a6e21dedea87109d6171a0ac77afa14205794a565d71cb10d2925f629a3922da61bf45ea52dbc30bce4d8636dc0a27213a88cbd600eab047d82f9a3a94c5 +"fraction.js@npm:^4.1.2": + version: 4.1.3 + resolution: "fraction.js@npm:4.1.3" + checksum: d00065afce4814998b6e42fd439bbed17edbd9616b134927dbd75ebe1b94d6eb0820c0ce0e2cf8f26100e552cb72aff83f4816ef90cb1b329b6d12a531a26aaa languageName: node linkType: hard @@ -5917,7 +5806,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^10.1.0": +"fs-extra@npm:^10.0.0, fs-extra@npm:^10.1.0": version: 10.1.0 resolution: "fs-extra@npm:10.1.0" dependencies: @@ -5996,26 +5885,20 @@ __metadata: languageName: node linkType: hard -"functions-have-names@npm:^1.2.2": - version: 1.2.3 - resolution: "functions-have-names@npm:1.2.3" - checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 - languageName: node - linkType: hard - -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" +"gauge@npm:^4.0.0": + version: 4.0.0 + resolution: "gauge@npm:4.0.0" dependencies: + ansi-regex: ^5.0.1 aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 + color-support: ^1.1.2 + console-control-strings: ^1.0.0 has-unicode: ^2.0.1 - signal-exit: ^3.0.7 + signal-exit: ^3.0.0 string-width: ^4.2.3 strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d + wide-align: ^1.1.2 + checksum: 637b34c84f518defa89319dbef68211a24e9302182ad2a619e3be1be5b7dcf2a962c8359e889294af667440f4722e7e6e61671859e00bd8ec280a136ded89b25 languageName: node linkType: hard @@ -6033,7 +5916,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1": +"get-intrinsic@npm:^1.0.2": version: 1.1.1 resolution: "get-intrinsic@npm:1.1.1" dependencies: @@ -6113,7 +5996,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0": +"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -6127,30 +6010,16 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.1.1 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 - languageName: node - linkType: hard - "glob@npm:^8.0.1": - version: 8.0.3 - resolution: "glob@npm:8.0.3" + version: 8.1.0 + resolution: "glob@npm:8.1.0" dependencies: fs.realpath: ^1.0.0 inflight: ^1.0.4 inherits: 2 minimatch: ^5.0.1 once: ^1.3.0 - checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 + checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 languageName: node linkType: hard @@ -6240,9 +6109,9 @@ __metadata: linkType: hard "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + version: 4.2.9 + resolution: "graceful-fs@npm:4.2.9" + checksum: 68ea4e07ff2c041ada184f9278b830375f8e0b75154e3f080af6b70f66172fabb4108d19b3863a96b53fc068a310b9b6493d86d1291acc5f3861eb4b79d26ad6 languageName: node linkType: hard @@ -6300,19 +6169,10 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" - dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb - languageName: node - linkType: hard - "has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2": - version: 1.0.3 - resolution: "has-symbols@npm:1.0.3" - checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 + version: 1.0.2 + resolution: "has-symbols@npm:1.0.2" + checksum: 2309c426071731be792b5be43b3da6fb4ed7cbe8a9a6bcfca1862587709f01b33d575ce8f5c264c1eaad09fca2f9a8208c0a2be156232629daa2dd0c0740976b languageName: node linkType: hard @@ -6388,9 +6248,9 @@ __metadata: linkType: hard "html-entities@npm:^2.3.2": - version: 2.3.3 - resolution: "html-entities@npm:2.3.3" - checksum: 92521501da8aa5f66fee27f0f022d6e9ceae62667dae93aa6a2f636afa71ad530b7fb24a18d4d6c124c9885970cac5f8a52dbf1731741161002816ae43f98196 + version: 2.3.2 + resolution: "html-entities@npm:2.3.2" + checksum: 522d8d202df301ff51b517a379e642023ed5c81ea9fb5674ffad88cff386165733d00b6089d5c2fcc644e44777d6072017b6216d8fa40f271d3610420d00a886 languageName: node linkType: hard @@ -6409,9 +6269,9 @@ __metadata: linkType: hard "http-cache-semantics@npm:^4.1.0": - version: 4.1.1 - resolution: "http-cache-semantics@npm:4.1.1" - checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 + version: 4.1.0 + resolution: "http-cache-semantics@npm:4.1.0" + checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 languageName: node linkType: hard @@ -6422,16 +6282,16 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:2.0.0": - version: 2.0.0 - resolution: "http-errors@npm:2.0.0" +"http-errors@npm:1.8.1": + version: 1.8.1 + resolution: "http-errors@npm:1.8.1" dependencies: - depd: 2.0.0 + depd: ~1.1.2 inherits: 2.0.4 setprototypeof: 1.2.0 - statuses: 2.0.1 + statuses: ">= 1.5.0 < 2" toidentifier: 1.0.1 - checksum: 9b0a3782665c52ce9dc658a0d1560bcb0214ba5699e4ea15aefb2a496e2ca83db03ebc42e1cce4ac1f413e4e0d2d736a3fd755772c556a9a06853ba2a0b7d920 + checksum: d3c7e7e776fd51c0a812baff570bdf06fe49a5dc448b700ab6171b1250e4cf7db8b8f4c0b133e4bfe2451022a5790c1ca6c2cae4094dedd6ac8304a1267f91d2 languageName: node linkType: hard @@ -6448,9 +6308,9 @@ __metadata: linkType: hard "http-parser-js@npm:>=0.5.1": - version: 0.5.6 - resolution: "http-parser-js@npm:0.5.6" - checksum: 8a92f6782542211c77936104ea1eca3c86a95420eb286b100f6421630f29d8f94fd4cc7a245df8e078791d86cd9a237091094440ffb0cd1b44a3f85bfbf539fa + version: 0.5.5 + resolution: "http-parser-js@npm:0.5.5" + checksum: 85e67f12d99d67565be6c82dd86d4cf71939825fdf9826e10047b2443460bfef13235859ca67c0235d54e553db242204ec813febc86f11f83ed8ebd3cd475b65 languageName: node linkType: hard @@ -6477,8 +6337,8 @@ __metadata: linkType: hard "http-proxy-middleware@npm:^2.0.0": - version: 2.0.6 - resolution: "http-proxy-middleware@npm:2.0.6" + version: 2.0.3 + resolution: "http-proxy-middleware@npm:2.0.3" dependencies: "@types/http-proxy": ^1.17.8 http-proxy: ^1.18.1 @@ -6490,7 +6350,7 @@ __metadata: peerDependenciesMeta: "@types/express": optional: true - checksum: 2ee85bc878afa6cbf34491e972ece0f5be0a3e5c98a60850cf40d2a9a5356e1fc57aab6cff33c1fc37691b0121c3a42602d2b1956c52577e87a5b77b62ae1c3a + checksum: f121d5515e3c4613b6e9299f03ab70021bf2b5794a4052bcd5218cc754a19b3375e4d3744f3e70c682d9a1a5e125c8769a211d883cd0a24455ef18a5a310abf5 languageName: node linkType: hard @@ -6516,7 +6376,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.0": +"https-proxy-agent@npm:5.0.0, https-proxy-agent@npm:^5.0.0": version: 5.0.0 resolution: "https-proxy-agent@npm:5.0.0" dependencies: @@ -6536,16 +6396,6 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -6743,9 +6593,16 @@ __metadata: linkType: hard "ip@npm:^1.1.0, ip@npm:^1.1.5": - version: 1.1.8 - resolution: "ip@npm:1.1.8" - checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb + version: 1.1.5 + resolution: "ip@npm:1.1.5" + checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a + languageName: node + linkType: hard + +"ip@npm:^2.0.0": + version: 2.0.0 + resolution: "ip@npm:2.0.0" + checksum: cfcfac6b873b701996d71ec82a7dd27ba92450afdb421e356f44044ed688df04567344c36cbacea7d01b1c39a4c732dc012570ebe9bebfb06f27314bca625349 languageName: node linkType: hard @@ -6790,11 +6647,11 @@ __metadata: linkType: hard "is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1": - version: 2.9.0 - resolution: "is-core-module@npm:2.9.0" + version: 2.8.1 + resolution: "is-core-module@npm:2.8.1" dependencies: has: ^1.0.3 - checksum: b27034318b4b462f1c8f1dfb1b32baecd651d891a4e2d1922135daeff4141dfced2b82b07aef83ef54275c4a3526aa38da859223664d0868ca24182badb784ce + checksum: 418b7bc10768a73c41c7ef497e293719604007f88934a6ffc5f7c78702791b8528102fb4c9e56d006d69361549b3d9519440214a74aefc7e0b79e5e4411d377f languageName: node linkType: hard @@ -6998,9 +6855,9 @@ __metadata: linkType: hard "isbinaryfile@npm:^4.0.8": - version: 4.0.10 - resolution: "isbinaryfile@npm:4.0.10" - checksum: a6b28db7e23ac7a77d3707567cac81356ea18bd602a4f21f424f862a31d0e7ab4f250759c98a559ece35ffe4d99f0d339f1ab884ffa9795172f632ab8f88e686 + version: 4.0.8 + resolution: "isbinaryfile@npm:4.0.8" + checksum: 606e3bb648d1a0dee23459d1d937bb2560e66a5281ec7c9ff50e585402d73321ac268d0f34cb7393125b3ebc4c7962d39e50a01cdb8904b52fce08b7ccd2bf9f languageName: node linkType: hard @@ -7040,15 +6897,15 @@ __metadata: linkType: hard "istanbul-lib-instrument@npm:^5.0.4": - version: 5.2.0 - resolution: "istanbul-lib-instrument@npm:5.2.0" + version: 5.1.0 + resolution: "istanbul-lib-instrument@npm:5.1.0" dependencies: "@babel/core": ^7.12.3 "@babel/parser": ^7.14.7 "@istanbuljs/schema": ^0.1.2 istanbul-lib-coverage: ^3.2.0 semver: ^6.3.0 - checksum: 7c242ed782b6bf7b655656576afae8b6bd23dcc020e5fdc1472cca3dfb6ddb196a478385206d0df5219b9babf46ac4f21fea5d8ea9a431848b6cca6007012353 + checksum: 8b82e733c69fe9f94d2e21f3e5760c9bedb110329aa75df4bd40df95f1cac3bf38767e43f35b125cc547ceca7376b72ce7d95cc5238b7e9088345c7b589233d3 languageName: node linkType: hard @@ -7100,20 +6957,13 @@ __metadata: languageName: node linkType: hard -"jasmine-core@npm:3.8.x": +"jasmine-core@npm:3.8.x, jasmine-core@npm:^3.6.0": version: 3.8.0 resolution: "jasmine-core@npm:3.8.0" checksum: 6490373bd96d4d2a3c74214d307f8c37394370b1e892b9f203d4c5eda56941d53f84aabee0e65538b6921a190710618f1e73af44bd1d93e6c6e6c3f5c4bbfab6 languageName: node linkType: hard -"jasmine-core@npm:^3.6.0": - version: 3.99.1 - resolution: "jasmine-core@npm:3.99.1" - checksum: 4e4a89739d99e471b86c7ccc4c5c244a77cc6d1e17b2b0d87d81266b8415697354d8873f7e764790a10661744f73a753a6e9bcd9b3e48c66a0c9b8a092b071b7 - languageName: node - linkType: hard - "jasmine-core@npm:~2.8.0": version: 2.8.0 resolution: "jasmine-core@npm:2.8.0" @@ -7266,22 +7116,24 @@ __metadata: linkType: hard "json5@npm:^1.0.1": - version: 1.0.2 - resolution: "json5@npm:1.0.2" + version: 1.0.1 + resolution: "json5@npm:1.0.1" dependencies: minimist: ^1.2.0 bin: json5: lib/cli.js - checksum: 866458a8c58a95a49bef3adba929c625e82532bcff1fe93f01d29cb02cac7c3fe1f4b79951b7792c2da9de0b32871a8401a6e3c5b36778ad852bf5b8a61165d7 + checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 languageName: node linkType: hard -"json5@npm:^2.1.2, json5@npm:^2.2.1": - version: 2.2.1 - resolution: "json5@npm:2.2.1" +"json5@npm:^2.1.2": + version: 2.2.0 + resolution: "json5@npm:2.2.0" + dependencies: + minimist: ^1.2.5 bin: json5: lib/cli.js - checksum: 74b8a23b102a6f2bf2d224797ae553a75488b5adbaee9c9b6e5ab8b510a2fc6e38f876d4c77dea672d4014a44b2399e15f2051ac2b37b87f74c0c7602003543b + checksum: e88fc5274bb58fc99547baa777886b069d2dd96d9cfc4490b305fd16d711dabd5979e35a4f90873cefbeb552e216b041a304fe56702bedba76e19bc7845f208d languageName: node linkType: hard @@ -7325,23 +7177,23 @@ __metadata: linkType: hard "jszip@npm:^3.1.3": - version: 3.10.0 - resolution: "jszip@npm:3.10.0" + version: 3.7.1 + resolution: "jszip@npm:3.7.1" dependencies: lie: ~3.3.0 pako: ~1.0.2 readable-stream: ~2.3.6 - setimmediate: ^1.0.5 - checksum: abc77bfbe33e691d4d1ac9c74c8851b5761fba6a6986630864f98d876f3fcc2d36817dfc183779f32c00157b5d53a016796677298272a714ae096dfe6b1c8b60 + set-immediate-shim: ~1.0.1 + checksum: 67d737a82b294cc102e7451e32d5acbbab29860399be460cae598084327e6f2ea0c9bca2d3dad701da6a75ddf77f34c6a1dd7db0c3d5c0fec5998b7e56d6d59d languageName: node linkType: hard "karma-chrome-launcher@npm:3.1.x": - version: 3.1.1 - resolution: "karma-chrome-launcher@npm:3.1.1" + version: 3.1.0 + resolution: "karma-chrome-launcher@npm:3.1.0" dependencies: which: ^1.2.1 - checksum: 8442219105e1f11a9284fd47f2e21e34720f7e725f25ea08f7525a7ec2088e2c1b65e2def4d7780139d296afc5c30bf4e1d4a839a097eb814031c2f6b379b39f + checksum: 63431ddec9aa40e2a0439d9e2bcfa58a6822efd08e2666bdbc3f55dfbe8fcc0b401035b71b1f6f21340339dc56c172edaed8e8c0ddc6949873318ad1666b2dd9 languageName: node linkType: hard @@ -7370,13 +7222,13 @@ __metadata: linkType: hard "karma-jasmine@npm:4.0.x": - version: 4.0.2 - resolution: "karma-jasmine@npm:4.0.2" + version: 4.0.1 + resolution: "karma-jasmine@npm:4.0.1" dependencies: jasmine-core: ^3.6.0 peerDependencies: karma: "*" - checksum: bf884704af1fd19816d9f4e96b25e286ff1a57adcabe1f15e3d2b3e9c1da873c1c843b9eab4274c27e63a99f1c3dea864f1f5eca1a10dc065e6e9d5796c207b4 + checksum: 28337c82941ed6c68e0c47ef432c2c91511214e84a336e78d9068daebd61a3c1cee6209207ddc5fe3ad78124597f58054b93aa0f973ff6dcc8a8fcd1951c9851 languageName: node linkType: hard @@ -7390,13 +7242,13 @@ __metadata: linkType: hard "karma@npm:6.3.x": - version: 6.3.20 - resolution: "karma@npm:6.3.20" + version: 6.3.16 + resolution: "karma@npm:6.3.16" dependencies: - "@colors/colors": 1.5.0 body-parser: ^1.19.0 braces: ^3.0.2 chokidar: ^3.5.1 + colors: 1.4.0 connect: ^3.7.0 di: ^0.0.1 dom-serialize: ^2.2.1 @@ -7412,14 +7264,14 @@ __metadata: qjobs: ^1.2.0 range-parser: ^1.2.1 rimraf: ^3.0.2 - socket.io: ^4.4.1 + socket.io: ^4.2.0 source-map: ^0.6.1 tmp: ^0.2.1 ua-parser-js: ^0.7.30 yargs: ^16.1.1 bin: karma: bin/karma - checksum: 7060afc367c49b2ce2e145f6555c428eadfd72bd68a5d4ae392ec51c8d1c0484706fdd52cfd06d69a811c25517fd5129689a69173be850814dc68c3b7eafa6c8 + checksum: eb1703d4907ac31a47019e2b6b5f69e1ecd7870dabee1ed8f284d9730f665e02ae9ef1a75733b5d4b6a27fe68069536d0845b9e41747c43507128b3ac645c87f languageName: node linkType: hard @@ -7501,9 +7353,9 @@ __metadata: languageName: node linkType: hard -"license-webpack-plugin@npm:4.0.2": - version: 4.0.2 - resolution: "license-webpack-plugin@npm:4.0.2" +"license-webpack-plugin@npm:4.0.1": + version: 4.0.1 + resolution: "license-webpack-plugin@npm:4.0.1" dependencies: webpack-sources: ^3.0.0 peerDependenciesMeta: @@ -7511,7 +7363,7 @@ __metadata: optional: true webpack-sources: optional: true - checksum: e88ebdb9c8bdfc0926dd7211d7fe2ee8697a44bb00a96bb5e6ca844b6acb7d24dd54eb17ec485e2e0140c3cc86709d1c2bd46e091ab52af076e1e421054c8322 + checksum: ed3f8183bcb6cc6ec8aeeb17823d11a99d872609eedcf704ff4bb1a212df8d653c83eb42b7da04e0e25bb4924d784b6b603c5ba3a816dd17d5419b2273f70e5c languageName: node linkType: hard @@ -7584,9 +7436,9 @@ __metadata: linkType: hard "loader-runner@npm:^4.2.0": - version: 4.3.0 - resolution: "loader-runner@npm:4.3.0" - checksum: a90e00dee9a16be118ea43fec3192d0b491fe03a32ed48a4132eb61d498f5536a03a1315531c19d284392a8726a4ecad71d82044c28d7f22ef62e029bf761569 + version: 4.2.0 + resolution: "loader-runner@npm:4.2.0" + checksum: e61aea8b6904b8af53d9de6f0484da86c462c0001f4511bedc837cec63deb9475cea813db62f702cd7930420ccb0e75c78112270ca5c8b61b374294f53c0cb3a languageName: node linkType: hard @@ -7642,13 +7494,6 @@ __metadata: languageName: node linkType: hard -"lodash.sortby@npm:^4.7.0": - version: 4.7.0 - resolution: "lodash.sortby@npm:4.7.0" - checksum: db170c9396d29d11fe9a9f25668c4993e0c1331bcb941ddbd48fb76f492e732add7f2a47cfdf8e9d740fa59ac41bbfaf931d268bc72aab3ab49e9f89354d718c - languageName: node - linkType: hard - "lodash.truncate@npm:^4.4.2": version: 4.4.2 resolution: "lodash.truncate@npm:4.4.2" @@ -7686,15 +7531,15 @@ __metadata: linkType: hard "log4js@npm:^6.4.1": - version: 6.5.1 - resolution: "log4js@npm:6.5.1" + version: 6.4.1 + resolution: "log4js@npm:6.4.1" dependencies: - date-format: ^4.0.10 - debug: ^4.3.4 - flatted: ^3.2.5 + date-format: ^4.0.3 + debug: ^4.3.3 + flatted: ^3.2.4 rfdc: ^1.3.0 - streamroller: ^3.1.1 - checksum: 53e16766caa2c8235b054420799d053e2397fc816e99c1d93119ee07ab7d06239ee319cc9f1627c6119dd30994e7c4941f71e03e9fab8996206303bde7867220 + streamroller: ^3.0.2 + checksum: 0614949662314573ec7dcd841769a4d23d8cb8268685458a40fcd94f2ae6ec628234cfb9a6bc17821fb6ea6ce3765e779b4966ba1cf918f393dc37155a3615cb languageName: node linkType: hard @@ -7724,14 +7569,21 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^7.3.1": + version: 7.3.1 + resolution: "lru-cache@npm:7.3.1" + checksum: 34bb50c015ffc29fd83545e912f28cea6e03fbf41c497fa220c4f131b990f9ddf95babac98745b416cbc6c0d835254d61668d09b8a4ecb476934546afc9e51bd + languageName: node + linkType: hard + "lru-cache@npm:^7.7.1": - version: 7.10.1 - resolution: "lru-cache@npm:7.10.1" - checksum: e8b190d71ed0fcd7b29c71a3e9b01f851c92d1ef8865ff06b5581ca991db1e5e006920ed4da8b56da1910664ed51abfd76c46fb55e82ac252ff6c970ff910d72 + version: 7.14.1 + resolution: "lru-cache@npm:7.14.1" + checksum: d72c6713c6a6d86836a7a6523b3f1ac6764768cca47ec99341c3e76db06aacd4764620e5e2cda719a36848785a52a70e531822dc2b33fb071fa709683746c104 languageName: node linkType: hard -"magic-string@npm:0.25.7": +"magic-string@npm:0.25.7, magic-string@npm:^0.25.0": version: 0.25.7 resolution: "magic-string@npm:0.25.7" dependencies: @@ -7740,15 +7592,6 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.26.0": - version: 0.26.2 - resolution: "magic-string@npm:0.26.2" - dependencies: - sourcemap-codec: ^1.4.8 - checksum: b4db4e2b370ac8d9ffc6443a2b591b75364bf1fc9121b5a4068d5b89804abff6709d1fa4a0e0c2d54f2e61e0e44db83efdfe219a5ab0ba6d25ee1f2b51fbed55 - languageName: node - linkType: hard - "make-dir@npm:^2.1.0": version: 2.1.0 resolution: "make-dir@npm:2.1.0" @@ -7775,9 +7618,33 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.1, make-fetch-happen@npm:^10.0.3": - version: 10.1.5 - resolution: "make-fetch-happen@npm:10.1.5" +"make-fetch-happen@npm:^10.0.1": + version: 10.0.2 + resolution: "make-fetch-happen@npm:10.0.2" + dependencies: + agentkeepalive: ^4.2.0 + cacache: ^15.3.0 + http-cache-semantics: ^4.1.0 + http-proxy-agent: ^5.0.0 + https-proxy-agent: ^5.0.0 + is-lambda: ^1.0.1 + lru-cache: ^7.3.1 + minipass: ^3.1.6 + minipass-collect: ^1.0.2 + minipass-fetch: ^1.4.1 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.3 + promise-retry: ^2.0.1 + socks-proxy-agent: ^6.1.1 + ssri: ^8.0.1 + checksum: 5a620f84ab9e28673f70b0640b15133cb329ca4dbeec17e24b5029f07b134c52d96ebac150cab99979c48c5e6e99056978ed0321e4ce1e22a4d4d6fc7148c2fd + languageName: node + linkType: hard + +"make-fetch-happen@npm:^10.0.3": + version: 10.2.1 + resolution: "make-fetch-happen@npm:10.2.1" dependencies: agentkeepalive: ^4.2.1 cacache: ^16.1.0 @@ -7793,9 +7660,9 @@ __metadata: minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 - socks-proxy-agent: ^6.1.1 + socks-proxy-agent: ^7.0.0 ssri: ^9.0.0 - checksum: b0b42a1ccdcbc3180749727a52cf6887d9df6218d8ca35101bb9f7ab35729dd166d99203b70149a19a818d1ba72de40b982002ddb0b308c548457f5725d6e7f6 + checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c languageName: node linkType: hard @@ -7851,12 +7718,12 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.2.2, memfs@npm:^3.4.3": - version: 3.4.3 - resolution: "memfs@npm:3.4.3" +"memfs@npm:^3.2.2": + version: 3.4.1 + resolution: "memfs@npm:3.4.1" dependencies: fs-monkey: 1.0.3 - checksum: c947ef46e2036524ba120cb42fa502fd75dae8d49d0c53e818d3d3780b9a3a47845705cd1cf51eec04c70f1db590ca7b6c7f78dd5a65883bb253fcedf86f412c + checksum: 6d2f49d447d1be24ff9c747618933784eeb059189bc6a0d77b7a51c7daf06e2d3a74674a2e2ff1520e2c312bf91e719ed37144cf05087379b3ba0aef0b6aa062 languageName: node linkType: hard @@ -7918,19 +7785,19 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": - version: 1.52.0 - resolution: "mime-db@npm:1.52.0" - checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f +"mime-db@npm:1.51.0, mime-db@npm:>= 1.43.0 < 2": + version: 1.51.0 + resolution: "mime-db@npm:1.51.0" + checksum: 613b1ac9d6e725cc24444600b124a7f1ce6c60b1baa654f39a3e260d0995a6dffc5693190217e271af7e2a5612dae19f2a73f3e316707d797a7391165f7ef423 languageName: node linkType: hard "mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": - version: 2.1.35 - resolution: "mime-types@npm:2.1.35" + version: 2.1.34 + resolution: "mime-types@npm:2.1.34" dependencies: - mime-db: 1.52.0 - checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 + mime-db: 1.51.0 + checksum: 67013de9e9d6799bde6d669d18785b7e18bcd212e710d3e04a4727f92f67a8ad4e74aee24be28b685adb794944814bde649119b58ee3282ffdbee58f9278d9f3 languageName: node linkType: hard @@ -7993,7 +7860,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -8022,7 +7889,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.6": +"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.6 resolution: "minimist@npm:1.2.6" checksum: d15428cd1e11eb14e1233bcfb88ae07ed7a147de251441d61158619dfb32c4d7e9061d09cab4825fdee18ecd6fce323228c8c47b5ba7cd20af378ca4048fb3fb @@ -8054,8 +7921,8 @@ __metadata: linkType: hard "minipass-fetch@npm:^2.0.3": - version: 2.1.0 - resolution: "minipass-fetch@npm:2.1.0" + version: 2.1.2 + resolution: "minipass-fetch@npm:2.1.2" dependencies: encoding: ^0.1.13 minipass: ^3.1.6 @@ -8064,7 +7931,7 @@ __metadata: dependenciesMeta: encoding: optional: true - checksum: 1334732859a3f7959ed22589bafd9c40384b885aebb5932328071c33f86b3eb181d54c86919675d1825ab5f1c8e4f328878c863873258d113c29d79a4b0c9c9f + checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91 languageName: node linkType: hard @@ -8114,6 +7981,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^4.0.0": + version: 4.0.3 + resolution: "minipass@npm:4.0.3" + checksum: a09f405e2f380ae7f6ee0cbb53b45c1fcc1b6c70fc3896f4d20649d92a10e61892c57bd9960a64cedf6c90b50022cb6c195905b515039c335b423202f99e6f18 + languageName: node + linkType: hard + "minizlib@npm:^2.0.0, minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -8125,13 +7999,13 @@ __metadata: linkType: hard "mkdirp@npm:^0.5.5": - version: 0.5.6 - resolution: "mkdirp@npm:0.5.6" + version: 0.5.5 + resolution: "mkdirp@npm:0.5.5" dependencies: - minimist: ^1.2.6 + minimist: ^1.2.5 bin: mkdirp: bin/cmd.js - checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 + checksum: 3bce20ea525f9477befe458ab85284b0b66c8dc3812f94155af07c827175948cdd8114852ac6c6d82009b13c1048c37f6d98743eb019651ee25c39acc8aabe7d languageName: node linkType: hard @@ -8151,14 +8025,14 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.2": +"ms@npm:2.1.2, ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -8235,8 +8109,8 @@ __metadata: linkType: hard "ng-apexcharts@npm:1.7.x": - version: 1.7.1 - resolution: "ng-apexcharts@npm:1.7.1" + version: 1.7.0 + resolution: "ng-apexcharts@npm:1.7.0" dependencies: tslib: ^2.0.0 peerDependencies: @@ -8244,20 +8118,20 @@ __metadata: "@angular/core": ">=13.0.0" apexcharts: ^3.31.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: 27b1de2ffde1e52e957c407accad6e03e5cb9ce03b40f627ac2bbe09c17143f3588f7c12f4dd0cd4c4ad033f97440d39fadca00aaf2aca3dae90d4cc4a8a3a88 + checksum: a615eea8a3c6f6fc145f6a16a77732207bf37a2387bcb9148a2da29ef58fbca10dedb3efb5f4ddb86bbe72850385f5ecd330dcca32ff5e0c9ffbf9ae862db5f9 languageName: node linkType: hard "ngx-color-picker@npm:12.0.x": - version: 12.0.1 - resolution: "ngx-color-picker@npm:12.0.1" + version: 12.0.0 + resolution: "ngx-color-picker@npm:12.0.0" dependencies: tslib: ^2.3.0 peerDependencies: "@angular/common": ">=9.0.0" "@angular/core": ">=9.0.0" "@angular/forms": ">=9.0.0" - checksum: f0c2361ec92c4cd76ddf5561e9beeb1b5da56d4aafe929189c648bda21166c0cddd6d3e44278fd28eb76e00e24a9254a73de6b9ffff9d3b9197c052ea373577e + checksum: 6e8f9676ddfc390e4a4d0e7751e97cebd4b16b6cb4086254e9f5f5823ada12a5ee101a2a868f726a1b1dc412fa47aee885173461228d2a9904ed36041df38772 languageName: node linkType: hard @@ -8281,21 +8155,32 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^1": - version: 1.3.1 - resolution: "node-forge@npm:1.3.1" - checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 +"node-forge@npm:^1.2.0": + version: 1.3.0 + resolution: "node-forge@npm:1.3.0" + checksum: 3d8124168dd82006fafbb079f40a529afa0de5bf4d77e6a5a471877e9d39bece31fdc8339e8aee30d5480dc79ffcd1059cfcb21983d350dd3f2a9f226db6ca85 + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.2.2": + version: 4.6.0 + resolution: "node-gyp-build@npm:4.6.0" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 25d78c5ef1f8c24291f4a370c47ba52fcea14f39272041a90a7894cd50d766f7c8cb8fb06c0f42bf6f69b204b49d9be3c8fc344aac09714d5bdb95965499eb15 languageName: node linkType: hard -"node-gyp-build@npm:^4.2.2, node-gyp-build@npm:^4.3.0": - version: 4.4.0 - resolution: "node-gyp-build@npm:4.4.0" +"node-gyp-build@npm:^4.3.0": + version: 4.3.0 + resolution: "node-gyp-build@npm:4.3.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 972a059f960253d254e0b23ce10f54c8982236fc0edcab85166d0b7f87443b2ce98391c877cfb2f6eeafcf03c538c5f4dd3e0bfff03828eb48634f58f4c64343 + checksum: 1ecab16d9f275174d516e223f60f65ebe07540347d5c04a6a7d6921060b7f2e3af4f19463d9d1dcedc452e275c2ae71354a99405e55ebd5b655bb2f38025c728 languageName: node linkType: hard @@ -8320,14 +8205,14 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 9.0.0 - resolution: "node-gyp@npm:9.0.0" + version: 9.3.1 + resolution: "node-gyp@npm:9.3.1" dependencies: env-paths: ^2.2.0 glob: ^7.1.4 graceful-fs: ^4.2.6 make-fetch-happen: ^10.0.3 - nopt: ^5.0.0 + nopt: ^6.0.0 npmlog: ^6.0.0 rimraf: ^3.0.2 semver: ^7.3.5 @@ -8335,14 +8220,14 @@ __metadata: which: ^2.0.2 bin: node-gyp: bin/node-gyp.js - checksum: 4d8ef8860f7e4f4d86c91db3f519d26ed5cc23b48fe54543e2afd86162b4acbd14f21de42a5db344525efb69a991e021b96a68c70c6e2d5f4a5cb770793da6d3 + checksum: b860e9976fa645ca0789c69e25387401b4396b93c8375489b5151a6c55cf2640a3b6183c212b38625ef7c508994930b72198338e3d09b9d7ade5acc4aaf51ea7 languageName: node linkType: hard -"node-releases@npm:^2.0.3": - version: 2.0.4 - resolution: "node-releases@npm:2.0.4" - checksum: b32d6c2032c7b169ae3938b416fc50f123f5bd577d54a79b2ae201febf27b22846b01c803dd35ac8689afe840f8ba4e5f7154723db629b80f359836b6707b92f +"node-releases@npm:^2.0.1": + version: 2.0.2 + resolution: "node-releases@npm:2.0.2" + checksum: da858bf86b4d512842379749f5a5e4196ddab05ba18ffcf29f05bf460beceaca927f070f4430bb5046efec18941ddbc85e4c5fdbb83afc28a38dd6069a2f255e languageName: node linkType: hard @@ -8357,6 +8242,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:^6.0.0": + version: 6.0.0 + resolution: "nopt@npm:6.0.0" + dependencies: + abbrev: ^1.0.0 + bin: + nopt: bin/nopt.js + checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac + languageName: node + linkType: hard + "normalize-package-data@npm:^2.5.0": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" @@ -8481,23 +8377,23 @@ __metadata: linkType: hard "npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" + version: 6.0.1 + resolution: "npmlog@npm:6.0.1" dependencies: are-we-there-yet: ^3.0.0 console-control-strings: ^1.1.0 - gauge: ^4.0.3 + gauge: ^4.0.0 set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a + checksum: f1a4078a73ebc89896a832bbf869f491c32ecb12e0434b9a7499878ce8f29f22e72befe3c53cd8cdc9dbf4b4057297e783ab0b6746a8b067734de6205af4d538 languageName: node linkType: hard "nth-check@npm:^2.0.1": - version: 2.1.0 - resolution: "nth-check@npm:2.1.0" + version: 2.0.1 + resolution: "nth-check@npm:2.0.1" dependencies: boolbase: ^1.0.0 - checksum: c555b2f7ccd2525aa5eb372cefd490b8a0d1310d751762593489de0e1338b1ce9a27b2c2f22094c0d0da9f9df725307f72df74bf0c22ea76392afb9f5e1bfc03 + checksum: 5386d035c48438ff304fe687704d93886397349d1bed136de97aeae464caba10e8ffac55a04b215b86b3bc8897f33e0a5aa1045a9d8b2f251ae61b2a3ad3e450 languageName: node linkType: hard @@ -8569,7 +8465,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.12.0": version: 1.12.1 resolution: "object-inspect@npm:1.12.1" checksum: 5c7c3b641417606db7f545760cfdbc686870c4ac03c86d05f3e1194b19de39b48030f2145ef813e6e8228268d48408eceb9bdcfeb0a502d8d9e5a057982c31a0 @@ -8586,7 +8482,7 @@ __metadata: languageName: node linkType: hard -"object-keys@npm:^1.1.1": +"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a @@ -8612,15 +8508,6 @@ __metadata: languageName: node linkType: hard -"on-finished@npm:2.4.1": - version: 2.4.1 - resolution: "on-finished@npm:2.4.1" - dependencies: - ee-first: 1.1.1 - checksum: d20929a25e7f0bb62f937a425b5edeb4e4cde0540d77ba146ec9357f00b0d497cdb3b9b05b9c8e46222407d1548d08166bff69cc56dfa55ba0e4469228920ff0 - languageName: node - linkType: hard - "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -8732,12 +8619,12 @@ __metadata: linkType: hard "p-retry@npm:^4.5.0": - version: 4.6.2 - resolution: "p-retry@npm:4.6.2" + version: 4.6.1 + resolution: "p-retry@npm:4.6.1" dependencies: - "@types/retry": 0.12.0 + "@types/retry": ^0.12.0 retry: ^0.13.1 - checksum: 45c270bfddaffb4a895cea16cb760dcc72bdecb6cb45fef1971fa6ea2e91ddeafddefe01e444ac73e33b1b3d5d29fb0dd18a7effb294262437221ddc03ce0f2e + checksum: e6d540413bb3d0b96e0db44f74a7af1dce41f5005e6e84d617960110b148348c86a3987be07797749e3ddd55817dd3a8ffd6eae3428758bc2994d987e48c3a70 languageName: node linkType: hard @@ -9031,13 +8918,13 @@ __metadata: linkType: hard "postcss-color-functional-notation@npm:^4.2.1": - version: 4.2.3 - resolution: "postcss-color-functional-notation@npm:4.2.3" + version: 4.2.2 + resolution: "postcss-color-functional-notation@npm:4.2.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 1be72dd64b99a33dd8827aec0373067568721cc06d1e059d72d9680280d06546fe67bc30ed508c89c7878a9bf8ac455ec8f12af9335dcfee45a4be872476abf1 + checksum: 77cc5d5526c3228737f2642472546498f0d963b8617c7cae453423331ecb868712ed1557007eab0cd5ff183d60bba24fa2e4bc83e550ddd45f1399e354704b81 languageName: node linkType: hard @@ -9073,13 +8960,13 @@ __metadata: linkType: hard "postcss-custom-properties@npm:^12.1.2": - version: 12.1.7 - resolution: "postcss-custom-properties@npm:12.1.7" + version: 12.1.4 + resolution: "postcss-custom-properties@npm:12.1.4" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 98c313c2318679b727080297a12fb6674e5ea5a3343f693167e985793afd9c7d71ce25a17139864ccfe76d32d7474bb89a2ad02830c8e40fa57ccb0a699b528d + checksum: c5ef5c5ff126a65adcae9f1842b6c82df8adc337481497a405de5dfb2121c799d381e75aed3dbfe855f4258e9d90927e77801012166fb667ea5c8f7d79562ada languageName: node linkType: hard @@ -9106,25 +8993,24 @@ __metadata: linkType: hard "postcss-double-position-gradients@npm:^3.0.4": - version: 3.1.1 - resolution: "postcss-double-position-gradients@npm:3.1.1" + version: 3.0.5 + resolution: "postcss-double-position-gradients@npm:3.0.5" dependencies: - "@csstools/postcss-progressive-custom-properties": ^1.1.0 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: c59131b2d03022fbb69336766786e8cc33f6e78c8040e17d2ba499fce789c675c5dcdc4fd3abe1e76e0ecd3dc910ad8c56d49a307c0115047d21a59544afc527 + checksum: 43c676a085ba908e7ceabffb180e367a949d3a566d31271ca2194fb398260393de4e1aaa95733184d5c337a97ec47cd8c42e42573ed5d60736533f44a1bb5ebd languageName: node linkType: hard "postcss-env-function@npm:^4.0.4": - version: 4.0.6 - resolution: "postcss-env-function@npm:4.0.6" + version: 4.0.5 + resolution: "postcss-env-function@npm:4.0.5" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 645b2363cfa21be9dcce7fe4a0f172f0af70c00d6a4c1eb3d7ff7e9cfe26d569e291ec2533114d77b12d610023cd168a92d62c38f2fc969fa333b5ae2bff5ffe + checksum: 0dca3569f136e8f6d11a98d61494054f8ca84de4a679ad80909891d60929f725a188c5e9dc53a3b1c419b00db240b3b64c43bf7bafac081fe718e10460fbb5be languageName: node linkType: hard @@ -9202,14 +9088,14 @@ __metadata: linkType: hard "postcss-lab-function@npm:^4.0.3": - version: 4.2.0 - resolution: "postcss-lab-function@npm:4.2.0" + version: 4.1.0 + resolution: "postcss-lab-function@npm:4.1.0" dependencies: "@csstools/postcss-progressive-custom-properties": ^1.1.0 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 89ca828b8ed16feb201be7b050254560786c76392ce0a4262732438521ce13d083d1e542addf9d14da75249c58802d721df3152316bb591c9f627c7038166c2a + checksum: cf48efa4e23a94a2c7c639b929e57b3ef1ccbb21f636d64ae571a1a72bb0538878c8239c95371e9372448e06890bc2010303e75cc8a715bd2323cc189895872e languageName: node linkType: hard @@ -9297,14 +9183,13 @@ __metadata: linkType: hard "postcss-nesting@npm:^10.1.2": - version: 10.1.7 - resolution: "postcss-nesting@npm:10.1.7" + version: 10.1.2 + resolution: "postcss-nesting@npm:10.1.2" dependencies: - "@csstools/selector-specificity": 1.0.0 - postcss-selector-parser: ^6.0.10 + postcss-selector-parser: ^6.0.8 peerDependencies: - postcss: ^8.4 - checksum: 610bf1f32ea235ea825b943dbe50cf0fcf5077a63272bee45e66761ffe4f665cf460f3b7ba171fb758a5082aa2893bc7c834d5cb9e6eb6759bb2c65f0141daa1 + postcss: ^8.3 + checksum: 571385eb40ba2874d9e87ed4f8e6e1743c2626e8e626d2d8dc857eeaff7299cc378aa5a662aea93de807955bd32282cf4eaa0434bdb4bd0fdaa2b74703ba98f7 languageName: node linkType: hard @@ -9381,13 +9266,13 @@ __metadata: linkType: hard "postcss-pseudo-class-any-link@npm:^7.0.2": - version: 7.1.4 - resolution: "postcss-pseudo-class-any-link@npm:7.1.4" + version: 7.1.1 + resolution: "postcss-pseudo-class-any-link@npm:7.1.1" dependencies: - postcss-selector-parser: ^6.0.10 + postcss-selector-parser: ^6.0.9 peerDependencies: postcss: ^8.4 - checksum: 86e10d56daf57837ccde1d6275425522cafecbae96bd955ef200befc7fd143eedc3b00907e53e8f5207c6290b277600a8ff3f72ec037113867d6031c5d31e7f2 + checksum: d177b7ad6025c1b0dd348b0efa49892d670bc5f4e742f53084625a3110595f45b30ae8fb60d19a09f57112e441d1e1e31421a0fb212e2fde5f8375dc07644efb languageName: node linkType: hard @@ -9436,7 +9321,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6, postcss-selector-parser@npm:^6.0.9": +"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6, postcss-selector-parser@npm:^6.0.8, postcss-selector-parser@npm:^6.0.9": version: 6.0.10 resolution: "postcss-selector-parser@npm:6.0.10" dependencies: @@ -9453,7 +9338,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.5": +"postcss@npm:8.4.5, postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7": version: 8.4.5 resolution: "postcss@npm:8.4.5" dependencies: @@ -9464,7 +9349,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7, postcss@npm:^8.4.13": +"postcss@npm:^8.4.13": version: 8.4.14 resolution: "postcss@npm:8.4.14" dependencies: @@ -9620,20 +9505,13 @@ __metadata: languageName: node linkType: hard -"q@npm:1.4.1": +"q@npm:1.4.1, q@npm:^1.4.1": version: 1.4.1 resolution: "q@npm:1.4.1" checksum: 22c8e1f24f416d0977e6da63f24712189c5dd789489999fc040467480e4e0ef4bd0e3126cce1b8ef72c709bbe1fcce10eba0f4991a03fc64ecb5a17e05ed8d35 languageName: node linkType: hard -"q@npm:^1.4.1": - version: 1.5.1 - resolution: "q@npm:1.5.1" - checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12 - languageName: node - linkType: hard - "qjobs@npm:^1.2.0": version: 1.2.0 resolution: "qjobs@npm:1.2.0" @@ -9641,12 +9519,10 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.10.3": - version: 6.10.3 - resolution: "qs@npm:6.10.3" - dependencies: - side-channel: ^1.0.4 - checksum: 0fac5e6c7191d0295a96d0e83c851aeb015df7e990e4d3b093897d3ac6c94e555dbd0a599739c84d7fa46d7fee282d94ba76943983935cf33bba6769539b8019 +"qs@npm:6.9.6": + version: 6.9.6 + resolution: "qs@npm:6.9.6" + checksum: cb6df402bb8a3dbefa4bd46eba0dfca427079baca923e6b8d28a03e6bfb16a5c1dcdb96e69388f9c5813ac8ff17bb8bbca22f2ecd31fe1e344a55cb531b5fabf languageName: node linkType: hard @@ -9687,15 +9563,15 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.1": - version: 2.5.1 - resolution: "raw-body@npm:2.5.1" +"raw-body@npm:2.4.2": + version: 2.4.2 + resolution: "raw-body@npm:2.4.2" dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 + bytes: 3.1.1 + http-errors: 1.8.1 iconv-lite: 0.4.24 unpipe: 1.0.0 - checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e + checksum: c6f8d6a75c65c0a047f888cb29efc97f60fb36e950ba2cb31fefce694f98186e844a03367920faa7dc5bffaf33df08aee0b9dd935280e366439fa6492a5b163e languageName: node linkType: hard @@ -9816,12 +9692,12 @@ __metadata: languageName: node linkType: hard -"regenerator-transform@npm:^0.15.0": - version: 0.15.0 - resolution: "regenerator-transform@npm:0.15.0" +"regenerator-transform@npm:^0.14.2": + version: 0.14.5 + resolution: "regenerator-transform@npm:0.14.5" dependencies: "@babel/runtime": ^7.8.4 - checksum: 86e54849ab1167618d28bb56d214c52a983daf29b0d115c976d79840511420049b6b42c9ebdf187defa8e7129bdd74b6dd266420d0d3868c9fa7f793b5d15d49 + checksum: a467a3b652b4ec26ff964e9c5f1817523a73fc44cb928b8d21ff11aebeac5d10a84d297fe02cea9f282bcec81a0b0d562237da69ef0f40a0160b30a4fa98bc94 languageName: node linkType: hard @@ -9833,13 +9709,12 @@ __metadata: linkType: hard "regexp.prototype.flags@npm:^1.2.0": - version: 1.4.3 - resolution: "regexp.prototype.flags@npm:1.4.3" + version: 1.4.1 + resolution: "regexp.prototype.flags@npm:1.4.1" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.3 - functions-have-names: ^1.2.2 - checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 + checksum: 77944a3ea5ae84f391fa80bff9babfedc47eadc9dc38e282b5fd746368fb787deec89c68ce3114195bf6b5782b160280a278b62d41ccc6e125afab1a7f816de8 languageName: node linkType: hard @@ -10083,7 +9958,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.5.x": +"rxjs@npm:6.5.x, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": version: 6.5.5 resolution: "rxjs@npm:6.5.5" dependencies: @@ -10092,7 +9967,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.6.7, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": +"rxjs@npm:6.6.7": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -10101,7 +9976,16 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": +"rxjs@npm:^7.2.0": + version: 7.5.4 + resolution: "rxjs@npm:7.5.4" + dependencies: + tslib: ^2.1.0 + checksum: 6f55f835f2543bc8214900f9e28b6320e6adc95875011fbca63e80a66eb18c9ff7cfdccb23b2180cbb6412762b98ed158c89fd51cb020799d127c66ea38c3c0e + languageName: node + linkType: hard + +"rxjs@npm:^7.5.5": version: 7.5.5 resolution: "rxjs@npm:7.5.5" dependencies: @@ -10110,14 +9994,14 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": +"safe-buffer@npm:5.1.2, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -10236,11 +10120,11 @@ __metadata: linkType: hard "selfsigned@npm:^2.0.0": - version: 2.0.1 - resolution: "selfsigned@npm:2.0.1" + version: 2.0.0 + resolution: "selfsigned@npm:2.0.0" dependencies: - node-forge: ^1 - checksum: 864e65c2f31ca877bce3ccdaa3bdef5e1e992b63b2a03641e00c24cd305bf2acce093431d1fed2e5ae9f526558db4be5e90baa2b3474c0428fcf7e25cc86ac93 + node-forge: ^1.2.0 + checksum: 43fca39a5aded2a8e97c1756af74c049a9dde12d47d302820f7d507d25c2ad7da4b04bc439a36620d63b4c0149bcf34ae7a729f978bf3b1bf48859c36ae34cee languageName: node linkType: hard @@ -10273,7 +10157,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.3.5": +"semver@npm:7.3.5, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": version: 7.3.5 resolution: "semver@npm:7.3.5" dependencies: @@ -10293,35 +10177,24 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": - version: 7.3.7 - resolution: "semver@npm:7.3.7" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 - languageName: node - linkType: hard - -"send@npm:0.18.0": - version: 0.18.0 - resolution: "send@npm:0.18.0" +"send@npm:0.17.2": + version: 0.17.2 + resolution: "send@npm:0.17.2" dependencies: debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 + depd: ~1.1.2 + destroy: ~1.0.4 encodeurl: ~1.0.2 escape-html: ~1.0.3 etag: ~1.8.1 fresh: 0.5.2 - http-errors: 2.0.0 + http-errors: 1.8.1 mime: 1.6.0 ms: 2.1.3 - on-finished: 2.4.1 + on-finished: ~2.3.0 range-parser: ~1.2.1 - statuses: 2.0.1 - checksum: 74fc07ebb58566b87b078ec63e5a3e41ecd987e4272ba67b7467e86c6ad51bc6b0b0154133b6d8b08a2ddda360464f71382f7ef864700f34844a76c8027817a8 + statuses: ~1.5.0 + checksum: c28f36deb4ccba9b8d6e6a1e472b8e7c40a1f51575bdf8f67303568cc9e71131faa3adc36fdb72611616ccad1584358bbe4c3ebf419e663ecc5de868ad3d3f03 languageName: node linkType: hard @@ -10349,15 +10222,15 @@ __metadata: languageName: node linkType: hard -"serve-static@npm:1.15.0": - version: 1.15.0 - resolution: "serve-static@npm:1.15.0" +"serve-static@npm:1.14.2": + version: 1.14.2 + resolution: "serve-static@npm:1.14.2" dependencies: encodeurl: ~1.0.2 escape-html: ~1.0.3 parseurl: ~1.3.3 - send: 0.18.0 - checksum: af57fc13be40d90a12562e98c0b7855cf6e8bd4c107fe9a45c212bf023058d54a1871b1c89511c3958f70626fff47faeb795f5d83f8cf88514dbaeb2b724464d + send: 0.17.2 + checksum: d97f3183b1dfcd8ce9c0e37e18e87fd31147ed6c8ee0b2c3a089d795e44ee851ca5061db01574f806d54f4e4b70bc694d9ca64578653514e04a28cbc97a1de05 languageName: node linkType: hard @@ -10368,10 +10241,10 @@ __metadata: languageName: node linkType: hard -"setimmediate@npm:^1.0.5": - version: 1.0.5 - resolution: "setimmediate@npm:1.0.5" - checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd +"set-immediate-shim@npm:~1.0.1": + version: 1.0.1 + resolution: "set-immediate-shim@npm:1.0.1" + checksum: 5085c84039d1e5eee73d2bf48ce765fcec76159021d0cc7b40e23bcdf62cb6d450ffb781e3c62c1118425242c48eae96df712cba0a20a437e86b0d4a15d51a11 languageName: node linkType: hard @@ -10414,18 +10287,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.4": - version: 1.0.4 - resolution: "side-channel@npm:1.0.4" - dependencies: - call-bind: ^1.0.0 - get-intrinsic: ^1.0.2 - object-inspect: ^1.9.0 - checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 - languageName: node - linkType: hard - -"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -10485,10 +10347,10 @@ __metadata: languageName: node linkType: hard -"socket.io-adapter@npm:~2.4.0": - version: 2.4.0 - resolution: "socket.io-adapter@npm:2.4.0" - checksum: a84639946dce13547b95f6e09fe167cdcd5d80941afc2e46790cc23384e0fd3c901e690ecc9bdd600939ce6292261ee15094a0b486f797ed621cfc8783d87a0c +"socket.io-adapter@npm:~2.3.3": + version: 2.3.3 + resolution: "socket.io-adapter@npm:2.3.3" + checksum: 73890e0a33e48a9e4be83e5fa2b8ea9728d2a35ae2fed373cad4d6744c6512c0e1c735e7820df9821e58c4738dc355bdaec5aae30bc56f4d6a41d999596d0c82 languageName: node linkType: hard @@ -10503,17 +10365,17 @@ __metadata: languageName: node linkType: hard -"socket.io@npm:^4.4.1": - version: 4.5.1 - resolution: "socket.io@npm:4.5.1" +"socket.io@npm:^4.2.0": + version: 4.4.1 + resolution: "socket.io@npm:4.4.1" dependencies: accepts: ~1.3.4 base64id: ~2.0.0 debug: ~4.3.2 - engine.io: ~6.2.0 - socket.io-adapter: ~2.4.0 + engine.io: ~6.1.0 + socket.io-adapter: ~2.3.3 socket.io-parser: ~4.0.4 - checksum: 86afd6dcce0c96de85b20a0e37fa4a21e2e96bd6e36d2518acfad37597bcb5208feafbbac20cd34ee4b9356d40418a43938bcf4a206ba693ba3c771ffcef724f + checksum: a559ae52359f1ca3ce5a347368cf985c72259e1ab1bf2bf769ca0add5db34e2a86f4e183a58f37f32676ec482c71fedb7b08d873dc31cf581f5ba0797a8382fe languageName: node linkType: hard @@ -10529,17 +10391,28 @@ __metadata: linkType: hard "socks-proxy-agent@npm:^6.0.0, socks-proxy-agent@npm:^6.1.1": - version: 6.2.0 - resolution: "socks-proxy-agent@npm:6.2.0" + version: 6.1.1 + resolution: "socks-proxy-agent@npm:6.1.1" + dependencies: + agent-base: ^6.0.2 + debug: ^4.3.1 + socks: ^2.6.1 + checksum: 9a8a4f791bba0060315cf7291ca6f9db37d6fc280fd0860d73d8887d3efe4c22e823aa25a8d5375f6079279f8dc91b50c075345179bf832bfe3c7c26d3582e3c + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "socks-proxy-agent@npm:7.0.0" dependencies: agent-base: ^6.0.2 debug: ^4.3.3 socks: ^2.6.2 - checksum: 6723fd64fb50334e2b340fd0a80fd8488ffc5bc43d85b7cf1d25612044f814dd7d6ea417fd47602159941236f7f4bd15669fa5d7e1f852598a31288e1a43967b + checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 languageName: node linkType: hard -"socks@npm:^2.6.2": +"socks@npm:^2.6.1": version: 2.6.2 resolution: "socks@npm:2.6.2" dependencies: @@ -10549,6 +10422,16 @@ __metadata: languageName: node linkType: hard +"socks@npm:^2.6.2": + version: 2.7.1 + resolution: "socks@npm:2.7.1" + dependencies: + ip: ^2.0.0 + smart-buffer: ^4.2.0 + checksum: 259d9e3e8e1c9809a7f5c32238c3d4d2a36b39b83851d0f573bfde5f21c4b1288417ce1af06af1452569cd1eb0841169afd4998f0e04ba04656f6b7f0e46d748 + languageName: node + linkType: hard + "source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2": version: 1.0.2 resolution: "source-map-js@npm:1.0.2" @@ -10619,15 +10502,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:~0.8.0-beta.0": - version: 0.8.0-beta.0 - resolution: "source-map@npm:0.8.0-beta.0" - dependencies: - whatwg-url: ^7.0.0 - checksum: e94169be6461ab0ac0913313ad1719a14c60d402bd22b0ad96f4a6cffd79130d91ab5df0a5336a326b04d2df131c1409f563c9dc0d21a6ca6239a44b6c8dbd92 - languageName: node - linkType: hard - "sourcemap-codec@npm:1.4.8, sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" @@ -10751,28 +10625,21 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": - version: 2.0.1 - resolution: "statuses@npm:2.0.1" - checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb - languageName: node - linkType: hard - -"statuses@npm:>= 1.4.0 < 2, statuses@npm:~1.5.0": +"statuses@npm:>= 1.4.0 < 2, statuses@npm:>= 1.5.0 < 2, statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c languageName: node linkType: hard -"streamroller@npm:^3.1.1": - version: 3.1.1 - resolution: "streamroller@npm:3.1.1" +"streamroller@npm:^3.0.2": + version: 3.0.2 + resolution: "streamroller@npm:3.0.2" dependencies: - date-format: ^4.0.10 - debug: ^4.3.4 - fs-extra: ^10.1.0 - checksum: 8a30f0940ffcd9eca3f0a06a938adb3a7ba08fc1d1e694570ce3a922054735badf27de4539fb5b26c90f44368ba044a00203a7540b6955a481c8de83e9c00bb4 + date-format: ^4.0.3 + debug: ^4.1.1 + fs-extra: ^10.0.0 + checksum: 1f323824f0e81cc085c24f33addfd8ef00d0c15aafee520a8cf207ca6e2dc674fd852528c7b4450cc87f4335d1269ed18b3f0188853d45d7f0912c9a205d1fc1 languageName: node linkType: hard @@ -11266,7 +11133,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.2": +"tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.2": version: 6.1.11 resolution: "tar@npm:6.1.11" dependencies: @@ -11280,6 +11147,20 @@ __metadata: languageName: node linkType: hard +"tar@npm:^6.1.11": + version: 6.1.13 + resolution: "tar@npm:6.1.13" + dependencies: + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + minipass: ^4.0.0 + minizlib: ^2.1.1 + mkdirp: ^1.0.3 + yallist: ^4.0.0 + checksum: 8a278bed123aa9f53549b256a36b719e317c8b96fe86a63406f3c62887f78267cea9b22dc6f7007009738509800d4a4dccc444abd71d762287c90f35b002eb1c + languageName: node + linkType: hard + "terser-webpack-plugin@npm:^5.1.3": version: 5.3.1 resolution: "terser-webpack-plugin@npm:5.3.1" @@ -11302,31 +11183,21 @@ __metadata: languageName: node linkType: hard -"terser@npm:5.11.0": - version: 5.11.0 - resolution: "terser@npm:5.11.0" +"terser@npm:5.10.0, terser@npm:^5.7.2": + version: 5.10.0 + resolution: "terser@npm:5.10.0" dependencies: - acorn: ^8.5.0 commander: ^2.20.0 source-map: ~0.7.2 source-map-support: ~0.5.20 - bin: - terser: bin/terser - checksum: cc72b7a0e87421b5a6ef3f8a3c86ef251f6e7f8d6327b83c63045b8991a041cc4a42ea64e07701128e1786489902c8c44b5904056b0f12ceedb52924d493db04 - languageName: node - linkType: hard - -"terser@npm:^5.7.2": - version: 5.13.1 - resolution: "terser@npm:5.13.1" - dependencies: + peerDependencies: acorn: ^8.5.0 - commander: ^2.20.0 - source-map: ~0.8.0-beta.0 - source-map-support: ~0.5.20 + peerDependenciesMeta: + acorn: + optional: true bin: terser: bin/terser - checksum: 0b1f5043cf5c3973005fe2ae4ff3be82511c336a6430599dacd4e2acf77c974d4474b0f1eec4823977c1f33823147e736ff712ca8e098bee3db25946480fa29d + checksum: 1080faeb6d5cd155bb39d9cc41d20a590eafc9869560d5285f255f6858604dcd135311e344188a106f87fedb12d096ad3799cfc2e65acd470b85d468b1c7bd4c languageName: node linkType: hard @@ -11422,15 +11293,6 @@ __metadata: languageName: node linkType: hard -"tr46@npm:^1.0.1": - version: 1.0.1 - resolution: "tr46@npm:1.0.1" - dependencies: - punycode: ^2.1.0 - checksum: 96d4ed46bc161db75dbf9247a236ea0bfcaf5758baae6749e92afab0bc5a09cb59af21788ede7e55080f2bf02dce3e4a8f2a484cc45164e29f4b5e68f7cbcc1a - languageName: node - linkType: hard - "tree-kill@npm:1.2.2": version: 1.2.2 resolution: "tree-kill@npm:1.2.2" @@ -11476,14 +11338,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.0.x": +"tslib@npm:2.0.x, tslib@npm:^2.0.0": version: 2.0.3 resolution: "tslib@npm:2.0.3" checksum: 00fcdd1f9995c9f8eb6a4a1ad03f55bc95946321b7f55434182dddac259d4e095fedf78a84f73b6e32dd3f881d9281f09cb583123d3159ed4bdac9ad7393ef8b languageName: node linkType: hard -"tslib@npm:2.3.1": +"tslib@npm:2.3.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0": version: 2.3.1 resolution: "tslib@npm:2.3.1" checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 @@ -11497,7 +11359,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": +"tslib@npm:^2.4.0": version: 2.4.0 resolution: "tslib@npm:2.4.0" checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 @@ -11586,13 +11448,13 @@ __metadata: linkType: hard "typed-assert@npm:^1.0.8": - version: 1.0.9 - resolution: "typed-assert@npm:1.0.9" - checksum: 79351bd3ea184a552bf55a77bd3012f128741c841ed718d054c5abbbc8925362aa033ae2cdcc79e1f445a15112447c8a95a08ddf7ff8aeb04f805e92187f77c1 + version: 1.0.8 + resolution: "typed-assert@npm:1.0.8" + checksum: bed460f76da5b142da561b75a10164c3a226ac99353fa503ad1874aa375b51823088c72406148c8cbfb277ca2a416fbbd250689be84b2734944be101e79f4117 languageName: node linkType: hard -"typescript@npm:4.5.x": +"typescript@npm:4.5.x, typescript@npm:^4.5.4": version: 4.5.5 resolution: "typescript@npm:4.5.5" bin: @@ -11602,17 +11464,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.5.4": - version: 4.6.4 - resolution: "typescript@npm:4.6.4" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: e7bfcc39cd4571a63a54e5ea21f16b8445268b9900bf55aee0e02ad981be576acc140eba24f1af5e3c1457767c96cea6d12861768fb386cf3ffb34013718631a - languageName: node - linkType: hard - -"typescript@patch:typescript@4.5.x#~builtin": +"typescript@patch:typescript@4.5.x#~builtin, typescript@patch:typescript@^4.5.4#~builtin": version: 4.5.5 resolution: "typescript@patch:typescript@npm%3A4.5.5#~builtin::version=4.5.5&hash=bda367" bin: @@ -11622,20 +11474,10 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^4.5.4#~builtin": - version: 4.6.4 - resolution: "typescript@patch:typescript@npm%3A4.6.4#~builtin::version=4.6.4&hash=bda367" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 1cb434fbc637d347be90e3a0c6cd05e33c38f941713c8786d3031faf1842c2c148ba91d2fac01e7276b0ae3249b8633f1660e32686cc7a8c6a8fd5361dc52c66 - languageName: node - linkType: hard - "ua-parser-js@npm:^0.7.30": - version: 0.7.33 - resolution: "ua-parser-js@npm:0.7.33" - checksum: 1510e9ec26fcaf0d8c6ae8f1078a8230e8816f083e1b5f453ea19d06b8ef2b8a596601c92148fd41899e8b3e5f83fa69c42332bd5729b931a721040339831696 + version: 0.7.31 + resolution: "ua-parser-js@npm:0.7.31" + checksum: e2f8324a83d1715601576af85b2b6c03890699aaa7272950fc77ea925c70c5e4f75060ae147dc92124e49f7f0e3d6dd2b0a91e7f40d267e92df8894be967ba8b languageName: node linkType: hard @@ -11679,6 +11521,15 @@ __metadata: languageName: node linkType: hard +"unique-filename@npm:^2.0.0": + version: 2.0.1 + resolution: "unique-filename@npm:2.0.1" + dependencies: + unique-slug: ^3.0.0 + checksum: 807acf3381aff319086b64dc7125a9a37c09c44af7620bd4f7f3247fcd5565660ac12d8b80534dcbfd067e6fe88a67e621386dd796a8af828d1337a8420a255f + languageName: node + linkType: hard + "unique-slug@npm:^2.0.0": version: 2.0.2 resolution: "unique-slug@npm:2.0.2" @@ -11688,6 +11539,15 @@ __metadata: languageName: node linkType: hard +"unique-slug@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-slug@npm:3.0.0" + dependencies: + imurmurhash: ^0.1.4 + checksum: 49f8d915ba7f0101801b922062ee46b7953256c93ceca74303bd8e6413ae10aa7e8216556b54dc5382895e8221d04f1efaf75f945c2e4a515b4139f77aa6640c + languageName: node + linkType: hard + "universalify@npm:^2.0.0": version: 2.0.0 resolution: "universalify@npm:2.0.0" @@ -11870,14 +11730,7 @@ __metadata: languageName: node linkType: hard -"webidl-conversions@npm:^4.0.2": - version: 4.0.2 - resolution: "webidl-conversions@npm:4.0.2" - checksum: c93d8dfe908a0140a4ae9c0ebc87a33805b416a33ee638a605b551523eec94a9632165e54632f6d57a39c5f948c4bab10e0e066525e9a4b87a79f0d04fbca374 - languageName: node - linkType: hard - -"webpack-dev-middleware@npm:5.3.0": +"webpack-dev-middleware@npm:5.3.0, webpack-dev-middleware@npm:^5.3.0": version: 5.3.0 resolution: "webpack-dev-middleware@npm:5.3.0" dependencies: @@ -11892,21 +11745,6 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:^5.3.0": - version: 5.3.3 - resolution: "webpack-dev-middleware@npm:5.3.3" - dependencies: - colorette: ^2.0.10 - memfs: ^3.4.3 - mime-types: ^2.1.31 - range-parser: ^1.2.1 - schema-utils: ^4.0.0 - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - checksum: dd332cc6da61222c43d25e5a2155e23147b777ff32fdf1f1a0a8777020c072fbcef7756360ce2a13939c3f534c06b4992a4d659318c4a7fe2c0530b52a8a6621 - languageName: node - linkType: hard - "webpack-dev-server@npm:4.7.3": version: 4.7.3 resolution: "webpack-dev-server@npm:4.7.3" @@ -12038,17 +11876,6 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^7.0.0": - version: 7.1.0 - resolution: "whatwg-url@npm:7.1.0" - dependencies: - lodash.sortby: ^4.7.0 - tr46: ^1.0.1 - webidl-conversions: ^4.0.2 - checksum: fecb07c87290b47d2ec2fb6d6ca26daad3c9e211e0e531dd7566e7ff95b5b3525a57d4f32640ad4adf057717e0c215731db842ad761e61d947e81010e05cf5fd - languageName: node - linkType: hard - "which-module@npm:^2.0.0": version: 2.0.0 resolution: "which-module@npm:2.0.0" @@ -12078,7 +11905,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": +"wide-align@npm:^1.1.2": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: @@ -12140,22 +11967,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.1.0": - version: 8.6.0 - resolution: "ws@npm:8.6.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: e2fca82059f1e087d0c78e2f37135e1b8332bc804fce46f83c2db1cb8571685abf9d2c99b964bab3752536ad90b99b46fb8d1428899aed3e560684ab4641bffd - languageName: node - linkType: hard - -"ws@npm:~8.2.3": +"ws@npm:^8.1.0, ws@npm:~8.2.3": version: 8.2.3 resolution: "ws@npm:8.2.3" peerDependencies: @@ -12303,10 +12115,10 @@ __metadata: linkType: hard "zone.js@npm:~0.11.4": - version: 0.11.5 - resolution: "zone.js@npm:0.11.5" + version: 0.11.4 + resolution: "zone.js@npm:0.11.4" dependencies: - tslib: ^2.3.0 - checksum: 7dba3af83cc68e881e8a5cc549e4a7bab5f32c5289838f14eb251871816210a39ed8bcd89030593b56d469b4aa8271d644e71ad408fcadd4ccd61b8c7203f2ef + tslib: ^2.0.0 + checksum: 20c3e39898019de4747a434a29ed528e5d730a674570c3e72775a57f9d57dba812e70d67c3932ff54e95db9b778f06a9b18119c5184dfd9552d3622544a6729f languageName: node linkType: hard From 428ea0f749ca01a072a8151f97a7e8211aadcfb6 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 8 Feb 2023 16:46:18 +0100 Subject: [PATCH 099/498] Small fix in CurrentTaskScoreHandler --- .../scores/CurrentTaskScoreHandler.kt | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 1b7ff7b7b..80d61f461 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -25,7 +25,8 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 2.0.0 */ -class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler { +class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), + GetRestHandler { override val route = "score/evaluation/{evaluationId}/current" @@ -43,20 +44,30 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiScoreOverview { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not scores for a current task.", ctx) + override fun doGet(ctx: Context): ApiScoreOverview = this.store.transactional(true) { + + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException( + 400, + "Specified evaluation ${ctx.evaluationId()} does not scores for a current task.", + ctx + ) if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } - return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, manager) - val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No active task run in evaluation ${ctx.evaluationId()}.", ctx) - val scores = scorer.teamScoreMap() - ApiScoreOverview("task", - manager.currentTaskTemplate(rac).taskGroup.name, - manager.template.teams.asSequence().map { team -> ApiScore(team.id, scores[team.id] ?: 0.0) }.toList() - ) - } + + val rac = RunActionContext.runActionContext(ctx, manager) + val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException( + 404, + "No active task run in evaluation ${ctx.evaluationId()}.", + ctx + ) + val scores = scorer.teamScoreMap() + return@transactional ApiScoreOverview( + "task", + manager.currentTaskTemplate(rac).taskGroup.name, + manager.template.teams.asSequence().map { team -> ApiScore(team.id, scores[team.id] ?: 0.0) }.toList() + ) } + } From 3da39e4ef9cdf940489908530d4fc278652cc78c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 8 Feb 2023 17:15:12 +0100 Subject: [PATCH 100/498] Addresed #387 auto-auth upon reload --- .../app/services/session/authentication.sevice.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index 2a6932a02..61e18eddc 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -17,11 +17,18 @@ export class AuthenticationService { * Constructor */ constructor(@Inject(UserService) private userService: UserService) { - this.userService.getApiV2UserSession() + // TODO there *must* be a more elegant OpenAPI way to do this ?!? + this.userService.getApiV2UserSession(null, null, null, {httpHeaderAccept: 'text/plain'}) .pipe( - catchError((e) => of(null)), + catchError((e) => { + console.log("error", e); + return of(null) + }), filter((s) => s != null), - flatMap((s) => this.userService.getApiV2User()), + flatMap((s) => { + console.log('before getApiV2User') + return this.userService.getApiV2User() + }), filter((u) => u != null) ) .subscribe((u) => { From 1f0ea9c1b4c43b7ad75b40d5d34ad545f3d84b71 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 8 Feb 2023 17:15:56 +0100 Subject: [PATCH 101/498] Addressed #310: Started to re-implement new builder ui --- .../template/builder/builder.component.html | 1 + .../template/builder/builder.component.scss | 0 .../app/template/builder/builder.component.ts | 61 ++++++++++++ .../abstract-template-builder.component.ts | 23 +++++ .../information/information.component.html | 16 ++++ .../information/information.component.scss | 0 .../information/information.component.ts | 47 ++++++++++ .../general-info-tab.component.html | 1 + .../general-info-tab.component.scss | 0 .../general-info-tab.component.ts | 15 +++ .../builder/template-builder.service.ts | 94 +++++++++++++++++++ frontend/src/app/template/template.module.ts | 25 +++++ 12 files changed, 283 insertions(+) create mode 100644 frontend/src/app/template/builder/builder.component.html create mode 100644 frontend/src/app/template/builder/builder.component.scss create mode 100644 frontend/src/app/template/builder/builder.component.ts create mode 100644 frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts create mode 100644 frontend/src/app/template/builder/components/information/information.component.html create mode 100644 frontend/src/app/template/builder/components/information/information.component.scss create mode 100644 frontend/src/app/template/builder/components/information/information.component.ts create mode 100644 frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html create mode 100644 frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.scss create mode 100644 frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts create mode 100644 frontend/src/app/template/builder/template-builder.service.ts create mode 100644 frontend/src/app/template/template.module.ts diff --git a/frontend/src/app/template/builder/builder.component.html b/frontend/src/app/template/builder/builder.component.html new file mode 100644 index 000000000..af41d55be --- /dev/null +++ b/frontend/src/app/template/builder/builder.component.html @@ -0,0 +1 @@ +

builder works!

diff --git a/frontend/src/app/template/builder/builder.component.scss b/frontend/src/app/template/builder/builder.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/builder/builder.component.ts b/frontend/src/app/template/builder/builder.component.ts new file mode 100644 index 000000000..d52068e23 --- /dev/null +++ b/frontend/src/app/template/builder/builder.component.ts @@ -0,0 +1,61 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {DeactivationGuarded} from '../../services/can-deactivate.guard'; +import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; +import {Observable, Subscription} from 'rxjs'; +import {TemplateBuilderService} from './template-builder.service'; +import {DownloadService, TemplateService, UserService} from '../../../../openapi'; +import {MatSnackBar} from '@angular/material/snack-bar'; +import {AbstractTemplateBuilderComponent} from './components/abstract-template-builder/abstract-template-builder.component'; + +@Component({ + selector: 'app-builder', + templateUrl: './builder.component.html', + styleUrls: ['./builder.component.scss'] +}) +export class BuilderComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy, DeactivationGuarded { + onChange() { + } + + routeSub: Subscription; + changeSub: Subscription; + + + constructor( + private templateService: TemplateService, + private userService: UserService, + private downloadService: DownloadService, + private route: ActivatedRoute, + private router: Router, + private snackBar: MatSnackBar, + builderService: TemplateBuilderService + ) { + super(builderService); + } + + canDeactivate(nextState?: RouterStateSnapshot): Observable | Promise | boolean { + return undefined; + } + + ngOnDestroy(): void { + this.onDestroy(); + this.changeSub.unsubscribe(); + this.routeSub.unsubscribe(); + } + + ngOnInit(): void { + this.routeSub = this.route.params.subscribe( (p) => { + this.templateService.getApiV2TemplateByTemplateId(p.templateId).subscribe((t) => { + /* initialise from route */ + this.builderService.initialise(t); + }, + (r) => { + this.snackBar.open(`Error: ${r?.error?.description}`, null,{duration: 5000}); + }); + }); + } + + fileProvider = () => { + return this.builderService.getTemplate()?.name ? this.builderService.getTemplate().name : 'evaluation-template-download.json' + } + +} diff --git a/frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts b/frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts new file mode 100644 index 000000000..36699feb1 --- /dev/null +++ b/frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import {ApiEvaluationTemplate} from '../../../../../../openapi'; +import {Subscription} from 'rxjs'; +import {TemplateBuilderService} from '../../template-builder.service'; + +export abstract class AbstractTemplateBuilderComponent { + + subscription: Subscription; + protected constructor(protected builderService: TemplateBuilderService) { } + + onInit(): void { + this.subscription = this.builderService.templateAsObservable().subscribe((t) => { + this.onChange(); + }) + } + + onDestroy(){ + this.subscription?.unsubscribe(); + } + + abstract onChange(); + +} diff --git a/frontend/src/app/template/builder/components/information/information.component.html b/frontend/src/app/template/builder/components/information/information.component.html new file mode 100644 index 000000000..10afb6241 --- /dev/null +++ b/frontend/src/app/template/builder/components/information/information.component.html @@ -0,0 +1,16 @@ +
+
+

+ + Name + + +

+

+ + Description + + +

+
+
diff --git a/frontend/src/app/template/builder/components/information/information.component.scss b/frontend/src/app/template/builder/components/information/information.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/builder/components/information/information.component.ts b/frontend/src/app/template/builder/components/information/information.component.ts new file mode 100644 index 000000000..27ff913f6 --- /dev/null +++ b/frontend/src/app/template/builder/components/information/information.component.ts @@ -0,0 +1,47 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {AbstractTemplateBuilderComponent} from '../abstract-template-builder/abstract-template-builder.component'; +import {ActivatedRoute} from '@angular/router'; +import {TemplateService} from '../../../../../../openapi'; +import {MatSnackBar} from '@angular/material/snack-bar'; +import {TemplateBuilderService} from '../../template-builder.service'; +import {Subscription} from 'rxjs'; +import {FormControl, FormGroup} from '@angular/forms'; + +@Component({ + selector: 'app-information', + templateUrl: './information.component.html', + styleUrls: ['./information.component.scss'] +}) +export class InformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + + form: FormGroup = new FormGroup({name: new FormControl(''), description: new FormControl('')}); + + private changeSub: Subscription; + + constructor( + private templateService: TemplateService, + builder: TemplateBuilderService + ) { + super(builder); + } + + ngOnInit(): void { + this.onInit(); + + this.changeSub = this.form.valueChanges.subscribe(() => { + this.builderService.markDirty(); + }); + } + + ngOnDestroy() { + this.onDestroy(); + this.changeSub.unsubscribe(); + } + + onChange() { + if(this.builderService.getTemplate()){ + this.form.get('name').setValue(this.builderService.getTemplate().name); + this.form.get('description').setValue(this.builderService.getTemplate().description); + } + } +} diff --git a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html new file mode 100644 index 000000000..c3aa46637 --- /dev/null +++ b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html @@ -0,0 +1 @@ + diff --git a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.scss b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts new file mode 100644 index 000000000..c1702363a --- /dev/null +++ b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-general-info-tab', + templateUrl: './general-info-tab.component.html', + styleUrls: ['./general-info-tab.component.scss'] +}) +export class GeneralInfoTabComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/template/builder/template-builder.service.ts b/frontend/src/app/template/builder/template-builder.service.ts new file mode 100644 index 000000000..b25a6e89c --- /dev/null +++ b/frontend/src/app/template/builder/template-builder.service.ts @@ -0,0 +1,94 @@ +import { Injectable } from '@angular/core'; +import {ApiEvaluationTemplate, ApiTaskGroup, ApiTaskTemplate, ApiTaskType} from '../../../../openapi'; +import {BehaviorSubject} from 'rxjs'; + + +/** + * A service to manage the currently actively edited evaluation template. + * The service provides the means to modify the template as needed and orchestrates updates. + */ +@Injectable({ + providedIn: 'root' +}) +export class TemplateBuilderService { + + // TODO might be worthwhile to be the sole provider for a template, i.e. fetching the template from the API would be handled here as well... + + private shouldLogDirtyChanges = true; + private templateSubject: BehaviorSubject = new BehaviorSubject(null); + private dirty = false; + + constructor() { + } + + public initialise(template: ApiEvaluationTemplate){ + this.templateSubject.next(template); + this.markDirty(); // might be a little early + } + + public getTemplate(){ + return this.templateSubject.getValue(); + } + + public templateAsObservable(){ + return this.templateSubject.asObservable(); + } + + public update(template: ApiEvaluationTemplate){ + this.templateSubject.next(template); + this.markDirty(); + } + + public updateTask(task: ApiTaskTemplate){ + const idx = this.getTemplate().tasks.findIndex(t => t.id === task.id); + this.templateSubject.getValue().tasks[idx] = task; + this.markDirty(); + } + + public hasTemplate(){ + return this.templateSubject != undefined; + } + + public clear(){ + this.unmarkDirty(); + this.templateSubject.unsubscribe(); + this.templateSubject = undefined; + } + + public checkDirty(){ + if(!this.dirty){ + return true; + } + return confirm('There are unsaved changes in this evaluation template that will be lost. Do you really want to proceed?') + } + + public markDirty(){ + this.dirty = true; + } + + public unmarkDirty(){ + this.dirty = false; + } + + public isDirty(){ + return this.dirty; + } + + public removeTaskType(taskType: ApiTaskType){ + this.getTemplate().taskTypes.splice(this.getTemplate().taskTypes.indexOf(taskType), 1); + this.getTemplate().taskGroups.filter((g) => g.type === taskType.name) + .forEach((g) => this.removeTaskGroup(g)); + } + + public removeTaskGroup(taskGroup: ApiTaskGroup){ + this.getTemplate().taskGroups.splice(this.getTemplate().taskGroups.indexOf(taskGroup), 1); + this.getTemplate().tasks.filter((t) => t.taskGroup === taskGroup.name) + .forEach((t) => this.removeTask(t)); + this.update(this.getTemplate()); + } + + public removeTask(task: ApiTaskTemplate){ + this.getTemplate().tasks.splice(this.getTemplate().tasks.indexOf(task), 1); + this.update(this.getTemplate()); + } +} diff --git a/frontend/src/app/template/template.module.ts b/frontend/src/app/template/template.module.ts new file mode 100644 index 000000000..1e486aa9f --- /dev/null +++ b/frontend/src/app/template/template.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { BuilderComponent } from './builder/builder.component'; +import {GeneralInfoTabComponent} from './builder/tabs/general-info-tab/general-info-tab.component'; +import { InformationComponent } from './builder/components/information/information.component'; +import { AbstractTemplateBuilderComponent } from './builder/components/abstract-template-builder/abstract-template-builder.component'; +import {MatInputModule} from '@angular/material/input'; +import {ReactiveFormsModule} from '@angular/forms'; + + + +@NgModule({ + declarations: [ + BuilderComponent, + GeneralInfoTabComponent, + InformationComponent, + AbstractTemplateBuilderComponent + ], + imports: [ + CommonModule, + MatInputModule, + ReactiveFormsModule + ] +}) +export class TemplateModule { } From c3129a3e3a27e7ee7b870feaa6bb8ab450ade059 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 8 Feb 2023 17:18:22 +0100 Subject: [PATCH 102/498] Fixed some naming inconsistencies in the cli --- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 4 +- .../dev/dres/api/cli/EvaluationCommand.kt | 462 +++++++++++------- .../dev/dres/api/cli/EvaluationRunCommand.kt | 400 --------------- .../dres/api/cli/EvaluationTemplateCommand.kt | 288 +++++++++++ 4 files changed, 577 insertions(+), 577 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 9eda4f797..09e25cb5e 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -41,10 +41,10 @@ object Cli { fun loop(store: TransientEntityStore, config: Config) { clikt = DRESBaseCommand().subcommands( - EvaluationCommand(store, config), + EvaluationTemplateCommand(store, config), UserCommand(store), MediaCollectionCommand(store), - EvaluationRunCommand(store), + EvaluationCommand(store), OpenApiCommand(), ExecutionCommand() ) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index b5fc73e24..77b7c51f0 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -4,15 +4,18 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.parameters.options.flag -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.required -import com.github.ajalt.clikt.parameters.options.validate +import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.path +import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter import com.jakewharton.picnic.table -import dev.dres.data.model.Config -import dev.dres.data.model.template.DbEvaluationTemplate -import dev.dres.utilities.FFmpegUtil +import dev.dres.data.model.template.task.DbTargetType +import dev.dres.data.model.run.* +import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.run.* +import dev.dres.utilities.extensions.toDateString import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import java.nio.file.Files @@ -21,268 +24,377 @@ import java.nio.file.Paths import java.nio.file.StandardOpenOption /** - * A collection of [CliktCommand]s for [DbEvaluationTemplate] management. + * A collection of [CliktCommand]s for [Run] management. * * @author Luca Rossetto * @author Ralph Gasser * @version 2.0.0 */ -class EvaluationCommand(private val store: TransientEntityStore, config: Config) : NoOpCliktCommand(name = "competition") { +class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCommand(name = "evaluation") { init { - this.subcommands( - Create(), + subcommands( + Ongoing(), List(), - Show(), - Prepare(), Delete(), - Copy(), Export(), - Import() + Reactivate(), + History(), + ResetSubmission(), + ExportJudgements() ) } override fun aliases(): Map> { return mapOf( - "ls" to listOf("list"), + "ls" to listOf("ongoing"), + "la" to listOf("list"), "remove" to listOf("delete"), - "drop" to listOf("delete"), - "add" to listOf("create") + "drop" to listOf("delete") ) } - /** The cache location [Paths]. */ - private val cacheLocation = Paths.get(config.cachePath, "tasks") - - abstract inner class AbstractEvaluationCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { - protected val id: String? by option("-i", "--id") - protected val name: String? by option("-c", "--competition") - } - /** - * [CliktCommand] to create a new [DbEvaluationTemplate]. + * Helper class that contains all information regarding a [RunManager]. */ - inner class Create : CliktCommand(name = "create", help = "Creates a new Competition") { - - private val name: String by option("-c", "--competition", help = "Name of the new Competition") - .required() - .validate { require(it.isNotEmpty()) { "Competition description must be non empty." } } - - private val description: String by option("-d", "--description", help = "Description of the new Competition") - .required() - .validate { require(it.isNotEmpty()) { "Competition description must be non empty." } } - - override fun run() { - val newCompetition = this@EvaluationCommand.store.transactional { - DbEvaluationTemplate.new { - this.name = this@Create.name - this.description = this@Create.description - } - } - println("New competition '$newCompetition' created with ID = ${newCompetition.id}.") - } - } + data class RunSummary(val id: String, val name: String, val description: String?, val task: String?) /** - * [CliktCommand] to delete a [DbEvaluationTemplate]. + * Lists all ongoing competitions runs for the current DRES instance. */ - inner class Delete : AbstractEvaluationCommand(name = "delete", help = "Deletes a competition") { + inner class Ongoing : + CliktCommand(name = "ongoing", help = "Lists all ongoing evaluations.") { + private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) override fun run() { - this@EvaluationCommand.store.transactional { - val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() - if (competition == null) { - println("Could not find competition to delete.") - return@transactional + if (RunExecutor.managers().isEmpty()) { + println("No evaluations are currently ongoing!") + return + } + if (this.plain) { + RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { + println("${RunSummary(it.id, it.name, it.template.description, it.currentTaskTemplate(RunActionContext.INTERNAL).name)} (${it.status})") } - competition.delete() + } else { + println( + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "type", "name", "description", "currentTask", "status") + } + body { + RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { + when(it) { + is InteractiveSynchronousRunManager -> row( + it.id, + "Synchronous", + it.name, + it.template.description, + it.currentTaskTemplate(RunActionContext.INTERNAL).name, + it.status + ) + is InteractiveAsynchronousRunManager -> row( + it.id, + "Asynchronous", + it.name, + it.template.description, + "N/A", + it.status + ) + else -> row("??", "??", "??", "??", "??", "??") + } + } + } + } + ) } - println("Successfully deleted competition description.") } } /** - * [CliktCommand] to copy a [DbEvaluationTemplate]. + * [CliktCommand] to list all evaluation runs (ongoing and past) for the current DRES instance. */ - inner class Copy : AbstractEvaluationCommand(name = "copy", help = "Copies a Competition") { - override fun run() { - this@EvaluationCommand.store.transactional { - val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() - if (competition == null) { - println("Could not find competition to copy.") - return@transactional + inner class List : CliktCommand(name = "list", help = "Lists all (ongoing and past) evaluations.") { + private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) + override fun run() = this@EvaluationCommand.store.transactional(true) { + val query = DbEvaluation.all().sortedBy(DbEvaluation::started) + if (this.plain) { + query.asSequence().forEach { + println( + "${ + RunSummary( + it.id, + it.name, + it.template.description, + if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { + it.tasks.firstOrNull()?.template?.name ?: "N/A" + } else { + "N/A" + } + ) + }" + ) } - - /* TODO: Copy competition. */ + } else { + println( + table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("id", "name", "description", "lastTask", "status", "start", "end") + } + body { + query.asSequence().forEach { + row( + it.id, + it.name, + it.template.description, + if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { + it.tasks.firstOrNull()?.template?.name ?: "N/A" + } else { + "N/A" + }, + it.started?.toDateString() ?: "-", + it.ended?.toDateString() ?: "-", + ) + } + } + } + ) } - println("Successfully copied competition.") } } /** - * [CliktCommand] to list all [DbEvaluationTemplate]s. + * [CliktCommand] to list past evaluation runs for the current DRES instance. */ - inner class List : CliktCommand(name = "list", help = "Lists an overview of all Competitions") { + inner class History : CliktCommand(name = "history", help = "Lists past evaluations.") { + // TODO fancification with table + override fun run() = this@EvaluationCommand.store.transactional(true) { - var no = 0 - println(table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 + val query = DbEvaluation.query(DbEvaluation::ended ne null).sortedBy(DbEvaluation::started) + query.asSequence().forEach { + println(it.name) + + println("Teams:") + it.template.teams.asSequence().forEach { team -> + println(team) } - header { - row("name", "id", "# teams", "# tasks", "description", ) + + println() + println("All Tasks:") + it.template.tasks.asSequence().forEach { task -> + println(task) } - body { - DbEvaluationTemplate.all().asSequence().forEach { c -> - row(c.name, c.id, c.teams.size(), c.tasks.size(), c.description).also { no++ } + + println() + println("Evaluated Tasks:") + it.tasks.asSequence().forEach { t -> + println(t.template) + if (t.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { + println("Submissions") + t.answerSets.asSequence().forEach { s -> println(s) } } } - }) - println("Listed $no competitions") + println() + } } } /** - * [CliktCommand] to show a specific [DbEvaluationTemplate]. + * Deletes a selected [DbEvaluation] for the current DRES instance. */ - inner class Show : AbstractEvaluationCommand(name = "show", help = "Shows details of a Competition") { - override fun run() = this@EvaluationCommand.store.transactional(true) { - val competition = DbEvaluationTemplate.query( - (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) - ).firstOrNull() + inner class Delete : CliktCommand(name = "delete", help = "Deletes an existing evaluation.", printHelpOnEmptyArgs = true) { + private val id: EvaluationId by option("-r", "--run").required() - if (competition == null) { - println("Could not find specified competition description.") - return@transactional + override fun run() { + if (RunExecutor.managers().any { it.id == id }) { + println("Evaluation with ID $id could not be deleted because it is still running! Terminate it and try again.") + return + } + + this@EvaluationCommand.store.transactional { + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation with ID ${this.id} could not be deleted because it doesn't exist!") + return@transactional + } + evaluation.delete() } + } + } + + /** + * [CliktCommand] to export a specific competition run as JSON. + */ + inner class Export : CliktCommand(name = "export", help = "Exports the selected evaluation to a JSON file.", printHelpOnEmptyArgs = true) { - println("${competition.name}: ${competition.description}") - println("Teams:") + /** [EvaluationId] of the [DbEvaluation] that should be exported. .*/ + private val id: EvaluationId by option("-i", "--id").required() - competition.teams.asSequence().forEach(::println) + /** Path to the file that should be created .*/ + private val path: Path by option("-o", "--output").path().required() - println() - println("Tasks:") + /** Flag indicating whether export should be pretty printed.*/ + private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) - competition.tasks.asSequence().forEach { _ -> - /* TODO: it.printOverview(System.out) */ - println() + override fun run() = this@EvaluationCommand.store.transactional(true) { + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional } - println() - } + val mapper = jacksonObjectMapper() + Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { + /*val writer = if (this.pretty) { + mapper.writerWithDefaultPrettyPrinter() + } else { + mapper.writer() + } + writer.writeValue(it, run)*/ + // TODO: Export must be re-conceived based on API classes. + } + println("Successfully exported evaluation ${this.id} to $path.") + } } /** - * [CliktCommand] to prepare a specific [DbEvaluationTemplate]. + * [CliktCommand] to reactivate an [DbEvaluation] that has previously ended. */ - inner class Prepare : AbstractEvaluationCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { + inner class Reactivate : CliktCommand(name = "reactivate", help = "Reactivates a previously ended evaluation", printHelpOnEmptyArgs = true) { - override fun run() = this@EvaluationCommand.store.transactional(true) { - val competition = DbEvaluationTemplate.query( - (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) - ).firstOrNull() + /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ + private val id: EvaluationId by option("-i", "--id").required() - if (competition == null) { - println("Could not find specified competition description.") + override fun run() = this@EvaluationCommand.store.transactional(true) { + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") return@transactional } - /* Fetch all videos in the competition. */ - val videos = competition.getAllVideos() - videos.forEach { item -> - val path = item.first.pathToOriginal() - if (!Files.exists(path)) { - println("ERROR: Media file $path not found for item ${item.first.name}") - return@forEach - } + if (evaluation.ended == null) { + println("Evaluation has not ended yet.") + return@transactional + } - println("Rendering ${item.first.name}$ at ${item.second}") - FFmpegUtil.extractSegment(item.first, item.second, this@EvaluationCommand.cacheLocation) + if (RunExecutor.managers().any { it.id == evaluation.id }) { + println("Evaluation is already active.") + return@transactional } + + /* Create run and reactivate. */ + val run = evaluation.toRun() + run.reactivate() + RunExecutor.schedule(run, this@EvaluationCommand.store) + println("Evaluation ${this.id} was reactivated.") } } - /** - * Exports a specific competition to a JSON file. + * [CliktCommand] to reset the status of [DbSubmission]s. */ - inner class Export : AbstractEvaluationCommand(name = "export", help = "Exports a competition description as JSON.") { + inner class ResetSubmission : CliktCommand(name = "resetSubmission", help = "Resets submission status to INDETERMINATE for selected submissions.", printHelpOnEmptyArgs = true) { - /** Path to the file that should be created .*/ - private val path: Path by option("-o", "--out", help = "The destination file for the competition.").path().required() + /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ + private val id: EvaluationId by option("-i", "--id").required() - /** Flag indicating whether export should be pretty printed.*/ - private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) + /** The [EvaluationId]s to reset. */ + private val submissionIds: kotlin.collections.List by option("-s", "--submissions", help = "IDs of the submissions to reset.").multiple() - override fun run() = this@EvaluationCommand.store.transactional(true) { - val competition = DbEvaluationTemplate.query( - (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) - ).firstOrNull() + /** The [EvaluationId]s to reset [DbSubmission]s for. */ + private val taskIds: kotlin.collections.List by option("-t", "--tasks", help = "IDs of the tasks to resetsubmissions for.").multiple() + + /** The names of the task groups to reset [DbSubmission]s for. */ + private val taskGroups: kotlin.collections.List by option("-g", "--groups", help = "Names of the task groups to reset submissions for.").multiple() - if (competition == null) { - println("Could not find specified competition description.") + override fun run() = this@EvaluationCommand.store.transactional { + /* Fetch competition run. */ + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") return@transactional } - val mapper = jacksonObjectMapper() - Files.newBufferedWriter(this.path, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE).use { - val writer = if (this.pretty) { - mapper.writerWithDefaultPrettyPrinter() + if (evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { + /* Prepare query. */ + var query = if (this.taskIds.isNotEmpty()) { + evaluation.tasks.filter { it.id.isIn(this@ResetSubmission.taskIds) }.flatMapDistinct { it.answerSets } + } else if (this.taskGroups.isNotEmpty()) { + evaluation.tasks.filter { it.template.taskGroup.name.isIn(this@ResetSubmission.taskGroups) }.flatMapDistinct { it.answerSets } } else { - mapper.writer() + evaluation.tasks.flatMapDistinct { it.answerSets } + } + + if (this.submissionIds.isNotEmpty()) { + query = query.filter { it.id.isIn(this@ResetSubmission.submissionIds) } + } + + var affected = 0 + query.asSequence().forEach { + affected += 1 + it.status = DbVerdictStatus.INDETERMINATE } - writer.writeValue(it, competition) + + println("Successfully reset $affected} submissions.") + } else { + println("Operation not supported for run type") } - println("Successfully wrote competition '${competition.name}' (ID = ${competition.id}) to $path.") } } - /** - * Imports a competition from a JSON file. + * [CliktCommand] to export judgements made for relevant tasks as CSVs. */ - inner class Import : CliktCommand(name = "import", help = "Imports a competition description from JSON.") { + inner class ExportJudgements : CliktCommand(name = "exportJudgements", help = "Exports all judgements made for all relevant tasks of an evaluation as CSV", printHelpOnEmptyArgs = true) { + /** [EvaluationId] of the [DbEvaluation] for which judgements should be exported.*/ + private val id: EvaluationId by option("-r", "--run", help = "Id of the run").required() - /** Flag indicating whether a new competition should be created.*/ - private val new: Boolean by option("-n", "--new", help = "Flag indicating whether a new competition should be created.").flag("-u", "--update", default = true) + /** The [Path] to the output file. */ + private val path: Path by option("-o", "--output", help = "Path to the file the judgements are to be exported to.").convert { Paths.get(it) }.required() - /** Path to the file that should be imported.*/ - private val path: Path by option("-i", "--in", help = "The file to import the competition from.").path().required() + private val header = listOf("TaskId", "TaskName", "ItemId", "StartTime", "EndTime", "Status") - override fun run() { - /* TODO: Probably won't work this way. */ - /* Read competition from file */ - /*val reader = jacksonObjectMapper().readerFor(CompetitionDescription::class.java) - val competition = try { - Files.newBufferedReader(this.path).use { - val tree = reader.readTree(it) - if (tree.get("id") != null && (tree.get("description") == null || tree.get("description").isNull || tree.get("description").isTextual)) { - reader.readValue(tree) - } else if (tree.get("id") != null && tree.get("description") != null && tree.get("description").isObject) { - reader.readValue(tree["description"]) - } else { - null - } - } - } catch (e: Throwable) { - println("Could not import competition from $path: ${e.message}.") - return + override fun run() = this@EvaluationCommand.store.transactional(true) { + /* Fetch competition run. */ + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional } - /* Create/update competition. */ - if (competition != null) { - if (this.new) { - val id = this@CompetitionCommand.competitions.append(competition) - println("Successfully imported new competition '${competition.name}' (ID = $id) from $path.") - } else { - this@CompetitionCommand.competitions.update(competition) - println("Successfully updated competition '${competition.name}' (ID = ${competition.id}) from $path.") + val tasks = evaluation.tasks.filter { + it.template.targets.filter { it.type.isIn(listOf(DbTargetType.JUDGEMENT,DbTargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() + } + + if (tasks.isEmpty) { + println("No judged tasks in run.") + return@transactional + } + + Files.newOutputStream(this.path, StandardOpenOption.WRITE).use { os -> + csvWriter().open(os) { + writeRow(header) + tasks.asSequence().forEach { task -> + val submittedItems = task.answerSets.asSequence().groupBy { s -> + Triple(s.answers.firstOrNull()?.item?.name?: "unknown", s.answers.firstOrNull()?.start, s.answers.firstOrNull()?.end) //TODO flatten? + } + submittedItems.entries.forEach { items -> + val status = items.value.map { s -> s.status }.toSet() //should only contain one element + writeRow( + listOf(task.id, task.template.name, items.key.first, items.key.second, items.key.third, status) + ) + } + } } - } else { - println("Could not import competition from $path: Unknown format.") - }*/ + } } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt deleted file mode 100644 index a15798394..000000000 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationRunCommand.kt +++ /dev/null @@ -1,400 +0,0 @@ -package dev.dres.api.cli - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.core.NoOpCliktCommand -import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.parameters.options.* -import com.github.ajalt.clikt.parameters.types.path -import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter -import com.jakewharton.picnic.table -import dev.dres.data.model.template.task.DbTargetType -import dev.dres.data.model.run.* -import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.run.* -import dev.dres.utilities.extensions.toDateString -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.* -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.nio.file.StandardOpenOption - -/** - * A collection of [CliktCommand]s for [Run] management. - * - * @author Luca Rossetto - * @author Ralph Gasser - * @version 2.0.0 - */ -class EvaluationRunCommand(internal val store: TransientEntityStore) : NoOpCliktCommand(name = "run") { - - init { - subcommands( - Ongoing(), - List(), - Delete(), - Export(), - Reactivate(), - History(), - ResetSubmission(), - ExportJudgements() - ) - } - - override fun aliases(): Map> { - return mapOf( - "ls" to listOf("ongoing"), - "la" to listOf("list"), - "remove" to listOf("delete"), - "drop" to listOf("delete") - ) - } - - /** - * Helper class that contains all information regarding a [RunManager]. - */ - data class RunSummary(val id: String, val name: String, val description: String?, val task: String?) - - /** - * Lists all ongoing competitions runs for the current DRES instance. - */ - inner class Ongoing : - CliktCommand(name = "ongoing", help = "Lists all ongoing evaluation runs.") { - private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) - override fun run() { - if (RunExecutor.managers().isEmpty()) { - println("No runs are currently ongoing!") - return - } - if (this.plain) { - RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - println("${RunSummary(it.id, it.name, it.template.description, it.currentTaskTemplate(RunActionContext.INTERNAL).name)} (${it.status})") - } - } else { - println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "type", "name", "description", "currentTask", "status") - } - body { - RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java).forEach { - when(it) { - is InteractiveSynchronousRunManager -> row( - it.id, - "Synchronous", - it.name, - it.template.description, - it.currentTaskTemplate(RunActionContext.INTERNAL).name, - it.status - ) - is InteractiveAsynchronousRunManager -> row( - it.id, - "Asynchronous", - it.name, - it.template.description, - "N/A", - it.status - ) - else -> row("??", "??", "??", "??", "??", "??") - } - } - } - } - ) - } - } - } - - /** - * [CliktCommand] to list all evaluation runs (ongoing and past) for the current DRES instance. - */ - inner class List : CliktCommand(name = "list", help = "Lists all (ongoing and past) competition runs.") { - private val plain by option("-p", "--plain", help = "Plain print. No fancy tables").flag(default = false) - override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val query = DbEvaluation.all().sortedBy(DbEvaluation::started) - if (this.plain) { - query.asSequence().forEach { - println( - "${ - RunSummary( - it.id, - it.name, - it.template.description, - if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { - it.tasks.firstOrNull()?.template?.name ?: "N/A" - } else { - "N/A" - } - ) - }" - ) - } - } else { - println( - table { - cellStyle { - border = true - paddingLeft = 1 - paddingRight = 1 - } - header { - row("id", "name", "description", "lastTask", "status", "start", "end") - } - body { - query.asSequence().forEach { - row( - it.id, - it.name, - it.template.description, - if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { - it.tasks.firstOrNull()?.template?.name ?: "N/A" - } else { - "N/A" - }, - it.started?.toDateString() ?: "-", - it.ended?.toDateString() ?: "-", - ) - } - } - } - ) - } - } - } - - /** - * [CliktCommand] to list past evaluation runs for the current DRES instance. - */ - inner class History : CliktCommand(name = "history", help = "Lists past evaluation runs.") { - // TODO fancification with table - - override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val query = DbEvaluation.query(DbEvaluation::ended ne null).sortedBy(DbEvaluation::started) - query.asSequence().forEach { - println(it.name) - - println("Teams:") - it.template.teams.asSequence().forEach { team -> - println(team) - } - - println() - println("All Tasks:") - it.template.tasks.asSequence().forEach { task -> - println(task) - } - - println() - println("Evaluated Tasks:") - it.tasks.asSequence().forEach { t -> - println(t.template) - if (t.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { - println("Submissions") - t.answerSets.asSequence().forEach { s -> println(s) } - } - } - println() - } - } - } - - /** - * Deletes a selected [DbEvaluation] for the current DRES instance. - */ - inner class Delete : CliktCommand(name = "delete", help = "Deletes an existing competition run.", printHelpOnEmptyArgs = true) { - private val id: EvaluationId by option("-r", "--run").required() - - override fun run() { - if (RunExecutor.managers().any { it.id == id }) { - println("Evaluation with ID $id could not be deleted because it is still running! Terminate it and try again.") - return - } - - this@EvaluationRunCommand.store.transactional { - val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() - if (evaluation == null) { - println("Evaluation with ID ${this.id} could not be deleted because it doesn't exist!") - return@transactional - } - evaluation.delete() - } - } - } - - /** - * [CliktCommand] to export a specific competition run as JSON. - */ - inner class Export : CliktCommand(name = "export", help = "Exports the selected competition run to a JSON file.", printHelpOnEmptyArgs = true) { - - /** [EvaluationId] of the [DbEvaluation] that should be exported. .*/ - private val id: EvaluationId by option("-i", "--id").required() - - /** Path to the file that should be created .*/ - private val path: Path by option("-o", "--output").path().required() - - /** Flag indicating whether export should be pretty printed.*/ - private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) - - override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() - if (evaluation == null) { - println("Evaluation ${this.id} does not seem to exist.") - return@transactional - } - - val mapper = jacksonObjectMapper() - Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { - /*val writer = if (this.pretty) { - mapper.writerWithDefaultPrettyPrinter() - } else { - mapper.writer() - } - writer.writeValue(it, run)*/ - // TODO: Export must be re-conceived based on API classes. - } - println("Successfully exported evaluation ${this.id} to $path.") - } - } - - /** - * [CliktCommand] to reactivate an [DbEvaluation] that has previously ended. - */ - inner class Reactivate : CliktCommand(name = "reactivate", help = "Reactivates a previously ended competition run", printHelpOnEmptyArgs = true) { - - /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ - private val id: EvaluationId by option("-i", "--id").required() - - override fun run() = this@EvaluationRunCommand.store.transactional(true) { - val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() - if (evaluation == null) { - println("Evaluation ${this.id} does not seem to exist.") - return@transactional - } - - if (evaluation.ended == null) { - println("Evaluation has not ended yet.") - return@transactional - } - - if (RunExecutor.managers().any { it.id == evaluation.id }) { - println("Evaluation is already active.") - return@transactional - } - - /* Create run and reactivate. */ - val run = evaluation.toRun() - run.reactivate() - RunExecutor.schedule(run, this@EvaluationRunCommand.store) - println("Evaluation ${this.id} was reactivated.") - } - } - - /** - * [CliktCommand] to reset the status of [DbSubmission]s. - */ - inner class ResetSubmission : CliktCommand(name = "resetSubmission", help = "Resets submission status to INDETERMINATE for selected submissions.", printHelpOnEmptyArgs = true) { - - /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ - private val id: EvaluationId by option("-i", "--id").required() - - /** The [EvaluationId]s to reset. */ - private val submissionIds: kotlin.collections.List by option("-s", "--submissions", help = "IDs of the submissions to reset.").multiple() - - /** The [EvaluationId]s to reset [DbSubmission]s for. */ - private val taskIds: kotlin.collections.List by option("-t", "--tasks", help = "IDs of the tasks to resetsubmissions for.").multiple() - - /** The names of the task groups to reset [DbSubmission]s for. */ - private val taskGroups: kotlin.collections.List by option("-g", "--groups", help = "Names of the task groups to reset submissions for.").multiple() - - override fun run() = this@EvaluationRunCommand.store.transactional { - /* Fetch competition run. */ - val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() - if (evaluation == null) { - println("Evaluation ${this.id} does not seem to exist.") - return@transactional - } - - if (evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { - /* Prepare query. */ - var query = if (this.taskIds.isNotEmpty()) { - evaluation.tasks.filter { it.id.isIn(this@ResetSubmission.taskIds) }.flatMapDistinct { it.answerSets } - } else if (this.taskGroups.isNotEmpty()) { - evaluation.tasks.filter { it.template.taskGroup.name.isIn(this@ResetSubmission.taskGroups) }.flatMapDistinct { it.answerSets } - } else { - evaluation.tasks.flatMapDistinct { it.answerSets } - } - - if (this.submissionIds.isNotEmpty()) { - query = query.filter { it.id.isIn(this@ResetSubmission.submissionIds) } - } - - var affected = 0 - query.asSequence().forEach { - affected += 1 - it.status = DbVerdictStatus.INDETERMINATE - } - - println("Successfully reset $affected} submissions.") - } else { - println("Operation not supported for run type") - } - } - } - - /** - * [CliktCommand] to export judgements made for relevant tasks as CSVs. - */ - inner class ExportJudgements : CliktCommand(name = "exportJudgements", help = "Exports all judgements made for all relevant tasks of an evaluation run as CSV", printHelpOnEmptyArgs = true) { - /** [EvaluationId] of the [DbEvaluation] for which judgements should be exported.*/ - private val id: EvaluationId by option("-r", "--run", help = "Id of the run").required() - - /** The [Path] to the output file. */ - private val path: Path by option("-o", "--output", help = "Path to the file the judgements are to be exported to.").convert { Paths.get(it) }.required() - - private val header = listOf("TaskId", "TaskName", "ItemId", "StartTime", "EndTime", "Status") - - override fun run() = this@EvaluationRunCommand.store.transactional(true) { - /* Fetch competition run. */ - val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() - if (evaluation == null) { - println("Evaluation ${this.id} does not seem to exist.") - return@transactional - } - - val tasks = evaluation.tasks.filter { - it.template.targets.filter { it.type.isIn(listOf(DbTargetType.JUDGEMENT,DbTargetType.JUDGEMENT_WITH_VOTE)) }.isNotEmpty() - } - - if (tasks.isEmpty) { - println("No judged tasks in run.") - return@transactional - } - - Files.newOutputStream(this.path, StandardOpenOption.WRITE).use { os -> - csvWriter().open(os) { - writeRow(header) - tasks.asSequence().forEach { task -> - val submittedItems = task.answerSets.asSequence().groupBy { s -> - Triple(s.answers.firstOrNull()?.item?.name?: "unknown", s.answers.firstOrNull()?.start, s.answers.firstOrNull()?.end) //TODO flatten? - } - submittedItems.entries.forEach { items -> - val status = items.value.map { s -> s.status }.toSet() //should only contain one element - writeRow( - listOf(task.id, task.template.name, items.key.first, items.key.second, items.key.third, status) - ) - } - } - } - } - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt new file mode 100644 index 000000000..9809a8488 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt @@ -0,0 +1,288 @@ +package dev.dres.api.cli + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.NoOpCliktCommand +import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required +import com.github.ajalt.clikt.parameters.options.validate +import com.github.ajalt.clikt.parameters.types.path +import com.jakewharton.picnic.table +import dev.dres.data.model.Config +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.utilities.FFmpegUtil +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardOpenOption + +/** + * A collection of [CliktCommand]s for [DbEvaluationTemplate] management. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +class EvaluationTemplateCommand(private val store: TransientEntityStore, config: Config) : NoOpCliktCommand(name = "template") { + + init { + this.subcommands( + Create(), + List(), + Show(), + Prepare(), + Delete(), + Copy(), + Export(), + Import() + ) + } + + override fun aliases(): Map> { + return mapOf( + "ls" to listOf("list"), + "remove" to listOf("delete"), + "drop" to listOf("delete"), + "add" to listOf("create") + ) + } + + /** The cache location [Paths]. */ + private val cacheLocation = Paths.get(config.cachePath, "tasks") + + abstract inner class AbstractEvaluationCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { + protected val id: String? by option("-i", "--id") + protected val name: String? by option("-t", "--template") + } + + /** + * [CliktCommand] to create a new [DbEvaluationTemplate]. + */ + inner class Create : CliktCommand(name = "create", help = "Creates a new Template") { + + private val name: String by option("-c", "--competition", help = "Name of the new Template") + .required() + .validate { require(it.isNotEmpty()) { "Template description must be non empty." } } + + private val description: String by option("-d", "--description", help = "Description of the new Template") + .required() + .validate { require(it.isNotEmpty()) { "Template description must be non empty." } } + + override fun run() { + val newCompetition = this@EvaluationTemplateCommand.store.transactional { + DbEvaluationTemplate.new { + this.name = this@Create.name + this.description = this@Create.description + }.toApi() + } + println("New template '$newCompetition' created with ID = ${newCompetition.id}.") + } + } + + /** + * [CliktCommand] to delete a [DbEvaluationTemplate]. + */ + inner class Delete : AbstractEvaluationCommand(name = "delete", help = "Deletes a template") { + override fun run() { + this@EvaluationTemplateCommand.store.transactional { + val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() + if (competition == null) { + println("Could not find template to delete.") + return@transactional + } + competition.delete() + } + println("Successfully deleted template.") + } + } + + /** + * [CliktCommand] to copy a [DbEvaluationTemplate]. + */ + inner class Copy : AbstractEvaluationCommand(name = "copy", help = "Copies a Template") { + override fun run() { + this@EvaluationTemplateCommand.store.transactional { + val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() + if (competition == null) { + println("Could not find template to copy.") + return@transactional + } + + /* TODO: Copy competition. */ + } + //println("Successfully copied template.") + } + } + + /** + * [CliktCommand] to list all [DbEvaluationTemplate]s. + */ + inner class List : CliktCommand(name = "list", help = "Lists an overview of all Templates") { + override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { + var no = 0 + println(table { + cellStyle { + border = true + paddingLeft = 1 + paddingRight = 1 + } + header { + row("name", "id", "# teams", "# tasks", "description", ) + } + body { + DbEvaluationTemplate.all().asSequence().forEach { c -> + row(c.name, c.id, c.teams.size(), c.tasks.size(), c.description).also { no++ } + } + } + }) + println("Listed $no templates") + } + } + + /** + * [CliktCommand] to show a specific [DbEvaluationTemplate]. + */ + inner class Show : AbstractEvaluationCommand(name = "show", help = "Shows details of a Template") { + override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { + val competition = DbEvaluationTemplate.query( + (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) + ).firstOrNull() + + if (competition == null) { + println("Could not find specified template.") + return@transactional + } + + println("${competition.name}: ${competition.description}") + println("Teams:") + + competition.teams.asSequence().forEach(::println) + + println() + println("Tasks:") + + competition.tasks.asSequence().forEach { _ -> + /* TODO: it.printOverview(System.out) */ + println() + } + println() + } + + } + + /** + * [CliktCommand] to prepare a specific [DbEvaluationTemplate]. + */ + inner class Prepare : AbstractEvaluationCommand(name = "prepare", help = "Checks the used Media Items and generates precomputed Queries") { + + override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { + val competition = DbEvaluationTemplate.query( + (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) + ).firstOrNull() + + if (competition == null) { + println("Could not find specified template.") + return@transactional + } + + /* Fetch all videos in the competition. */ + val videos = competition.getAllVideos() + videos.forEach { item -> + val path = item.first.pathToOriginal() + if (!Files.exists(path)) { + println("ERROR: Media file $path not found for item ${item.first.name}") + return@forEach + } + + println("Rendering ${item.first.name}$ at ${item.second}") + FFmpegUtil.extractSegment(item.first, item.second, this@EvaluationTemplateCommand.cacheLocation) + } + } + } + + + /** + * Exports a specific competition to a JSON file. + */ + inner class Export : AbstractEvaluationCommand(name = "export", help = "Exports a template as JSON.") { + + /** Path to the file that should be created .*/ + private val path: Path by option("-o", "--out", help = "The destination file for the template.").path().required() + + /** Flag indicating whether export should be pretty printed.*/ + private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) + + override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { + val competition = DbEvaluationTemplate.query( + (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) + ).firstOrNull() + + if (competition == null) { + println("Could not find specified template.") + return@transactional + } + + val mapper = jacksonObjectMapper() + Files.newBufferedWriter(this.path, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE).use { + val writer = if (this.pretty) { + mapper.writerWithDefaultPrettyPrinter() + } else { + mapper.writer() + } + writer.writeValue(it, competition) + } + println("Successfully wrote template '${competition.name}' (ID = ${competition.id}) to $path.") + } + } + + + /** + * Imports a competition from a JSON file. + */ + inner class Import : CliktCommand(name = "import", help = "Imports a template from JSON.") { + + /** Flag indicating whether a new competition should be created.*/ + private val new: Boolean by option("-n", "--new", help = "Flag indicating whether a new template should be created.").flag("-u", "--update", default = true) + + /** Path to the file that should be imported.*/ + private val path: Path by option("-i", "--in", help = "The file to import the template from.").path().required() + + override fun run() { + /* TODO: Probably won't work this way. */ + /* Read competition from file */ + /*val reader = jacksonObjectMapper().readerFor(CompetitionDescription::class.java) + val competition = try { + Files.newBufferedReader(this.path).use { + val tree = reader.readTree(it) + if (tree.get("id") != null && (tree.get("description") == null || tree.get("description").isNull || tree.get("description").isTextual)) { + reader.readValue(tree) + } else if (tree.get("id") != null && tree.get("description") != null && tree.get("description").isObject) { + reader.readValue(tree["description"]) + } else { + null + } + } + } catch (e: Throwable) { + println("Could not import competition from $path: ${e.message}.") + return + } + + /* Create/update competition. */ + if (competition != null) { + if (this.new) { + val id = this@CompetitionCommand.competitions.append(competition) + println("Successfully imported new competition '${competition.name}' (ID = $id) from $path.") + } else { + this@CompetitionCommand.competitions.update(competition) + println("Successfully updated competition '${competition.name}' (ID = ${competition.id}) from $path.") + } + } else { + println("Could not import competition from $path: Unknown format.") + }*/ + } + } +} \ No newline at end of file From 18e173367efdcf0a7d2a8ad629ba6c4ae9bae455 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 8 Feb 2023 18:42:55 +0100 Subject: [PATCH 103/498] Addressed #387, #310: Re-adding new builder ui --- frontend/src/app/app-routing.module.ts | 8 ++ frontend/src/app/app.module.ts | 2 + .../template/builder/builder.component.html | 1 - .../app/template/builder/builder.component.ts | 61 ------------ .../information/information.component.ts | 47 --------- .../general-info-tab.component.html | 1 - .../general-info-tab.component.scss | 0 .../general-info-tab.component.ts | 15 --- .../abstract-template-builder.component.ts | 4 +- .../template-builder-components.module.ts | 22 +++++ .../template-information.component.html} | 0 .../template-information.component.scss} | 0 .../template-information.component.ts | 46 +++++++++ .../template-builder.component.html | 48 +++++++++ .../template-builder.component.scss} | 0 .../template-builder.component.ts | 98 +++++++++++++++++++ .../template-builder.module.ts | 24 +++++ .../template-builder.service.ts | 0 frontend/src/app/template/template.module.ts | 23 ++--- 19 files changed, 258 insertions(+), 142 deletions(-) delete mode 100644 frontend/src/app/template/builder/builder.component.html delete mode 100644 frontend/src/app/template/builder/builder.component.ts delete mode 100644 frontend/src/app/template/builder/components/information/information.component.ts delete mode 100644 frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html delete mode 100644 frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.scss delete mode 100644 frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts rename frontend/src/app/template/{builder/components/abstract-template-builder => template-builder/components}/abstract-template-builder.component.ts (77%) create mode 100644 frontend/src/app/template/template-builder/components/template-builder-components.module.ts rename frontend/src/app/template/{builder/components/information/information.component.html => template-builder/components/template-information/template-information.component.html} (100%) rename frontend/src/app/template/{builder/builder.component.scss => template-builder/components/template-information/template-information.component.scss} (100%) create mode 100644 frontend/src/app/template/template-builder/components/template-information/template-information.component.ts create mode 100644 frontend/src/app/template/template-builder/template-builder.component.html rename frontend/src/app/template/{builder/components/information/information.component.scss => template-builder/template-builder.component.scss} (100%) create mode 100644 frontend/src/app/template/template-builder/template-builder.component.ts create mode 100644 frontend/src/app/template/template-builder/template-builder.module.ts rename frontend/src/app/template/{builder => template-builder}/template-builder.service.ts (100%) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index d0896e363..d50d9fde7 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -20,6 +20,7 @@ import { JudgementVotingViewerComponent } from './judgement/judgement-voting-vie import { RunAsyncAdminViewComponent } from './run/run-async-admin-view/run-async-admin-view.component'; import { NonescapingUrlserializerClass } from './nonescaping-urlserializer.class'; import {ApiRole} from '../../openapi'; +import {TemplateBuilderComponent} from './template/template-builder/template-builder.component'; /** * The ROUTE for evaluation templates. @@ -46,6 +47,13 @@ const routes: Routes = [ canDeactivate: [CanDeactivateGuard], data: { roles: [ApiRole.ADMIN] }, }, + { + path: TEMPLATE_ROUTE+'/builder2/:templateId', + component: TemplateBuilderComponent, + canActivate: [AuthenticationGuard], + canDeactivate: [CanDeactivateGuard], + data: { roles: [ApiRole.ADMIN] }, + }, { path: EVALUATION_ROUTE+'/list', component: RunListComponent, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index afe2ee7f7..aa47ad7d8 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -23,6 +23,7 @@ import { AuditlogModule } from './auditlog/auditlog.module'; import { SharedModule } from './shared/shared.module'; import { CollectionModule } from './collection/collection.module'; import { CompetitionBuilderModule } from './competition/competition-builder/competition-builder.module'; +import {TemplateModule} from './template/template.module'; /** * Method used to load application config. @@ -59,6 +60,7 @@ export function initializeApp(appConfig: AppConfig) { RunModule, CollectionModule, JudgementModule, + TemplateModule ], providers: [ AppConfig, diff --git a/frontend/src/app/template/builder/builder.component.html b/frontend/src/app/template/builder/builder.component.html deleted file mode 100644 index af41d55be..000000000 --- a/frontend/src/app/template/builder/builder.component.html +++ /dev/null @@ -1 +0,0 @@ -

builder works!

diff --git a/frontend/src/app/template/builder/builder.component.ts b/frontend/src/app/template/builder/builder.component.ts deleted file mode 100644 index d52068e23..000000000 --- a/frontend/src/app/template/builder/builder.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {DeactivationGuarded} from '../../services/can-deactivate.guard'; -import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; -import {Observable, Subscription} from 'rxjs'; -import {TemplateBuilderService} from './template-builder.service'; -import {DownloadService, TemplateService, UserService} from '../../../../openapi'; -import {MatSnackBar} from '@angular/material/snack-bar'; -import {AbstractTemplateBuilderComponent} from './components/abstract-template-builder/abstract-template-builder.component'; - -@Component({ - selector: 'app-builder', - templateUrl: './builder.component.html', - styleUrls: ['./builder.component.scss'] -}) -export class BuilderComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy, DeactivationGuarded { - onChange() { - } - - routeSub: Subscription; - changeSub: Subscription; - - - constructor( - private templateService: TemplateService, - private userService: UserService, - private downloadService: DownloadService, - private route: ActivatedRoute, - private router: Router, - private snackBar: MatSnackBar, - builderService: TemplateBuilderService - ) { - super(builderService); - } - - canDeactivate(nextState?: RouterStateSnapshot): Observable | Promise | boolean { - return undefined; - } - - ngOnDestroy(): void { - this.onDestroy(); - this.changeSub.unsubscribe(); - this.routeSub.unsubscribe(); - } - - ngOnInit(): void { - this.routeSub = this.route.params.subscribe( (p) => { - this.templateService.getApiV2TemplateByTemplateId(p.templateId).subscribe((t) => { - /* initialise from route */ - this.builderService.initialise(t); - }, - (r) => { - this.snackBar.open(`Error: ${r?.error?.description}`, null,{duration: 5000}); - }); - }); - } - - fileProvider = () => { - return this.builderService.getTemplate()?.name ? this.builderService.getTemplate().name : 'evaluation-template-download.json' - } - -} diff --git a/frontend/src/app/template/builder/components/information/information.component.ts b/frontend/src/app/template/builder/components/information/information.component.ts deleted file mode 100644 index 27ff913f6..000000000 --- a/frontend/src/app/template/builder/components/information/information.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {AbstractTemplateBuilderComponent} from '../abstract-template-builder/abstract-template-builder.component'; -import {ActivatedRoute} from '@angular/router'; -import {TemplateService} from '../../../../../../openapi'; -import {MatSnackBar} from '@angular/material/snack-bar'; -import {TemplateBuilderService} from '../../template-builder.service'; -import {Subscription} from 'rxjs'; -import {FormControl, FormGroup} from '@angular/forms'; - -@Component({ - selector: 'app-information', - templateUrl: './information.component.html', - styleUrls: ['./information.component.scss'] -}) -export class InformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { - - form: FormGroup = new FormGroup({name: new FormControl(''), description: new FormControl('')}); - - private changeSub: Subscription; - - constructor( - private templateService: TemplateService, - builder: TemplateBuilderService - ) { - super(builder); - } - - ngOnInit(): void { - this.onInit(); - - this.changeSub = this.form.valueChanges.subscribe(() => { - this.builderService.markDirty(); - }); - } - - ngOnDestroy() { - this.onDestroy(); - this.changeSub.unsubscribe(); - } - - onChange() { - if(this.builderService.getTemplate()){ - this.form.get('name').setValue(this.builderService.getTemplate().name); - this.form.get('description').setValue(this.builderService.getTemplate().description); - } - } -} diff --git a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html deleted file mode 100644 index c3aa46637..000000000 --- a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.scss b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts b/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts deleted file mode 100644 index c1702363a..000000000 --- a/frontend/src/app/template/builder/tabs/general-info-tab/general-info-tab.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-general-info-tab', - templateUrl: './general-info-tab.component.html', - styleUrls: ['./general-info-tab.component.scss'] -}) -export class GeneralInfoTabComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { - } - -} diff --git a/frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts b/frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts similarity index 77% rename from frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts rename to frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts index 36699feb1..058b55302 100644 --- a/frontend/src/app/template/builder/components/abstract-template-builder/abstract-template-builder.component.ts +++ b/frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import {ApiEvaluationTemplate} from '../../../../../../openapi'; +import {ApiEvaluationTemplate} from '../../../../../openapi'; import {Subscription} from 'rxjs'; -import {TemplateBuilderService} from '../../template-builder.service'; +import {TemplateBuilderService} from '../template-builder.service'; export abstract class AbstractTemplateBuilderComponent { diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts new file mode 100644 index 000000000..3d3e57100 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -0,0 +1,22 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {TemplateInformationComponent} from './template-information/template-information.component'; +import {ReactiveFormsModule} from '@angular/forms'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; + + +@NgModule({ + declarations: [ + TemplateInformationComponent + ], + imports: [ + CommonModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule + ], + exports: [TemplateInformationComponent] +}) +export class TemplateBuilderComponentsModule { +} diff --git a/frontend/src/app/template/builder/components/information/information.component.html b/frontend/src/app/template/template-builder/components/template-information/template-information.component.html similarity index 100% rename from frontend/src/app/template/builder/components/information/information.component.html rename to frontend/src/app/template/template-builder/components/template-information/template-information.component.html diff --git a/frontend/src/app/template/builder/builder.component.scss b/frontend/src/app/template/template-builder/components/template-information/template-information.component.scss similarity index 100% rename from frontend/src/app/template/builder/builder.component.scss rename to frontend/src/app/template/template-builder/components/template-information/template-information.component.scss diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts new file mode 100644 index 000000000..a94a75fe4 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts @@ -0,0 +1,46 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; +import {FormControl, FormGroup} from '@angular/forms'; +import {Subscription} from 'rxjs'; +import {TemplateService} from '../../../../../../openapi'; +import {TemplateBuilderService} from '../../template-builder.service'; + +@Component({ + selector: 'app-template-information', + templateUrl: './template-information.component.html', + styleUrls: ['./template-information.component.scss'] +}) +export class TemplateInformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + + form: FormGroup = new FormGroup({name: new FormControl(''), description: new FormControl('')}); + + private changeSub: Subscription; + + constructor( + private templateService: TemplateService, + builder: TemplateBuilderService + ) { + super(builder); + } + + ngOnInit(): void { + this.onInit(); + + this.changeSub = this.form.valueChanges.subscribe(() => { + this.builderService.markDirty(); + }); + } + + ngOnDestroy() { + this.onDestroy(); + this.changeSub.unsubscribe(); + } + + onChange() { + if(this.builderService.getTemplate()){ + this.form.get('name').setValue(this.builderService.getTemplate().name); + this.form.get('description').setValue(this.builderService.getTemplate().description); + } + } + +} diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html new file mode 100644 index 000000000..3b71da67c --- /dev/null +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -0,0 +1,48 @@ +
+
+ +

Edit competition {{(builderService.templateAsObservable() | async)?.id}} (unsaved changes)

+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+ + + + + + teams judges + + + types and groups + + + tasks + + + + +
diff --git a/frontend/src/app/template/builder/components/information/information.component.scss b/frontend/src/app/template/template-builder/template-builder.component.scss similarity index 100% rename from frontend/src/app/template/builder/components/information/information.component.scss rename to frontend/src/app/template/template-builder/template-builder.component.scss diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts new file mode 100644 index 000000000..ee22bbcef --- /dev/null +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -0,0 +1,98 @@ +import {Component, HostListener, OnDestroy, OnInit} from '@angular/core'; +import {AbstractTemplateBuilderComponent} from './components/abstract-template-builder.component'; +import {DeactivationGuarded} from '../../services/can-deactivate.guard'; +import {Observable, Subscription} from 'rxjs'; +import {DownloadService, TemplateService, UserService} from '../../../../openapi'; +import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; +import {MatSnackBar} from '@angular/material/snack-bar'; +import {TemplateBuilderService} from './template-builder.service'; +import {take} from 'rxjs/operators'; + +@Component({ + selector: 'app-template-builder', + templateUrl: './template-builder.component.html', + styleUrls: ['./template-builder.component.scss'] +}) +export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy, DeactivationGuarded { + onChange() { + + } + + routeSub: Subscription; + changeSub: Subscription; + + + constructor( + private templateService: TemplateService, + private userService: UserService, + private downloadService: DownloadService, + private route: ActivatedRoute, + private router: Router, + private snackBar: MatSnackBar, + public builderService: TemplateBuilderService +) { + super(builderService); + } + + canDeactivate(nextState?: RouterStateSnapshot): Observable | Promise | boolean { + return this.builderService.checkDirty(); + } + + ngOnDestroy(): void { + this.onDestroy(); + this.changeSub.unsubscribe(); + this.routeSub.unsubscribe(); + } + + ngOnInit(): void { + this.routeSub = this.route.params.subscribe( (p) => { + this.templateService.getApiV2TemplateByTemplateId(p.templateId).subscribe((t) => { + /* initialise from route */ + this.builderService.initialise(t); + }, + (r) => { + this.snackBar.open(`Error: ${r?.error?.description}`, null,{duration: 5000}); + }); + }); + } + + fileProvider = () => { + return this.builderService.getTemplate()?.name ? this.builderService.getTemplate().name : 'evaluation-template-download.json' + } + + downloadProvider = () => { + return this.downloadService.getApiV2DownloadTemplateByTemplateId(this.builderService.getTemplate()?.id).pipe(take(1)); + } + + public save(){ + // FIXME re-enable form validation. possibly on the form-builder? + console.log("save") + this.templateService.patchApiV2TemplateByTemplateId(this.builderService.getTemplate().id, this.builderService.getTemplate()).subscribe((s) => { + this.snackBar.open(s.description, null, {duration: 5000}); + this.builderService.unmarkDirty(); + }, (r) => this.snackBar.open(`Error: ${r?.error?.description}`, null, {duration: 5000})); + } + + public back(){ + if(this.builderService.checkDirty()){ + this.router.navigate(['/template/list']); + } + } + + @HostListener('window:beforeunload', ['$event']) + handleBeforeUnload(event: BeforeUnloadEvent) { + if (!this.builderService.checkDirty()) { + event.preventDefault(); + event.returnValue = ''; + return; + } + delete event.returnValue; + } + + refresh() { + if(this.builderService.checkDirty()){ + this.ngOnInit(); + } + } + +} diff --git a/frontend/src/app/template/template-builder/template-builder.module.ts b/frontend/src/app/template/template-builder/template-builder.module.ts new file mode 100644 index 000000000..9205ca81e --- /dev/null +++ b/frontend/src/app/template/template-builder/template-builder.module.ts @@ -0,0 +1,24 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {TemplateBuilderComponent} from './template-builder.component'; +import {TemplateBuilderComponentsModule} from './components/template-builder-components.module'; +import {MatIconModule} from '@angular/material/icon'; +import {MatTabsModule} from '@angular/material/tabs'; +import {SharedModule} from '../../shared/shared.module'; + + +@NgModule({ + declarations: [ + TemplateBuilderComponent + ], + imports: [ + CommonModule, + TemplateBuilderComponentsModule, + MatIconModule, + MatTabsModule, + SharedModule + ], + exports: [TemplateBuilderComponent] +}) +export class TemplateBuilderModule { +} diff --git a/frontend/src/app/template/builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts similarity index 100% rename from frontend/src/app/template/builder/template-builder.service.ts rename to frontend/src/app/template/template-builder/template-builder.service.ts diff --git a/frontend/src/app/template/template.module.ts b/frontend/src/app/template/template.module.ts index 1e486aa9f..41aed6a8d 100644 --- a/frontend/src/app/template/template.module.ts +++ b/frontend/src/app/template/template.module.ts @@ -1,25 +1,18 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { BuilderComponent } from './builder/builder.component'; -import {GeneralInfoTabComponent} from './builder/tabs/general-info-tab/general-info-tab.component'; -import { InformationComponent } from './builder/components/information/information.component'; -import { AbstractTemplateBuilderComponent } from './builder/components/abstract-template-builder/abstract-template-builder.component'; +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; import {MatInputModule} from '@angular/material/input'; import {ReactiveFormsModule} from '@angular/forms'; - +import {TemplateBuilderModule} from './template-builder/template-builder.module'; @NgModule({ - declarations: [ - BuilderComponent, - GeneralInfoTabComponent, - InformationComponent, - AbstractTemplateBuilderComponent - ], + declarations: [], imports: [ CommonModule, MatInputModule, - ReactiveFormsModule + ReactiveFormsModule, + TemplateBuilderModule ] }) -export class TemplateModule { } +export class TemplateModule { +} From 46a0d8e25a5c7554654eb03b81bfc5c122e6db01 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 9 Feb 2023 17:10:37 +0100 Subject: [PATCH 104/498] Removed BatchSubmissionHandler and renamed SubmissionHandler to LegacySubmissionHandler --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 6 +- .../submission/BatchSubmissionHandler.kt | 158 ------------------ ...nHandler.kt => LegacySubmissionHandler.kt} | 8 +- 3 files changed, 6 insertions(+), 166 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt rename backend/src/main/kotlin/dev/dres/api/rest/handler/submission/{SubmissionHandler.kt => LegacySubmissionHandler.kt} (97%) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 03ca99c34..562ed88a2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -22,8 +22,7 @@ import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.MediaPreviewHandler import dev.dres.api.rest.handler.preview.SubmissionPreviewHandler import dev.dres.api.rest.handler.scores.ListEvaluationScoreHandler -import dev.dres.api.rest.handler.submission.BatchSubmissionHandler -import dev.dres.api.rest.handler.submission.SubmissionHandler +import dev.dres.api.rest.handler.submission.LegacySubmissionHandler import dev.dres.api.rest.handler.system.CurrentTimeHandler import dev.dres.api.rest.handler.system.InfoHandler import dev.dres.api.rest.handler.system.LoginHandler @@ -117,8 +116,7 @@ object RestApi { GetTeamLogoHandler(store), // Submission - SubmissionHandler(store, config), - BatchSubmissionHandler(store, config), + LegacySubmissionHandler(store, config), // Log QueryLogHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt deleted file mode 100644 index 04290e878..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/BatchSubmissionHandler.kt +++ /dev/null @@ -1,158 +0,0 @@ -package dev.dres.api.rest.handler.submission - -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.handler.AccessManagedRestHandler -import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.api.rest.types.submission.RunResult -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.Config -import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.admin.UserId -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.run.InteractiveRunManager -import dev.dres.run.NonInteractiveRunManager -import dev.dres.utilities.extensions.evaluationId -import dev.dres.utilities.extensions.sessionToken -import io.javalin.http.Context -import io.javalin.http.bodyAsClass -import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.query.query - -/** - * An [GetRestHandler] used to process batched [DbSubmission]s. - * - * @author Luca Rossetto - * @author Loris Sauter - * @version 2.0.0 - */ -class BatchSubmissionHandler(private val store: TransientEntityStore, private val config: Config) : PostRestHandler, AccessManagedRestHandler { - /** [BatchSubmissionHandler] requires [ApiRole.PARTICIPANT]. */ - override val permittedRoles = setOf(ApiRole.PARTICIPANT) - - /** All [BatchSubmissionHandler]s are part of the v1 API. */ - override val apiVersion = "v2" - - override val route: String = "submit/{evaluationId}" - - @OpenApi(summary = "Endpoint to accept batch submissions in JSON format", - path = "/api/v2/submit/{evaluationId}", - operationId = OpenApiOperation.AUTO_GENERATE, - methods = [HttpMethod.POST], - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], - requestBody = OpenApiRequestBody([OpenApiContent(RunResult::class)]), - tags = ["Batch Submission"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) - ]) - override fun doPost(ctx: Context): SuccessStatus { - /* Try to parse message. */ - val runResult = try { - ctx.bodyAsClass() - } catch (e: Exception) { - throw ErrorStatusException(400, "Error parsing JSON body", ctx) - } - - /* Obtain basic information required for submission processing. */ - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) - val runManager = this.getEligibleRunManager(userId, ctx) - val time = System.currentTimeMillis() - this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, runManager) - val submission = toSubmission(userId, runManager, runResult, time, ctx) - runManager.postSubmission(rac, submission) - } - return SuccessStatus("Submission received.") - } - - /** - * Converts the user request tu a [DbSubmission]. - * - * Creates the associated database entry. Requires an ongoing transaction. - * - * @param userId The [UserId] of the user who triggered the [DbSubmission]. - * @param runManager The [InteractiveRunManager] - * @param submission The submitted [RunResult]s. - * @param submissionTime Time of the submission. - * @param ctx The HTTP [Context] - */ - private fun toSubmission(userId: UserId, runManager: NonInteractiveRunManager, submission: RunResult, submissionTime: Long, ctx: Context): DbSubmission { - /* Find team that the user belongs to. */ - val user = DbUser.query(DbUser::id eq userId).firstOrNull() - ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) - val team = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull() - ?: throw ErrorStatusException(404, "No team for user '$userId' could not be found.", ctx) - - /* Create new submission. */ - val new = DbSubmission.new { - this.user = user - this.team = team - this.timestamp = submissionTime - } - - /* Process submitted results. */ - val resultBatches = submission.tasks.mapNotNull { taskResult -> - /*val task = runManager.tasks(rac).find { it.template.name == taskResult.task } ?: return@mapNotNull null - val mediaCollectionId = task.template.mediaCollectionId - val results = taskResult.results.map { result -> - if (result.item != null) { - val mediaItem = - this.itemIndex[mediaCollectionId to result.item].first() //TODO deal with invalid name - return@map if (mediaItem is MediaItem.VideoItem && (result.startTimeCode != null || result.endTimeCode != null || result.index != null)) { - - val time = if (result.index != null) { - val segmentList = segmentIndex[mediaItem.id].first() - TimeUtil.shotToTime(result.index.toString(), segmentList)!! - } else { - val start = if (result.startTimeCode != null) { - TemporalPoint.Timecode.timeCodeToMilliseconds(result.startTimeCode, mediaItem.fps)!! //FIXME error handling - } else { - TemporalPoint.Timecode.timeCodeToMilliseconds(result.endTimeCode!!, mediaItem.fps)!! - } - val end = if (result.endTimeCode != null) { - TemporalPoint.Timecode.timeCodeToMilliseconds(result.endTimeCode, mediaItem.fps)!! - } else { - start - } - start to end - } - TemporalBatchElement(mediaItem, time.first, time.second) - } else { - ItemBatchElement(mediaItem) - } - } else { //TODO deal with text - TODO("text batch submissions not yet supported") - } - } - - if (results.all { it is TemporalBatchElement }) { - @Suppress("UNCHECKED_CAST") - (BaseResultBatch(mediaCollectionId, taskResult.resultName, team, results as List)) - } else { - BaseResultBatch(mediaCollectionId, taskResult.resultName, team, results) - }*/ - } - - return new - } - - /** - * Returns the [NonInteractiveRunManager] that is eligible for the given [Context] - */ - private fun getEligibleRunManager(userId: UserId, ctx: Context): NonInteractiveRunManager { - val evaluationId = ctx.evaluationId() - return AccessManager.getRunManagerForUser(userId).filterIsInstance().find { it.id == evaluationId } - ?: throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt similarity index 97% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 7fbbedd0b..ea3ed4eac 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -42,13 +42,13 @@ import java.nio.file.Paths * @author Loris Sauter * @version 2.0.0 */ -class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): GetRestHandler, AccessManagedRestHandler { +class LegacySubmissionHandler(private val store: TransientEntityStore, private val config: Config): GetRestHandler, AccessManagedRestHandler { - /** [SubmissionHandler] requires [ApiRole.PARTICIPANT]. */ + /** [LegacySubmissionHandler] requires [ApiRole.PARTICIPANT]. */ override val permittedRoles = setOf(ApiRole.PARTICIPANT) - /** All [SubmissionHandler]s are part of the v1 API. */ - override val apiVersion = "v2" + /** All [LegacySubmissionHandler]s are part of the v1 API. */ + override val apiVersion = "v1" override val route = "submit" From 33d883821c8bea57da47fba18cf130053c72623a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 9 Feb 2023 18:28:11 +0100 Subject: [PATCH 105/498] More abstraction interfaces --- .../dev/dres/api/cli/EvaluationCommand.kt | 2 +- .../admin/AbstractEvaluationAdminHandler.kt | 2 +- .../viewer/AbstractEvaluationViewerHandler.kt | 2 +- .../judgement/DequeueJudgementHandler.kt | 12 +-- .../handler/judgement/DequeueVoteHandler.kt | 12 +-- .../handler/submission/SubmissionHandler.kt | 89 +++++++++++++++++++ .../api/rest/types/collection/ApiMediaItem.kt | 8 +- .../api/rest/types/collection/ApiMediaType.kt | 5 +- .../api/rest/types/evaluation/ApiAnswerSet.kt | 24 ++++- .../rest/types/evaluation/ApiEvaluation.kt | 7 +- .../rest/types/evaluation/ApiSubmission.kt | 15 +++- .../types/evaluation/ApiSubmissionInfo.kt | 2 +- .../dres/api/rest/types/evaluation/ApiTask.kt | 2 +- .../evaluation/websocket/ServerMessage.kt | 2 +- .../data/model/media/DbMediaCollection.kt | 2 +- .../dev/dres/data/model/media/DbMediaItem.kt | 4 +- .../dev/dres/data/model/media/DbMediaType.kt | 4 +- .../dev/dres/data/model/media/MediaItem.kt | 4 + .../dres/data/model/run/AbstractEvaluation.kt | 1 + .../dev/dres/data/model/run/AbstractTask.kt | 4 +- .../dev/dres/data/model/run/DbEvaluation.kt | 8 +- .../kotlin/dev/dres/data/model/run/DbTask.kt | 4 +- .../run/InteractiveAsynchronousEvaluation.kt | 9 +- .../run/InteractiveSynchronousEvaluation.kt | 9 +- .../model/run/NonInteractiveEvaluation.kt | 9 +- .../kotlin/dev/dres/data/model/run/Task.kt | 4 + .../model/run/interfaces/EvaluationRun.kt | 1 - .../dres/data/model/run/interfaces/TaskRun.kt | 1 + .../dres/data/model/submissions/AnswerSet.kt | 6 +- .../data/model/submissions/DbAnswerSet.kt | 9 +- .../model/template/DbEvaluationTemplate.kt | 5 +- .../model/template/task/DbTaskTemplate.kt | 2 +- .../data/model/template/task/TaskTemplate.kt | 3 +- .../run/InteractiveAsynchronousRunManager.kt | 15 ++-- .../dev/dres/run/InteractiveRunManager.kt | 1 + .../run/InteractiveSynchronousRunManager.kt | 11 ++- .../dev/dres/run/NonInteractiveRunManager.kt | 4 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 2 +- .../main/kotlin/dev/dres/run/RunManager.kt | 5 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 32 ++++--- .../dev/dres/run/eventstream/StreamEvent.kt | 6 +- .../handlers/ResultLogStatisticsHandler.kt | 2 +- .../handlers/SubmissionStatisticsHandler.kt | 15 ++-- .../handlers/TeamCombinationScoreHandler.kt | 5 +- .../dres/run/filter/CorrectPerTeamFilter.kt | 2 +- .../run/filter/CorrectPerTeamItemFilter.kt | 2 +- .../run/filter/CorrectPerTeamMemberFilter.kt | 2 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 2 +- .../kotlin/dev/dres/run/score/TaskContext.kt | 2 +- .../dres/run/updatables/ScoresUpdatable.kt | 7 +- .../validation/ChainedSubmissionValidator.kt | 10 ++- .../MediaItemsSubmissionValidator.kt | 11 +-- .../TemporalContainmentSubmissionValidator.kt | 17 ++-- .../TemporalOverlapSubmissionValidator.kt | 17 ++-- .../dev/dres/run/validation/TextValidator.kt | 18 ++-- .../interfaces/JudgementValidator.kt | 3 +- .../interfaces/SubmissionValidator.kt | 3 +- .../validation/interfaces/VoteValidator.kt | 3 +- .../judged/BasicJudgementValidator.kt | 34 ++++--- .../validation/judged/BasicVoteValidator.kt | 9 +- .../dres/run/validation/judged/ItemRange.kt | 5 +- .../utilities/extensions/ContextExtensions.kt | 3 +- 62 files changed, 345 insertions(+), 176 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index 77b7c51f0..8e972bd8e 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -10,7 +10,7 @@ import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter import com.jakewharton.picnic.table import dev.dres.data.model.template.task.DbTargetType import dev.dres.data.model.run.* -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt index a473c13da..1ea465266 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt index 894a273cc..681ce8ff4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.* import dev.dres.api.rest.handler.evaluation.client.AbstractEvaluationClientHandler import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 73110b314..96c78aee1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -49,21 +49,21 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break val next = validator.next(ctx.sessionToken()!!) ?: break val taskDescription = next.second.task.template.textualDescription() - when (next.second.answers.firstOrNull()?.type) { + when (next.second.answers().firstOrNull()?.type) { DbAnswerType.TEXT -> { - val text = next.second.answers.firstOrNull()?.text ?: continue + val text = next.second.answers().firstOrNull()?.text ?: continue return@transactional ApiJudgementRequest(next.first, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } DbAnswerType.ITEM -> { - val item = next.second.answers.firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) + val item = next.second.answers().firstOrNull()?.item ?: continue + return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) } DbAnswerType.TEMPORAL -> { - val answer = next.second.answers.firstOrNull() ?: continue + val answer = next.second.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue val end = answer.end ?: continue - return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, start, end) + return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) } else -> continue } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 28c21ebc7..06aa0c503 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -47,21 +47,21 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break val next = validator.nextSubmissionToVoteOn() ?: break val taskDescription = next.task.template.textualDescription() - when (next.answers.firstOrNull()?.type) { + when (next.answers().firstOrNull()?.type) { DbAnswerType.TEXT -> { - val text = next.answers.firstOrNull()?.text ?: continue + val text = next.answers().firstOrNull()?.text ?: continue return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } DbAnswerType.ITEM -> { - val item = next.answers.firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, null, null) + val item = next.answers().firstOrNull()?.item ?: continue + return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) } DbAnswerType.TEMPORAL -> { - val answer = next.answers.firstOrNull() ?: continue + val answer = next.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue val end = answer.end ?: continue - return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id, taskDescription, start, end) + return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) } else -> continue } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt new file mode 100644 index 000000000..22b8d3f32 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -0,0 +1,89 @@ +package dev.dres.api.rest.handler.submission + +import dev.dres.api.rest.AccessManager +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.competition.ApiEvaluationStartMessage +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.Config +import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.options.DbTaskOption +import dev.dres.run.InteractiveRunManager +import dev.dres.run.NonInteractiveRunManager +import dev.dres.run.audit.AuditLogger +import dev.dres.run.exceptions.IllegalRunStateException +import dev.dres.run.exceptions.IllegalTeamIdException +import dev.dres.run.filter.SubmissionRejectedException +import dev.dres.utilities.extensions.sessionToken +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.contains +import kotlinx.dnq.query.first +import org.slf4j.LoggerFactory + +class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): PostRestHandler, AccessManagedRestHandler { + + override val permittedRoles = setOf(ApiRole.PARTICIPANT) + + /** All [LegacySubmissionHandler]s are part of the v1 API. */ + override val apiVersion = "v2" + + override val route = "submit/{evaluationId}" + + private val logger = LoggerFactory.getLogger(this.javaClass) + + override fun doPost(ctx: Context): SuccessfulSubmissionsStatus { + + return this.store.transactional { + + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val evaluationId = ctx.pathParam("evaluationId") + val runManager = AccessManager.getRunManagerForUser(userId).find { it.id == evaluationId } ?: throw ErrorStatusException(404, "Evaluation with id '$evaluationId' not found.", ctx) + + val rac = RunActionContext.runActionContext(ctx, runManager) + + val apiSubmission = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid submission, cannot parse: ${e.message}", ctx) + } + + val result = try { + runManager.postSubmission(rac, apiSubmission) + } catch (e: SubmissionRejectedException) { + throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) + } catch (e: IllegalRunStateException) { + logger.info("Submission was received while run manager was not accepting submissions.") + throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + } catch (e: IllegalTeamIdException) { + logger.info("Submission with unknown team id '${rac.teamId}' was received.") + throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) + } + + AuditLogger.submission(apiSubmission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) + + logger.info("Submission ${apiSubmission.id} received status $result.") + + return@transactional when (result) { + DbVerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(DbVerdictStatus.CORRECT.toApi(), "Submission correct!") + DbVerdictStatus.WRONG -> SuccessfulSubmissionsStatus(DbVerdictStatus.WRONG.toApi(), "Submission incorrect! Try again") + DbVerdictStatus.INDETERMINATE -> { + ctx.status(202) /* HTTP Accepted. */ + SuccessfulSubmissionsStatus(DbVerdictStatus.INDETERMINATE.toApi(), "Submission received. Waiting for verdict!") + } + + DbVerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(DbVerdictStatus.UNDECIDABLE.toApi(),"Submission undecidable. Try again!") + else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) + } + + } + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index c1f983a78..d92cd9d56 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -2,6 +2,7 @@ package dev.dres.api.rest.types.collection import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.MediaItemCollection /** * The RESTful API equivalent for [DbMediaItem]. @@ -11,9 +12,9 @@ import dev.dres.data.model.media.MediaItem * @version 1.1.0 */ data class ApiMediaItem( - val id: String?, + override val id: String?, override val name: String, - val type: ApiMediaType, + override val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, @@ -25,4 +26,7 @@ data class ApiMediaItem( require(this.fps != null) { "Duration must be set for a video item." } } } + + override val collection: MediaItemCollection + get() = TODO("Not yet implemented") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt index f6434506f..f1a9f6765 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.types.collection import dev.dres.data.model.media.DbMediaType +import dev.dres.data.model.media.MediaItemType /** * The RESTful API equivalent for the type of a [ApiMediaItem] @@ -9,7 +10,7 @@ import dev.dres.data.model.media.DbMediaType * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiMediaType { +enum class ApiMediaType : MediaItemType { IMAGE, VIDEO, TEXT; /** @@ -22,4 +23,6 @@ enum class ApiMediaType { VIDEO -> DbMediaType.VIDEO TEXT -> DbMediaType.TEXT } + + override fun toApi(): ApiMediaType = this } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index ddc15651d..722ca3df5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -1,9 +1,15 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.data.model.run.Task +import dev.dres.data.model.submissions.Answer +import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.interfaces.EvaluationTemplate /** - * The RESTful API equivalent for the type of a [ApiAnswerSet]. + * The RESTful API equivalent for the type of [ApiAnswerSet]. * * @see ApiAnswerSet * @author Ralph Gasser @@ -12,4 +18,18 @@ import dev.dres.api.rest.types.collection.ApiMediaItem data class ApiAnswerSet( val status: ApiVerdictStatus, val answers: List -) +) : AnswerSet { + override val task: Task + get() = TODO("Not yet implemented") + override val submission: Submission + get() = TODO("Not yet implemented") + + override fun answers(): Sequence = answers.asSequence() + override fun status(): VerdictStatus { + TODO("Not yet implemented") + } + + override fun status(status: VerdictStatus) { + TODO("Not yet implemented") + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt index 615a9fa98..195767955 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluation.kt @@ -2,7 +2,8 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.Evaluation +import dev.dres.data.model.run.interfaces.EvaluationId /** * The RESTful API equivalent of a [DbEvaluation]. @@ -12,7 +13,7 @@ import dev.dres.data.model.run.EvaluationId * @version 2.0.0 */ data class ApiEvaluation( - val evaluationId: EvaluationId, + override val evaluationId: EvaluationId, val name: String, val type: ApiEvaluationType, val template: ApiEvaluationTemplate, @@ -20,4 +21,4 @@ data class ApiEvaluation( val started: Long?, val ended: Long?, val tasks: List -) \ No newline at end of file +) : Evaluation \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index 71e55a1b4..e489fd48e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -1,7 +1,11 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.data.model.admin.User +import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.template.team.Team /** * The RESTful API equivalent of a [DbSubmission]. @@ -16,9 +20,16 @@ data class ApiSubmission( val teamName: String, val memberId: String, val memberName: String, - val timestamp: Long, + override val timestamp: Long, val answers: List, -) { +) : Submission { + override val submissionId: SubmissionId + get() = TODO("Not yet implemented") + override val team: Team + get() = TODO("Not yet implemented") + override val user: User + get() = TODO("Not yet implemented") + override fun answerSets(): Sequence = this.answers.asSequence() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt index 8a617199a..824603629 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.TemplateId /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index f53284a83..76c8a02a9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.DbTask -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.TemplateId /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt index c5a01ff8d..f3f2b93fe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.evaluation.websocket -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId /** * Message send by the DRES server via WebSocket to inform clients about the state of the run. diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt index 8cc5a35f5..eb3eba4cb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt @@ -12,7 +12,7 @@ typealias CollectionId = String * @author Ralph Gasser * @version 2.0.0 */ -class DbMediaCollection(entity: Entity): PersistentEntity(entity) { +class DbMediaCollection(entity: Entity): PersistentEntity(entity), MediaItemCollection { companion object : XdNaturalEntityType() /** The name of this [DbMediaItem]. */ var name: String by xdRequiredStringProp(unique = true, trimmed = false) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt index 8ebe84dd8..ce3361416 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt @@ -28,7 +28,7 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { override var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [DbMediaType] of this [DbMediaItem]. */ - var type by xdLink1(DbMediaType) + override var type by xdLink1(DbMediaType) /** The location of this [DbMediaItem] on disk. */ var location by xdRequiredStringProp(unique = false, trimmed = false) @@ -40,7 +40,7 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { var durationMs by xdNullableLongProp() { requireIf { this.type == DbMediaType.VIDEO } } /** The [DbMediaCollection] this [DbMediaItem] belongs to. */ - var collection: DbMediaCollection by xdParent(DbMediaCollection::items) + override var collection: DbMediaCollection by xdParent(DbMediaCollection::items) /** List of [DbMediaSegment] that this [DbMediaItem] contains. */ val segments by xdChildren0_N(DbMediaSegment::item) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt index a36090768..e6a340dff 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt @@ -12,7 +12,7 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 1.0.0 */ -class DbMediaType(entity: Entity) : XdEnumEntity(entity) { +class DbMediaType(entity: Entity) : XdEnumEntity(entity), MediaItemType { companion object : XdEnumEntityType() { val IMAGE by enumField { description = "IMAGE"; suffix = "mp4"; } val VIDEO by enumField { description = "VIDEO"; suffix = "jpg"; } @@ -30,7 +30,7 @@ class DbMediaType(entity: Entity) : XdEnumEntity(entity) { * * This is a convenience method and requires an active transaction context. */ - fun toApi(): ApiMediaType + override fun toApi(): ApiMediaType = ApiMediaType.values().find { it.toDb() == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") override fun toString(): String = this.description diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index a782dc6a2..57d11ed32 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -2,6 +2,10 @@ package dev.dres.data.model.media interface MediaItem { //TODO + val name: String + val id: String? + val type: MediaItemType + val collection: MediaItemCollection } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index df1518cfc..095c2c7a3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.run +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.Run diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index a48f25e84..a85807f43 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -1,9 +1,11 @@ package dev.dres.data.model.run +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.interfaces.SubmissionValidator @@ -122,5 +124,5 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * @param submission The [DbSubmission] to append. */ - abstract fun postSubmission(submission: DbSubmission) + abstract fun postSubmission(submission: Submission) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 343bca644..2143d19a3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -2,21 +2,21 @@ package dev.dres.data.model.run import dev.dres.api.rest.types.evaluation.ApiEvaluation import dev.dres.data.model.PersistentEntity +import dev.dres.data.model.run.interfaces.Evaluation +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence -typealias EvaluationId = String - /** * Represents a [DbEvaluation], i.e., a concrete instance of a [DbEvaluationTemplate], as executed by DRES. * * @author Ralph Gasser * @version 1.0.0 */ -class DbEvaluation(entity: Entity) : PersistentEntity(entity) { +class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { companion object : XdNaturalEntityType() override fun constructor() { @@ -25,7 +25,7 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity) { } /** The [EvaluationId] of this [DbEvaluation]. */ - var evaluationId: EvaluationId + override var evaluationId: EvaluationId get() = this.id set(value) { this.id = value } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index fcaaa1af3..47aa100e3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -34,13 +34,13 @@ class DbTask(entity: Entity) : PersistentEntity(entity), Task { override var ended by xdNullableLongProp() /** The [DbTaskTemplate] this [DbTask] is an instance of. */ - var template by xdLink1(DbTaskTemplate) + override var template by xdLink1(DbTaskTemplate) /** Link to a [DbTeam] this [DbTask] was created for. Can be NULL!*/ var team by xdLink0_1(DbTeam) /** The [DbEvaluation] this [DbTask] belongs to. */ - var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) + override var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) /** List of [DbSubmission]s received by this [DbTask]. */ val answerSets by xdChildren0_N(DbAnswerSet::task) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 5a669c3e9..2a2c38a66 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -6,6 +6,7 @@ import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionFilter @@ -203,15 +204,17 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe * @throws IllegalArgumentException If [DbSubmission] could not be added for any reason. */ @Synchronized - override fun postSubmission(submission: DbSubmission) { + override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveAsynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this.teamId == submission.team.id) { "Team ${submission.team.id} is not eligible to submit to this task. This is a programmer's error!" } + check(this.teamId == submission.team.teamId) { "Team ${submission.team.teamId} is not eligible to submit to this task. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) + val dbSubmission: DbSubmission = TODO("submission needs to be stored at this point and not earlier") + /* Process Submission. */ - this.submissions.add(submission) + this.submissions.add(dbSubmission) this.validator.validate(submission) AuditLogger.validateSubmission(submission, this.validator) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 21efa2359..923b6c7dc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.audit.AuditLogger import dev.dres.run.filter.SubmissionFilter import kotlinx.dnq.query.* @@ -107,17 +108,19 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu * @param submission The [DbSubmission] to add. */ @Synchronized - override fun postSubmission(submission: DbSubmission) { + override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this@InteractiveSynchronousEvaluation.description.teams.filter { it eq submission.team }.any()) { + check(this@InteractiveSynchronousEvaluation.description.teams.asSequence().filter { it == submission.team }.any()) { "Team ${submission.team.teamId} does not exists for evaluation run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) + val dbSubmission: DbSubmission = TODO("submission needs to be stored at this point and not earlier") + /* Process Submission. */ - this.submissions.add(submission) + this.submissions.add(dbSubmission) this.validator.validate(submission) AuditLogger.validateSubmission(submission, this.validator) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 4719037c5..8762edd24 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -6,6 +6,7 @@ import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.filter.SubmissionFilter import kotlinx.dnq.query.* @@ -51,16 +52,18 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev get() = TODO("Can there be submission filters for non-interactive tasks?") @Synchronized - override fun postSubmission(submission: DbSubmission) { - check(this@NonInteractiveEvaluation.description.teams.filter { it eq submission.team }.any()) { + override fun postSubmission(submission: Submission) { + check(this@NonInteractiveEvaluation.description.teams.asSequence().filter { it == submission.team }.any()) { "Team ${submission.team.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) + val dbSubmission: DbSubmission = TODO("submission needs to be stored at this point and not earlier") + /* Process Submission. */ - this.submissions.add(submission) + this.submissions.add(dbSubmission) /* TODO: Validation? */ } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index 73ce50be5..cb098c5ab 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -1,6 +1,8 @@ package dev.dres.data.model.run +import dev.dres.data.model.run.interfaces.Evaluation import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.template.task.TaskTemplate typealias TaskId = String @@ -9,6 +11,8 @@ interface Task { //TODO val taskId: TaskId val started: Long? val ended: Long? + val evaluation: Evaluation + val template: TaskTemplate fun answerSets(): Sequence diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index fc215b34a..ba4dd6fb4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -2,7 +2,6 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.EvaluationId /** * Represents a [DbEvaluation] that a DRES user or client takes place in and that groups several [TaskRun]s * diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index e5b370118..965ba39a9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -3,6 +3,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.TaskStatus import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.TaskScorer diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index 717b27ab0..e6d09ffe4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -1,12 +1,16 @@ package dev.dres.data.model.submissions import dev.dres.data.model.run.Task +import dev.dres.data.model.template.interfaces.EvaluationTemplate interface AnswerSet { //TODO - val status: VerdictStatus + val task: Task val submission: Submission fun answers() : Sequence + + fun status() : VerdictStatus + fun status(status: VerdictStatus) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 7800865cd..858759702 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -18,7 +18,7 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { companion object : XdNaturalEntityType() /** The [DbVerdictStatus] of this [DbAnswerSet]. */ - override var status by xdLink1(DbVerdictStatus) + var status: DbVerdictStatus by xdLink1(DbVerdictStatus) /** The [DbSubmission] this [DbAnswerSet] belongs to. */ override var submission: DbSubmission by xdParent(DbSubmission::answerSets) @@ -29,6 +29,13 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { val answers by xdChildren1_N(DbAnswer::answerSet) override fun answers(): Sequence = answers.asSequence() + override fun status(): VerdictStatus { + TODO("Not yet implemented") + } + + override fun status(status: VerdictStatus) { + TODO("Not yet implemented") + } /** * Converts this [DbVerdictStatus] to a RESTful API representation [ApiAnswerSet]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 029c99108..8e37dce6e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -11,6 +11,7 @@ import dev.dres.data.model.template.task.DbTaskType import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.template.interfaces.EvaluationTemplate import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard @@ -29,11 +30,11 @@ typealias TemplateId = String * @version 2.0.0 * @author Luca Rossetto & Ralph Gasser */ -class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity){ +class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), EvaluationTemplate{ companion object: XdNaturalEntityType() /** The [TemplateId] of this [DbEvaluationTemplate]. */ - var templateId: TemplateId + override var templateId: TemplateId get() = this.id set(value) { this.id = value } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 03ee3cc87..e5310a8c8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -141,7 +141,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), CachingTaskScor * * @return Textual description of this [DbTaskTemplate]'s content, */ - fun textualDescription(): String = this.hints.asSequence().filter { it.type == DbHintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name + override fun textualDescription(): String = this.hints.asSequence().filter { it.type == DbHintType.TEXT }.maxByOrNull { it.start ?: 0 }?.text ?: name /** * Converts this [DbTaskTemplate] to a RESTful API representation [ApiTaskTemplate]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt index 7719b5056..25b280db9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt @@ -2,7 +2,8 @@ package dev.dres.data.model.template.task import dev.dres.data.model.template.TemplateId -interface TaskTemplate { //TODO +interface TaskTemplate { + fun textualDescription(): String val templateId: TemplateId diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index eae457c14..019f65abe 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -11,10 +11,7 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.SubmissionId -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.team.TeamId import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -137,7 +134,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(Pair(task, sub)) - if (sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { + if (sub.answerSets().filter { v -> v.status() == DbVerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } } @@ -509,10 +506,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ - override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } - require(submission.answerSets.size() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ + require(submission.answerSets().count() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ /* Register submission. */ val task = this.currentTask(context) @@ -524,7 +521,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId) - return submission.answerSets.first().status + return submission.answerSets().first().status() } /** @@ -616,7 +613,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn e ) - // oh shit, something went horribly horribly wrong + // oh shit, something went horribly, horribly wrong if (errorCounter >= MAXIMUM_ERROR_COUNT) { LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_ERROR_COUNT; terminating loop...") RunExecutor.dump(this.evaluation) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 5e4f8db55..b59198007 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -3,6 +3,7 @@ package dev.dres.run import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index f10236c4f..20fc886c6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -7,12 +7,11 @@ import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.audit.AuditLogger import dev.dres.run.eventstream.EventStreamProcessor @@ -445,7 +444,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param context The [RunActionContext] used for the invocation * @param submission [DbSubmission] that should be registered. */ - override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { assureTaskRunning() /* Register submission. */ @@ -463,7 +462,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED)) - return submission.answerSets.first().status + return submission.answerSets().first().status() } /** @@ -614,7 +613,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param context [RunActionContext] used for invocation. * @param sub The [DbSubmission] to apply the [Option] for. */ - private fun prolongOnSubmit(context: RunActionContext, sub: DbSubmission) { + private fun prolongOnSubmit(context: RunActionContext, sub: Submission) { /* require(option.option == SimpleOption.PROLONG_ON_SUBMISSION) { "Cannot process ${option.option} in prolongOnSubmit()." } val limit = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM) ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 72b95481c..15e694504 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -8,6 +8,8 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable @@ -165,7 +167,7 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation * */ override fun tasks(context: RunActionContext): List = this.evaluation.tasks - override fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus { + override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 6b0a49992..48c5aab2b 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -8,10 +8,10 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.run.EvaluationId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.NonInteractiveEvaluation +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.run.audit.AuditLogger import dev.dres.run.validation.interfaces.JudgementValidator diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 48dfc6843..e88f777a4 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -3,11 +3,14 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.data.model.run.* +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator @@ -107,7 +110,7 @@ interface RunManager : Runnable { * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(context: RunActionContext, submission: DbSubmission): DbVerdictStatus + fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index beba5d7c5..758702943 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -5,12 +5,10 @@ import dev.dres.data.model.admin.UserId import dev.dres.data.model.audit.DbAuditLogEntry import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.audit.DbAuditLogType -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionValidator @@ -139,18 +137,18 @@ object AuditLogger { * @param sessionToken The identifier of the user session. * @param address The IP address of the submitter. */ - fun submission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) = this.store.transactional { + fun submission(submission: Submission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) = this.store.transactional { DbAuditLogEntry.new { this.type = DbAuditLogType.SUBMISSION this.source = api this.timestamp = DateTime.now() - this.submissionId = submission.id - this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId - this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ + this.submissionId = submission.submissionId + this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId + this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ this.session = sessionToken this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.answerSets.first().task.evaluation.evaluationId, submission.answerSets.first().task.id, submission)) + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.answerSets().first().task.evaluation.evaluationId, submission.answerSets().first().task.taskId, submission)) } /** @@ -159,16 +157,16 @@ object AuditLogger { * @param submission The [DbSubmission] the submission that was validated * @param validator The [SubmissionValidator] instance. */ - fun validateSubmission(submission: DbSubmission, validator: SubmissionValidator) = this.store.transactional { + fun validateSubmission(submission: Submission, validator: SubmissionValidator) = this.store.transactional { this.store.transactional { DbAuditLogEntry.new { this.type = DbAuditLogType.SUBMISSION_VALIDATION this.source = DbAuditLogSource.INTERNAL this.timestamp = DateTime.now() - this.submissionId = submission.id - this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId - this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ - this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets.first().status.description}" /* TODO: Here name, there ID. Why? */ + this.submissionId = submission.submissionId + this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId + this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ + this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets().first().status()}" /* TODO: Here name, there ID. Why? */ } } } @@ -202,15 +200,15 @@ object AuditLogger { * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system */ - fun prepareJudgement(answerSet: DbAnswerSet, validator: JudgementValidator, token: String) = this.store.transactional { + fun prepareJudgement(answerSet: AnswerSet, validator: JudgementValidator, token: String) = this.store.transactional { DbAuditLogEntry.new { this.type = DbAuditLogType.PREPARE_JUDGEMENT this.source = DbAuditLogSource.INTERNAL this.timestamp = DateTime.now() - this.submissionId = answerSet.submission.id + this.submissionId = answerSet.submission.submissionId this.evaluationId = answerSet.task.evaluation.evaluationId this.taskId = answerSet.task.taskId - this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${answerSet.status.description}" + this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${answerSet.status()}" } } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index ae9627725..a7141d072 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -5,8 +5,8 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog -import dev.dres.data.model.run.EvaluationId -import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.data.model.submissions.Submission @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") sealed class StreamEvent(var timeStamp : Long = System.currentTimeMillis(), var session: String? = null) @@ -14,7 +14,7 @@ class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val task class TaskEndEvent(val runId: EvaluationId, val taskId: EvaluationId) : StreamEvent() class RunStartEvent(val runId: EvaluationId, val description: DbEvaluationTemplate) : StreamEvent() class RunEndEvent(val runId: EvaluationId) : StreamEvent() -class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : DbSubmission) : StreamEvent(session = session) +class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : Submission) : StreamEvent(session = session) class QueryEventLogEvent(session: String?, val runId: EvaluationId, val queryEventLog: QueryEventLog) : StreamEvent(session = session) class QueryResultLogEvent(session: String?, val runId: EvaluationId, val queryResultLog: QueryResultLog) : StreamEvent(session = session) class InvalidRequestEvent(session: String?, val runId: EvaluationId, val requestData: String) : StreamEvent(session = session) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt index a58ae8e14..424a29a7f 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt @@ -2,7 +2,7 @@ package dev.dres.run.eventstream.handlers import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.eventstream.StreamEvent import dev.dres.run.eventstream.StreamEventHandler diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 05762da8e..dd1b46e5b 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -1,8 +1,9 @@ package dev.dres.run.eventstream.handlers -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* import kotlinx.dnq.query.first import kotlinx.dnq.query.size @@ -14,7 +15,7 @@ class SubmissionStatisticsHandler : StreamEventHandler { private val writer = PrintWriter(File("statistics/submission_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - private val submissionTaskMap = mutableMapOf>() + private val submissionTaskMap = mutableMapOf>() private val taskStartMap = mutableMapOf() private val taskNameMap = mutableMapOf() @@ -59,22 +60,22 @@ class SubmissionStatisticsHandler : StreamEventHandler { * * I assume here, that there this handler requires a single verdict per submission. Is this a valid assumption? */ - private fun computeStatistics(submissions: List, taskStart: Long, task: String) { + private fun computeStatistics(submissions: List, taskStart: Long, task: String) { val submissionsByTeam = submissions.groupBy { it.team.teamId } submissionsByTeam.mapValues { it.value.size }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"totalSubmissionsPerTeam\",$count") } submissionsByTeam.mapValues { it.value.firstOrNull { s -> - require(s.answerSets.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.answerSets.first().status == DbVerdictStatus.CORRECT + require(s.answerSets().count() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } + s.answerSets().first().status() == DbVerdictStatus.CORRECT }?.timestamp?.minus(taskStart) }.filter { it.value != null }.forEach{ (teamId, time) -> writer.println("$task,${teamId},\"timeUntilCorrectSubmission\",$time") } submissionsByTeam.mapValues { it.value.indexOfFirst { s -> - require(s.answerSets.size() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.answerSets.first().status == DbVerdictStatus.CORRECT + require(s.answerSets().count() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } + s.answerSets().first().status() == DbVerdictStatus.CORRECT } }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"incorrectBeforeCorrectSubmissions\",$count") diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 14771d36a..9f06718ac 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -1,8 +1,9 @@ package dev.dres.run.eventstream.handlers -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.run.eventstream.* import java.io.File import java.io.PrintWriter @@ -28,7 +29,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { /** * */ - private val submissionTaskMap = mutableMapOf>() + private val submissionTaskMap = mutableMapOf>() init { writer.println("task,team1,team2,score") diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index 0290751f3..4e9d8794e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -29,7 +29,7 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> answer.task.answerSets().filter { - (it.status eq VerdictStatus.Status.CORRECT) && it.submission.team == submission.team + (it.status() eq VerdictStatus.Status.CORRECT) && it.submission.team == submission.team }.count() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index fa19f8361..2b38a0ccb 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -19,7 +19,7 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { val submittedItems = submission.answerSets().flatMap { it.answers() }.mapNotNull { it.item }.toSet() return submission.answerSets().all { answerSet -> answerSet.task.answerSets().filter { taskAnswerSets -> - (taskAnswerSets.status eq VerdictStatus.Status.CORRECT) && taskAnswerSets.submission.team == submission.team && taskAnswerSets.answers().any { it.item in submittedItems } + (taskAnswerSets.status() eq VerdictStatus.Status.CORRECT) && taskAnswerSets.submission.team == submission.team && taskAnswerSets.answers().any { it.item in submittedItems } }.count() < this.limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index e05d81080..3f8f95115 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -28,7 +28,7 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> answer.task.answerSets().filter { - (it.status eq VerdictStatus.Status.CORRECT) && it.submission.team == submission.team && it.submission.user == submission.user + (it.status() eq VerdictStatus.Status.CORRECT) && it.submission.team == submission.team && it.submission.user == submission.user }.count() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 8c4f35d0d..134bbe748 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -28,7 +28,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi */ override fun test(submission: Submission): Boolean { return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { (it.submission.team == submission.team) and (it.status eq VerdictStatus.Status.WRONG) }.count() < max + answerSet.task.answerSets().filter { (it.submission.team == submission.team) and (it.status() eq VerdictStatus.Status.WRONG) }.count() < max } } } diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt index 8f1179af7..63b47504b 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt @@ -1,6 +1,6 @@ package dev.dres.run.score -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.team.TeamId /** diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 8727ad419..a2fda82e8 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -3,9 +3,10 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.Submission import dev.dres.run.RunManagerStatus import dev.dres.run.score.TaskContext import kotlinx.dnq.query.asSequence @@ -24,13 +25,13 @@ class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreb } /** Internal list of [DbAnswerSet] that pend processing. */ - private val list = LinkedList>() + private val list = LinkedList>() /** The [Phase] this [ScoresUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN /** Enqueues a new [DbAnswerSet] for post-processing. */ - fun enqueue(submission: Pair) = this.list.add(submission) + fun enqueue(submission: Pair) = this.list.add(submission) override fun update(status: RunManagerStatus) { if (!this.list.isEmpty()) { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt index 5229daffc..80fda550f 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt @@ -2,6 +2,8 @@ package dev.dres.run.validation import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -12,10 +14,10 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 1.1.0 */ -class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { +class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { companion object{ - fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { + fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { return when { validator.size < 2 -> throw IllegalArgumentException("Chain needs at least two validators") validator.size == 2 -> ChainedSubmissionValidator(validator[0], continueStates, validator[1]) @@ -36,9 +38,9 @@ class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: DbSubmission) { + override fun validate(submission: Submission) { this.firstValidator.validate(submission) - if (submission.answerSets.asSequence().any { this.continueStates.contains(it.status) }) { + if (submission.answerSets().any { this.continueStates.contains(it.status()) }) { this.secondValidator.validate(submission) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index ec8233a2f..58a68e560 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -3,6 +3,7 @@ package dev.dres.run.validation import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -22,13 +23,13 @@ class MediaItemsSubmissionValidator(private val items : Set) : Subm * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: DbSubmission) { - submission.answerSets.asSequence().forEach { answerSet -> + override fun validate(submission: Submission) { + submission.answerSets().forEach { answerSet -> - if (answerSet.answers.asSequence().any { it.item == null || it.item !in this.items} ) { - answerSet.status = DbVerdictStatus.WRONG + if (answerSet.answers().any { it.item == null || it.item !in this.items} ) { + answerSet.status(DbVerdictStatus.WRONG) } else { - answerSet.status = DbVerdictStatus.CORRECT + answerSet.status(DbVerdictStatus.CORRECT) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index d15f43e01..11351ef69 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.Submission import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -26,14 +27,14 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: DbSubmission) { - submission.answerSets.asSequence().forEach { answerSet -> + override fun validate(submission: Submission) { + submission.answerSets().forEach { answerSet -> - answerSet.answers.asSequence().forEach { answer -> + answerSet.answers().forEach { answer -> /* Perform sanity checks. */ if (answer.type != DbAnswerType.TEMPORAL) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } @@ -41,23 +42,23 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien val end = answer.end val item = answer.item if (item == null || start == null || end == null || start > end) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } /* Perform item validation. */ if (answer.item != this.targetSegment.first) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() if (outer.first <= start && outer.second >= end) { - answerSet.status = DbVerdictStatus.CORRECT + answerSet.status(DbVerdictStatus.CORRECT) } else { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 8fe519c1c..42a4c56eb 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -6,6 +6,7 @@ import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.Submission import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -30,14 +31,14 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: DbSubmission) { - submission.answerSets.asSequence().forEach { answerSet -> + override fun validate(submission: Submission) { + submission.answerSets().forEach { answerSet -> - answerSet.answers.asSequence().forEach { answer -> + answerSet.answers().forEach { answer -> /* Perform sanity checks. */ if (answer.type != DbAnswerType.TEMPORAL) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } @@ -45,22 +46,22 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed val end = answer.end val item = answer.item if (item == null || start == null || end == null || start > end) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } /* Perform item validation. */ if (answer.item != this.targetSegment.first) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { - answerSet.status = DbVerdictStatus.CORRECT + answerSet.status(DbVerdictStatus.CORRECT) } else { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 30a6f91cd..3a0686cd0 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -1,8 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -46,29 +44,29 @@ class TextValidator(targets: List) : SubmissionValidator { * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: DbSubmission) { - submission.answerSets.asSequence().forEach { answerSet -> + override fun validate(submission: Submission) { + submission.answerSets().forEach { answerSet -> - answerSet.answers.asSequence().forEach { + answerSet.answers().forEach { answer -> /* Perform sanity checks. */ if (answer.type != DbAnswerType.TEXT) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } /* Perform text validation. */ val text = answer.text if (text == null) { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) return@forEach } if (regex.any { it matches text }) { - answerSet.status = DbVerdictStatus.CORRECT + answerSet.status(DbVerdictStatus.CORRECT) } else { - answerSet.status = DbVerdictStatus.WRONG + answerSet.status(DbVerdictStatus.WRONG) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index eaef729db..b0142176d 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -1,5 +1,6 @@ package dev.dres.run.validation.interfaces +import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus @@ -33,7 +34,7 @@ interface JudgementValidator { * * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - fun next(queue: String): Pair? + fun next(queue: String): Pair? /** * Places a verdict for the [DbSubmission] identified by the given token. diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt index fe164d353..618de774a 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt @@ -2,6 +2,7 @@ package dev.dres.run.validation.interfaces import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission /** * A validator class that checks, if a [DbSubmission] is correct. @@ -15,7 +16,7 @@ interface SubmissionValidator { * * @param submission The [DbSubmission] to validate. */ - fun validate(submission: DbSubmission) + fun validate(submission: Submission) /** * Indicates whether this [SubmissionValidator] needs to defer the validation to some later point in time diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index f132bfffa..8bdc76bc8 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,5 +1,6 @@ package dev.dres.run.validation.interfaces +import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus @@ -26,5 +27,5 @@ interface VoteValidator : JudgementValidator { /** * */ - fun nextSubmissionToVoteOn() : DbAnswerSet? + fun nextSubmissionToVoteOn() : AnswerSet? } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 71b8a93c2..971d6ef4c 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,8 +1,6 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator @@ -41,19 +39,19 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e private val updateLock = ReentrantReadWriteLock() /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ - private val queue: Queue = LinkedList() + private val queue: Queue = LinkedList() /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ - private val queuedItemRanges: MutableMap> = HashMap() + private val queuedItemRanges: MutableMap> = HashMap() /** Internal map of all [DbAnswerSet]s that have been retrieved by a judge and are pending a verdict. */ - private val waiting = HashMap() + private val waiting = HashMap() /** Helper structure to keep track when a request needs to be re-scheduled */ private val timeouts = mutableListOf>() /** Internal map of already judged [DbSubmission]s, independent of their source. */ - private val cache: MutableMap = ConcurrentHashMap() + private val cache: MutableMap = ConcurrentHashMap() init { knownCorrectRanges.forEach { cache[it] = DbVerdictStatus.CORRECT } @@ -94,22 +92,22 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: DbSubmission) = this.updateLock.read { - for (verdict in submission.answerSets.asSequence()) { + override fun validate(submission: Submission) = this.updateLock.read { + for (verdict in submission.answerSets()) { //only validate submissions which are not already validated - if (verdict.status != DbVerdictStatus.INDETERMINATE){ + if (verdict.status() != DbVerdictStatus.INDETERMINATE){ continue } //check cache first - val itemRange = ItemRange(submission.answerSets.first().answers.first()) //TODO reason about semantics + val itemRange = ItemRange(submission.answerSets().first().answers().first()) //TODO reason about semantics val cachedStatus = this.cache[itemRange] if (cachedStatus != null) { - verdict.status = cachedStatus + verdict.status(cachedStatus) } else if (itemRange !in queuedItemRanges.keys) { updateLock.write { this.queue.offer(verdict) - verdict.status = DbVerdictStatus.INDETERMINATE + verdict.status(DbVerdictStatus.INDETERMINATE) this.queuedItemRanges[itemRange] = mutableListOf(verdict) } } else { @@ -127,7 +125,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - override fun next(queue: String): Pair? = updateLock.write { + override fun next(queue: String): Pair? = updateLock.write { checkTimeOuts() val next = this.queue.poll() return if (next != null) { @@ -148,16 +146,16 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * @param verdict The verdict of the judge. */ override fun judge(token: String, verdict: DbVerdictStatus) { - processSubmission(token, verdict).status = verdict + processSubmission(token, verdict).status(verdict) } /** * */ - fun processSubmission(token: String, status: DbVerdictStatus) : DbAnswerSet = this.updateLock.write { + fun processSubmission(token: String, status: VerdictStatus) : AnswerSet = this.updateLock.write { val verdict = this.waiting[token] ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged - val itemRange = ItemRange(verdict.answers.first()) //TODO reason about semantics + val itemRange = ItemRange(verdict.answers().first()) //TODO reason about semantics //add to cache this.cache[itemRange] = status @@ -167,7 +165,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e //remove from queue set val otherSubmissions = this.queuedItemRanges.remove(itemRange) - otherSubmissions?.forEach { it.status = status } + otherSubmissions?.forEach { it.status(status) } return@write verdict } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index cc7198340..6e02aacc7 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -1,5 +1,6 @@ package dev.dres.run.validation.judged +import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.validation.interfaces.VoteValidator @@ -25,7 +26,7 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() private val defaultVoteDifference = 1 } - private val submissionQueue = ConcurrentLinkedQueue() + private val submissionQueue = ConcurrentLinkedQueue() private val voteCountMap = ConcurrentHashMap() private val updateLock = ReentrantReadWriteLock() @@ -45,7 +46,7 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() if (enoughVotes()){ val finalVerdict = this.voteCountMap.entries.maxByOrNull { it.value }!!.key - verdict.status = finalVerdict + verdict.status(finalVerdict) this.submissionQueue.poll() this.voteCountMap.clear() } @@ -59,14 +60,14 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() return max - others >= voteDifference } - override fun nextSubmissionToVoteOn(): DbAnswerSet? = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? + override fun nextSubmissionToVoteOn() = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? //siphon of undecidable submission from logic of super class override fun judge(token: String, status: DbVerdictStatus) { val verdict = super.processSubmission(token, status) when (status){ DbVerdictStatus.CORRECT, - DbVerdictStatus.WRONG -> verdict.status = status + DbVerdictStatus.WRONG -> verdict.status(status) DbVerdictStatus.INDETERMINATE -> {} DbVerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 2e1ddbee4..ec4b0b8f0 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -1,6 +1,7 @@ package dev.dres.run.validation.judged import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.submissions.Answer import dev.dres.data.model.submissions.DbAnswer import dev.dres.data.model.submissions.DbAnswerType @@ -13,9 +14,9 @@ import dev.dres.data.model.submissions.DbAnswerType data class ItemRange(val element: String, val start: Long, val end: Long){ constructor(item: DbMediaItem): this(item.id, 0, 0) constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) - constructor(answer: DbAnswer): this(when (answer.type){ + constructor(answer: Answer): this(when (answer.type){ DbAnswerType.ITEM, - DbAnswerType.TEMPORAL -> answer.item!!.id + DbAnswerType.TEMPORAL -> answer.item!!.id!! DbAnswerType.TEXT -> answer.text!! else -> throw IllegalStateException("Submission contains neither item nor text.") }, answer.start ?: 0, answer.end ?: 0) diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 7860376e9..0fbe791b1 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -6,12 +6,11 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.util.MimeTypeHelper import dev.dres.data.model.admin.UserId -import dev.dres.data.model.run.EvaluationId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.run.RunExecutor import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus import io.javalin.http.Context -import kotlinx.dnq.query.FilteringContext.eq import kotlinx.dnq.query.filter import kotlinx.dnq.query.flatMapDistinct import kotlinx.dnq.query.isEmpty From d3398ac1b743fc58a8e71d3d71d414624db35b0b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 08:51:35 +0100 Subject: [PATCH 106/498] Added files git decided to ignore for some reason --- .../dev/dres/data/model/media/MediaItemCollection.kt | 7 +++++++ .../kotlin/dev/dres/data/model/media/MediaItemType.kt | 7 +++++++ .../dev/dres/data/model/run/interfaces/Evaluation.kt | 9 +++++++++ .../data/model/template/interfaces/EvaluationTemplate.kt | 9 +++++++++ 4 files changed, 32 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/interfaces/EvaluationTemplate.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt new file mode 100644 index 000000000..918921527 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt @@ -0,0 +1,7 @@ +package dev.dres.data.model.media + +interface MediaItemCollection { + + val id: String + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt new file mode 100644 index 000000000..3a9f96939 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt @@ -0,0 +1,7 @@ +package dev.dres.data.model.media + +import dev.dres.api.rest.types.collection.ApiMediaType + +interface MediaItemType { + fun toApi(): ApiMediaType +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt new file mode 100644 index 000000000..083a22ecf --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt @@ -0,0 +1,9 @@ +package dev.dres.data.model.run.interfaces + +typealias EvaluationId = String + +interface Evaluation { + + val evaluationId: EvaluationId + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/EvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/EvaluationTemplate.kt new file mode 100644 index 000000000..cd99ac454 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/EvaluationTemplate.kt @@ -0,0 +1,9 @@ +package dev.dres.data.model.template.interfaces + +import dev.dres.data.model.template.TemplateId + +interface EvaluationTemplate { + + val templateId: TemplateId + +} \ No newline at end of file From cc6c3f23f81a78e2a12f52dd297a810705b00267 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 09:02:44 +0100 Subject: [PATCH 107/498] Simplified VerdictStatus --- .../handler/judgement/PostJudgementHandler.kt | 3 +- .../rest/handler/judgement/PostVoteHandler.kt | 3 +- .../submission/LegacySubmissionHandler.kt | 11 +++--- .../handler/submission/SubmissionHandler.kt | 12 +++--- .../rest/types/evaluation/ApiVerdictStatus.kt | 9 +---- .../dres/data/model/submissions/AnswerSet.kt | 1 - .../data/model/submissions/DbAnswerSet.kt | 6 +-- .../data/model/submissions/DbVerdictStatus.kt | 21 +++++----- .../data/model/submissions/VerdictStatus.kt | 38 +++++++++++++++++-- .../run/InteractiveAsynchronousRunManager.kt | 2 +- .../handlers/SubmissionStatisticsHandler.kt | 5 ++- .../dres/run/filter/CorrectPerTeamFilter.kt | 2 +- .../run/filter/CorrectPerTeamItemFilter.kt | 2 +- .../run/filter/CorrectPerTeamMemberFilter.kt | 2 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 2 +- .../MediaItemsSubmissionValidator.kt | 5 ++- .../TemporalContainmentSubmissionValidator.kt | 15 +++----- .../TemporalOverlapSubmissionValidator.kt | 15 +++----- .../dev/dres/run/validation/TextValidator.kt | 8 ++-- .../interfaces/JudgementValidator.kt | 11 ++---- .../validation/interfaces/VoteValidator.kt | 5 +-- .../judged/BasicJudgementValidator.kt | 10 ++--- .../validation/judged/BasicVoteValidator.kt | 17 +++++---- 23 files changed, 112 insertions(+), 93 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 5346403c4..fc8c2a52e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.utilities.extensions.eligibleManagerForId @@ -56,7 +57,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) try { - validator.judge(judgement.token, judgement.verdict.toDb()) + validator.judge(judgement.token, VerdictStatus.fromApi(judgement.verdict)) } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index 67d099952..94d26b3dd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.judgement.ApiVote import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -50,7 +51,7 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto val validator = evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } // Get first active vote validator ?: throw ErrorStatusException(404, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx) validator as VoteValidator - validator.vote(vote.verdict.toDb()) + validator.vote(VerdictStatus.fromApi(vote.verdict)) } return SuccessStatus("vote received") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index ea3ed4eac..dab59c1a8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus @@ -116,13 +117,13 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v logger.info("Submission ${s.id} received status $r.") return when (r) { - DbVerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(DbVerdictStatus.CORRECT.toApi(), "Submission correct!") - DbVerdictStatus.WRONG -> SuccessfulSubmissionsStatus(DbVerdictStatus.WRONG.toApi(), "Submission incorrect! Try again") - DbVerdictStatus.INDETERMINATE -> { + VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") + VerdictStatus.WRONG -> SuccessfulSubmissionsStatus(ApiVerdictStatus.WRONG, "Submission incorrect! Try again") + VerdictStatus.INDETERMINATE -> { ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(DbVerdictStatus.INDETERMINATE.toApi(), "Submission received. Waiting for verdict!") + SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") } - DbVerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(DbVerdictStatus.UNDECIDABLE.toApi(),"Submission undecidable. Try again!") + VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(ApiVerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 22b8d3f32..44fbd4420 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.competition.ApiEvaluationStartMessage import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus import dev.dres.api.rest.types.users.ApiRole @@ -12,6 +13,7 @@ import dev.dres.data.model.Config import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import dev.dres.run.NonInteractiveRunManager @@ -72,14 +74,14 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con logger.info("Submission ${apiSubmission.id} received status $result.") return@transactional when (result) { - DbVerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(DbVerdictStatus.CORRECT.toApi(), "Submission correct!") - DbVerdictStatus.WRONG -> SuccessfulSubmissionsStatus(DbVerdictStatus.WRONG.toApi(), "Submission incorrect! Try again") - DbVerdictStatus.INDETERMINATE -> { + VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") + VerdictStatus.WRONG -> SuccessfulSubmissionsStatus(ApiVerdictStatus.WRONG, "Submission incorrect! Try again") + VerdictStatus.INDETERMINATE -> { ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(DbVerdictStatus.INDETERMINATE.toApi(), "Submission received. Waiting for verdict!") + SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") } - DbVerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(DbVerdictStatus.UNDECIDABLE.toApi(),"Submission undecidable. Try again!") + VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(ApiVerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt index faddd886a..23d5a61c0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.VerdictStatus /** * The RESTful API equivalent for the type of a [DbVerdictStatus] @@ -10,11 +9,8 @@ import dev.dres.data.model.submissions.VerdictStatus * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiVerdictStatus(private val status: VerdictStatus.Status) : VerdictStatus { - CORRECT(VerdictStatus.Status.CORRECT), - WRONG(VerdictStatus.Status.WRONG), - INDETERMINATE(VerdictStatus.Status.INDETERMINATE), - UNDECIDABLE(VerdictStatus.Status.INDETERMINATE); +enum class ApiVerdictStatus() { + CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; /** * Converts this [ApiVerdictStatus] to a [DbVerdictStatus] representation. Requires an ongoing transaction. @@ -28,5 +24,4 @@ enum class ApiVerdictStatus(private val status: VerdictStatus.Status) : VerdictS UNDECIDABLE -> DbVerdictStatus.UNDECIDABLE } - override fun eq(status: VerdictStatus.Status): Boolean = status == this.status } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index e6d09ffe4..5bef6c673 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.submissions import dev.dres.data.model.run.Task -import dev.dres.data.model.template.interfaces.EvaluationTemplate interface AnswerSet { //TODO diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 858759702..5f2470ea6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -29,12 +29,10 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { val answers by xdChildren1_N(DbAnswer::answerSet) override fun answers(): Sequence = answers.asSequence() - override fun status(): VerdictStatus { - TODO("Not yet implemented") - } + override fun status(): VerdictStatus = VerdictStatus.fromDb(status) override fun status(status: VerdictStatus) { - TODO("Not yet implemented") + this.status = status.toDb() } /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt index 83af93dc4..6a2ca7850 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt @@ -13,12 +13,12 @@ import kotlinx.dnq.xdRequiredStringProp * @author Luca Rossetto * @version 2.0.0 */ -class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity), VerdictStatus { +class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { - val CORRECT by enumField { description = VerdictStatus.Status.CORRECT.name } /** Submission has been deemed as correct. */ - val WRONG by enumField { description = VerdictStatus.Status.WRONG.name } - val INDETERMINATE by enumField { description = VerdictStatus.Status.INDETERMINATE.name } /** Submission has been deemed as wrong. */ - val UNDECIDABLE by enumField { description = VerdictStatus.Status.UNDECIDABLE.name } /** Submission has not been validated yet. */ + val CORRECT by enumField { description = VerdictStatus.CORRECT.name } /** Submission has been deemed as correct. */ + val WRONG by enumField { description = VerdictStatus.WRONG.name } + val INDETERMINATE by enumField { description = VerdictStatus.INDETERMINATE.name } /** Submission has been deemed as wrong. */ + val UNDECIDABLE by enumField { description = VerdictStatus.UNDECIDABLE.name } /** Submission has not been validated yet. */ /** * Returns a list of all [DbRole] values. @@ -31,10 +31,10 @@ class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity), VerdictStatus { * Parses a [DbRole] instance from a [String]. */ fun parse(string: String) = when (string.uppercase()) { - VerdictStatus.Status.CORRECT.name -> CORRECT - VerdictStatus.Status.WRONG.name -> WRONG - VerdictStatus.Status.INDETERMINATE.name -> INDETERMINATE - VerdictStatus.Status.UNDECIDABLE.name -> UNDECIDABLE + VerdictStatus.CORRECT.name -> CORRECT + VerdictStatus.WRONG.name -> WRONG + VerdictStatus.INDETERMINATE.name -> INDETERMINATE + VerdictStatus.UNDECIDABLE.name -> UNDECIDABLE else -> throw IllegalArgumentException("Failed to parse submission status '$string'.") } } @@ -48,8 +48,7 @@ class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity), VerdictStatus { * * @return [ApiVerdictStatus] */ - fun toApi() = ApiVerdictStatus.values().find { it.toDb() == this } ?: throw IllegalStateException("Verdict status ${this.description} is not supported.") - override fun eq(status: VerdictStatus.Status): Boolean = status.name == this.description + fun toApi() = VerdictStatus.fromDb(this).toApi() override fun toString(): String = this.description diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt index 0bba5ba9f..21ed06c12 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt @@ -1,11 +1,41 @@ package dev.dres.data.model.submissions -interface VerdictStatus { +import dev.dres.api.rest.types.evaluation.ApiVerdictStatus - enum class Status { - CORRECT, WRONG, INDETERMINATE, UNDECIDABLE +enum class VerdictStatus { + CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; + + fun toApi(): ApiVerdictStatus = when(this) { + CORRECT -> ApiVerdictStatus.CORRECT + WRONG -> ApiVerdictStatus.WRONG + INDETERMINATE -> ApiVerdictStatus.INDETERMINATE + UNDECIDABLE -> ApiVerdictStatus.UNDECIDABLE + } + + fun toDb(): DbVerdictStatus = when(this) { + CORRECT -> DbVerdictStatus.CORRECT + WRONG -> DbVerdictStatus.WRONG + INDETERMINATE -> DbVerdictStatus.INDETERMINATE + UNDECIDABLE -> DbVerdictStatus.UNDECIDABLE } - infix fun eq(status: Status): Boolean + companion object { + + fun fromApi(status: ApiVerdictStatus): VerdictStatus = when(status) { + ApiVerdictStatus.CORRECT -> CORRECT + ApiVerdictStatus.WRONG -> WRONG + ApiVerdictStatus.INDETERMINATE -> INDETERMINATE + ApiVerdictStatus.UNDECIDABLE -> UNDECIDABLE + } + + fun fromDb(status: DbVerdictStatus): VerdictStatus = when(status) { + DbVerdictStatus.CORRECT -> CORRECT + DbVerdictStatus.WRONG -> WRONG + DbVerdictStatus.INDETERMINATE -> INDETERMINATE + DbVerdictStatus.UNDECIDABLE -> UNDECIDABLE + else -> throw IllegalStateException("Unknown DbVerdictStatus $status") + } + + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 019f65abe..59e3ce95a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -134,7 +134,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(Pair(task, sub)) - if (sub.answerSets().filter { v -> v.status() == DbVerdictStatus.INDETERMINATE }.any()) { + if (sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index dd1b46e5b..7723e219c 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.eventstream.* import kotlinx.dnq.query.first import kotlinx.dnq.query.size @@ -68,14 +69,14 @@ class SubmissionStatisticsHandler : StreamEventHandler { submissionsByTeam.mapValues { it.value.firstOrNull { s -> require(s.answerSets().count() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.answerSets().first().status() == DbVerdictStatus.CORRECT + s.answerSets().first().status() == VerdictStatus.CORRECT }?.timestamp?.minus(taskStart) }.filter { it.value != null }.forEach{ (teamId, time) -> writer.println("$task,${teamId},\"timeUntilCorrectSubmission\",$time") } submissionsByTeam.mapValues { it.value.indexOfFirst { s -> require(s.answerSets().count() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.answerSets().first().status() == DbVerdictStatus.CORRECT + s.answerSets().first().status() == VerdictStatus.CORRECT } }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"incorrectBeforeCorrectSubmissions\",$count") diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index 4e9d8794e..a28d8e72c 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -29,7 +29,7 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> answer.task.answerSets().filter { - (it.status() eq VerdictStatus.Status.CORRECT) && it.submission.team == submission.team + (it.status() == VerdictStatus.CORRECT) && it.submission.team == submission.team }.count() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index 2b38a0ccb..0a40491bc 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -19,7 +19,7 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { val submittedItems = submission.answerSets().flatMap { it.answers() }.mapNotNull { it.item }.toSet() return submission.answerSets().all { answerSet -> answerSet.task.answerSets().filter { taskAnswerSets -> - (taskAnswerSets.status() eq VerdictStatus.Status.CORRECT) && taskAnswerSets.submission.team == submission.team && taskAnswerSets.answers().any { it.item in submittedItems } + (taskAnswerSets.status() == VerdictStatus.CORRECT) && taskAnswerSets.submission.team == submission.team && taskAnswerSets.answers().any { it.item in submittedItems } }.count() < this.limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index 3f8f95115..f44ef7424 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -28,7 +28,7 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> answer.task.answerSets().filter { - (it.status() eq VerdictStatus.Status.CORRECT) && it.submission.team == submission.team && it.submission.user == submission.user + (it.status() == VerdictStatus.CORRECT) && it.submission.team == submission.team && it.submission.user == submission.user }.count() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 134bbe748..6ff9506b6 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -28,7 +28,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi */ override fun test(submission: Submission): Boolean { return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { (it.submission.team == submission.team) and (it.status() eq VerdictStatus.Status.WRONG) }.count() < max + answerSet.task.answerSets().filter { (it.submission.team == submission.team) and (it.status() == VerdictStatus.WRONG) }.count() < max } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index 58a68e560..f8046fbc1 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -27,9 +28,9 @@ class MediaItemsSubmissionValidator(private val items : Set) : Subm submission.answerSets().forEach { answerSet -> if (answerSet.answers().any { it.item == null || it.item !in this.items} ) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) } else { - answerSet.status(DbVerdictStatus.CORRECT) + answerSet.status(VerdictStatus.CORRECT) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 11351ef69..c5bbcb3c1 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -1,10 +1,7 @@ package dev.dres.run.validation +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.DbAnswerType -import dev.dres.data.model.submissions.Submission import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -34,7 +31,7 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien /* Perform sanity checks. */ if (answer.type != DbAnswerType.TEMPORAL) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } @@ -42,23 +39,23 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien val end = answer.end val item = answer.item if (item == null || start == null || end == null || start > end) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } /* Perform item validation. */ if (answer.item != this.targetSegment.first) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() if (outer.first <= start && outer.second >= end) { - answerSet.status(DbVerdictStatus.CORRECT) + answerSet.status(VerdictStatus.CORRECT) } else { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 42a4c56eb..6f9c3852a 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -3,10 +3,7 @@ package dev.dres.run.validation import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.DbAnswerType -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence @@ -38,7 +35,7 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed /* Perform sanity checks. */ if (answer.type != DbAnswerType.TEMPORAL) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } @@ -46,22 +43,22 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed val end = answer.end val item = answer.item if (item == null || start == null || end == null || start > end) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } /* Perform item validation. */ if (answer.item != this.targetSegment.first) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { - answerSet.status(DbVerdictStatus.CORRECT) + answerSet.status(VerdictStatus.CORRECT) } else { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 3a0686cd0..3c1042e8e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -52,21 +52,21 @@ class TextValidator(targets: List) : SubmissionValidator { /* Perform sanity checks. */ if (answer.type != DbAnswerType.TEXT) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } /* Perform text validation. */ val text = answer.text if (text == null) { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) return@forEach } if (regex.any { it matches text }) { - answerSet.status(DbVerdictStatus.CORRECT) + answerSet.status(VerdictStatus.CORRECT) } else { - answerSet.status(DbVerdictStatus.WRONG) + answerSet.status(VerdictStatus.WRONG) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index b0142176d..9e8428b62 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -1,9 +1,6 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.* /** * A [SubmissionValidator] that bases validation on human (manual) verdicts. @@ -37,11 +34,11 @@ interface JudgementValidator { fun next(queue: String): Pair? /** - * Places a verdict for the [DbSubmission] identified by the given token. + * Places a verdict for the [Submission] identified by the given token. * - * @param token The token used to identify the [DbSubmission]. + * @param token The token used to identify the [Submission]. * @param verdict The verdict of the judge. */ - fun judge(token: String, verdict: DbVerdictStatus) + fun judge(token: String, verdict: VerdictStatus) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index 8bdc76bc8..a49ab1f98 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,8 +1,7 @@ package dev.dres.run.validation.interfaces import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.VerdictStatus /** * @@ -22,7 +21,7 @@ interface VoteValidator : JudgementValidator { /** * Places a verdict for the currently active Submission */ - fun vote(verdict: DbVerdictStatus) + fun vote(verdict: VerdictStatus) /** * diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 971d6ef4c..ecaeb2662 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -54,8 +54,8 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e private val cache: MutableMap = ConcurrentHashMap() init { - knownCorrectRanges.forEach { cache[it] = DbVerdictStatus.CORRECT } - knownWrongRanges.forEach { cache[it] = DbVerdictStatus.WRONG } + knownCorrectRanges.forEach { cache[it] = VerdictStatus.CORRECT } + knownWrongRanges.forEach { cache[it] = VerdictStatus.WRONG } } private fun checkTimeOuts() = updateLock.write { @@ -95,7 +95,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e override fun validate(submission: Submission) = this.updateLock.read { for (verdict in submission.answerSets()) { //only validate submissions which are not already validated - if (verdict.status() != DbVerdictStatus.INDETERMINATE){ + if (verdict.status() != VerdictStatus.INDETERMINATE){ continue } @@ -107,7 +107,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e } else if (itemRange !in queuedItemRanges.keys) { updateLock.write { this.queue.offer(verdict) - verdict.status(DbVerdictStatus.INDETERMINATE) + verdict.status(VerdictStatus.INDETERMINATE) this.queuedItemRanges[itemRange] = mutableListOf(verdict) } } else { @@ -145,7 +145,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * @param token The token used to identify the [DbSubmission]. * @param verdict The verdict of the judge. */ - override fun judge(token: String, verdict: DbVerdictStatus) { + override fun judge(token: String, verdict: VerdictStatus) { processSubmission(token, verdict).status(verdict) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 6e02aacc7..ce80165e6 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -3,6 +3,7 @@ package dev.dres.run.validation.judged import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.validation.interfaces.VoteValidator import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue @@ -27,7 +28,7 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() } private val submissionQueue = ConcurrentLinkedQueue() - private val voteCountMap = ConcurrentHashMap() + private val voteCountMap = ConcurrentHashMap() private val updateLock = ReentrantReadWriteLock() override val isActive: Boolean @@ -36,8 +37,8 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() override val voteCount: Map get() = voteCountMap.mapKeys { it.toString() } - override fun vote(status: DbVerdictStatus) = updateLock.write { - if (status == DbVerdictStatus.INDETERMINATE || status == DbVerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does + override fun vote(status: VerdictStatus) = updateLock.write { + if (status == VerdictStatus.INDETERMINATE || status == VerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does return@write } @@ -63,13 +64,13 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() override fun nextSubmissionToVoteOn() = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? //siphon of undecidable submission from logic of super class - override fun judge(token: String, status: DbVerdictStatus) { + override fun judge(token: String, status: VerdictStatus) { val verdict = super.processSubmission(token, status) when (status){ - DbVerdictStatus.CORRECT, - DbVerdictStatus.WRONG -> verdict.status(status) - DbVerdictStatus.INDETERMINATE -> {} - DbVerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) + VerdictStatus.CORRECT, + VerdictStatus.WRONG -> verdict.status(status) + VerdictStatus.INDETERMINATE -> {} + VerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) } } } \ No newline at end of file From 875ef495d0a53416c81b422988eaccc7b8cc7df6 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 10:08:29 +0100 Subject: [PATCH 108/498] Fixed and optimized import of collection and segments --- .../dres/api/cli/MediaCollectionCommand.kt | 189 ++++++++++++++---- .../api/rest/types/collection/ApiMediaItem.kt | 5 +- 2 files changed, 151 insertions(+), 43 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 90b8e278b..f034fe405 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -40,7 +40,20 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik private val logger = LoggerFactory.getLogger(this.javaClass) init { - this.subcommands(Create(), Delete(), Update(), List(), Show(), Check(), Scan(), AddItem(), DeleteItem(), Export(), Import(), ImportSegments()) + this.subcommands( + Create(), + Delete(), + Update(), + List(), + Show(), + Check(), + Scan(), + AddItem(), + DeleteItem(), + Export(), + Import(), + ImportSegments() + ) } override fun aliases(): Map> { @@ -60,7 +73,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /** * */ - abstract inner class AbstractCollectionCommand(name: String, help: String) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = true) { + abstract inner class AbstractCollectionCommand(name: String, help: String) : + CliktCommand(name = name, help = help, printHelpOnEmptyArgs = true) { /** The [CollectionId] of the [DbMediaCollection] affected by this [AbstractCollectionCommand]. */ protected val id: CollectionId? by option("-i", "--id", help = "ID of a media collection.") @@ -74,23 +88,33 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik * * @return [DbMediaCollection] or null */ - protected fun getCollection(): DbMediaCollection? - = DbMediaCollection.query((DbMediaCollection::id eq this.id).or(DbMediaCollection::name eq this.name)).firstOrNull() + protected fun getCollection(): DbMediaCollection? = + DbMediaCollection.query((DbMediaCollection::id eq this.id).or(DbMediaCollection::name eq this.name)) + .firstOrNull() } /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Create: CliktCommand(name = "create", help = "Creates a new media collection.", printHelpOnEmptyArgs = true) { + inner class Create : + CliktCommand(name = "create", help = "Creates a new media collection.", printHelpOnEmptyArgs = true) { /** The name of the new [DbMediaCollection]. */ private val name: String by option("-n", "--name", help = "Name of the Collection to be created").required() /** A description of the new [DbMediaCollection]. */ - private val description: String by option("-d", "--description", help = "Description of the Collection to be created").default("") + private val description: String by option( + "-d", + "--description", + help = "Description of the Collection to be created" + ).default("") /** The base path to the new [DbMediaCollection]. */ - private val basePath: String by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to").required() + private val basePath: String by option( + "-p", + "--path", + help = "Base path of the Collection all contained Items will be specified relative to" + ).required() override fun run() { if (!Files.exists(Paths.get(this.basePath))) { @@ -110,7 +134,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Delete: AbstractCollectionCommand("delete", help = "Deletes a media collection.") { + inner class Delete : AbstractCollectionCommand("delete", help = "Deletes a media collection.") { override fun run() { this@MediaCollectionCommand.store.transactional { val collection = this.getCollection() @@ -133,10 +157,18 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik private val newName: String? by option("-n", "--name", help = "The new name of the collection") /** The new description for the [DbMediaCollection]. */ - private val newDescription: String? by option("-d", "--description", help = "Description of the Collection to be created") + private val newDescription: String? by option( + "-d", + "--description", + help = "Description of the Collection to be created" + ) /** The new path for the [DbMediaCollection]. */ - private val newPath: String? by option("-p", "--path", help = "Base path of the Collection all contained Items will be specified relative to") + private val newPath: String? by option( + "-p", + "--path", + help = "Base path of the Collection all contained Items will be specified relative to" + ) override fun run() { this@MediaCollectionCommand.store.transactional { @@ -160,7 +192,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik * [CliktCommand] to list all [DbMediaCollection]s. */ inner class List : CliktCommand(name = "list", help = "Lists all media collections.") { - val plain by option("-p", "--plain", help = "Plain print: No fancy table presentation for machine readable output").flag(default = false) + val plain by option( + "-p", + "--plain", + help = "Plain print: No fancy table presentation for machine readable output" + ).flag(default = false) override fun run() = this@MediaCollectionCommand.store.transactional(true) { println("Available media collections ${DbMediaCollection.all().size()}") @@ -194,7 +230,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class Show : AbstractCollectionCommand("show", help = "Lists the content of a media collection.") { /** The property of the [DbMediaItem]s to sort by. */ - private val sort by option("-s", "--sort", help = "Chose which sorting to use").enum(ignoreCase = true).defaultLazy { SortField.NAME } + private val sort by option( + "-s", + "--sort", + help = "Chose which sorting to use" + ).enum(ignoreCase = true).defaultLazy { SortField.NAME } private val plain by option("-p", "--plain", help = "Plain formatting. No fancy tables").flag(default = false) @@ -233,7 +273,14 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } body { query.asSequence().forEach { - row(it.id, it.name, it.location, it.type.description, it.durationMs ?: "n/a", it.fps ?: "n/a") + row( + it.id, + it.name, + it.location, + it.type.description, + it.durationMs ?: "n/a", + it.fps ?: "n/a" + ) } } } @@ -245,7 +292,10 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik /** * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ - inner class Check : AbstractCollectionCommand("check", help = "Checks if all the files in a media collection are present and accessible.") { + inner class Check : AbstractCollectionCommand( + "check", + help = "Checks if all the files in a media collection are present and accessible." + ) { override fun run() = this@MediaCollectionCommand.store.transactional(true) { /* Find media collection. */ val collection = this.getCollection() @@ -278,10 +328,19 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class Scan : AbstractCollectionCommand("scan", help = "Scans a collection directory and adds found items") { /** The file suffices that should be considered as images. */ - private val imageTypes by option("-it", "--imageType", help = "Image file types (endings) to be considered in the scan").convert { it.lowercase() }.multiple() /** The file suffices that should be considered as images. */ + private val imageTypes by option( + "-it", + "--imageType", + help = "Image file types (endings) to be considered in the scan" + ).convert { it.lowercase() }.multiple() + /** The file suffices that should be considered as images. */ /** The file suffices that should be considered as videos. */ - private val videoTypes by option("-vt", "--videoType", help = "Video file types (endings) to be considered in the scan").convert { it.lowercase() }.multiple() + private val videoTypes by option( + "-vt", + "--videoType", + help = "Video file types (endings) to be considered in the scan" + ).convert { it.lowercase() }.multiple() override fun run() = this@MediaCollectionCommand.store.transactional { /* Sanity cehck. */ @@ -320,7 +379,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) }.forEach { val relativePath = it.relativeTo(base) - val exists = DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty + val exists = + DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty if (!exists) { try { when (it.extension.lowercase()) { @@ -332,6 +392,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik this.location = relativePath.toString() }) } + in videoTypes -> { println("Found video $it; analyzing...") val result = FFmpegUtil.analyze(it).streams.first() @@ -358,8 +419,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik }) } } - } catch(e:Throwable) { - this@MediaCollectionCommand.logger.error(this@MediaCollectionCommand.logMarker, "An error occurred with $it. Noting and skipping...") + } catch (e: Throwable) { + this@MediaCollectionCommand.logger.error( + this@MediaCollectionCommand.logMarker, + "An error occurred with $it. Noting and skipping..." + ) println("An error occurred with $it. Noting and skipping...") issues[it] = e.stackTraceToString() } @@ -388,8 +452,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik private val itemName: String? by option("-in", "--itemName", help = "The exact name of the media item.") /** A RegEx matching the name of the [DbMediaItem] to delete. */ - private val itemNameRegex: Regex? by option("-e", "--regex", help="Regex for item names").convert { it.toRegex() } - + private val itemNameRegex: Regex? by option( + "-e", + "--regex", + help = "Regex for item names" + ).convert { it.toRegex() } override fun run() = this@MediaCollectionCommand.store.transactional { @@ -430,10 +497,18 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class AddItem : AbstractCollectionCommand(name = "add", help = "Adds a media item to a media collection.") { /** The [ApiMediaType] of the new [DbMediaItem]. */ - private val type: ApiMediaType by option("-t", "--type", help = "Type of the new media item.").enum().required() + private val type: ApiMediaType by option( + "-t", + "--type", + help = "Type of the new media item." + ).enum().required() /** The relative path of the new [DbMediaItem]. */ - private val path: String by option("-p", "--path", help = "Path of the new media item. relative to the collection base path").required() + private val path: String by option( + "-p", + "--path", + help = "Path of the new media item. relative to the collection base path" + ).required() /** The duration of the new [DbMediaItem]. */ private val duration: Long? by option("-d", "--duration", help = "video duration in seconds").long() @@ -474,7 +549,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class Export : AbstractCollectionCommand("export", help = "Exports a media collection into a CSV file.") { /** The output path for the export.. */ - private val output: Path by option("-o", "--output", help = "Path of the file the media collection should to be exported to.").convert { Paths.get(it)}.required() + private val output: Path by option( + "-o", + "--output", + help = "Path of the file the media collection should to be exported to." + ).convert { Paths.get(it) }.required() /** The header of an exported CSV file. */ private val header = listOf("itemType", "name", "location", "duration", "fps") @@ -491,7 +570,15 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik csvWriter().open(os) { writeRow(this@Export.header) collection.items.asSequence().forEach { - writeRow(listOf(it.type.description, it.name, it.location, it.durationMs?.toString(), it.fps?.toString())) + writeRow( + listOf( + it.type.description, + it.name, + it.location, + it.durationMs?.toString(), + it.fps?.toString() + ) + ) } } } @@ -504,7 +591,10 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik inner class Import : AbstractCollectionCommand("import", help = "Imports a media collection from a CSV file.") { /** [Path] to the input file. */ - private val input: Path by option("--input", help = "Path of the file the media collection should be imported from.") + private val input: Path by option( + "--input", + help = "Path of the file the media collection should be imported from." + ) .convert { Paths.get(it) }.required() override fun run() { @@ -530,7 +620,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik for (row in rows) { inserted += 1 collection.items.add(DbMediaItem.new { - this.type = ApiMediaType.valueOf(row.getValue("type").uppercase()).toDb() + this.type = ApiMediaType.valueOf(row.getValue("itemType").uppercase()).toDb() this.name = row.getValue("name") this.location = row.getValue("location") this.durationMs = row["duration"]?.toLongOrNull() @@ -545,15 +635,24 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik } + data class ListSegment(val video: String, val name: String, val start: Int, val end: Int) + /** * [CliktCommand] to import a [DbMediaSegment]s. * * Uses the VBS format. */ - inner class ImportSegments : AbstractCollectionCommand("importSegments", "Imports the Segment information for the Items in a Collection from a CSV file") { + inner class ImportSegments : AbstractCollectionCommand( + "importSegments", + "Imports the Segment information for the Items in a Collection from a CSV file" + ) { + /** [Path] to the input file. */ - private val input: Path by option("--input", help = "Path of the file the media segments should be imported from.") + private val input: Path by option( + "--input", + help = "Path of the file the media segments should be imported from." + ) .convert { Paths.get(it) }.required() override fun run() { @@ -578,23 +677,29 @@ class MediaCollectionCommand(private val store: TransientEntityStore) : NoOpClik print("Reading input file...") val rows: kotlin.collections.List> = csvReader().readAllWithHeader(ips) println("Done! Reading ${rows.size} rows") - for (row in rows) { - val videoName = row["name"] ?: continue - val start = row["start"]?.toIntOrNull() ?: continue - val end = row["end"]?.toIntOrNull() ?: continue - val videoItem = collection.items.filter { it.name eq videoName }.firstOrNull() - if (videoItem != null) { - inserted += 1 - videoItem.segments.add( - DbMediaSegment.new { - this.name = videoName - this.start = start - this.end = end + + rows.mapNotNull { + val segmentName = it["name"] ?: return@mapNotNull null + val videoName = it["video"] ?: return@mapNotNull null + val start = it["start"]?.toIntOrNull() ?: return@mapNotNull null + val end = it["end"]?.toIntOrNull() ?: return@mapNotNull null + ListSegment(videoName, segmentName, start, end) + } + .groupBy(ListSegment::video) + .forEach { (videoName, segments) -> + val videoItem = collection.items.filter { it.name eq videoName }.firstOrNull() + videoItem?.segments?.addAll( + segments.map { + inserted += 1 + DbMediaSegment.new { + this.name = it.name + this.start = it.start + this.end = it.end + } } ) } - } println("Done! Read $inserted valid segments.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index d92cd9d56..11abb9be3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.collection +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaItemCollection @@ -27,6 +28,8 @@ data class ApiMediaItem( } } - override val collection: MediaItemCollection + + override val collection: MediaItemCollection //TODO do we want this here? + @JsonIgnore get() = TODO("Not yet implemented") } \ No newline at end of file From bfab5cd8ab353244dff8bf17cb9ed657d86a8a1b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 10:20:57 +0100 Subject: [PATCH 109/498] Fixed team logo display in (legacy) template builder --- .../competition-builder/competition-builder.component.ts | 2 +- frontend/src/app/viewer/teams-viewer.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 9ed756d0c..8e57a8071 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -410,7 +410,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat if (team.logoData != null) { return team.logoData; } else { - return this.config.resolveApiUrl(`/template/logo/${team.teamId}`); + return this.config.resolveApiUrl(`/template/logo/${team.id}`); } } diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index 864e3f735..3c178d302 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -264,7 +264,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { * Generates a URL for the logo of the team. */ public teamLogo(team: ApiTeam): string { - return this.config.resolveApiUrl(`/competition/logo/${team.logoData}`); + return this.config.resolveApiUrl(`/competition/logo/${team.id}`); } /** From 9c12ffb0772fa8da2b73424d08493a465274c203 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 10:49:11 +0100 Subject: [PATCH 110/498] Slight restructuring of Submission datastructures --- .../admin/OverrideSubmissionHandler.kt | 6 +++--- .../handler/submission/SubmissionHandler.kt | 2 +- .../api/rest/types/evaluation/ApiSubmission.kt | 17 ++++++++--------- .../run/InteractiveAsynchronousEvaluation.kt | 5 +++-- .../run/InteractiveSynchronousEvaluation.kt | 7 ++++--- .../data/model/run/NonInteractiveEvaluation.kt | 7 ++++--- .../dres/data/model/submissions/DbSubmission.kt | 14 +++++++++++--- .../dres/data/model/submissions/Submission.kt | 10 ++++++---- .../handlers/SubmissionStatisticsHandler.kt | 2 +- .../handlers/TeamCombinationScoreHandler.kt | 2 +- .../dev/dres/run/filter/CorrectPerTeamFilter.kt | 2 +- .../dres/run/filter/CorrectPerTeamItemFilter.kt | 2 +- .../run/filter/CorrectPerTeamMemberFilter.kt | 2 +- .../run/filter/MaximumTotalPerTeamFilter.kt | 2 +- .../run/filter/MaximumWrongPerTeamFilter.kt | 2 +- 15 files changed, 47 insertions(+), 35 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index 0a198084e..c582b8f20 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -67,11 +67,11 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation val rac = RunActionContext.runActionContext(ctx, evaluationManager) /* Sanity check to see, whether the submission exists */ - if (evaluationManager.allSubmissions(rac).none { it.id == submissionInfo.id }) { + if (evaluationManager.allSubmissions(rac).none { it.id == submissionInfo.submissionId }) { throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) } - if (evaluationManager.updateSubmission(rac, submissionInfo.id, submissionInfo.answers.first().status.toDb())) { - val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.id } + if (evaluationManager.updateSubmission(rac, submissionInfo.submissionId, submissionInfo.answers.first().status.toDb())) { + val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.submissionId } AuditLogger.overrideSubmission(submission, DbAuditLogSource.REST, ctx.sessionToken()) submission.toApi() } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 44fbd4420..3b0edda80 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -71,7 +71,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con AuditLogger.submission(apiSubmission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) - logger.info("Submission ${apiSubmission.id} received status $result.") + logger.info("Submission ${apiSubmission.submissionId} received status $result.") return@transactional when (result) { VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index e489fd48e..056ded616 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -1,11 +1,13 @@ package dev.dres.api.rest.types.evaluation +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.admin.User import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionId import dev.dres.data.model.template.team.Team +import java.util.UUID /** * The RESTful API equivalent of a [DbSubmission]. @@ -15,21 +17,18 @@ import dev.dres.data.model.template.team.Team * @version 2.0.0 */ data class ApiSubmission( - val id: SubmissionId, - val teamId: String, + override val teamId: String, val teamName: String, - val memberId: String, + override val memberId: String, val memberName: String, override val timestamp: Long, val answers: List, + override val submissionId: SubmissionId = UUID.randomUUID().toString() //TODO is there a use case where this needs to be settable via an API request? ) : Submission { - override val submissionId: SubmissionId - get() = TODO("Not yet implemented") - override val team: Team - get() = TODO("Not yet implemented") - override val user: User - get() = TODO("Not yet implemented") override fun answerSets(): Sequence = this.answers.asSequence() + override fun toDb(): DbSubmission { + TODO("Not yet implemented") + } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 2a2c38a66..98d9ea442 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -206,12 +206,13 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe @Synchronized override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveAsynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this.teamId == submission.team.teamId) { "Team ${submission.team.teamId} is not eligible to submit to this task. This is a programmer's error!" } + check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) - val dbSubmission: DbSubmission = TODO("submission needs to be stored at this point and not earlier") + /* At this point, the submission is considered valid and is persisted */ + val dbSubmission: DbSubmission = submission.toDb() /* Process Submission. */ this.submissions.add(dbSubmission) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 923b6c7dc..13f034afc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -110,14 +110,15 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu @Synchronized override fun postSubmission(submission: Submission) { check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this@InteractiveSynchronousEvaluation.description.teams.asSequence().filter { it == submission.team }.any()) { - "Team ${submission.team.teamId} does not exists for evaluation run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" + check(this@InteractiveSynchronousEvaluation.description.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { + "Team ${submission.teamId} does not exists for evaluation run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) - val dbSubmission: DbSubmission = TODO("submission needs to be stored at this point and not earlier") + /* At this point, the submission is considered valid and is persisted */ + val dbSubmission: DbSubmission = submission.toDb() /* Process Submission. */ this.submissions.add(dbSubmission) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 8762edd24..65e4058e3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -53,14 +53,15 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev @Synchronized override fun postSubmission(submission: Submission) { - check(this@NonInteractiveEvaluation.description.teams.asSequence().filter { it == submission.team }.any()) { - "Team ${submission.team.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" + check(this@NonInteractiveEvaluation.description.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { + "Team ${submission.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" } /* Execute submission filters. */ this.filter.acceptOrThrow(submission) - val dbSubmission: DbSubmission = TODO("submission needs to be stored at this point and not earlier") + /* At this point, the submission is considered valid and is persisted */ + val dbSubmission: DbSubmission = submission.toDb() /* Process Submission. */ this.submissions.add(dbSubmission) diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 48672a63a..9dd07050a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -3,7 +3,9 @@ package dev.dres.data.model.submissions import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.team.DbTeam +import dev.dres.data.model.template.team.TeamId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -32,15 +34,21 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { override var timestamp by xdRequiredLongProp { min(0L) } /** The [DbTeam] that submitted this [DbSubmission] */ - override var team by xdLink1(DbTeam) + var team by xdLink1(DbTeam) + override val teamId: TeamId + get() = this.team.teamId /** The [DbUser] that submitted this [DbSubmission] */ - override var user by xdLink1(DbUser) + var user by xdLink1(DbUser) + + override val memberId: UserId + get() = this.user.userId /** The [DbAnswerSet]s that make-up this [DbSubmission]. For batched submissions, more than one verdict can be possible. */ val answerSets by xdChildren1_N(DbAnswerSet::submission) override fun answerSets(): Sequence = answerSets.asSequence() + override fun toDb(): DbSubmission = this /** * Converts this [DbSubmission] to a RESTful API representation [ApiSubmission]. @@ -51,7 +59,7 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { * @return [ApiSubmission] */ fun toApi(blind: Boolean = false): ApiSubmission = ApiSubmission( - id = this.id, + submissionId = this.id, teamId = this.team.id, teamName = this.team.name, memberId = this.user.id, diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 2c60c1ede..89281f24a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -1,7 +1,7 @@ package dev.dres.data.model.submissions -import dev.dres.data.model.admin.User -import dev.dres.data.model.template.team.Team +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.template.team.TeamId typealias SubmissionId = String @@ -9,9 +9,11 @@ interface Submission { val submissionId: SubmissionId val timestamp: Long - val team: Team - val user: User + val teamId: TeamId + val memberId: UserId fun answerSets(): Sequence + fun toDb(): DbSubmission + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt index 7723e219c..dfba798d2 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt @@ -62,7 +62,7 @@ class SubmissionStatisticsHandler : StreamEventHandler { * I assume here, that there this handler requires a single verdict per submission. Is this a valid assumption? */ private fun computeStatistics(submissions: List, taskStart: Long, task: String) { - val submissionsByTeam = submissions.groupBy { it.team.teamId } + val submissionsByTeam = submissions.groupBy { it.teamId } submissionsByTeam.mapValues { it.value.size }.forEach{ (teamId, count) -> writer.println("$task,${teamId},\"totalSubmissionsPerTeam\",$count") } diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt index 9f06718ac..3bf4632e9 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt @@ -54,7 +54,7 @@ class TeamCombinationScoreHandler : StreamEventHandler { val submissions = submissionTaskMap[event.taskId] ?: return - val teams = submissions.map { it.team.teamId }.toSet().toList().sortedBy { it } + val teams = submissions.map { it.teamId }.toSet().toList().sortedBy { it } val combinations = teams.mapIndexed { firstIndex, uidA -> teams.mapIndexed {secondIndex, uidB -> if (firstIndex > secondIndex) (uidA to uidB) else null} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index a28d8e72c..c4716c228 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -29,7 +29,7 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> answer.task.answerSets().filter { - (it.status() == VerdictStatus.CORRECT) && it.submission.team == submission.team + (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId }.count() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index 0a40491bc..3f6467f3b 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -19,7 +19,7 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { val submittedItems = submission.answerSets().flatMap { it.answers() }.mapNotNull { it.item }.toSet() return submission.answerSets().all { answerSet -> answerSet.task.answerSets().filter { taskAnswerSets -> - (taskAnswerSets.status() == VerdictStatus.CORRECT) && taskAnswerSets.submission.team == submission.team && taskAnswerSets.answers().any { it.item in submittedItems } + (taskAnswerSets.status() == VerdictStatus.CORRECT) && taskAnswerSets.submission.teamId == submission.teamId && taskAnswerSets.answers().any { it.item in submittedItems } }.count() < this.limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index f44ef7424..d1d20947f 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -28,7 +28,7 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> answer.task.answerSets().filter { - (it.status() == VerdictStatus.CORRECT) && it.submission.team == submission.team && it.submission.user == submission.user + (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId && it.submission.memberId == submission.memberId }.count() < limit } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index 73d00b563..a5c0177aa 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -22,7 +22,7 @@ class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi */ override fun test(submission: Submission): Boolean { return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { it.submission.team == submission.team }.count() < max + answerSet.task.answerSets().filter { it.submission.teamId == submission.teamId }.count() < max } } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 6ff9506b6..8b695eebb 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -28,7 +28,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi */ override fun test(submission: Submission): Boolean { return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { (it.submission.team == submission.team) and (it.status() == VerdictStatus.WRONG) }.count() < max + answerSet.task.answerSets().filter { (it.submission.teamId == submission.teamId) and (it.status() == VerdictStatus.WRONG) }.count() < max } } } From bcce3e2578e5345c453684eb8e9f1c4d3db3298c Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 12:59:59 +0100 Subject: [PATCH 111/498] Restructured AnswerType --- .../judgement/DequeueJudgementHandler.kt | 9 ++--- .../handler/judgement/DequeueVoteHandler.kt | 9 ++--- .../api/rest/types/evaluation/ApiAnswer.kt | 14 ++++++-- .../api/rest/types/evaluation/ApiAnswerSet.kt | 28 ++++++++++----- .../rest/types/evaluation/ApiAnswerType.kt | 5 +-- .../rest/types/evaluation/ApiSubmission.kt | 19 +++++++++-- .../dev/dres/data/model/submissions/Answer.kt | 5 ++- .../dres/data/model/submissions/AnswerSet.kt | 2 ++ .../dres/data/model/submissions/AnswerType.kt | 34 ++++++++++++++++--- .../dres/data/model/submissions/DbAnswer.kt | 7 ++-- .../data/model/submissions/DbAnswerSet.kt | 2 ++ .../data/model/submissions/DbAnswerType.kt | 18 +++++----- .../dres/run/filter/ItemSubmissionFilter.kt | 2 +- .../run/filter/TemporalSubmissionFilter.kt | 2 +- .../run/filter/TextualSubmissionFilter.kt | 3 +- .../TemporalContainmentSubmissionValidator.kt | 2 +- .../TemporalOverlapSubmissionValidator.kt | 2 +- .../dev/dres/run/validation/TextValidator.kt | 2 +- .../dres/run/validation/judged/ItemRange.kt | 9 ++--- 19 files changed, 122 insertions(+), 52 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 96c78aee1..789e04934 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswerType import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken @@ -49,16 +50,16 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break val next = validator.next(ctx.sessionToken()!!) ?: break val taskDescription = next.second.task.template.textualDescription() - when (next.second.answers().firstOrNull()?.type) { - DbAnswerType.TEXT -> { + when (next.second.answers().firstOrNull()?.type()) { + AnswerType.TEXT -> { val text = next.second.answers().firstOrNull()?.text ?: continue return@transactional ApiJudgementRequest(next.first, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } - DbAnswerType.ITEM -> { + AnswerType.ITEM -> { val item = next.second.answers().firstOrNull()?.item ?: continue return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) } - DbAnswerType.TEMPORAL -> { + AnswerType.TEMPORAL -> { val answer = next.second.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 06aa0c503..03c915a6f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.Context @@ -47,16 +48,16 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break val next = validator.nextSubmissionToVoteOn() ?: break val taskDescription = next.task.template.textualDescription() - when (next.answers().firstOrNull()?.type) { - DbAnswerType.TEXT -> { + when (next.answers().firstOrNull()?.type()) { + AnswerType.TEXT -> { val text = next.answers().firstOrNull()?.text ?: continue return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) } - DbAnswerType.ITEM -> { + AnswerType.ITEM -> { val item = next.answers().firstOrNull()?.item ?: continue return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) } - DbAnswerType.TEMPORAL -> { + AnswerType.TEMPORAL -> { val answer = next.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt index a8fe1e32a..910bd2b84 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt @@ -3,11 +3,21 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.submissions.Answer import dev.dres.data.model.submissions.AnswerType +import dev.dres.data.model.submissions.DbAnswer data class ApiAnswer( - override val type: AnswerType, + val type: ApiAnswerType, override val item: ApiMediaItem?, override val text: String?, override val start: Long?, override val end: Long? - ) : Answer + ) : Answer { + override fun toDb(): DbAnswer { + return DbAnswer.new { + this.type = this@ApiAnswer.type.toDb() + + } + } + + override fun type(): AnswerType = AnswerType.fromApi(this.type) +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index 722ca3df5..8a9ab014d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -1,12 +1,11 @@ package dev.dres.api.rest.types.evaluation +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.run.Task -import dev.dres.data.model.submissions.Answer -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.interfaces.EvaluationTemplate +import kotlinx.dnq.query.addAll /** * The RESTful API equivalent for the type of [ApiAnswerSet]. @@ -16,20 +15,31 @@ import dev.dres.data.model.template.interfaces.EvaluationTemplate * @version 1.0.0 */ data class ApiAnswerSet( - val status: ApiVerdictStatus, + var status: ApiVerdictStatus, val answers: List ) : AnswerSet { override val task: Task + @JsonIgnore get() = TODO("Not yet implemented") override val submission: Submission + @JsonIgnore get() = TODO("Not yet implemented") override fun answers(): Sequence = answers.asSequence() - override fun status(): VerdictStatus { - TODO("Not yet implemented") - } + override fun status(): VerdictStatus = VerdictStatus.fromApi(this.status) override fun status(status: VerdictStatus) { - TODO("Not yet implemented") + this.status = status.toApi() + } + + override fun toDb(): DbAnswerSet { + return DbAnswerSet.new { + this.status = this@ApiAnswerSet.status.toDb() + this.answers.addAll( + this@ApiAnswerSet.answers.map { + it.toDb() + } + ) + } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt index dc38cfb25..ff7d45796 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt @@ -10,12 +10,9 @@ import dev.dres.data.model.submissions.DbAnswerType * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiAnswerType: AnswerType { +enum class ApiAnswerType { ITEM, TEMPORAL, TEXT; - override fun eq(status: AnswerType.Type): Boolean { - return this.name == status.name - } /** * Converts this [ApiAnswerType] to a [DbAnswerType] representation. Requires an ongoing transaction. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index 056ded616..dbefa4e10 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -1,12 +1,17 @@ package dev.dres.api.rest.types.evaluation import com.fasterxml.jackson.annotation.JsonIgnore +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.User import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.Team +import kotlinx.dnq.query.addAll +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first import java.util.UUID /** @@ -21,14 +26,24 @@ data class ApiSubmission( val teamName: String, override val memberId: String, val memberName: String, - override val timestamp: Long, val answers: List, + override val timestamp: Long = System.currentTimeMillis(), override val submissionId: SubmissionId = UUID.randomUUID().toString() //TODO is there a use case where this needs to be settable via an API request? ) : Submission { override fun answerSets(): Sequence = this.answers.asSequence() override fun toDb(): DbSubmission { - TODO("Not yet implemented") + + return DbSubmission.new { + this.id = this@ApiSubmission.submissionId + this.timestamp = this@ApiSubmission.timestamp + this.team = DbTeam.filter { teamId eq this@ApiSubmission.teamId }.first() + this.user = DbUser.filter { id eq this@ApiSubmission.memberId }.first() + this.answerSets.addAll( + this@ApiSubmission.answers.map { it.toDb() } + ) + } + } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt index 0630048eb..3184e2874 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt @@ -6,7 +6,6 @@ import dev.dres.data.model.media.time.TemporalRange interface Answer { - val type: AnswerType val item: MediaItem? val start: Long? val end: Long? @@ -22,4 +21,8 @@ interface Answer { return TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) } + fun toDb(): DbAnswer + + fun type(): AnswerType + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index 5bef6c673..6ef364ede 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -12,4 +12,6 @@ interface AnswerSet { //TODO fun status() : VerdictStatus fun status(status: VerdictStatus) + + fun toDb(): DbAnswerSet } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt index 4bae15a13..ee0ccc928 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt @@ -1,11 +1,37 @@ package dev.dres.data.model.submissions -interface AnswerType { +import dev.dres.api.rest.types.evaluation.ApiAnswerType - enum class Type { - ITEM, TEMPORAL, TEXT +enum class AnswerType { + ITEM, TEMPORAL, TEXT; + + fun toDb(): DbAnswerType = when(this) { + ITEM -> DbAnswerType.ITEM + TEMPORAL -> DbAnswerType.TEMPORAL + TEXT -> DbAnswerType.TEXT + } + + fun toApi() : ApiAnswerType = when(this) { + ITEM -> ApiAnswerType.ITEM + TEMPORAL -> ApiAnswerType.TEMPORAL + TEXT -> ApiAnswerType.TEXT } - infix fun eq(status: Type): Boolean + companion object { + + fun fromApi(apiAnswerType: ApiAnswerType) = when(apiAnswerType) { + ApiAnswerType.ITEM -> ITEM + ApiAnswerType.TEMPORAL -> TEMPORAL + ApiAnswerType.TEXT -> TEXT + } + + fun fromDb(dbAnswerType: DbAnswerType) = when(dbAnswerType) { + DbAnswerType.ITEM -> ITEM + DbAnswerType.TEMPORAL -> TEMPORAL + DbAnswerType.TEXT -> TEXT + else -> throw IllegalStateException("Unknown DbAnswerType $dbAnswerType") + } + + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt index 15fcd3bda..238205bea 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt @@ -15,7 +15,7 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { var answerSet: DbAnswerSet by xdParent(DbAnswerSet::answers) /** The [DbAnswerType] of this [DbAnswerSet]. */ - override var type by xdLink1(DbAnswerType) + var type by xdLink1(DbAnswerType) /** The [DbMediaItem] submitted. Only for [DbAnswerType.ITEM] or [DbAnswerType.TEMPORAL]. */ override var item by xdLink0_1(DbMediaItem) @@ -28,11 +28,12 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { /** The text submitted. Only for [DbAnswerType.TEXT] . */ override var text by xdStringProp { requireIf { this.type == DbAnswerType.TEXT } } - + override fun toDb(): DbAnswer = this + override fun type(): AnswerType = AnswerType.fromDb(this.type) fun toApi() = ApiAnswer( - type = this.type, + type = this.type().toApi(), item = this.item?.toApi(), start = this.start, end = this.end, diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 5f2470ea6..cda2ae54d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -35,6 +35,8 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { this.status = status.toDb() } + override fun toDb(): DbAnswerSet = this + /** * Converts this [DbVerdictStatus] to a RESTful API representation [ApiAnswerSet]. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt index b75eae676..5749cbbd4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt @@ -13,11 +13,11 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class DbAnswerType(entity: Entity) : XdEnumEntity(entity), AnswerType { +class DbAnswerType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { - val ITEM by enumField { description = AnswerType.Type.ITEM.name } - val TEMPORAL by enumField { description = AnswerType.Type.TEMPORAL.name } - val TEXT by enumField { description = AnswerType.Type.TEXT.name } + val ITEM by enumField { description = AnswerType.ITEM.name } + val TEMPORAL by enumField { description = AnswerType.TEMPORAL.name } + val TEXT by enumField { description = AnswerType.TEXT.name } /** * Returns a list of all [DbRole] values. @@ -30,9 +30,9 @@ class DbAnswerType(entity: Entity) : XdEnumEntity(entity), AnswerType { * Parses a [DbRole] instance from a [String]. */ fun parse(string: String) = when (string.uppercase()) { - AnswerType.Type.ITEM.name -> ITEM - AnswerType.Type.TEMPORAL.name -> TEMPORAL - AnswerType.Type.TEXT.name -> TEXT + AnswerType.ITEM.name -> ITEM + AnswerType.TEMPORAL.name -> TEMPORAL + AnswerType.TEXT.name -> TEXT else -> throw IllegalArgumentException("Failed to parse submission type '$string'.") } } @@ -46,6 +46,6 @@ class DbAnswerType(entity: Entity) : XdEnumEntity(entity), AnswerType { * * @return [DbAnswerType] */ - fun toApi() = ApiAnswerType.values().find { it.toDb() == this } ?: throw IllegalStateException("Verdict type ${this.description} is not supported.") - override fun eq(status: AnswerType.Type): Boolean = status.name == this.description + fun toApi() = AnswerType.fromDb(this).toApi() + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt index 3c2859b6e..b3a5b1911 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt @@ -15,5 +15,5 @@ import kotlinx.dnq.query.asSequence class ItemSubmissionFilter : SubmissionFilter { override val reason = "Submission does include temporal information, but whole item was expected" override fun test(submission: Submission): Boolean - = submission.answerSets().any { it.answers().any { it.type eq AnswerType.Type.ITEM } } + = submission.answerSets().any { it.answers().any { it.type() == AnswerType.ITEM } } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt index 0f7d5499a..d1b1eb4bc 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt @@ -17,5 +17,5 @@ class TemporalSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include temporal information." override fun test(submission: Submission): Boolean - = submission.answerSets().all { it.answers().all { it.type eq AnswerType.Type.TEMPORAL && it.start != null && it.end != null } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + = submission.answerSets().all { set -> set.answers().all { it.type() == AnswerType.TEMPORAL && it.start != null && it.end != null } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt index d31ca4d8e..aa6e195a6 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerType import dev.dres.data.model.submissions.Submission @@ -16,5 +17,5 @@ class TextualSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include textual information (or is an empty submission)" override fun test(submission: Submission): Boolean - = submission.answerSets().all { it.answers().all { it.text != null && it.type == DbAnswerType.TEXT } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + = submission.answerSets().all { set -> set.answers().all { it.text != null && it.type() == AnswerType.TEXT } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index c5bbcb3c1..3dd7797a7 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -30,7 +30,7 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien answerSet.answers().forEach { answer -> /* Perform sanity checks. */ - if (answer.type != DbAnswerType.TEMPORAL) { + if (answer.type() != AnswerType.TEMPORAL) { answerSet.status(VerdictStatus.WRONG) return@forEach } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 6f9c3852a..0b3d5d821 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -34,7 +34,7 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed answerSet.answers().forEach { answer -> /* Perform sanity checks. */ - if (answer.type != DbAnswerType.TEMPORAL) { + if (answer.type() != AnswerType.TEMPORAL) { answerSet.status(VerdictStatus.WRONG) return@forEach } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 3c1042e8e..1c56b6975 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -51,7 +51,7 @@ class TextValidator(targets: List) : SubmissionValidator { answer -> /* Perform sanity checks. */ - if (answer.type != DbAnswerType.TEXT) { + if (answer.type() != AnswerType.TEXT) { answerSet.status(VerdictStatus.WRONG) return@forEach } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index ec4b0b8f0..6c4588171 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -2,6 +2,7 @@ package dev.dres.run.validation.judged import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.submissions.Answer +import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswer import dev.dres.data.model.submissions.DbAnswerType @@ -14,10 +15,10 @@ import dev.dres.data.model.submissions.DbAnswerType data class ItemRange(val element: String, val start: Long, val end: Long){ constructor(item: DbMediaItem): this(item.id, 0, 0) constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) - constructor(answer: Answer): this(when (answer.type){ - DbAnswerType.ITEM, - DbAnswerType.TEMPORAL -> answer.item!!.id!! - DbAnswerType.TEXT -> answer.text!! + constructor(answer: Answer): this(when (answer.type()){ + AnswerType.ITEM, + AnswerType.TEMPORAL -> answer.item!!.id!! + AnswerType.TEXT -> answer.text!! else -> throw IllegalStateException("Submission contains neither item nor text.") }, answer.start ?: 0, answer.end ?: 0) From 9dcd7afe1e327dfe4a95d9ad562e16023a90a71d Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 13:43:34 +0100 Subject: [PATCH 112/498] KisTaskScorer working and testable again --- .../api/rest/types/evaluation/ApiAnswerSet.kt | 13 +- .../rest/types/evaluation/ApiSubmission.kt | 5 + .../dres/run/score/scorer/AvsTaskScorer.kt | 22 +- .../run/score/scorer/CachingTaskScorer.kt | 3 +- .../scorer/InferredAveragePrecisionScorer.kt | 3 +- .../dres/run/score/scorer/KisTaskScorer.kt | 10 +- .../dres/run/score/scorer/NewAvsTaskScorer.kt | 20 +- .../dev/dres/run/score/scorer/TaskScorer.kt | 9 +- .../run/score/scorer/KisTaskScorerTest.kt | 239 +++++++++++------- 9 files changed, 192 insertions(+), 132 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index 8a9ab014d..61e7f9039 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -18,12 +18,13 @@ data class ApiAnswerSet( var status: ApiVerdictStatus, val answers: List ) : AnswerSet { - override val task: Task - @JsonIgnore - get() = TODO("Not yet implemented") - override val submission: Submission - @JsonIgnore - get() = TODO("Not yet implemented") + + @JsonIgnore + override lateinit var task: Task + + @JsonIgnore + override lateinit var submission: ApiSubmission + internal set override fun answers(): Sequence = answers.asSequence() override fun status(): VerdictStatus = VerdictStatus.fromApi(this.status) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index dbefa4e10..fe6351b28 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -31,6 +31,11 @@ data class ApiSubmission( override val submissionId: SubmissionId = UUID.randomUUID().toString() //TODO is there a use case where this needs to be settable via an API request? ) : Submission { + init { + answers.forEach { + it.submission = this + } + } override fun answerSets(): Sequence = this.answers.asSequence() override fun toDb(): DbSubmission { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 0dbe6cfa1..e1d5843bb 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -2,9 +2,7 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.media.DbMediaType -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.run.score.TaskContext import dev.dres.utilities.TimeUtil import kotlinx.dnq.query.asSequence @@ -24,13 +22,13 @@ object AvsTaskScorer : TaskScorer { /** * TODO: Check for correctness especially if a submission has more than one verdict. Maybe add sanity checks. */ - override fun computeScores(submissions: Sequence, context: TaskContext): Map { + override fun computeScores(submissions: Sequence, context: TaskContext): Map { val correctSubmissions = - submissions.flatMap { s -> s.answerSets.filter { v -> v.status eq DbVerdictStatus.CORRECT }.asSequence() } + submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.CORRECT } } val wrongSubmissions = - submissions.flatMap { s -> s.answerSets.filter { v -> v.status eq DbVerdictStatus.WRONG }.asSequence() } - val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.team.id } - val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.team.id } + submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.WRONG } } + val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.teamId } + val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.teamId } val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() return context.teamIds.map { teamid -> @@ -42,14 +40,14 @@ object AvsTaskScorer : TaskScorer { } - private fun countQuantized(submissions: Sequence): Int = submissions - .filter { it.answers.firstOrNull()?.item != null } - .groupBy { it.answers.first().item } + private fun countQuantized(submissions: Sequence): Int = submissions + .filter { it.answers().firstOrNull()?.item != null } + .groupBy { it.answers().first().item } .map { when (it.key!!.type) { DbMediaType.IMAGE -> 1 DbMediaType.VIDEO -> { - val ranges = it.value.asSequence().map { s -> s.answers.first().temporalRange!! }.toList() + val ranges = it.value.asSequence().map { s -> s.answers().first().temporalRange!! }.toList() TimeUtil.merge(ranges, overlap = 1).size } else -> throw IllegalStateException("Unsupported media type ${it.key!!.type} for AVS task scorer.") diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt index 529edd59b..c1e97fc75 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt @@ -1,6 +1,7 @@ package dev.dres.run.score.scorer import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext @@ -8,7 +9,7 @@ class CachingTaskScorer(private val innerScorer: TaskScorer) : TaskScorer { private var latest: Map = emptyMap() - override fun computeScores(submissions: Sequence, context: TaskContext): Map { + override fun computeScores(submissions: Sequence, context: TaskContext): Map { latest = innerScorer.computeScores(submissions, context) return latest } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index 2bd26a97f..356e8c9d5 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,6 +1,7 @@ package dev.dres.run.score.scorer import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext @@ -13,7 +14,7 @@ object InferredAveragePrecisionScorer : TaskScorer { private val epsilon = 0.01 //TODO check what TRECVID uses - override fun computeScores(submissions: Sequence, context: TaskContext): Map { + override fun computeScores(submissions: Sequence, context: TaskContext): Map { TODO("Not yet implemented") } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 499ea3c34..dbcdb309f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -3,6 +3,8 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.TaskContext import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter @@ -27,15 +29,15 @@ class KisTaskScorer( } - override fun computeScores(submissions: Sequence, context: TaskContext): Map { + override fun computeScores(submissions: Sequence, context: TaskContext): Map { val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("No task start time specified.") val taskDuration = context.taskDuration ?: throw IllegalArgumentException("No task duration specified.") val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition return context.teamIds.associateWith { teamId -> - val verdicts = submissions.filter { it.team.id == teamId }.sortedBy { it.timestamp }.flatMap { sub -> - sub.answerSets.filter { (it.status eq DbVerdictStatus.CORRECT) or (it.status eq DbVerdictStatus.WRONG) }.asSequence() + val verdicts = submissions.filter { it.teamId == teamId }.sortedBy { it.timestamp }.flatMap { sub -> + sub.answerSets().filter { (it.status() == VerdictStatus.CORRECT) or (it.status() == VerdictStatus.WRONG) }.asSequence() }.toList() - val firstCorrect = verdicts.indexOfFirst { it.status == DbVerdictStatus.CORRECT } + val firstCorrect = verdicts.indexOfFirst { it.status() == VerdictStatus.CORRECT } val score = if (firstCorrect > -1) { val timeFraction = 1.0 - (verdicts[firstCorrect].submission.timestamp - taskStartTime) / tDur max( diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt index 46e1499a8..9ef7bdc0f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt @@ -3,6 +3,8 @@ package dev.dres.run.score.scorer import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext import kotlinx.dnq.query.asSequence @@ -53,13 +55,13 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } override fun computeScores( - submissions: Sequence, + submissions: Sequence, context: TaskContext ): Map { val distinctCorrectVideos = submissions.flatMap { submission -> - submission.answerSets.asSequence().filter { it.status == DbVerdictStatus.CORRECT && it.answers.firstOrNull()?.item != null } - }.mapNotNullTo(mutableSetOf()) { it.answers.firstOrNull()?.item } + submission.answerSets().filter { it.status() == VerdictStatus.CORRECT && it.answers().firstOrNull()?.item != null } + }.mapNotNullTo(mutableSetOf()) { it.answers().firstOrNull()?.item } .size @@ -73,15 +75,15 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } } - return teamScoreMapSanitised(submissions.groupBy { it.team }.map { submissionsPerTeam -> + return teamScoreMapSanitised(submissions.groupBy { it.teamId }.map { submissionsPerTeam -> val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { - it.answerSets.asSequence() - .filter { v -> v.answers.firstOrNull()?.item != null && (v.status == DbVerdictStatus.CORRECT || v.status == DbVerdictStatus.WRONG) } + it.answerSets() + .filter { v -> v.answers().firstOrNull()?.item != null && (v.status() == VerdictStatus.CORRECT || v.status() == VerdictStatus.WRONG) } } - submissionsPerTeam.key.teamId to + submissionsPerTeam.key to max(0.0, //prevent negative total scores - verdicts.groupBy { it.answers.firstOrNull()?.item!! }.map { - val firstCorrectIdx = it.value.indexOfFirst { v -> v.status == DbVerdictStatus.CORRECT } + verdicts.groupBy { it.answers().firstOrNull()?.item!! }.map { + val firstCorrectIdx = it.value.indexOfFirst { v -> v.status() == VerdictStatus.CORRECT } if (firstCorrectIdx < 0) { //no correct submissions, only penalty it.value.size * -penaltyConstant } else { //apply penalty for everything before correct submission diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt index 2b93b2dce..407c1f917 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt @@ -2,6 +2,7 @@ package dev.dres.run.score.scorer import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.TaskContext @@ -10,7 +11,7 @@ typealias ScoreEntry = Triple /** * A [TaskScorer] that re-computes the current scores of all teams for a given [TaskRun] based on the - * entire [DbSubmission] history. As opposed to the [IncrementalSubmissionTaskScorer], incremental updates are not possible. + * entire [Submission] history. * * @author Luca Rossetto * @author Ralph Gasser @@ -18,10 +19,10 @@ typealias ScoreEntry = Triple */ interface TaskScorer { /** - * Re-computes this [RecalculatingSubmissionTaskScorer]'s score based on the given [DbSubmission] history. + * Re-computes this [TaskScorer]'s score based on the given [Submission] history. * - * @param submissions The [DbSubmission]s used to update this [RecalculatingSubmissionTaskScorer] with. + * @param submissions The [Submission]s used to update this [TaskScorer] with. * @param context The [TaskContext] in which scoring takes place. */ - fun computeScores(submissions: Sequence, context: TaskContext): Map + fun computeScores(submissions: Sequence, context: TaskContext): Map } \ No newline at end of file diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index b8c380a6b..30d25e41e 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -1,6 +1,8 @@ package dres.run.score.scorer -import dev.dres.data.model.media.DbMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.api.rest.types.evaluation.* import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext @@ -10,99 +12,146 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class KisTaskScorerTest { -// -// private lateinit var scorer: KisTaskScorer -// private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids -// private val dummyImageItems = listOf(DbMediaItem.ImageItem(EvaluationId(), "image 1", "images/1", EvaluationId())) -// private val defaultTaskDuration = 5 * 60L -// private val maxPointsPerTask = 100.0 -// private val maxPointsAtTaskEnd = 50.0 -// private val penaltyPerWrongSubmission = 10.0 -// -// @BeforeEach -// fun setup() { -// this.scorer = KisTaskScorer(maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) -// } -// -// @Test -// fun noSubmissions() { -// val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) -// assertEquals(0.0, scores[teams[0]]) -// assertEquals(0.0, scores[teams[1]]) -// assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// fun allWrong() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(0.0, scores[teams[0]]) -// assertEquals(0.0, scores[teams[1]]) -// assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// fun immediatelyRight() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(maxPointsPerTask, scores[teams[0]]) -// assertEquals(0.0, scores[teams[1]]) -// assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// fun rightAtTheEnd() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) -// } -// -// @Test -// fun rightInTheMiddle() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) -// } -// -// @Test -// fun wrongSubmissionPenalty() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// -// //incorrect submissions -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 4, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 5, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 6, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// -// //correct submissions at 1/2 the task time -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + (defaultTaskDuration * 1000 / 2), dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// -// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission, scores[teams[0]]) -// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 2, scores[teams[1]]) -// assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 3, scores[teams[2]]) -// } + + private lateinit var scorer: KisTaskScorer + private val teams = listOf("team-1", "team-2", "team-3") + private val dummyImageItem = ApiMediaItem("image1", "image 1", ApiMediaType.IMAGE, "testcollection", "images/1") + private val defaultTaskDuration = 5 * 60L + private val maxPointsPerTask = 100.0 + private val maxPointsAtTaskEnd = 50.0 + private val penaltyPerWrongSubmission = 10.0 + + private val wrongAnswer = listOf( + ApiAnswerSet( + ApiVerdictStatus.WRONG, + listOf( + ApiAnswer( + ApiAnswerType.ITEM, + dummyImageItem, + null, null, null + ) + ) + ) + ) + + private val correctAnswer = listOf( + ApiAnswerSet( + ApiVerdictStatus.CORRECT, + listOf( + ApiAnswer( + ApiAnswerType.ITEM, + dummyImageItem, + null, null, null + ) + ) + ) + ) + + @BeforeEach + fun setup() { + this.scorer = KisTaskScorer(maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) + } + + @Test + fun noSubmissions() { + val scores = + this.scorer.computeScores(emptySequence(), TaskContext("task1", teams, 100_000, defaultTaskDuration)) + assertEquals(0.0, scores[teams[0]]) + assertEquals(0.0, scores[teams[1]]) + assertEquals(0.0, scores[teams[2]]) + } + + @Test + fun allWrong() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", wrongAnswer, taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", wrongAnswer, taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 3000) + ) + val scores = + this.scorer.computeScores(submissions, TaskContext("task2", teams, taskStartTime, defaultTaskDuration)) + assertEquals(0.0, scores[teams[0]]) + assertEquals(0.0, scores[teams[1]]) + assertEquals(0.0, scores[teams[2]]) + } + + @Test + fun immediatelyRight() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime), + ) + val scores = + this.scorer.computeScores(submissions, TaskContext("task3", teams, taskStartTime, defaultTaskDuration)) + assertEquals(maxPointsPerTask, scores[teams[0]]) + assertEquals(0.0, scores[teams[1]]) + assertEquals(0.0, scores[teams[2]]) + } + + @Test + fun rightAtTheEnd() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000)), + ) + + val scores = + this.scorer.computeScores(submissions, TaskContext("task4", teams, taskStartTime, defaultTaskDuration)) + assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) + } + + @Test + fun rightInTheMiddle() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), + ) + + val scores = + this.scorer.computeScores(submissions, TaskContext("task5", teams, taskStartTime, defaultTaskDuration)) + assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) + } + + @Test + fun wrongSubmissionPenalty() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + + //incorrect submissions + ApiSubmission(teams[0], teams[0], "user1", "user1", wrongAnswer, taskStartTime + 1), + + ApiSubmission(teams[1], teams[1], "user2", "user2", wrongAnswer, taskStartTime + 2), + ApiSubmission(teams[1], teams[1], "user2", "user2", wrongAnswer, taskStartTime + 3), + + ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 4), + ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 5), + ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 6), + + //correct submissions at 1/2 the task time + ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), + ApiSubmission(teams[1], teams[1], "user2", "user2", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), + ApiSubmission(teams[2], teams[2], "user3", "user3", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), + + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task6", teams, taskStartTime, defaultTaskDuration)) + + assertEquals( + maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission, + scores[teams[0]] + ) + assertEquals( + maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 2, + scores[teams[1]] + ) + assertEquals( + maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission * 3, + scores[teams[2]] + ) + } } \ No newline at end of file From 556cf5fe5b96042a31a9eaea05269ea4c7c49a12 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 14:37:45 +0100 Subject: [PATCH 113/498] LegacyAvsTaskScorer again tested and operational --- .../judgement/DequeueJudgementHandler.kt | 4 +- .../handler/judgement/DequeueVoteHandler.kt | 4 +- .../api/rest/types/collection/ApiMediaItem.kt | 6 +- .../api/rest/types/collection/ApiMediaType.kt | 3 +- .../dev/dres/data/model/media/DbMediaItem.kt | 3 +- .../dev/dres/data/model/media/DbMediaType.kt | 4 +- .../dev/dres/data/model/media/MediaItem.kt | 5 +- .../dres/data/model/media/MediaItemType.kt | 33 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 15 +- .../run/score/scorer/AvsTaskScorerTest.kt | 701 ++++++++++-------- .../run/score/scorer/KisTaskScorerTest.kt | 2 +- 11 files changed, 427 insertions(+), 353 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 789e04934..6893c3738 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -57,14 +57,14 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa } AnswerType.ITEM -> { val item = next.second.answers().firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) + return@transactional ApiJudgementRequest(next.first, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) } AnswerType.TEMPORAL -> { val answer = next.second.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue val end = answer.end ?: continue - return@transactional ApiJudgementRequest(next.first, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) + return@transactional ApiJudgementRequest(next.first, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) } else -> continue } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 03c915a6f..661c56487 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -55,14 +55,14 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( } AnswerType.ITEM -> { val item = next.answers().firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) + return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) } AnswerType.TEMPORAL -> { val answer = next.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue val end = answer.end ?: continue - return@transactional ApiJudgementRequest(null, item.type.toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) + return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) } else -> continue } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 11abb9be3..f11cce6fb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.MediaItemCollection +import dev.dres.data.model.media.MediaItemType /** * The RESTful API equivalent for [DbMediaItem]. @@ -15,7 +16,7 @@ import dev.dres.data.model.media.MediaItemCollection data class ApiMediaItem( override val id: String?, override val name: String, - override val type: ApiMediaType, + val type: ApiMediaType, val collectionId: String, val location: String, val durationMs: Long? = null, @@ -28,8 +29,9 @@ data class ApiMediaItem( } } - override val collection: MediaItemCollection //TODO do we want this here? @JsonIgnore get() = TODO("Not yet implemented") + + override fun type(): MediaItemType = MediaItemType.fromApi(this.type) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt index f1a9f6765..69e67ca69 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaType.kt @@ -10,7 +10,7 @@ import dev.dres.data.model.media.MediaItemType * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiMediaType : MediaItemType { +enum class ApiMediaType { IMAGE, VIDEO, TEXT; /** @@ -24,5 +24,4 @@ enum class ApiMediaType : MediaItemType { TEXT -> DbMediaType.TEXT } - override fun toApi(): ApiMediaType = this } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt index ce3361416..61108e6e5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt @@ -28,7 +28,7 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { override var name by xdRequiredStringProp(unique = false, trimmed = false) /** The [DbMediaType] of this [DbMediaItem]. */ - override var type by xdLink1(DbMediaType) + var type by xdLink1(DbMediaType) /** The location of this [DbMediaItem] on disk. */ var location by xdRequiredStringProp(unique = false, trimmed = false) @@ -41,6 +41,7 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { /** The [DbMediaCollection] this [DbMediaItem] belongs to. */ override var collection: DbMediaCollection by xdParent(DbMediaCollection::items) + override fun type(): MediaItemType = MediaItemType.fromDb(this.type) /** List of [DbMediaSegment] that this [DbMediaItem] contains. */ val segments by xdChildren0_N(DbMediaSegment::item) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt index e6a340dff..a36090768 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt @@ -12,7 +12,7 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 1.0.0 */ -class DbMediaType(entity: Entity) : XdEnumEntity(entity), MediaItemType { +class DbMediaType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { val IMAGE by enumField { description = "IMAGE"; suffix = "mp4"; } val VIDEO by enumField { description = "VIDEO"; suffix = "jpg"; } @@ -30,7 +30,7 @@ class DbMediaType(entity: Entity) : XdEnumEntity(entity), MediaItemType { * * This is a convenience method and requires an active transaction context. */ - override fun toApi(): ApiMediaType + fun toApi(): ApiMediaType = ApiMediaType.values().find { it.toDb() == this } ?: throw IllegalStateException("Media type ${this.description} is not supported.") override fun toString(): String = this.description diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index 57d11ed32..af66abb6a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -1,11 +1,10 @@ package dev.dres.data.model.media -interface MediaItem { //TODO - +interface MediaItem { val name: String val id: String? - val type: MediaItemType val collection: MediaItemCollection + fun type(): MediaItemType } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt index 3a9f96939..36037b7e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemType.kt @@ -2,6 +2,35 @@ package dev.dres.data.model.media import dev.dres.api.rest.types.collection.ApiMediaType -interface MediaItemType { - fun toApi(): ApiMediaType +enum class MediaItemType { + IMAGE, VIDEO, TEXT; + + fun toApi() = when(this) { + IMAGE -> ApiMediaType.IMAGE + VIDEO -> ApiMediaType.VIDEO + TEXT -> ApiMediaType.TEXT + } + + fun toDb() = when(this) { + IMAGE -> DbMediaType.IMAGE + VIDEO -> DbMediaType.VIDEO + TEXT -> DbMediaType.TEXT + } + + companion object { + + fun fromApi(type: ApiMediaType) = when(type) { + ApiMediaType.IMAGE -> IMAGE + ApiMediaType.VIDEO -> VIDEO + ApiMediaType.TEXT -> TEXT + } + + fun fromDb(type: DbMediaType) = when(type) { + DbMediaType.IMAGE -> IMAGE + DbMediaType.VIDEO -> VIDEO + DbMediaType.TEXT -> TEXT + else -> throw IllegalStateException("Unknown DbMediaType $type") + } + + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index e1d5843bb..74316d47c 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -1,15 +1,10 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.media.DbMediaType +import dev.dres.data.model.media.MediaItemType import dev.dres.data.model.submissions.* import dev.dres.run.score.TaskContext import dev.dres.utilities.TimeUtil -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.first -import kotlinx.dnq.query.firstOrNull - /** * A [TeamTaskScorer] used for AVS tasks. @@ -44,13 +39,13 @@ object AvsTaskScorer : TaskScorer { .filter { it.answers().firstOrNull()?.item != null } .groupBy { it.answers().first().item } .map { - when (it.key!!.type) { - DbMediaType.IMAGE -> 1 - DbMediaType.VIDEO -> { + when (it.key!!.type()) { + MediaItemType.IMAGE -> 1 + MediaItemType.VIDEO -> { val ranges = it.value.asSequence().map { s -> s.answers().first().temporalRange!! }.toList() TimeUtil.merge(ranges, overlap = 1).size } - else -> throw IllegalStateException("Unsupported media type ${it.key!!.type} for AVS task scorer.") + else -> throw IllegalStateException("Unsupported media type ${it.key!!.type()} for AVS task scorer.") } }.sum() diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 73fcd7b2f..d6647b783 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -1,5 +1,8 @@ package dres.run.score.scorer +import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.api.rest.types.evaluation.* import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus @@ -11,331 +14,377 @@ import org.junit.jupiter.api.Test class LegacyAvsTaskScorerTest { -// -// private lateinit var scorer: AvsTaskScorer -// private val teams = listOf(EvaluationId(), EvaluationId(), EvaluationId()) //3 random team ids -// private val defaultTaskDuration = 5 * 60L -// private val dummyImageItems: List -// private val dummyVideoItems: List -// -// init { -// val collectionId = EvaluationId() -// -// dummyImageItems = listOf( -// DbMediaItem.ImageItem(EvaluationId(), "Image 1", "images/1", collectionId), -// DbMediaItem.ImageItem(EvaluationId(), "Image 2", "images/2", collectionId), -// DbMediaItem.ImageItem(EvaluationId(), "Image 3", "images/3", collectionId), -// DbMediaItem.ImageItem(EvaluationId(), "Image 4", "images/4", collectionId), -// DbMediaItem.ImageItem(EvaluationId(), "Image 5", "images/5", collectionId), -// DbMediaItem.ImageItem(EvaluationId(), "Image 6", "images/6", collectionId) -// ) -// -// dummyVideoItems = listOf( -// DbMediaItem.VideoItem(EvaluationId(), "Video 1", "video/1", collectionId, 10 * 60 * 1000, 24f), -// DbMediaItem.VideoItem(EvaluationId(), "Video 2", "video/2", collectionId, 20 * 60 * 1000, 24f), -// DbMediaItem.VideoItem(EvaluationId(), "Video 3", "video/3", collectionId, 30 * 60 * 1000, 24f), -// DbMediaItem.VideoItem(EvaluationId(), "Video 4", "video/4", collectionId, 40 * 60 * 1000, 24f), -// DbMediaItem.VideoItem(EvaluationId(), "Video 5", "video/5", collectionId, 50 * 60 * 1000, 24f) -// ) -// -// } -// -// @BeforeEach -// fun setup() { -// this.scorer = AvsTaskScorer() -// } -// -// @Test -// fun noSubmissions() { -// val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) -// assertEquals(0.0, scores[teams[0]]) -// assertEquals(0.0, scores[teams[1]]) -// assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// fun allWrongSameImage() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(0.0, scores[teams[0]]) -// assertEquals(0.0, scores[teams[1]]) -// assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// fun allWrongDifferentImages() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.WRONG } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(0.0, scores[teams[0]]) -// assertEquals(0.0, scores[teams[1]]) -// assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// fun allSameImageAllCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(100.0, scores[teams[0]]) -// assertEquals(100.0, scores[teams[1]]) -// assertEquals(100.0, scores[teams[2]]) -// } -// -// @Test -// fun allDifferentImageAllCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) -// assertEquals(100.0 / 3.0, scores[teams[1]]!!, 0.001) -// assertEquals(100.0 / 3.0, scores[teams[2]]!!, 0.001) -// } -// -// @Test -// fun someDifferentImageSomeCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// -// /* -// total correct: 4 (0, 1, 2, 3) -// total wrong: 2 (4, 5) -// */ -// -// //3 out of 4 correct -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 2000, dummyImageItems[1]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 3000, dummyImageItems[2]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[0], EvaluationId(), taskStartTime + 4000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, -// -// -// //1 out of 3 correct -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 1000, dummyImageItems[0]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 2000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Item(teams[1], EvaluationId(), taskStartTime + 3000, dummyImageItems[4]).also { it.status = DbVerdictStatus.WRONG }, -// -// //1 out of 2 correct -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 2000, dummyImageItems[3]).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Item(teams[2], EvaluationId(), taskStartTime + 3000, dummyImageItems[5]).also { it.status = DbVerdictStatus.WRONG } -// ) -// -// -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// -// /* -// c = q(c) = 3, i = 1, q(p) = 4 -// -// 100 * 3 3 -// ------- * - = 64.28571428571429 -// 3 + 1/2 4 -// */ -// assertEquals(64.28571428571429, scores[teams[0]]!!, 0.001) -// -// /* -// c = q(c) = 1, i = 1, q(p) = 4 -// -// 100 * 1 1 -// ------- * - = 12.5 -// 1 + 2/2 4 -// */ -// assertEquals(12.5, scores[teams[1]]!!, 0.001) -// -// /* -// c = q(c) = 3, i = 1, q(p) = 4 -// -// 100 * 1 1 -// ------- * - = 16.66666666666667 -// 1 + 1/2 4 -// */ -// assertEquals(16.66666666666667, scores[teams[2]]!!, 0.001) -// } -// -// -// @Test -// fun allSameVideoSameSegmentAllCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(100.0, scores[teams[0]]) -// assertEquals(100.0, scores[teams[1]]) -// assertEquals(100.0, scores[teams[2]]) -// } -// -// -// @Test -// fun allSameVideoDifferentSegmentAllCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) -// assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) -// assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) -// } -// -// -// @Test -// fun allDifferentVideoDifferentSegmentAllCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[1], 30_000, 40_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[2], 50_000, 60_000).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) -// assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) -// assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) -// } -// -// @Test -// fun allSameVideoOverlappingSegmentAllCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, -// -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, -// -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT } -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// assertEquals(100.0, scores[teams[0]]) -// assertEquals(100.0, scores[teams[1]]) -// assertEquals(100.0, scores[teams[2]]) -// } -// -// @Test -// fun allSameVideoOverlappingSegmentSomeCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, -// -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, -// -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 11_000, 21_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 12_000, 22_000).also { it.status = DbVerdictStatus.CORRECT }, -// -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 2000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[3], 10_000, 20_000).also { it.status = DbVerdictStatus.WRONG } -// -// -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// -// assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) -// assertEquals(85.71428571428571, scores[teams[1]]!!, 0.001) -// assertEquals(85.71428571428571, scores[teams[2]]!!, 0.001) -// } -// -// -// @Test -// fun partiallyMergedSubmissionSomeCorrect() { -// val taskStartTime = System.currentTimeMillis() - 100_000 -// val submissions = listOf( -// //team 1 -// //gets merged to one -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, -// //plus 2 independent correct ones -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 5000, dummyVideoItems[2], 3_000, 4_000).also { it.status = DbVerdictStatus.CORRECT }, -// //and an incorrect one directly next to it -// DbSubmission.Temporal(teams[0], EvaluationId(), taskStartTime + 6000, dummyVideoItems[2], 4_001, 5_000).also { it.status = DbVerdictStatus.WRONG }, -// -// //c = 5, q(c) = 3, i = 1 -// -// -// //team 2 -// //the center part is missing, so it's counted as two in the quantization -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, -// //another correct one, same as team 1 -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, -// //and two wrong ones -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Temporal(teams[1], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, -// -// //c = 3, q(c) = 3, i = 2 -// -// //team 3 -// //same as team 2, but with all 3 segments of the 1st video, same as team 1 -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 1000, dummyVideoItems[0], 10_000, 20_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 2000, dummyVideoItems[0], 20_001, 25_000).also { it.status = DbVerdictStatus.CORRECT }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 3000, dummyVideoItems[0], 25_001, 30_000).also { it.status = DbVerdictStatus.CORRECT }, -// //another correct one, same as team 1 -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 4000, dummyVideoItems[1], 5_000, 6_000).also { it.status = DbVerdictStatus.CORRECT }, -// //and two wrong ones -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 0, 5_000).also { it.status = DbVerdictStatus.WRONG }, -// DbSubmission.Temporal(teams[2], EvaluationId(), taskStartTime + 6000, dummyVideoItems[3], 10_000, 15_000).also { it.status = DbVerdictStatus.WRONG }, -// -// //c = 4, q(c) = 2, i = 2 -// -// ) -// val scores = this.scorer.computeScores(submissions, TaskContext(teams, taskStartTime, defaultTaskDuration)) -// -// /* -// c = 5, q(c) = 3, i = 1, q(p) = 3 -// -// 100 * 5 3 -// ------- * - = 90.909090909090 -// 5 + 1/2 3 -// */ -// assertEquals(90.909090909090, scores[teams[0]]!!, 0.001) -// -// /* -// c = 3, q(c) = 3, i = 2, q(p) = 3 -// -// 100 * 3 3 -// ------- * - = 75 -// 3 + 2/2 3 -// */ -// assertEquals(75.0, scores[teams[1]]) -// -// /* -// c = 4, q(c) = 2, i = 2, q(p) = 3 -// -// 100 * 4 2 -// ------- * - = 53.33333333333 -// 4 + 2/2 3 -// */ -// assertEquals(53.33333333333, scores[teams[2]]!!, 0.001) -// } + + private lateinit var scorer: AvsTaskScorer + private val teams = listOf("team-1", "team-2", "team-3") + private val defaultTaskDuration = 5 * 60L + private val dummyImageItems: List + private val dummyVideoItems: List + + init { + val collectionId = "testCollection" + + dummyImageItems = listOf( + ApiMediaItem("image1", "image 1", ApiMediaType.IMAGE, collectionId, "images/1"), + ApiMediaItem("image2", "image 2", ApiMediaType.IMAGE, collectionId, "images/2"), + ApiMediaItem("image3", "image 3", ApiMediaType.IMAGE, collectionId, "images/3"), + ApiMediaItem("image4", "image 4", ApiMediaType.IMAGE, collectionId, "images/4"), + ApiMediaItem("image5", "image 5", ApiMediaType.IMAGE, collectionId, "images/5"), + ApiMediaItem("image6", "image 6", ApiMediaType.IMAGE, collectionId, "images/6") + ) + + dummyVideoItems = listOf( + ApiMediaItem("video1", "video 1", ApiMediaType.VIDEO, collectionId, "videos/1", 10 * 60 * 1000, 24f), + ApiMediaItem("video2", "video 2", ApiMediaType.VIDEO, collectionId, "videos/2", 10 * 60 * 1000, 24f), + ApiMediaItem("video3", "video 3", ApiMediaType.VIDEO, collectionId, "videos/3", 10 * 60 * 1000, 24f), + ApiMediaItem("video4", "video 4", ApiMediaType.VIDEO, collectionId, "videos/4", 10 * 60 * 1000, 24f), + ApiMediaItem("video5", "video 5", ApiMediaType.VIDEO, collectionId, "videos/5", 10 * 60 * 1000, 24f), + ApiMediaItem("video6", "video 6", ApiMediaType.VIDEO, collectionId, "videos/6", 10 * 60 * 1000, 24f), + ) + + } + + private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem) = listOf( + ApiAnswerSet( + status, + listOf( + ApiAnswer( + ApiAnswerType.ITEM, + item, + null, null, null + ) + ) + ) + ) + + private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long) = listOf( + ApiAnswerSet( + status, + listOf( + ApiAnswer( + ApiAnswerType.TEMPORAL, + item, + null, + start, + end + ) + ) + ) + ) + + @BeforeEach + fun setup() { + this.scorer = AvsTaskScorer + } + + @Test + fun noSubmissions() { + val scores = this.scorer.computeScores(emptySequence(), TaskContext("task1", teams, 100_000, defaultTaskDuration)) + assertEquals(0.0, scores[teams[0]]) + assertEquals(0.0, scores[teams[1]]) + assertEquals(0.0, scores[teams[2]]) + } + + @Test + fun allWrongSameImage() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 3000) + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task2", teams, 100_000, defaultTaskDuration)) + assertEquals(0.0, scores[teams[0]]) + assertEquals(0.0, scores[teams[1]]) + assertEquals(0.0, scores[teams[2]]) + } + + @Test + fun allWrongDifferentImages() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[1]), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[2]), taskStartTime + 3000) + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task3", teams, 100_000, defaultTaskDuration)) + assertEquals(0.0, scores[teams[0]]) + assertEquals(0.0, scores[teams[1]]) + assertEquals(0.0, scores[teams[2]]) + } + + @Test + fun allSameImageAllCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 3000) + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task4", teams, 100_000, defaultTaskDuration)) + assertEquals(100.0, scores[teams[0]]) + assertEquals(100.0, scores[teams[1]]) + assertEquals(100.0, scores[teams[2]]) + } + + @Test + fun allDifferentImageAllCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000) + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task5", teams, 100_000, defaultTaskDuration)) + assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) + assertEquals(100.0 / 3.0, scores[teams[1]]!!, 0.001) + assertEquals(100.0 / 3.0, scores[teams[2]]!!, 0.001) + } + + @Test + fun someDifferentImageSomeCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + + /* + total correct: 4 (0, 1, 2, 3) + total wrong: 2 (4, 5) + */ + + //3 out of 4 correct + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[3]), taskStartTime + 4000), + + //1 out of 3 correct + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[4]), taskStartTime + 2000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000), + + //1 out of 2 correct + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[3]), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000), + ) + + + val scores = this.scorer.computeScores(submissions, TaskContext("task6", teams, 100_000, defaultTaskDuration)) + + /* + c = q(c) = 3, i = 1, q(p) = 4 + + 100 * 3 3 + ------- * - = 64.28571428571429 + 3 + 1/2 4 + */ + assertEquals(64.28571428571429, scores[teams[0]]!!, 0.001) + + /* + c = q(c) = 1, i = 1, q(p) = 4 + + 100 * 1 1 + ------- * - = 12.5 + 1 + 2/2 4 + */ + assertEquals(12.5, scores[teams[1]]!!, 0.001) + + /* + c = q(c) = 3, i = 1, q(p) = 4 + + 100 * 1 1 + ------- * - = 16.66666666666667 + 1 + 1/2 4 + */ + assertEquals(16.66666666666667, scores[teams[2]]!!, 0.001) + } + + + @Test + fun allSameVideoSameSegmentAllCorrect() { + + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 3000) + ) + + val scores = this.scorer.computeScores(submissions, TaskContext("task7", teams, 100_000, defaultTaskDuration)) + assertEquals(100.0, scores[teams[0]]) + assertEquals(100.0, scores[teams[1]]) + assertEquals(100.0, scores[teams[2]]) + } + + + @Test + fun allSameVideoDifferentSegmentAllCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 30_000, 40_000), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 50_000, 60_000), taskStartTime + 3000) + ) + + val scores = this.scorer.computeScores(submissions, TaskContext("task8", teams, 100_000, defaultTaskDuration)) + assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) + assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) + assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) + } + + + @Test + fun allDifferentVideoDifferentSegmentAllCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[3], 50_000, 60_000), taskStartTime + 3000) + ) + + val scores = this.scorer.computeScores(submissions, TaskContext("task9", teams, 100_000, defaultTaskDuration)) + assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) + assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) + assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) + } + + @Test + fun allSameVideoOverlappingSegmentAllCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + + val submissions = sequenceOf( + + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000), + ) + + val scores = this.scorer.computeScores(submissions, TaskContext("task10", teams, 100_000, defaultTaskDuration)) + assertEquals(100.0, scores[teams[0]]) + assertEquals(100.0, scores[teams[1]]) + assertEquals(100.0, scores[teams[2]]) + } + + @Test + fun allSameVideoOverlappingSegmentSomeCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000), + + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 3000), + + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task11", teams, 100_000, defaultTaskDuration)) + + assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) + assertEquals(85.71428571428571, scores[teams[1]]!!, 0.001) + assertEquals(85.71428571428571, scores[teams[2]]!!, 0.001) + } + + + @Test + fun partiallyMergedSubmissionSomeCorrect() { + val taskStartTime = System.currentTimeMillis() - 100_000 + val submissions = sequenceOf( + //team 1 + //gets merged to one + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000), + + //plus 2 independent correct ones + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 3_000, 4_000), taskStartTime + 5000), + + //and an incorrect one directly next to it + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 4_001, 5_000), taskStartTime + 6000), + + //c = 5, q(c) = 3, i = 1 + + + //team 2 + //the center part is missing, so it's counted as two in the quantization + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000), + + //another correct one, same as team 1 + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000), + + //and two wrong ones + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 6000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 6000), + + + //c = 3, q(c) = 3, i = 2 + + //team 3 + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000), + + //another correct one, same as team 1 + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000), + + + //and two wrong ones + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 4000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 4000), + + //c = 4, q(c) = 2, i = 2 + + ) + val scores = this.scorer.computeScores(submissions, TaskContext("task12", teams, 100_000, defaultTaskDuration)) + + /* + c = 5, q(c) = 3, i = 1, q(p) = 3 + + 100 * 5 3 + ------- * - = 90.909090909090 + 5 + 1/2 3 + */ + assertEquals(90.909090909090, scores[teams[0]]!!, 0.001) + + /* + c = 3, q(c) = 3, i = 2, q(p) = 3 + + 100 * 3 3 + ------- * - = 75 + 3 + 2/2 3 + */ + assertEquals(75.0, scores[teams[1]]) + + /* + c = 4, q(c) = 2, i = 2, q(p) = 3 + + 100 * 4 2 + ------- * - = 53.33333333333 + 4 + 2/2 3 + */ + assertEquals(53.33333333333, scores[teams[2]]!!, 0.001) + } } diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 30d25e41e..d837976ac 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -136,7 +136,7 @@ class KisTaskScorerTest { ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), ApiSubmission(teams[1], teams[1], "user2", "user2", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), ApiSubmission(teams[2], teams[2], "user3", "user3", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), - + ) val scores = this.scorer.computeScores(submissions, TaskContext("task6", teams, taskStartTime, defaultTaskDuration)) From 2c34a325cf16140556a9667e9bccb513e5bd5a25 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 14:38:19 +0100 Subject: [PATCH 114/498] Renamed test for consistency --- .../{AvsTaskScorerTest.kt => LegacyAvsTaskScorerTest.kt} | 3 --- 1 file changed, 3 deletions(-) rename backend/src/test/kotlin/dres/run/score/scorer/{AvsTaskScorerTest.kt => LegacyAvsTaskScorerTest.kt} (99%) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt similarity index 99% rename from backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt rename to backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index d6647b783..02fe642b1 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -3,9 +3,6 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.evaluation.* -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.TaskContext import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals From ba25fa86fd062c0a10ea55442a9a355cc54653f9 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 10 Feb 2023 14:55:42 +0100 Subject: [PATCH 115/498] Fixed unit tests for AvsTaskScorer --- .../run/score/scorer/AvsTaskScorerTest.kt | 154 ++++++++++++++++++ .../run/score/scorer/NewAvsTaskScorerTest.kt | 134 --------------- 2 files changed, 154 insertions(+), 134 deletions(-) create mode 100644 backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt delete mode 100644 backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt new file mode 100644 index 000000000..fd21b9fee --- /dev/null +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -0,0 +1,154 @@ +package dres.run.score.scorer + + +import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.api.rest.types.collection.ApiMediaType +import dev.dres.api.rest.types.evaluation.* +import dev.dres.run.score.TaskContext +import dev.dres.run.score.scorer.NewAvsTaskScorer +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class AvsTaskScorerTest { + + private lateinit var scorer: NewAvsTaskScorer + private val teams = listOf("team-1", "team-2", "team-3") + private val defaultTaskDuration = 3 * 60L // 3min + private val dummyVideoItems: List + private val maxPointsPerTask = 1000.0 + private val penalty = 0.2 + + init { + val collectionId = "testCollection" + val list = mutableListOf() + for (i in 1..10){ + list.add(ApiMediaItem("video$i", "video $i", ApiMediaType.VIDEO, collectionId, "videos/$i", 10 * 60 * 1000 * i.toLong(), 24f)) + } + dummyVideoItems = list + } + + private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long) = listOf( + ApiAnswerSet( + status, + listOf( + ApiAnswer( + ApiAnswerType.TEMPORAL, + item, + null, + start, + end + ) + ) + ) + ) + + @BeforeEach + fun setup(){ + this.scorer = NewAvsTaskScorer(penaltyConstant = penalty, maxPointsPerTask) + } + + @Test + @DisplayName("Three teams all without a submission. Expected score: 0.0") + fun noSubmissions(){ + val scores = this.scorer.computeScores(emptySequence(), TaskContext("task1", teams, 100_000, defaultTaskDuration)) + + Assertions.assertEquals(0.0, scores[teams[0]]) + Assertions.assertEquals(0.0, scores[teams[1]]) + Assertions.assertEquals(0.0, scores[teams[2]]) + } + + @Test + @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") + fun onlyTeamOneWithAllEqualsOneCorrect(){ + val taskStart = 100_000L + val subs = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ) + val scores = this.scorer.computeScores(subs, TaskContext("task2", teams, 100_000, defaultTaskDuration)) + Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) + Assertions.assertEquals(0.0, scores[teams[1]]) + Assertions.assertEquals(0.0, scores[teams[2]]) + } + + @Test + @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") + fun allTeamsWithAllEqualsOneCorrect(){ + val taskStart = 100_000L + val subs = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 3000) + ) + val scores = this.scorer.computeScores(subs, TaskContext("task2", teams, 100_000, defaultTaskDuration)) + Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) + Assertions.assertEquals(maxPointsPerTask, scores[teams[1]]) + Assertions.assertEquals(maxPointsPerTask, scores[teams[2]]) + } + + @Test + @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") + fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ + val taskStart = 100_000L + val subs = sequenceOf( + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000), taskStart + 2000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 3000), + ) + val scores = this.scorer.computeScores(subs, TaskContext("task3", teams, 100_000, defaultTaskDuration)) + Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) + Assertions.assertEquals(maxPointsPerTask/2.0, scores[teams[1]]) + Assertions.assertEquals(0.0, scores[teams[2]]) + } + + @Test + @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") + fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ + val taskStart = 100_000L + val subs = sequenceOf( + /* Team One: All correct */ + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000), taskStart + 2000), + ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStart + 3000), + + /* Team Two: One correct, One correct with one wrong */ + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000), taskStart + 2000), + ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000), taskStart + 3000), + + /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ + /* v1 */ + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + /* v2 */ + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000), taskStart + 2000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000), taskStart + 3000), + /* v3 */ + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000), taskStart + 4000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000), taskStart + 5000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStart + 6000), + /* v4 */ + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStart + 7000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000), taskStart + 8000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000), taskStart + 9000), + /* v5 */ + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000), taskStart + 10000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000), taskStart + 11000), + ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000), taskStart + 12000), + + ) + val scores = this.scorer.computeScores(subs, TaskContext("task4", teams, 100_000, defaultTaskDuration)) + /* + Team One: No penalty => 1000 = 1000 * [1/3 * 1+1+1] => 1000 * 3/3 + */ + Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) + /* + Team Two: One penalty => 600 = 1000 * [1/3 * (1 + 1-0.2)] => 1000 * 1.8/3 + */ + Assertions.assertEquals(600.0, scores[teams[1]]!!, 0.001) + /* + Team Three: Lucky Brute Force => 400 = 1000 * [1/3 * (1 + {1-0.2} + {1-0.4} + {-3*0.2} + {-3*0.2})] => 1000 * 1.2/3 + */ + Assertions.assertEquals(400.0, scores[teams[2]]!!, 0.001) + } +} diff --git a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt deleted file mode 100644 index 1fc9762de..000000000 --- a/backend/src/test/kotlin/dres/run/score/scorer/NewAvsTaskScorerTest.kt +++ /dev/null @@ -1,134 +0,0 @@ -package dres.run.score.scorer - - -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.run.score.TaskContext -import dev.dres.run.score.scorer.NewAvsTaskScorer -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - -class NewAvsTaskScorerTest { - -// private lateinit var scorer: NewAvsTaskScorer -// private val teams = listOf(UID(),UID(),UID()) // 3 teams -// private val defaultTaskDuration = 3 * 60L // 3min -// private val dummyVideoItems: List -// private val maxPointsPerTask = 1000.0 -// private val penalty = 0.2 -// -// init { -// val collectionId = UID() -// val list = mutableListOf() -// for (i in 1..10){ -// list.add(MediaItem.VideoItem(UID(), "Video $i", "videos/$i", collectionId, i*10*60*1000L,24f)) -// } -// dummyVideoItems = list -// } -// -// @BeforeEach -// fun setup(){ -// this.scorer = NewAvsTaskScorer(penaltyConstant = penalty, maxPointsPerTask) -// } -// -// @Test -// @DisplayName("Three teams all without a submission. Expected score: 0.0") -// fun noSubmissions(){ -// val scores = this.scorer.computeScores(emptyList(), TaskContext(teams, 100_000, defaultTaskDuration)) -// -// Assertions.assertEquals(0.0, scores[teams[0]]) -// Assertions.assertEquals(0.0, scores[teams[1]]) -// Assertions.assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") -// fun onlyTeamOneWithAllEqualsOneCorrect(){ -// val taskStart = 100_000L -// val subs = listOf( -// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} -// ) -// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) -// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) -// Assertions.assertEquals(0.0, scores[teams[1]]) -// Assertions.assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") -// fun allTeamsWithAllEuqalsOneCorrect(){ -// val taskStart = 100_000L -// val subs = listOf( -// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} -// ) -// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) -// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) -// Assertions.assertEquals(maxPointsPerTask, scores[teams[1]]) -// Assertions.assertEquals(maxPointsPerTask, scores[teams[2]]) -// } -// -// @Test -// @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") -// fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ -// val taskStart = 100_000L -// val subs = listOf( -// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT} -// ) -// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) -// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) -// Assertions.assertEquals(maxPointsPerTask/2.0, scores[teams[1]]) -// Assertions.assertEquals(0.0, scores[teams[2]]) -// } -// -// @Test -// @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") -// fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ -// val taskStart = 100_000L -// val subs = listOf( -// /* Team One: All correct */ -// DbSubmission.Temporal(teams[0], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[0], UID(), taskStart+2000, dummyVideoItems[1], 20_000, 30_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[0], UID(), taskStart+3000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, -// /* Team Two: One correct, One correct with one wrong */ -// DbSubmission.Temporal(teams[1], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// DbSubmission.Temporal(teams[1], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[1], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, -// /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ -// /* v1 */ -// DbSubmission.Temporal(teams[2], UID(), taskStart+1000, dummyVideoItems[0], 10_000, 20_000).also{it.status = SubmissionStatus.CORRECT}, -// /* v2 */ -// DbSubmission.Temporal(teams[2], UID(), taskStart+2000, dummyVideoItems[1], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+3000, dummyVideoItems[1], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, -// /* v3 */ -// DbSubmission.Temporal(teams[2], UID(), taskStart+4000, dummyVideoItems[2], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+5000, dummyVideoItems[2], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+6000, dummyVideoItems[2], 30_000, 40_000).also{it.status = SubmissionStatus.CORRECT}, -// /* v4 */ -// DbSubmission.Temporal(teams[2], UID(), taskStart+7000, dummyVideoItems[3], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+8000, dummyVideoItems[3], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+9000, dummyVideoItems[3], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, -// /* v5 */ -// DbSubmission.Temporal(teams[2], UID(), taskStart+10_000, dummyVideoItems[4], 10_000, 20_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+11_000, dummyVideoItems[4], 20_000, 30_000).also{it.status = SubmissionStatus.WRONG}, -// DbSubmission.Temporal(teams[2], UID(), taskStart+12_000, dummyVideoItems[4], 30_000, 40_000).also{it.status = SubmissionStatus.WRONG}, -// ) -// val scores = this.scorer.computeScores(subs, TaskContext(teams, taskStart, defaultTaskDuration)) -// /* -// Team One: No penalty => 1000 = 1000 * [1/3 * 1+1+1] => 1000 * 3/3 -// */ -// Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) -// /* -// Team Two: One penalty => 600 = 1000 * [1/3 * (1 + 1-0.2)] => 1000 * 1.8/3 -// */ -// Assertions.assertEquals(600.0, scores[teams[1]]!!, 0.001) -// /* -// Team Three: Lucky Brute Force => 400 = 1000 * [1/3 * (1 + {1-0.2} + {1-0.4} + {-3*0.2} + {-3*0.2})] => 1000 * 1.2/3 -// */ -// Assertions.assertEquals(400.0, scores[teams[2]]!!, 0.001) -// } -} From 3b5a5fcf3f8e2b1cbfec5a01c4f9c2ce0a0ea4bd Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 11 Feb 2023 17:53:31 +0100 Subject: [PATCH 116/498] Initial work on audit cli command --- .../kotlin/dev/dres/api/cli/AuditCommand.kt | 38 +++++++++++++++++++ .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 3 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt new file mode 100644 index 000000000..f2c4450b0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt @@ -0,0 +1,38 @@ +package dev.dres.api.cli + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.NoOpCliktCommand +import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.options.convert +import com.github.ajalt.clikt.parameters.options.default +import com.github.ajalt.clikt.parameters.options.option +import dev.dres.data.model.audit.DbAuditLogEntry +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.sortedBy +import kotlinx.dnq.query.take + +class AuditCommand(private val store: TransientEntityStore) : NoOpCliktCommand(name = "audit") { + + init { + subcommands(Show()) + } + + inner class Show() : CliktCommand(name = "shows", help = "Shows latest audit log entries", printHelpOnEmptyArgs = true) { + + private val number: Int by option("-n", "--number", help = "Number of audit log entries to show") + .convert { it.toInt() } + .default(10) + + override fun run() { + + this@AuditCommand.store.transactional(readonly = true) { + DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = true) + .take(number).asSequence().forEach { entry -> + println("${entry.timestamp}: (${entry.type.description}@${entry.source.description}) user: ${entry.userId}, evaluation: ${entry.evaluationId}, task: ${entry.taskId}, description: ${entry.description}") + } + } + } + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 09e25cb5e..fe4e2502c 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -46,7 +46,8 @@ object Cli { MediaCollectionCommand(store), EvaluationCommand(store), OpenApiCommand(), - ExecutionCommand() + ExecutionCommand(), + AuditCommand(store) ) val terminal = try { From 45a6f2407b6aaa957477e4f3abfe42affee0fe1e Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 12 Feb 2023 20:20:07 +0100 Subject: [PATCH 117/498] Added cli export command for audit log entries --- .../kotlin/dev/dres/api/cli/AuditCommand.kt | 80 ++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt index f2c4450b0..2c5c84bfa 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt @@ -1,38 +1,96 @@ package dev.dres.api.cli +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.parameters.options.convert import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required import dev.dres.data.model.audit.DbAuditLogEntry import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.sortedBy -import kotlinx.dnq.query.take +import kotlinx.dnq.query.* +import org.joda.time.DateTime +import org.joda.time.format.DateTimeFormat +import java.io.File + class AuditCommand(private val store: TransientEntityStore) : NoOpCliktCommand(name = "audit") { init { - subcommands(Show()) + subcommands(Show(), Export()) } - inner class Show() : CliktCommand(name = "shows", help = "Shows latest audit log entries", printHelpOnEmptyArgs = true) { + inner class Show() : + CliktCommand(name = "show", help = "Shows latest audit log entries", printHelpOnEmptyArgs = false) { private val number: Int by option("-n", "--number", help = "Number of audit log entries to show") - .convert { it.toInt() } - .default(10) + .convert { it.toInt() } + .default(10) override fun run() { - this@AuditCommand.store.transactional(readonly = true) { DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = true) - .take(number).asSequence().forEach { entry -> - println("${entry.timestamp}: (${entry.type.description}@${entry.source.description}) user: ${entry.userId}, evaluation: ${entry.evaluationId}, task: ${entry.taskId}, description: ${entry.description}") - } + .take(number).asSequence().forEach { entry -> + println("${entry.timestamp}: (${entry.type.description}@${entry.source.description}) user: ${entry.userId}, evaluation: ${entry.evaluationId}, task: ${entry.taskId}, description: ${entry.description}") + } + } + } + } + + inner class Export() : CliktCommand( + name = "export", + help = "Exports audit log entries from within a specified time frame to a file as one JSON object per line", + printHelpOnEmptyArgs = true + ) { + + /** The output path for the export. */ + private val output: File by option( + "-o", + "--output", + help = "Path of the file the media collection should to be exported to." + ).convert { File(it) }.required() + + private val pattern = DateTimeFormat.forPattern("dd.MM.yyyy-HH:mm:ss") + + private val startTimeStamp: DateTime by option( + "--from", + help = "start time stamp from which to export in the format dd.MM.yyyy-HH:mm:ss" + ).convert { + DateTime.parse(it, pattern) + }.default(DateTime(0)) + + private val endTimeStamp: DateTime by option( + "--to", + help = "end time stamp from which to export in the format dd.MM.yyyy-HH:mm:ss" + ).convert { + DateTime.parse(it, pattern) + }.default(DateTime(Long.MAX_VALUE)) + + override fun run() { + val writer = output.printWriter(Charsets.UTF_8) + val mapper = jacksonObjectMapper() + + var counter = 0 + + this@AuditCommand.store.transactional(readonly = true) { + DbAuditLogEntry.query((DbAuditLogEntry::timestamp ge startTimeStamp) and (DbAuditLogEntry::timestamp le endTimeStamp)) + .sortedBy(DbAuditLogEntry::timestamp, asc = true) + .asSequence().forEach { + writer.println(mapper.writeValueAsString(it.toApi())) + ++counter + } } + + writer.flush() + writer.close() + + println("wrote $counter entries to ${output.absolutePath}") + } + } + } \ No newline at end of file From a6bcb1e156e0546963a1199b09dddb5e796f181a Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 13 Feb 2023 08:31:10 +0100 Subject: [PATCH 118/498] Adding styles for #310 --- frontend/src/styles.scss | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 4ca6d6458..ffe4b1e26 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -72,3 +72,39 @@ $warn: mat-palette($mat-red, 500); .warn-color { color: mat-color($warn) !important; } + +.p1r { + padding: 1rem; +} + +.p2r { + padding: 2rem; +} + +.p1e { + padding: 1em; +} + +.p2e { + padding: 2em; +} + +.pt2e { + padding-top: 2em; +} + +.pt1e { + padding-top: 1em; +} + +.pt1r { + padding-top: 1rem; +} + +.pt2r { + padding-top: 2rem; +} + +.pb1e { + padding-bottom: 1em; +} From ba1f1c8f988e079fdd1a0aae6150871fd57ce4de Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Feb 2023 09:06:07 +0100 Subject: [PATCH 119/498] Fixes in UpdateUsersHandler --- .../dres/api/rest/handler/users/UpdateUsersHandler.kt | 11 +++++++++-- .../main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt index 4cc6ebb5f..41ab436f2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -56,7 +56,14 @@ class UpdateUsersHandler(private val store: TransientEntityStore) : AbstractUser val caller = userFromSession(ctx) if (caller.role == DbRole.ADMIN || user.id == caller.id) { - val success = DbUserManager.update(id = user.id, request = request) + + val sanitized = if (caller.role == DbRole.ADMIN) { + request + } else { //non admins can only change their passwords, nothing else + request.copy(username = user.username, role = user.role.toApi()) + } + + val success = DbUserManager.update(id = user.id, request = sanitized) if (success) { return@transactional DbUserManager.get(id = user.id)!!.toApi() } else { @@ -65,7 +72,7 @@ class UpdateUsersHandler(private val store: TransientEntityStore) : AbstractUser } else { throw ErrorStatusException( 403, - "You do not have permissions to edit user (${user.id}) as $caller!", + "You do not have permissions to edit user (${user.id}) as ${caller.id}!", ctx ) } diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt index 7f77b3637..8b8b60abf 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt @@ -101,7 +101,7 @@ object DbUserManager { * @return True on success, false otherwise. */ fun update(id: UserId?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { Password.Plain(it) }, role = request.role?.toDb()) + = update(id = id, username = request.username, password = request.password?.let { if (it.isNotBlank()) Password.Plain(it) else null }, role = request.role?.toDb()) /** * Deletes the [DbUser] for the given [UserId]. From 7b8fd5482434d4ac78a07fc0ed58cc59cbd0f8ed Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Feb 2023 09:37:58 +0100 Subject: [PATCH 120/498] Some cleanup in audit log mechanism --- backend/src/main/kotlin/dev/dres/DRES.kt | 6 +- .../kotlin/dev/dres/api/cli/AuditCommand.kt | 2 +- .../handler/audit/ListAuditLogsHandler.kt | 3 +- .../evaluation/admin/AdjustDurationHandler.kt | 4 +- .../admin/OverrideSubmissionHandler.kt | 4 +- .../admin/StartEvaluationHandler.kt | 5 +- .../evaluation/admin/StartTaskHandler.kt | 4 +- .../evaluation/admin/StopEvaluationHandler.kt | 5 +- .../evaluation/admin/StopTaskHandler.kt | 4 +- .../handler/judgement/PostJudgementHandler.kt | 4 +- .../submission/LegacySubmissionHandler.kt | 4 +- .../handler/submission/SubmissionHandler.kt | 11 +-- .../api/rest/handler/system/LoginHandler.kt | 4 +- .../api/rest/handler/system/LogoutHandler.kt | 4 +- .../dres/data/model/audit/DbAuditLogEntry.kt | 6 ++ .../run/InteractiveAsynchronousEvaluation.kt | 4 +- .../run/InteractiveSynchronousEvaluation.kt | 4 +- .../run/InteractiveAsynchronousRunManager.kt | 6 +- .../run/InteractiveSynchronousRunManager.kt | 6 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 4 +- .../{AuditLogger.kt => DbAuditLogger.kt} | 81 +++++++------------ .../judged/BasicJudgementValidator.kt | 6 +- 22 files changed, 78 insertions(+), 103 deletions(-) rename backend/src/main/kotlin/dev/dres/run/audit/{AuditLogger.kt => DbAuditLogger.kt} (74%) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 0d1093506..92227b0c2 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -26,9 +26,8 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.DbAnswerType -import dev.dres.mgmt.admin.DbUserManager import dev.dres.run.RunExecutor -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.handlers.ResultLogStatisticsHandler import dev.dres.run.eventstream.handlers.SubmissionStatisticsHandler @@ -71,9 +70,6 @@ object DRES { /* Initialize RunExecutor. */ RunExecutor.init(store) - /* Initialize AuditLogger */ - AuditLogger.init(store) - /* Initialize EventStreamProcessor */ EventStreamProcessor.register(SubmissionStatisticsHandler(), ResultLogStatisticsHandler(store), TeamCombinationScoreHandler()) EventStreamProcessor.init() diff --git a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt index 2c5c84bfa..05972a9cf 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt @@ -31,7 +31,7 @@ class AuditCommand(private val store: TransientEntityStore) : NoOpCliktCommand(n override fun run() { this@AuditCommand.store.transactional(readonly = true) { - DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = true) + DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = false) .take(number).asSequence().forEach { entry -> println("${entry.timestamp}: (${entry.type.description}@${entry.source.description}) user: ${entry.userId}, evaluation: ${entry.evaluationId}, task: ${entry.taskId}, description: ${entry.description}") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index c7e285102..3bbb74da8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -10,6 +10,7 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.drop +import kotlinx.dnq.query.sortedBy import kotlinx.dnq.query.take /** @@ -50,6 +51,6 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle override fun doGet(ctx: Context): List = this.store.transactional(true) { val limit = (ctx.pathParamMap()[LIMIT_PARAM]?.toIntOrNull() ?: DEFAULT_LIMIT).coerceAtLeast(0) val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) - DbAuditLogEntry.all().drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() + DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = false).drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 962c7c761..a8c392660 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -50,7 +50,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.adjustDuration(rac, duration) - AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Duration for run $evaluationId was successfully adjusted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt index c582b8f20..33536b961 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse @@ -72,7 +72,7 @@ class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluation } if (evaluationManager.updateSubmission(rac, submissionInfo.submissionId, submissionInfo.answers.first().status.toDb())) { val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.submissionId } - AuditLogger.overrideSubmission(submission, DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.overrideSubmission(submission, DbAuditLogSource.REST, ctx.sessionToken()) submission.toApi() } else { throw ErrorStatusException(500, "Could not update the submission. Please see the backend's log.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index fdfb3a327..8f21d3478 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -1,12 +1,13 @@ package dev.dres.api.rest.handler.evaluation.admin +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -45,7 +46,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.start(rac) - AuditLogger.competitionStart(evaluationManager.id, evaluationManager.template, DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template, DbAuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index fe3f90b61..6566b0145 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunActionContext -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -55,7 +55,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.startTask(rac) - AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index c200b0c35..17cd57c93 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -1,12 +1,13 @@ package dev.dres.api.rest.handler.evaluation.admin +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -46,7 +47,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.end(rac) - AuditLogger.competitionEnd(evaluationManager.id, DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.evaluationEnd(evaluationManager.id, DbAuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully stopped.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 429310fff..6887df5b1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunActionContext -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -46,7 +46,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl try { val task = evaluationManager.currentTaskTemplate(rac) evaluationManager.abortTask(rac) - AuditLogger.taskEnd(evaluationManager.id, task.id, DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.taskEnd(evaluationManager.id, task.id, DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index fc8c2a52e..b21f8a991 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -8,7 +8,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken @@ -61,7 +61,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } - AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.toDb(), DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.toDb(), DbAuditLogSource.REST, ctx.sessionToken()) } return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index dab59c1a8..76fbc75e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -19,7 +19,7 @@ import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask import dev.dres.data.model.submissions.* import dev.dres.run.InteractiveRunManager -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException @@ -107,7 +107,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - AuditLogger.submission(submission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) + DbAuditLogger.submission(submission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS)) { //pre-generate preview generatePreview(submission.answerSets.first()) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 3b0edda80..5a0ce30d2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -3,7 +3,6 @@ package dev.dres.api.rest.handler.submission import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.competition.ApiEvaluationStartMessage import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatusException @@ -12,12 +11,8 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.Config import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.template.task.options.DbTaskOption -import dev.dres.run.InteractiveRunManager -import dev.dres.run.NonInteractiveRunManager -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException @@ -26,8 +21,6 @@ import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.contains -import kotlinx.dnq.query.first import org.slf4j.LoggerFactory class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): PostRestHandler, AccessManagedRestHandler { @@ -69,7 +62,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) } - AuditLogger.submission(apiSubmission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) + DbAuditLogger.submission(apiSubmission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) logger.info("Submission ${apiSubmission.submissionId} received status $result.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index eea2e9463..acd2ede79 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -9,7 +9,7 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Password import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.mgmt.admin.DbUserManager -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.getOrCreateSessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -63,7 +63,7 @@ class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostR val sessionToken = ctx.getOrCreateSessionToken() AccessManager.registerUserForSession(sessionToken, user) - AuditLogger.login(loginRequest.username, DbAuditLogSource.REST, sessionToken) + DbAuditLogger.login(loginRequest.username, DbAuditLogSource.REST, sessionToken) //explicitly set cookie on login ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index 95cf3d9e3..82e414c70 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* @@ -40,7 +40,7 @@ class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetR val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) return store.transactional { - AuditLogger.logout(userId, DbAuditLogSource.REST, ctx.sessionToken()!!) + DbAuditLogger.logout(userId, DbAuditLogSource.REST, ctx.sessionToken()!!) AccessManager.deregisterUserSession(ctx.sessionToken()!!) SuccessStatus("User '${username}' logged out successfully.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt index aefc73ead..718b24c4f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt @@ -7,6 +7,7 @@ import kotlinx.dnq.XdNaturalEntityType import kotlinx.dnq.xdLink1 import kotlinx.dnq.xdRequiredDateTimeProp import kotlinx.dnq.xdStringProp +import org.joda.time.DateTime /** * @@ -14,6 +15,11 @@ import kotlinx.dnq.xdStringProp class DbAuditLogEntry(entity: Entity): PersistentEntity(entity) { companion object : XdNaturalEntityType() + override fun constructor() { + super.constructor() + this.timestamp = DateTime.now() + } + /** The type of [DbAuditLogEntry]. */ var type by xdLink1(DbAuditLogType) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 98d9ea442..cc4832cea 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -7,7 +7,7 @@ import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionFilter import kotlinx.dnq.query.* @@ -217,7 +217,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe /* Process Submission. */ this.submissions.add(dbSubmission) this.validator.validate(submission) - AuditLogger.validateSubmission(submission, this.validator) + DbAuditLogger.validateSubmission(submission, this.validator) } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 13f034afc..32fd345f6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -6,7 +6,7 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.filter.SubmissionFilter import kotlinx.dnq.query.* @@ -123,7 +123,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /* Process Submission. */ this.submissions.add(dbSubmission) this.validator.validate(submission) - AuditLogger.validateSubmission(submission, this.validator) + DbAuditLogger.validateSubmission(submission, this.validator) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 59e3ce95a..b479d3b7e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.data.model.template.team.TeamId -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.score.ScoreTimePoint @@ -662,7 +662,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn if (timeLeft <= 0) { this.stateLock.write { task.end() - AuditLogger.taskEnd(this.id, task.id, DbAuditLogSource.INTERNAL, null) + DbAuditLogger.taskEnd(this.id, task.id, DbAuditLogSource.INTERNAL, null) } /* Enqueue WS message for sending */ @@ -672,7 +672,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn val task = this.evaluation.currentTaskForTeam(team.teamId) ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") task.start() - AuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_START), team.teamId) } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 20fc886c6..25c05fc6e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.submissions.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.options.DbTaskOption -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent import dev.dres.run.exceptions.IllegalRunStateException @@ -579,7 +579,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.write { this.evaluation.currentTask!!.start() //this.status = RunManagerStatus.RUNNING_TASK - AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, DbAuditLogSource.INTERNAL, null) + DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, DbAuditLogSource.INTERNAL, null) } /* Enqueue WS message for sending */ @@ -597,7 +597,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.write { task.end() //this.status = RunManagerStatus.TASK_ENDED - AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, DbAuditLogSource.INTERNAL, null) + DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, DbAuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.id)) } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 48c5aab2b..dd7b50a34 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.NonInteractiveEvaluation import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write @@ -63,7 +63,7 @@ object RunExecutor : Consumer { /** Internal array of [Future]s for cleaning after [RunManager]s. See [RunExecutor.cleanerThread]*/ private val results = HashMap, EvaluationId>() - /** The [TransientEntityStore] instance used by this [AuditLogger]. */ + /** The [TransientEntityStore] instance used by this [DbAuditLogger]. */ private lateinit var store: TransientEntityStore /** diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt similarity index 74% rename from backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt rename to backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt index 758702943..1c0f5fe37 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt @@ -22,14 +22,7 @@ import org.joda.time.DateTime * @author Luca Rossetto * @version 2.0.0 */ -object AuditLogger { - /** The [TransientEntityStore] instance used by this [AuditLogger]. */ - private lateinit var store: TransientEntityStore - - /** Initializes this [AuditLogger]. */ - fun init(store: TransientEntityStore) { - this.store = store - } +object DbAuditLogger { /** * Logs the start of a DRES competition. @@ -38,12 +31,12 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun competitionStart(evaluationId: EvaluationId, description: DbEvaluationTemplate, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + fun evaluationStart(evaluationId: EvaluationId, description: DbEvaluationTemplate, api: DbAuditLogSource, userId: UserId?, session: SessionToken?) { DbAuditLogEntry.new { this.type = DbAuditLogType.COMPETITION_START this.source = api - this.timestamp = DateTime.now() this.evaluationId = evaluationId + this.userId = userId this.session = session } EventStreamProcessor.event(RunStartEvent(evaluationId, description)) @@ -56,12 +49,12 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun competitionEnd(evaluationId: EvaluationId, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + fun evaluationEnd(evaluationId: EvaluationId, api: DbAuditLogSource, userId: UserId?, session: SessionToken?) { DbAuditLogEntry.new { this.type = DbAuditLogType.COMPETITION_END this.source = api - this.timestamp = DateTime.now() this.evaluationId = evaluationId + this.userId = userId this.session = session } EventStreamProcessor.event(RunEndEvent(evaluationId)) @@ -76,11 +69,10 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: DbTaskTemplate, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: DbTaskTemplate, api: DbAuditLogSource, session: SessionToken?) { DbAuditLogEntry.new { this.type = DbAuditLogType.TASK_START this.source = api - this.timestamp = DateTime.now() this.evaluationId = evaluationId this.taskId = taskId this.session = session @@ -97,11 +89,10 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: DbAuditLogSource, session: String?) = this.store.transactional { + fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: DbAuditLogSource, session: String?) { DbAuditLogEntry.new { this.type = DbAuditLogType.TASK_MODIFIED this.source = api - this.timestamp = DateTime.now() this.evaluationId = evaluationId this.taskId = taskId this.description = modification @@ -117,11 +108,10 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: DbAuditLogSource, session: SessionToken?) = this.store.transactional { + fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: DbAuditLogSource, session: SessionToken?) { DbAuditLogEntry.new { this.type = DbAuditLogType.TASK_END this.source = api - this.timestamp = DateTime.now() this.evaluationId = evaluationId this.taskId = taskId this.session = session @@ -137,11 +127,10 @@ object AuditLogger { * @param sessionToken The identifier of the user session. * @param address The IP address of the submitter. */ - fun submission(submission: Submission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) = this.store.transactional { + fun submission(submission: Submission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) { DbAuditLogEntry.new { this.type = DbAuditLogType.SUBMISSION this.source = api - this.timestamp = DateTime.now() this.submissionId = submission.submissionId this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ @@ -157,17 +146,14 @@ object AuditLogger { * @param submission The [DbSubmission] the submission that was validated * @param validator The [SubmissionValidator] instance. */ - fun validateSubmission(submission: Submission, validator: SubmissionValidator) = this.store.transactional { - this.store.transactional { - DbAuditLogEntry.new { - this.type = DbAuditLogType.SUBMISSION_VALIDATION - this.source = DbAuditLogSource.INTERNAL - this.timestamp = DateTime.now() - this.submissionId = submission.submissionId - this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId - this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ - this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets().first().status()}" /* TODO: Here name, there ID. Why? */ - } + fun validateSubmission(submission: Submission, validator: SubmissionValidator) { + DbAuditLogEntry.new { + this.type = DbAuditLogType.SUBMISSION_VALIDATION + this.source = DbAuditLogSource.INTERNAL + this.submissionId = submission.submissionId + this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId + this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ + this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets().first().status()}" /* TODO: Here name, there ID. Why? */ } } @@ -178,18 +164,15 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param sessionToken The identifier of the user session. */ - fun overrideSubmission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?) = this.store.transactional { - this.store.transactional { - DbAuditLogEntry.new { - this.type = DbAuditLogType.SUBMISSION_STATUS_OVERWRITE - this.source = api - this.timestamp = DateTime.now() - this.submissionId = submission.id - this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId - this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ - this.description = "Verdict: ${submission.answerSets.first().status.description}" - this.session = sessionToken - } + fun overrideSubmission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?) { + DbAuditLogEntry.new { + this.type = DbAuditLogType.SUBMISSION_STATUS_OVERWRITE + this.source = api + this.submissionId = submission.id + this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId + this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ + this.description = "Verdict: ${submission.answerSets.first().status.description}" + this.session = sessionToken } } @@ -200,11 +183,10 @@ object AuditLogger { * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system */ - fun prepareJudgement(answerSet: AnswerSet, validator: JudgementValidator, token: String) = this.store.transactional { + fun prepareJudgement(answerSet: AnswerSet, validator: JudgementValidator, token: String) { DbAuditLogEntry.new { this.type = DbAuditLogType.PREPARE_JUDGEMENT this.source = DbAuditLogSource.INTERNAL - this.timestamp = DateTime.now() this.submissionId = answerSet.submission.submissionId this.evaluationId = answerSet.task.evaluation.evaluationId this.taskId = answerSet.task.taskId @@ -222,11 +204,10 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param sessionToken The identifier of the user session. */ - fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: DbVerdictStatus, api: DbAuditLogSource, sessionToken: SessionToken?) = this.store.transactional { + fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: DbVerdictStatus, api: DbAuditLogSource, sessionToken: SessionToken?) { DbAuditLogEntry.new { this.type = DbAuditLogType.JUDGEMENT this.source = api - this.timestamp = DateTime.now() this.evaluationId = evaluationId this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.description}" this.session = sessionToken @@ -240,11 +221,10 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param sessionToken The [SessionToken] */ - fun login(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) = this.store.transactional { + fun login(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) { DbAuditLogEntry.new { this.type = DbAuditLogType.LOGIN this.source = api - this.timestamp = DateTime.now() this.userId = userId this.session = sessionToken } @@ -257,11 +237,10 @@ object AuditLogger { * @param api The [DbAuditLogSource] * @param sessionToken The [SessionToken] */ - fun logout(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) = this.store.transactional { + fun logout(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) { DbAuditLogEntry.new { this.type = DbAuditLogType.LOGOUT this.source = api - this.timestamp = DateTime.now() this.userId = userId this.session = sessionToken } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index ecaeb2662..947d68e77 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,12 +1,10 @@ package dev.dres.run.validation.judged import dev.dres.data.model.submissions.* -import dev.dres.run.audit.AuditLogger +import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.first import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -132,7 +130,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e val token = UUID.randomUUID().toString() this.waiting[token] = next this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) - AuditLogger.prepareJudgement(next, this, token) + DbAuditLogger.prepareJudgement(next, this, token) Pair(token, next) } else { null From b78e1ed45a448a92c1efa1481d4e8ccd3df1ff57 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Feb 2023 09:41:23 +0100 Subject: [PATCH 121/498] Minor fix in audit log display --- .../admin-auditlog-overview.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html index 70820c917..e82e19e31 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html +++ b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html @@ -23,7 +23,7 @@ API - {{ row.api ? row.api : 'N/A' }} + {{ row.source ? row.source : 'N/A' }} From e54ed4a52ec0d543453cc190ac5efcf844f775f3 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 14:17:59 +0100 Subject: [PATCH 122/498] Fixed type. --- .../main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index 76c8a02a9..291dcb96b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.DbTask -import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.TemplateId /** @@ -12,7 +12,7 @@ import dev.dres.data.model.template.TemplateId * @version 2.0.0 */ data class ApiTask( - val taskId: EvaluationId, + val taskId: TaskId, val templateId: TemplateId, val started: Long?, val ended: Long?, From acafcf41c67bcfa14a6061c04ad27bd1a1f12c09 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 14:19:19 +0100 Subject: [PATCH 123/498] Adjusted JavaScript version. --- frontend/tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index aad6fd54b..3a93e284b 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -10,8 +10,8 @@ "module": "es2020", "moduleResolution": "node", "importHelpers": true, - "target": "es2015", - "lib": ["es2018", "dom"] + "target": "es2020", + "lib": ["es2020", "dom"] }, "angularCompilerOptions": { "fullTemplateTypeCheck": true, From ce75b11b3222d45662b230c37eaccef1801a7c26 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 14:21:14 +0100 Subject: [PATCH 124/498] Fixed type of taskStarted, taskChanged and taskEnded observable. --- frontend/src/app/viewer/task-viewer.component.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index b288614ce..50be4746d 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -23,11 +23,10 @@ import { AudioPlayerUtilities } from '../utilities/audio-player.utilities'; import { fromArray } from 'rxjs/internal/observable/fromArray'; import { ApiContentElement, ApiContentType, - ApiEvaluationState, ApiHint, + ApiEvaluationState, ApiHintContent, - ApiTarget, ApiTargetContent, - ApiTaskStatus, + ApiTaskStatus, ApiTaskTemplateInfo, EvaluationService } from '../../../openapi'; @@ -51,9 +50,9 @@ enum ViewerState { export class TaskViewerComponent implements AfterViewInit, OnDestroy { @Input() runId: Observable; @Input() state: Observable; - @Input() taskStarted: Observable; - @Input() taskChanged: Observable; - @Input() taskEnded: Observable; + @Input() taskStarted: Observable; + @Input() taskChanged: Observable; + @Input() taskEnded: Observable; @Input() webSocketSubject: WebSocketSubject; /** Time that is still left (only when a task is running). */ @@ -90,7 +89,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskHint = this.taskChanged.pipe( withLatestFrom(this.runId), switchMap(([task, runId]) => - this.runService.getApiV2EvaluationByEvaluationIdHintByTaskId(runId, task.id).pipe( + this.runService.getApiV2EvaluationByEvaluationIdHintByTaskId(runId, task.templateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current query hint due to an error.', e); return of(null); From c05d79fe590e3566be0a9d9f1afded7293129fa8 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 14:26:04 +0100 Subject: [PATCH 125/498] Unified naming and structure of ApiEvaluationState --- .../types/evaluation/ApiEvaluationState.kt | 10 +++--- .../types/evaluation/ApiEvaluationStatus.kt | 32 +++++++++++++++++++ .../kotlin/dev/dres/run/RunManagerStatus.kt | 17 ++++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt index e40aca891..437bc708f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt @@ -12,16 +12,16 @@ import dev.dres.run.* * @version 1.2.0 */ data class ApiEvaluationState( - val id: String, - val runStatus: RunManagerStatus, // FIXME non-api type exposed via api - val taskRunStatus: ApiTaskStatus, - val currentTask: ApiTaskTemplateInfo?, + val evaluationId: String, + val evaluationStatus: ApiEvaluationStatus, + val taskStatus: ApiTaskStatus, + val currentTemplate: ApiTaskTemplateInfo?, val timeLeft: Long, val timeElapsed: Long ) { constructor(run: InteractiveRunManager, context: RunActionContext) : this( run.id, - run.status, + run.status.toApi(), when(run.currentTask(context)?.status) { TaskStatus.CREATED -> ApiTaskStatus.CREATED TaskStatus.PREPARING -> ApiTaskStatus.PREPARING diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt new file mode 100644 index 000000000..dc0f197c8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt @@ -0,0 +1,32 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.run.RunManager +import dev.dres.run.RunManagerStatus + +/** + * The collection auf statuses a [RunManager] can be in. API version of [RunManagerStatus] + * + * @author Ralph Gasser + * @version 1.0.0 + */ +enum class ApiEvaluationStatus { + /** [RunManager] was created and has not started yet. It can now be setup. */ + CREATED, + + /** [RunManager] was started and setup; it is ready to run [Task]s. */ + ACTIVE, + + /** [RunManager] was terminated and cannot run anymore [Task]s. */ + TERMINATED; + + /** + * Converts this [ApiEvaluationStatus] to the corresponding [RunManagerStatus]. + * + * @return [RunManagerStatus] + */ + fun asRunManagerStatus(): RunManagerStatus = when(this) { + CREATED -> RunManagerStatus.CREATED + ACTIVE -> RunManagerStatus.ACTIVE + TERMINATED -> RunManagerStatus.TERMINATED + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunManagerStatus.kt b/backend/src/main/kotlin/dev/dres/run/RunManagerStatus.kt index 97ea75794..9779c65d0 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManagerStatus.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManagerStatus.kt @@ -1,10 +1,12 @@ package dev.dres.run +import dev.dres.api.rest.types.evaluation.ApiEvaluationStatus + /** * The collection auf statuses a [RunManager] can be in. * * @author Ralph Gasser - * @version 1.0 + * @version 1.0.0 */ enum class RunManagerStatus { /** [RunManager] was created and has not started yet. It can now be setup. */ @@ -14,5 +16,16 @@ enum class RunManagerStatus { ACTIVE, /** [RunManager] was terminated and cannot run anymore [Task]s. */ - TERMINATED + TERMINATED; + + /** + * Converts this [RunManagerStatus] to the corresponding [ApiEvaluationStatus]. + * + * @return [ApiEvaluationStatus] + */ + fun toApi(): ApiEvaluationStatus = when (this) { + CREATED -> ApiEvaluationStatus.CREATED + ACTIVE -> ApiEvaluationStatus.ACTIVE + TERMINATED -> ApiEvaluationStatus.TERMINATED + } } \ No newline at end of file From ec348a7930df28298d1c15601a7bd3932db53cf2 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 14:34:40 +0100 Subject: [PATCH 126/498] Adjusted Open API definition. --- .../rest/types/evaluation/ApiSubmission.kt | 6 +- doc/oas-client.json | 9545 ++++++------ doc/oas.json | 12235 ++++++++-------- 3 files changed, 10814 insertions(+), 10972 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index fe6351b28..c6faa64d6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -22,13 +22,13 @@ import java.util.UUID * @version 2.0.0 */ data class ApiSubmission( + override val submissionId: SubmissionId = UUID.randomUUID().toString(), //TODO is there a use case where this needs to be settable via an API request? override val teamId: String, - val teamName: String, override val memberId: String, + val teamName: String, val memberName: String, val answers: List, - override val timestamp: Long = System.currentTimeMillis(), - override val submissionId: SubmissionId = UUID.randomUUID().toString() //TODO is there a use case where this needs to be settable via an API request? + override val timestamp: Long = System.currentTimeMillis() ) : Submission { init { diff --git a/doc/oas-client.json b/doc/oas-client.json index 87b172b1c..4f4cae817 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1,4813 +1,4734 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : null, - "summary" : null, - "description" : null, - "termsOfService" : null, - "contact" : null, - "license" : null, - "version" : null - }, - "paths" : { - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "query", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the submission status for a given submission.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV2Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit/{evaluationId}" : { - "post" : { - "tags" : [ "Batch Submission" ], - "summary" : "Endpoint to accept batch submissions in JSON format", - "operationId" : "postApiV2SubmitByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunResult" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific competition.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiAuditLogEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiAuditLogType" - }, - "source" : { - "$ref" : "#/components/schemas/ApiAuditLogSource" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "competitionId" : { - "type" : "string" - }, - "userId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "id", "type", "source", "timestamp" ] - }, - "ApiAuditLogSource" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "ApiAuditLogType" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] - }, - "AuditLogInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "size" : { - "type" : "integer", - "format" : "int32" - }, - "latest" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - } - }, - "required" : [ "name" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - } - }, - "required" : [ "name", "type", "collectionId", "location" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "type" : "string" - } - }, - "required" : [ "teams" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/AnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "status", "answers" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "runStatus" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "taskRunStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "currentTask" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "runStatus", "taskRunStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - } - }, - "required" : [ "id", "teamId", "teamName", "memberId", "memberName", "timestamp", "answers" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/TaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "mediaType" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "validator" : { - "type" : "string" - }, - "collection" : { - "type" : "string" - }, - "item" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "endTime" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "ResultElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "startTimeCode" : { - "type" : "string" - }, - "endTimeCode" : { - "type" : "string" - }, - "index" : { - "type" : "integer", - "format" : "int32" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - }, - "weight" : { - "type" : "number", - "format" : "float" - } - } - }, - "RunResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskResult" - } - }, - "timeStamp" : { - "type" : "integer", - "format" : "int32" - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] - }, - "TaskResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "task" : { - "type" : "string" - }, - "resultName" : { - "type" : "string" - }, - "resultType" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ResultElement" - } - } - }, - "required" : [ "task", "resultName", "results" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "UserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "AnswerType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "TaskStatus" : { - "type" : "string", - "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] - } - }, - "securitySchemes" : { } - }, - "servers" : [ ], - "security" : null +{ + "openapi" : "3.0.3", + "info" : { + "title" : null, + "summary" : null, + "description" : null, + "termsOfService" : null, + "contact" : null, + "license" : null, + "version" : null + }, + "paths" : { + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "query", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the submission status for a given submission.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV2Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific competition.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiAuditLogEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiAuditLogType" + }, + "source" : { + "$ref" : "#/components/schemas/ApiAuditLogSource" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "competitionId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "submissionId" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "id", "type", "source", "timestamp" ] + }, + "ApiAuditLogSource" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "ApiAuditLogType" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + }, + "AuditLogInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "size" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + }, + "required" : [ "name" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "collection" : { + "$ref" : "#/components/schemas/MediaItemCollection" + } + }, + "required" : [ "name", "type", "collectionId", "location", "collection" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "type" : "string" + } + }, + "required" : [ "teams" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + }, + "task" : { + "$ref" : "#/components/schemas/Task" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + }, + "required" : [ "status", "answers", "task", "submission" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "currentTemplate" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/TaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "mediaType" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "endTime" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "UserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "MediaItemCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + } + }, + "required" : [ "id" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "Task" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + }, + "evaluation" : { + "$ref" : "#/components/schemas/Evaluation" + }, + "template" : { + "$ref" : "#/components/schemas/TaskTemplate" + } + }, + "required" : [ "taskId", "evaluation", "template" ] + }, + "Evaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + } + }, + "required" : [ "evaluationId" ] + }, + "TaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + } + }, + "required" : [ "templateId" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "TaskStatus" : { + "type" : "string", + "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] + } + }, + "securitySchemes" : { } + }, + "servers" : [ ], + "security" : null } \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 954b22585..7f8e07391 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1,6158 +1,6079 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : null, - "summary" : null, - "description" : null, - "termsOfService" : null, - "contact" : null, - "license" : null, - "version" : null - }, - "paths" : { - "/api/v2/audit/info" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", - "operationId" : "getApiV2AuditInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "The audit log info.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/AuditLogInfo" - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user executes the call.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/log/list/limit/{limit}/{page}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query.", - "operationId" : "getApiV2AuditLogListLimitByLimitByPage", - "parameters" : [ { - "name" : "limit", - "in" : "path", - "description" : "The maximum number of results. Default: 500", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "page", - "in" : "path", - "description" : "The page index offset, relative to the limit.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAuditLogEntry" - } - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user starts the call", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/log/list/since/{since}/{upto}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query", - "operationId" : "getApiV2AuditLogListSinceBySinceByUpto", - "parameters" : [ { - "name" : "since", - "in" : "path", - "description" : "Timestamp of the earliest audit log to include", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "upto", - "in" : "path", - "description" : "Timestamp of the latest audit log to include.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAuditLogEntry" - } - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user starts the call", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "query", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "operationId" : "getApiV2CollectionList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/random" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "operationId" : "getApiV2CollectionByCollectionIdRandom", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", - "operationId" : "postApiV2CollectionByCollectionIdResolve", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", - "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", - "in" : "path", - "description" : "Name the item(s) should start with.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given evaluation.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the submission status for a given submission.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a list of available scoreboard names for the given evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", - "operationId" : "getApiV2ExternalByStartsWith", - "parameters" : [ { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a media item to the specified media collection.", - "operationId" : "postApiV2MediaItem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Tries to delete a specific media item.", - "operationId" : "deleteApiV2MediaItemByMediaId", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "getApiV2MediaItemByMediaId", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the score overviews of a specific evaluation run.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/current" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeries" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns team group aggregated values of the current task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroupValue" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV2Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit/{evaluationId}" : { - "post" : { - "tags" : [ "Batch Submission" ], - "summary" : "Endpoint to accept batch submissions in JSON format", - "operationId" : "postApiV2SubmitByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunResult" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific competition.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "operationId" : "getApiV2UserList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "operationId" : "getApiV2UserSessionActiveList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiAuditLogEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiAuditLogType" - }, - "source" : { - "$ref" : "#/components/schemas/ApiAuditLogSource" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "competitionId" : { - "type" : "string" - }, - "userId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "id", "type", "source", "timestamp" ] - }, - "ApiAuditLogSource" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "ApiAuditLogType" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] - }, - "AuditLogInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "size" : { - "type" : "integer", - "format" : "int32" - }, - "latest" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - } - }, - "required" : [ "name" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - } - }, - "required" : [ "name", "type", "collectionId", "location" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "type" : "string" - } - }, - "required" : [ "teams" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/AnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "status", "answers" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "runStatus" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "taskRunStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "currentTask" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "runStatus", "taskRunStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - } - }, - "required" : [ "id", "teamId", "teamName", "memberId", "memberName", "timestamp", "answers" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/TaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "mediaType" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "validator" : { - "type" : "string" - }, - "collection" : { - "type" : "string" - }, - "item" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "endTime" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "ResultElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "startTimeCode" : { - "type" : "string" - }, - "endTimeCode" : { - "type" : "string" - }, - "index" : { - "type" : "integer", - "format" : "int32" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - }, - "weight" : { - "type" : "number", - "format" : "float" - } - } - }, - "RunResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/TaskResult" - } - }, - "timeStamp" : { - "type" : "integer", - "format" : "int32" - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "tasks", "timeStamp", "serverTimeStamp$backend" ] - }, - "TaskResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "task" : { - "type" : "string" - }, - "resultName" : { - "type" : "string" - }, - "resultType" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ResultElement" - } - } - }, - "required" : [ "task", "resultName", "results" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "UserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "AnswerType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "TaskStatus" : { - "type" : "string", - "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] - } - }, - "securitySchemes" : { } - }, - "servers" : [ ], - "security" : null +{ + "openapi" : "3.0.3", + "info" : { + "title" : null, + "summary" : null, + "description" : null, + "termsOfService" : null, + "contact" : null, + "license" : null, + "version" : null + }, + "paths" : { + "/api/v2/audit/info" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", + "operationId" : "getApiV2AuditInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "The audit log info.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AuditLogInfo" + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user executes the call.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/log/list/limit/{limit}/{page}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query.", + "operationId" : "getApiV2AuditLogListLimitByLimitByPage", + "parameters" : [ { + "name" : "limit", + "in" : "path", + "description" : "The maximum number of results. Default: 500", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "page", + "in" : "path", + "description" : "The page index offset, relative to the limit.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/log/list/since/{since}/{upto}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", + "operationId" : "getApiV2AuditLogListSinceBySinceByUpto", + "parameters" : [ { + "name" : "since", + "in" : "path", + "description" : "Timestamp of the earliest audit log to include", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "upto", + "in" : "path", + "description" : "Timestamp of the latest audit log to include.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "query", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiV2CollectionList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiV2CollectionByCollectionIdRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", + "operationId" : "postApiV2CollectionByCollectionIdResolve", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name the item(s) should start with.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the submission status for a given submission.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2ExternalByStartsWith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV2Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific competition.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiAuditLogEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiAuditLogType" + }, + "source" : { + "$ref" : "#/components/schemas/ApiAuditLogSource" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "competitionId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "submissionId" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "id", "type", "source", "timestamp" ] + }, + "ApiAuditLogSource" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "ApiAuditLogType" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + }, + "AuditLogInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "size" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + }, + "required" : [ "name" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "collection" : { + "$ref" : "#/components/schemas/MediaItemCollection" + } + }, + "required" : [ "name", "type", "collectionId", "location", "collection" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "type" : "string" + } + }, + "required" : [ "teams" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + }, + "task" : { + "$ref" : "#/components/schemas/Task" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + }, + "required" : [ "status", "answers", "task", "submission" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "currentTemplate" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/TaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "mediaType" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "endTime" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "UserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "MediaItemCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + } + }, + "required" : [ "id" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "Task" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + }, + "evaluation" : { + "$ref" : "#/components/schemas/Evaluation" + }, + "template" : { + "$ref" : "#/components/schemas/TaskTemplate" + } + }, + "required" : [ "taskId", "evaluation", "template" ] + }, + "Evaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + } + }, + "required" : [ "evaluationId" ] + }, + "TaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + } + }, + "required" : [ "templateId" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "TaskStatus" : { + "type" : "string", + "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] + } + }, + "securitySchemes" : { } + }, + "servers" : [ ], + "security" : null } \ No newline at end of file From b89a5eaa45e38c7be9cfb15cb289d002249ef4d3 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 17:05:09 +0100 Subject: [PATCH 127/498] NextTaskHandler, PreviousTaskHandler and SwitchTaskHandler now use read-only transaction since only transient state is changed. --- .../dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/PreviousTaskHandler.kt | 2 +- .../dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index 0e046c8a3..f7547c397 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -52,7 +52,7 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl synchronousAdminCheck(evaluationManager, ctx) - return this.store.transactional { + return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, evaluationManager) if (evaluationManager is InteractiveAsynchronousRunManager && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index db1b646d5..f70421fdc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -38,7 +38,7 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional { + return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { if (evaluationManager.previous(rac)) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index 4ef120c9b..e32285077 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -41,7 +41,7 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan val evaluationId = ctx.evaluationId() val idx = ctx.pathParamMap()["idx"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'idx' is missing!'", ctx) val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional { + return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.goTo(rac, idx) From 42583ddb0bafe6962febc80bb0007b5cc74d9e64 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 17:08:38 +0100 Subject: [PATCH 128/498] Fixed server-side WebSocket message handling. --- .../evaluation/admin/ListViewersHandler.kt | 13 +++++- .../api/rest/types/WebSocketConnection.kt | 13 ++---- .../evaluation/websocket/ClientMessage.kt | 2 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 40 +++++++------------ 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index 425c35335..fbfa0b29e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -1,10 +1,13 @@ package dev.dres.api.rest.handler.evaluation.admin +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiViewerInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.admin.DbUser +import dev.dres.mgmt.admin.DbUserManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -34,6 +37,14 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa override fun doGet(ctx: Context): List { val evaluationId = ctx.evaluationId() val evaluation = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) - return evaluation.viewers().map { ApiViewerInfo(it.key.sessionId, it.key.userName, it.key.host, it.value) } + return this.store.transactional(true) { + evaluation.viewers().map { + ApiViewerInfo( + it.key.sessionId, + DbUserManager.get(AccessManager.userIdForSession(it.key.httpSessionId))?.username ?: "UNKNOWN", + it.key.host, + it.value) + } + } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt index 763611274..ac57ad769 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt @@ -18,22 +18,16 @@ value class WebSocketConnection(val context: WsContext) { companion object { val jsonMapper = jacksonObjectMapper() - const val UNKNOWN_USER = "UNKNOWN" - } /** ID of the WebSocket session. */ val sessionId - get() = context.sessionId + get() = this.context.sessionId /** ID of the HTTP session that generated this [WebSocketConnection]. */ - val httpSessionId - get() = (this.context.session as Session).id - - /** Name of the user that generated this [WebSocketConnection]. */ - val userName - get() = DbUserManager.get(AccessManager.userIdForSession(this.httpSessionId))?.username ?: UNKNOWN_USER + val httpSessionId: String + get() = this.context.cookie("SESSIONID") ?: throw IllegalStateException("Unable to obtain HTTP Session ID associated with WebSocket connection request.") /** IP address of the client. */ val host: String @@ -44,7 +38,6 @@ value class WebSocketConnection(val context: WsContext) { } else { this.context.host() } - } /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt index 3f11e44c1..6f378717f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ClientMessage.kt @@ -6,5 +6,5 @@ package dev.dres.api.rest.types.evaluation.websocket * @author Ralph Gasser * @version 1.0 */ -data class ClientMessage(val runId: String, val type: ClientMessageType) +data class ClientMessage(val evaluationId: String, val type: ClientMessageType) diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index dd7b50a34..96fe66de4 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -13,7 +13,6 @@ import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.NonInteractiveEvaluation import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.run.audit.DbAuditLogger import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write @@ -49,7 +48,7 @@ object RunExecutor : Consumer { private val judgementValidators = LinkedList() /** List of [WsContext] that are currently connected. */ - private val connectedClients = HashSet() + private val connectedClients = HashMap() /** List of session IDs that are currently observing a competition. */ private val observingClients = HashMap>() @@ -63,16 +62,12 @@ object RunExecutor : Consumer { /** Internal array of [Future]s for cleaning after [RunManager]s. See [RunExecutor.cleanerThread]*/ private val results = HashMap, EvaluationId>() - /** The [TransientEntityStore] instance used by this [DbAuditLogger]. */ - private lateinit var store: TransientEntityStore - /** * Initializes this [RunExecutor]. * * @param store The shared [TransientEntityStore]. */ fun init(store: TransientEntityStore) { - this.store = store /* TODO: Schedule runs that have not ended * this.runs.filter { !it.hasEnded }.forEach { //TODO needs more distinction schedule(it) @@ -101,7 +96,7 @@ object RunExecutor : Consumer { * * @param manager [RunManager] to execute. */ - fun schedule(manager: RunManager) = this.runManagerLock.write { + private fun schedule(manager: RunManager) = this.runManagerLock.write { if (this.runManagers.containsKey(manager.id)) { throw IllegalArgumentException("This RunExecutor already runs a RunManager with the given ID ${manager.id}. The same RunManager cannot be executed twice!") } @@ -163,17 +158,14 @@ object RunExecutor : Consumer { /* Add WSContext to set of connected clients. */ this@RunExecutor.clientLock.write { val connection = WebSocketConnection(it) - //only keep connections from known users - if (connection.userName != WebSocketConnection.UNKNOWN_USER) { - this.connectedClients.add(connection) - } + this.connectedClients[connection.httpSessionId] = connection } } t.onClose { val session = WebSocketConnection(it) this@RunExecutor.clientLock.write { val connection = WebSocketConnection(it) - this.connectedClients.remove(connection) + this.connectedClients.remove(connection.httpSessionId) this.runManagerLock.read { for (m in this.runManagers) { if (this.observingClients[m.key]?.contains(connection) == true) { @@ -194,14 +186,14 @@ object RunExecutor : Consumer { val session = WebSocketConnection(it) logger.debug("Received WebSocket message: $message from ${it.session.policy}") this.runManagerLock.read { - if (this.runManagers.containsKey(message.runId)) { + if (this.runManagers.containsKey(message.evaluationId)) { when (message.type) { ClientMessageType.ACK -> {} - ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.runId]?.add(WebSocketConnection(it)) } - ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.runId]?.remove(WebSocketConnection(it)) } - ClientMessageType.PING -> it.send(ServerMessage(message.runId, ServerMessageType.PING)) + ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.add(WebSocketConnection(it)) } + ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.remove(WebSocketConnection(it)) } + ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, ServerMessageType.PING)) } - this.runManagers[message.runId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ + this.runManagers[message.evaluationId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ } } } @@ -232,7 +224,7 @@ object RunExecutor : Consumer { * @param message The [ServerMessage] that should be broadcast. */ fun broadcastWsMessage(message: ServerMessage) = this.clientLock.read { - this.connectedClients.forEach { + this.connectedClients.values.forEach { it.send(message) } } @@ -240,12 +232,12 @@ object RunExecutor : Consumer { /** * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager]. * - * @param evaluationId The run ID identifying the [RunManager] for which clients should received the message. + * @param evaluationId The [EvaluationId] identifying the [RunManager] for which clients should receive the message. * @param message The [ServerMessage] that should be broadcast. */ fun broadcastWsMessage(evaluationId: EvaluationId, message: ServerMessage) = this.clientLock.read { this.runManagerLock.read { - this.connectedClients.filter { + this.connectedClients.values.filter { this.observingClients[evaluationId]?.contains(it) ?: false }.forEach { it.send(message) @@ -263,12 +255,10 @@ object RunExecutor : Consumer { fun broadcastWsMessage(evaluationId: EvaluationId, teamId: TeamId, message: ServerMessage) = this.clientLock.read { val manager = managerForId(evaluationId) if (manager != null) { - val teamMembers = this.store.transactional(true) { - manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() - } + val teamMembers = manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() this.runManagerLock.read { - this.connectedClients.filter { - this.observingClients[evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.httpSessionId) in teamMembers + this.connectedClients.values.filter { + this.observingClients[evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.sessionId) in teamMembers }.forEach { it.send(message) } From 7c0b50a8d3336ac2bf80858c9c6f609d8598a636 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 17:09:34 +0100 Subject: [PATCH 129/498] Fixed a potential exception when converting DbHint to a content element in case an asset file is missing. --- .../kotlin/dev/dres/data/model/template/task/DbHint.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index 6552b2ae8..b70f590b0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -125,10 +125,11 @@ class DbHint(entity: Entity) : XdEntity(entity) { } else { throw IllegalArgumentException("A hint of type ${this.type.description} must have a valid media item or external path.") } - val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> - stream.readAllBytes() + if (Files.exists(path)) { + Base64.getEncoder().encodeToString(Files.readAllBytes(path)) + } else { + "" } - Base64.getEncoder().encodeToString(data) } DbHintType.EMPTY -> "" DbHintType.TEXT -> this.text ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") From a96e4673f30cf169afbd4339c70fc17c0e0f9361 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Feb 2023 17:10:38 +0100 Subject: [PATCH 130/498] Various fixes to the view components. --- .../run/InteractiveSynchronousRunManager.kt | 7 - .../model/ws/ws-client-message.interface.ts | 2 +- .../src/app/model/ws/ws-message.interface.ts | 2 +- .../model/ws/ws-server-message.interface.ts | 2 +- .../app/run/abstract-run-list.component.ts | 8 +- .../run-admin-submissions-list.component.ts | 6 +- .../src/app/run/run-admin-view.component.html | 24 +-- .../src/app/run/run-admin-view.component.ts | 6 +- ...competition-scoreboard-viewer.component.ts | 6 +- .../src/app/viewer/run-viewer.component.html | 14 +- .../src/app/viewer/run-viewer.component.ts | 44 ++--- .../src/app/viewer/task-viewer.component.ts | 20 +-- .../app/viewer/teams-viewer.component.html | 28 ++-- .../src/app/viewer/teams-viewer.component.ts | 154 +++++++++--------- 14 files changed, 153 insertions(+), 170 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 25c05fc6e..ecdee4572 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -207,20 +207,14 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() if (index >= 0 && index < this.template.tasks.size()) { - /* Update active task. */ this.evaluation.goTo(index) - //FIXME since task run and competition run states are separated, this is not actually a state change -// /* Update RunManager status. */ -// this.status = RunManagerStatus.ACTIVE - /* Mark scoreboards for update. */ this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE)) - LOGGER.info("SynchronousRunManager ${this.id} set to task $index") } else { throw IndexOutOfBoundsException("Index $index is out of bounds for the number of available tasks.") @@ -578,7 +572,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (this.evaluation.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { this.evaluation.currentTask!!.start() - //this.status = RunManagerStatus.RUNNING_TASK DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, DbAuditLogSource.INTERNAL, null) } diff --git a/frontend/src/app/model/ws/ws-client-message.interface.ts b/frontend/src/app/model/ws/ws-client-message.interface.ts index fa11ec6bc..a09c5c2f5 100644 --- a/frontend/src/app/model/ws/ws-client-message.interface.ts +++ b/frontend/src/app/model/ws/ws-client-message.interface.ts @@ -6,6 +6,6 @@ import { IWsMessage } from './ws-message.interface'; * Structure of a message as generated by the DRES client. */ export interface IWsClientMessage extends IWsMessage { - runId: string; + evaluationId: string; type: ClientMessageTypeEnum; } diff --git a/frontend/src/app/model/ws/ws-message.interface.ts b/frontend/src/app/model/ws/ws-message.interface.ts index 24c63558d..f69997d2e 100644 --- a/frontend/src/app/model/ws/ws-message.interface.ts +++ b/frontend/src/app/model/ws/ws-message.interface.ts @@ -7,6 +7,6 @@ import { ClientMessageType } from './client-message-type.enum'; import ClientMessageTypeEnum = ClientMessageType.ClientMessageTypeEnum; export interface IWsMessage { - runId: string; + evaluationId: string; type: ClientMessageTypeEnum | ServerMessageTypeEnum; } diff --git a/frontend/src/app/model/ws/ws-server-message.interface.ts b/frontend/src/app/model/ws/ws-server-message.interface.ts index 87fd8a811..4a1795b99 100644 --- a/frontend/src/app/model/ws/ws-server-message.interface.ts +++ b/frontend/src/app/model/ws/ws-server-message.interface.ts @@ -6,7 +6,7 @@ import { IWsMessage } from './ws-message.interface'; * Structure of a ServerMessage as generated by the DRES server. */ export interface IWsServerMessage extends IWsMessage { - runId: string; + evaluationId: string; type: ServerMessageTypeEnum; timestamp: number; } diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index c0da5da01..fb62b6ca5 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -162,15 +162,15 @@ export class AbstractRunListComponent { flatMap((t) => query), map(([info, state]) => { return info.map((v, i) => { - const s = state.find((_) => _.id === v.id); + const s = state.find((_) => _.evaluationId === v.id); return { id: v.id, name: v.name, description: v.templateDescription, teams: v.teams.length, - runStatus: s.runStatus, - taskRunStatus: s.taskRunStatus, - currentTask: s.currentTask?.name, + runStatus: s.evaluationStatus, + taskRunStatus: s.taskStatus, + currentTask: s.currentTemplate?.name, timeLeft: s.timeLeft > -1 ? `${Math.round(s.timeLeft)}s` : 'n/a', asynchronous: v.type === 'ASYNCHRONOUS', runProperties: v.properties, diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts index baca4b457..6cfc016a1 100644 --- a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts +++ b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts @@ -8,7 +8,7 @@ import { MatButtonToggleGroup } from '@angular/material/button-toggle'; import { catchError, filter, map, switchMap, withLatestFrom } from 'rxjs/operators'; import { MatPaginator } from '@angular/material/paginator'; import { AppConfig } from '../../app.config'; -import {ApiAnswerSet, ApiSubmission, ApiSubmissionInfo, ApiVerdictStatus, EvaluationAdministratorService} from 'openapi'; +import { ApiSubmission, ApiSubmissionInfo, ApiVerdictStatus, EvaluationAdministratorService} from 'openapi'; @Component({ selector: 'app-run-admin-submissions-list', @@ -140,7 +140,7 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro * Generates a URL for the preview image of a submission. */ public previewForSubmission(submission: ApiSubmission): Observable { - return this.runId.pipe(map((runId) => this.config.resolveApiUrl(`/preview/submission/${runId}/${submission.id}`))); + return this.runId.pipe(map((runId) => this.config.resolveApiUrl(`/preview/submission/${runId}/${submission.submissionId}`))); } resolveIdBySelf(_: number, item: string) { @@ -148,6 +148,6 @@ export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestro } resolveSubmissionById(_: number, item: ApiSubmission) { - return item.id; + return item.submissionId; } } diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 1dae79260..fb22cd2a6 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -1,25 +1,25 @@ + + +
+ + Judge Selection + + + + {{ judge.username }} + + + +
+
+ + + + + + + + + + + + + + + +
Name{{ (judgeForId(judge) | async)?.username }}Action + +
diff --git a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.scss b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts new file mode 100644 index 000000000..d2dc1ee34 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts @@ -0,0 +1,85 @@ +import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {MatTable} from '@angular/material/table'; +import {ApiRole, ApiUser, UserService} from '../../../../../../openapi'; +import {combineLatest, Observable} from 'rxjs'; +import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; +import {TemplateBuilderService} from '../../template-builder.service'; +import {filter, map, shareReplay, withLatestFrom} from 'rxjs/operators'; +import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; + +@Component({ + selector: 'app-judges-list', + templateUrl: './judges-list.component.html', + styleUrls: ['./judges-list.component.scss'] +}) +export class JudgesListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + + @ViewChild('judgesTable') + judgesTable: MatTable; + + availableJudges: Observable; + + displayedColumns: string[] = ['name', 'action']; + + judges: Observable> = new Observable>((x) => x.next([])); + + constructor(private userService: UserService, builderService: TemplateBuilderService) { + super(builderService); + this.refreshAvailableJudges(); + } + + addJudge(event: MatAutocompleteSelectedEvent) { + if (this.builderService.getTemplate().judges.includes(event.option.value.id)) { + return; + } + this.builderService.getTemplate().judges.push(event.option.value.id); + this.builderService.update(); + this.judgesTable.renderRows(); + } + + removeJudge(judgeId: string) { + this.builderService.getTemplate().judges.splice(this.builderService.getTemplate().judges.indexOf(judgeId), 1); + this.builderService.update(); + this.judgesTable.renderRows(); + } + + judgeForId(id: string) { + return this.availableJudges.pipe(map((users) => users.find((u) => u.id === id))); + } + + displayJudge(judge: ApiUser) { + return judge.username; + } + + ngOnInit(): void { + this.onInit(); + } + + ngOnDestroy(): void { + this.onDestroy(); + } + + onChange() { + this.judges = this.builderService.templateAsObservable().pipe( + map((t) => { + if (t) { + return t.judges; + } else { + return []; + } + }) + ); + } + + refreshAvailableJudges() { + this.availableJudges = this.userService.getApiV2UserList().pipe( + map((users) => users.filter((user) => user.role === ApiRole.JUDGE)), + /*withLatestFrom(this.judges), + map(([users, judges]) => { + return users.filter((u) => !judges.includes(u.id)) + }),*/ + shareReplay(1) + ); + } + +} diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index 3d3e57100..72b024d7c 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -4,19 +4,35 @@ import {TemplateInformationComponent} from './template-information/template-info import {ReactiveFormsModule} from '@angular/forms'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; +import { JudgesListComponent } from './judges-list/judges-list.component'; +import {FlexModule} from '@angular/flex-layout'; +import {MatButtonModule} from '@angular/material/button'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {MatIconModule} from '@angular/material/icon'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatAutocompleteModule} from '@angular/material/autocomplete'; +import {MatTableModule} from '@angular/material/table'; @NgModule({ declarations: [ - TemplateInformationComponent + TemplateInformationComponent, + JudgesListComponent ], imports: [ CommonModule, ReactiveFormsModule, MatFormFieldModule, - MatInputModule + MatInputModule, + FlexModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatMenuModule, + MatAutocompleteModule, + MatTableModule ], - exports: [TemplateInformationComponent] + exports: [TemplateInformationComponent, JudgesListComponent] }) export class TemplateBuilderComponentsModule { } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 3b71da67c..1f09f759d 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -24,7 +24,7 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} @@ -34,7 +34,10 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} - teams judges +
+

TEAMS

+ +
types and groups diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index ee22bbcef..b4b0498bd 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -61,7 +61,9 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i } downloadProvider = () => { - return this.downloadService.getApiV2DownloadTemplateByTemplateId(this.builderService.getTemplate()?.id).pipe(take(1)); + if(this.builderService.hasTemplate()){ + return this.downloadService.getApiV2DownloadTemplateByTemplateId(this.builderService.getTemplate()?.id).pipe(take(1)); + } } public save(){ diff --git a/frontend/src/app/template/template-builder/template-builder.module.ts b/frontend/src/app/template/template-builder/template-builder.module.ts index 9205ca81e..d76f06b3b 100644 --- a/frontend/src/app/template/template-builder/template-builder.module.ts +++ b/frontend/src/app/template/template-builder/template-builder.module.ts @@ -5,6 +5,7 @@ import {TemplateBuilderComponentsModule} from './components/template-builder-com import {MatIconModule} from '@angular/material/icon'; import {MatTabsModule} from '@angular/material/tabs'; import {SharedModule} from '../../shared/shared.module'; +import {FlexModule} from '@angular/flex-layout'; @NgModule({ @@ -16,7 +17,8 @@ import {SharedModule} from '../../shared/shared.module'; TemplateBuilderComponentsModule, MatIconModule, MatTabsModule, - SharedModule + SharedModule, + FlexModule ], exports: [TemplateBuilderComponent] }) diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index b25a6e89c..cec2d2f40 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -34,7 +34,8 @@ export class TemplateBuilderService { return this.templateSubject.asObservable(); } - public update(template: ApiEvaluationTemplate){ + public update(template: ApiEvaluationTemplate = null){ + template = template ? template : this.templateSubject.getValue(); this.templateSubject.next(template); this.markDirty(); } @@ -46,7 +47,7 @@ export class TemplateBuilderService { } public hasTemplate(){ - return this.templateSubject != undefined; + return this.templateSubject != undefined && this.templateSubject.getValue(); } public clear(){ diff --git a/frontend/src/app/user/login/login.component.html b/frontend/src/app/user/login/login.component.html index eeed0e491..ca50561c5 100644 --- a/frontend/src/app/user/login/login.component.html +++ b/frontend/src/app/user/login/login.component.html @@ -12,7 +12,7 @@

Password: - +

From 51a1e18e668d4f5e3df204a7b9efd55c16d59c08 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 12:11:12 +0100 Subject: [PATCH 136/498] Fixed predicate. --- .../kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index bb4590110..ed537b670 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -38,7 +38,7 @@ abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { init { this.validator = when (val targetOption = this.template.taskGroup.type.target) { - DbTargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + DbTargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.filter { it.item ne null }.mapDistinct { it.item }.toSet()) DbTargetOption.MEDIA_SEGMENT -> { val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) From b9866a7cbb5eab3749daa16e02aa0e862f4c1491 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 12:13:23 +0100 Subject: [PATCH 137/498] DbTask.started is now nullable, as it should be. --- .../kotlin/dev/dres/data/model/run/AbstractTask.kt | 2 +- .../main/kotlin/dev/dres/data/model/run/DbTask.kt | 2 +- .../dres/run/InteractiveAsynchronousRunManager.kt | 11 ++++++----- .../dres/run/InteractiveSynchronousRunManager.kt | 13 ++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index a85807f43..ba6139586 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -43,7 +43,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { protected val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() /** Timestamp of when this [AbstractTask] was started. */ - final override var started: Long + final override var started: Long? get() = this.task.started protected set(value) { this.task.started = value diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index 47aa100e3..38fda8efd 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -28,7 +28,7 @@ class DbTask(entity: Entity) : PersistentEntity(entity), Task { set(value) { this.id = value } /** Timestamp of when this [DbEvaluation] started. */ - override var started by xdRequiredLongProp() + override var started by xdNullableLongProp() /** Timestamp of when this [DbEvaluation] ended. */ override var ended by xdNullableLongProp() diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index b479d3b7e..977c869cf 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -372,10 +372,11 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } val currentTaskRun = this.currentTask(context) + return if (currentTaskRun?.isRunning == true) { max( 0L, - currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started) + InteractiveRunManager.COUNTDOWN_DURATION + currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!) + InteractiveRunManager.COUNTDOWN_DURATION ) } else { -1L @@ -391,7 +392,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { val currentTaskRun = this.currentTask(context) return if (currentTaskRun?.isRunning == true) { - System.currentTimeMillis() - (currentTaskRun.started + InteractiveRunManager.COUNTDOWN_DURATION) + System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) } else { -1L } @@ -476,9 +477,9 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("No active TaskRun found. This is a serious error!") val newDuration = currentTaskRun.duration + s - check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } + check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration - return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) + return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) } @@ -657,7 +658,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") val timeLeft = max( 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started) + InteractiveRunManager.COUNTDOWN_DURATION + task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION ) if (timeLeft <= 0) { this.stateLock.write { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index ecdee4572..4ce84ac1d 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -331,12 +331,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch assureTaskRunning() checkContext(context) - val currentTaskRun = this.currentTask(context) - ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") + val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") val newDuration = currentTaskRun.duration + s - check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } + check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration - return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started)) + return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) } /** @@ -352,7 +351,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") max( 0L, - currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started) + InteractiveRunManager.COUNTDOWN_DURATION + currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!) + InteractiveRunManager.COUNTDOWN_DURATION ) } else { -1L @@ -369,7 +368,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch return if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") - System.currentTimeMillis() - (currentTaskRun.started + InteractiveRunManager.COUNTDOWN_DURATION) + System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) } else { -1L } @@ -584,7 +583,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch val task = this.evaluation.currentTask!! val timeLeft = max( 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started) + InteractiveRunManager.COUNTDOWN_DURATION + task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION ) if (timeLeft <= 0) { this.stateLock.write { From 2aaef4993de5dba42247f635e3af6a8aff53af28 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 13:13:55 +0100 Subject: [PATCH 138/498] Fixed team logo display. --- frontend/src/app/run/run-admin-view.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index c7a0196f2..860d66270 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -279,7 +279,7 @@ export class RunAdminViewComponent { * Generates a URL for the logo of the team. */ public teamLogo(team: ApiTeam): string { - return this.config.resolveApiUrl(`/competition/logo/${team.logoData}`); + return this.config.resolveApiUrl(`/template/logo/${team.id}`); } resolveTeamByName(idx: number, item: ApiTeam) { From d8945b378397933a5482a59c9db1a361b13243cb Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 13:14:27 +0100 Subject: [PATCH 139/498] InteractiveSynchronousEvaluations can now be started and end automatically. --- .../dev/dres/data/model/run/AbstractTask.kt | 46 ++++++++++---- .../run/InteractiveSynchronousEvaluation.kt | 34 ++++++++-- .../dres/data/model/run/interfaces/TaskRun.kt | 8 ++- .../run/InteractiveSynchronousRunManager.kt | 63 ++++++++++--------- 4 files changed, 103 insertions(+), 48 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index ba6139586..5326fd283 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -1,11 +1,11 @@ package dev.dres.data.model.run -import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.TemplateId import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.interfaces.SubmissionValidator @@ -24,7 +24,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - protected val xdId = task.xdId + private val xdId = task.xdId /** * Accessor for the [DbTask] underpinning this [AbstractTask] @@ -33,33 +33,53 @@ abstract class AbstractTask(task: DbTask): TaskRun { get() = DbTask.findById(this.xdId) /** - * The [EvaluationId] of this [AbstractTask]. + * The [TaskId] of this [AbstractTask]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - final override val id: EvaluationId = this.task.id + final override val id: TaskId = this.task.id + + /** + * The [TemplateId] of this [AbstractTask]. + * + * Since this cannot change during the lifetime of an evaluation, it is kept in memory. + */ + final override val templateId: TemplateId = this.task.template.templateId /** List of [DbSubmission]s* registered for this [AbstractTask]. */ protected val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() - /** Timestamp of when this [AbstractTask] was started. */ - final override var started: Long? - get() = this.task.started + /** + * Timestamp of when this [AbstractTask] was started. + * + * Setter requires active database transaction! + */ + final override var started: Long? = null protected set(value) { - this.task.started = value + field = value + this.task.started = value /* Update backing database field. */ } - /** Timestamp of when this [AbstractTask] was ended. */ - final override var ended: Long? - get() = this.task.ended + /** + * Timestamp of when this [AbstractTask] was ended. + * + * Setter requires active database transaction! + */ + final override var ended: Long? = null protected set(value) { - this.task.ended = value + field = value + this.task.ended = value /* Update backing database field. */ } - /** Reference to the [DbTaskTemplate] describing this [AbstractTask]. */ + /** + * Reference to the [DbTaskTemplate] describing this [AbstractTask]. + * + * Requires active database transaction! + */ final override val template: DbTaskTemplate get() = this.task.template + /** The current status of this [AbstractTask]. This is a transient property. */ @Volatile final override var status: TaskStatus = TaskStatus.CREATED protected set diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 32fd345f6..65fa2f1c5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -6,9 +6,11 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.TemplateId import dev.dres.run.audit.DbAuditLogger import dev.dres.run.filter.SubmissionFilter import kotlinx.dnq.query.* +import java.lang.IndexOutOfBoundsException /** * Represents a concrete, interactive and synchronous [Run] of a [DbEvaluationTemplate]. @@ -46,14 +48,34 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu }.toMutableList() /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ - var currentTaskTemplate = this.description.tasks.first() + private val templates = this.description.tasks.asSequence().map { it.templateId }.toList() + + /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ + var templateIndex: Int = 0 private set /** Returns the last [TaskRun]. */ val currentTask: AbstractInteractiveTask? - get() = this.tasks.firstOrNull { it.template.id == this.currentTaskTemplate.id } + get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex] } - override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" + + /** + * Returns the [TemplateId] this [InteractiveSynchronousEvaluation] is currently pointing to. + * + * @return [TemplateId] + */ + fun getCurrentTemplateId(): TemplateId = this.templates[this.templateIndex] + + /** + * Returns the [DbTaskTemplate] this [InteractiveSynchronousEvaluation] is currently pointing to. + * + * Requires an active database transaction. + * + * @return [DbTaskTemplate] + */ + fun getCurrentTemplate(): DbTaskTemplate = this.evaluation.template.tasks.filter { + it.id eq this@InteractiveSynchronousEvaluation.getCurrentTemplateId() + }.first() /** * Moves this [InteractiveSynchronousEvaluation] to the given task index. @@ -61,9 +83,13 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu * @param index The new task index to move to. */ fun goTo(index: Int) { - this.currentTaskTemplate = this.description.tasks.drop(index).first() + if (index < 0) throw IndexOutOfBoundsException("The template index must be greater or equal to zero.") + if (index >= this.templates.size) throw IndexOutOfBoundsException("The template index cannot exceed the number of templates.") + this.templateIndex = index } + override fun toString(): String = "InteractiveSynchronousCompetition(id=$id, name=${name})" + /** * Represents a concrete [Run] of a [DbTaskTemplate]. [DbTask]s always exist within a [InteractiveSynchronousEvaluation]. * As a [InteractiveSynchronousEvaluation], [DbTask]s can be started and ended and they can be used to register [DbSubmission]s. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 965ba39a9..792bd92fc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.TemplateId import dev.dres.run.TaskStatus import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.TaskScorer @@ -16,9 +17,12 @@ typealias TaskId = String * @version 1.0.0 */ interface TaskRun: Run { - /** The unique [TaskId] that identifies this [IATaskRun]. Used by the persistence layer. */ + /** The unique [TaskId] that identifies this [TaskRun]. */ val id: TaskId + /** The unique [TemplateId] that identifies the task template underpinning [TaskRun]. */ + val templateId: TemplateId + /** Reference to the [EvaluationRun] this [IATaskRun] belongs to. */ val competition: EvaluationRun @@ -31,7 +35,7 @@ interface TaskRun: Run { /** The [TaskScorer] used to update score for this [IATaskRun]. */ val scorer: CachingTaskScorer - /** The current status of this [IATaskRun]. */ + /** The current status of this [TaskRun]. This is typically a transient property. */ val status: TaskStatus /** diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 4ce84ac1d..2c79abab6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -24,6 +24,7 @@ import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.ReadyLatch import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* +import kotlinx.dnq.transactional import org.slf4j.LoggerFactory import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -169,21 +170,28 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch LOGGER.info("SynchronousRunManager ${this.id} terminated") } + /** + * + */ override fun updateProperties(properties: RunProperties) { TODO("Not yet implemented") } + /** + * Returns the [DbTaskTemplate] this [InteractiveAsynchronousRunManager] is currently pointing to. + * + * Requires an active database transaction. + * + * @return [DbTaskTemplate] + */ override fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate = this.stateLock.write { - checkStatus( - RunManagerStatus.CREATED, - RunManagerStatus.ACTIVE/*, RunManagerStatus.PREPARING_TASK, RunManagerStatus.RUNNING_TASK, RunManagerStatus.TASK_ENDED*/ - ) - this.evaluation.currentTaskTemplate + checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) + this.evaluation.getCurrentTemplate() } override fun previous(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.template.tasks.indexOf(this.evaluation.currentTaskTemplate) - 1 + val newIndex = this.evaluation.templateIndex - 1 return try { this.goTo(context, newIndex) true @@ -194,7 +202,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override fun next(context: RunActionContext): Boolean = this.stateLock.write { checkContext(context) - val newIndex = this.template.tasks.indexOf(this.evaluation.currentTaskTemplate) + 1 + val newIndex = this.evaluation.templateIndex + 1 return try { this.goTo(context, newIndex) true @@ -246,7 +254,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_PREPARE)) - LOGGER.info("SynchronousRunManager ${this.id} started task task ${this.evaluation.currentTaskTemplate}") + LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") } override fun abortTask(context: RunActionContext) = this.stateLock.write { @@ -262,7 +270,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END)) - LOGGER.info("SynchronousRunManager ${this.id} aborted task task ${this.evaluation.currentTaskTemplate}") + LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") } /** List of [DbTask] for this [InteractiveSynchronousRunManager]. */ @@ -505,15 +513,15 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch try { /* Obtain lock on current state. */ this.stateLock.read { - /* 2) Invoke all relevant [Updatable]s. */ + /* 1) Invoke all relevant [Updatable]s. */ this.invokeUpdatables() - /* 3) Process internal state updates (if necessary). */ + /* 2) Process internal state updates (if necessary). */ this.internalStateUpdate() } /* 3) Yield to other threads. */ - Thread.sleep(10) + Thread.sleep(50) /* Reset error counter. */ errorCounter = 0 @@ -521,10 +529,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch LOGGER.info("Interrupted SynchronousRunManager, exiting") return } catch (e: Throwable) { - LOGGER.error( - "Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", - e - ) + LOGGER.error("Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) LOGGER.error("This is the ${++errorCounter}. in a row, will terminate loop after $maxErrorCount errors") // oh shit, something went horribly, horribly wrong @@ -570,8 +575,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ if (this.evaluation.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { - this.evaluation.currentTask!!.start() - DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.currentTaskTemplate, DbAuditLogSource.INTERNAL, null) + this.store.transactional { + this.evaluation.currentTask!!.start() + DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.getCurrentTemplate(), DbAuditLogSource.INTERNAL, null) + } } /* Enqueue WS message for sending */ @@ -580,17 +587,15 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { - val task = this.evaluation.currentTask!! - val timeLeft = max( - 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION - ) - if (timeLeft <= 0) { - this.stateLock.write { - task.end() - //this.status = RunManagerStatus.TASK_ENDED - DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, DbAuditLogSource.INTERNAL, null) - EventStreamProcessor.event(TaskEndEvent(this.id, task.id)) + this.stateLock.write { + val task = this.evaluation.currentTask!! + val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) + if (timeLeft <= 0) { + this.store.transactional { + task.end() + DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, DbAuditLogSource.INTERNAL, null) + EventStreamProcessor.event(TaskEndEvent(this.id, task.id)) + } } /* Enqueue WS message for sending */ From 25e4a7db3c666f2d1029a99faa290aa20144bb1d Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 13:40:34 +0100 Subject: [PATCH 140/498] Fixed erroneous suffix. --- .../src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt index a36090768..35ddd11e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaType.kt @@ -14,8 +14,8 @@ import kotlinx.dnq.xdRequiredStringProp */ class DbMediaType(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { - val IMAGE by enumField { description = "IMAGE"; suffix = "mp4"; } - val VIDEO by enumField { description = "VIDEO"; suffix = "jpg"; } + val IMAGE by enumField { description = "IMAGE"; suffix = "jpg"; } + val VIDEO by enumField { description = "VIDEO"; suffix = "mp4"; } val TEXT by enumField { description = "TEXT"; suffix = "txt"; } } From f51a2f2e8e25343f5d77a1235e9f758ecc41df01 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 14:01:35 +0100 Subject: [PATCH 141/498] Fixed clients-side handling of task hints and task target display. --- .../image-object-preview.component.ts | 17 +++++++---------- .../text-object-preview.component.ts | 8 ++++---- .../video-object-preview.component.ts | 11 +++++------ .../src/app/viewer/task-viewer.component.html | 5 +---- .../src/app/viewer/task-viewer.component.ts | 4 ++-- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts b/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts index d04237e76..7d28d6abe 100644 --- a/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts +++ b/frontend/src/app/viewer/query-object-preview/image-object-preview.component.ts @@ -1,9 +1,9 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { DataUtilities } from '../../utilities/data.utilities'; -import {ApiHint} from '../../../../openapi'; +import { ApiContentElement } from '../../../../openapi'; @Component({ selector: 'app-image-object-preview', @@ -13,9 +13,9 @@ import {ApiHint} from '../../../../openapi'; `, }) -export class ImageObjectPreviewComponent implements OnInit, OnDestroy { +export class ImageObjectPreviewComponent implements OnInit { /** Observable of current {@link QueryContentElement} that should be displayed. */ - @Input() queryContent: Observable; + @Input() queryContent: Observable; /** Current image to display (as data URL). */ imageUrl: Observable; @@ -24,17 +24,14 @@ export class ImageObjectPreviewComponent implements OnInit, OnDestroy { ngOnInit(): void { this.imageUrl = this.queryContent.pipe( - filter((q) => q.type === 'IMAGE'), + filter((q) => q.contentType === 'IMAGE'), map((q) => { - if (q.mediaItem) { - // FIXME differ between external image and internal (i.e. media item). the following code is very likely broken - return this.sanitizer.bypassSecurityTrustUrl(DataUtilities.base64ToUrl(q.mediaItem, 'image/jpg')); // FIXME should the content type be used from api q.dataType ? + if (q.content) { + return this.sanitizer.bypassSecurityTrustUrl(DataUtilities.base64ToUrl(q.content, 'image/jpg')); } else { return null; } }) ); } - - ngOnDestroy(): void {} } diff --git a/frontend/src/app/viewer/query-object-preview/text-object-preview.component.ts b/frontend/src/app/viewer/query-object-preview/text-object-preview.component.ts index f195a5bf1..e2341192b 100644 --- a/frontend/src/app/viewer/query-object-preview/text-object-preview.component.ts +++ b/frontend/src/app/viewer/query-object-preview/text-object-preview.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { AppConfig } from '../../app.config'; -import {ApiHint} from '../../../../openapi'; +import { ApiContentElement } from '../../../../openapi'; @Component({ selector: 'app-text-object-preview', @@ -16,7 +16,7 @@ import {ApiHint} from '../../../../openapi'; }) export class TextObjectPreviewComponent implements OnInit { /** Observable of current {@link QueryContentElement} that should be displayed. */ - @Input() queryContent: Observable; + @Input() queryContent: Observable; /** Current text to display. */ currentText: Observable; @@ -28,8 +28,8 @@ export class TextObjectPreviewComponent implements OnInit { ngOnInit(): void { this.currentText = this.queryContent.pipe( - filter((q) => q.type === 'TEXT'), - map((q) => q.description) + filter((q) => q.contentType === 'TEXT'), + map((q) => q.content) ); } } diff --git a/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts b/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts index c5e81a0dd..780aee421 100644 --- a/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts +++ b/frontend/src/app/viewer/query-object-preview/video-object-preview.component.ts @@ -3,7 +3,7 @@ import { Observable } from 'rxjs'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { filter, map } from 'rxjs/operators'; import { DataUtilities } from '../../utilities/data.utilities'; -import {ApiHint} from '../../../../openapi'; +import {ApiContentElement, ApiHint} from '../../../../openapi'; @Component({ selector: 'app-video-object-preview', @@ -25,7 +25,7 @@ import {ApiHint} from '../../../../openapi'; }) export class VideoObjectPreviewComponent implements OnInit { /** Observable of current {@link ContentElement} that should be displayed. Provided by user of this component. */ - @Input() queryObject: Observable; + @Input() queryObject: Observable; /** Flag indicating whether video player should be muted or not. Can be provided by a user of this component. */ @Input() muted = true; @@ -44,11 +44,10 @@ export class VideoObjectPreviewComponent implements OnInit { ngOnInit(): void { this.videoUrl = this.queryObject.pipe( - filter((q) => q.type === 'VIDEO'), + filter((q) => q.contentType === 'VIDEO'), map((q) => { - if (q.mediaItem) { - // FIXME differ between external video and internal (i.e. media item). the following code is very likely broken - return this.sanitizer.bypassSecurityTrustUrl(DataUtilities.base64ToUrl(q.mediaItem, 'video/mp4')); // FIXME should q.dataType be respected? + if (q.content) { + return this.sanitizer.bypassSecurityTrustUrl(DataUtilities.base64ToUrl(q.content, 'video/mp4')); } else { return null; } diff --git a/frontend/src/app/viewer/task-viewer.component.html b/frontend/src/app/viewer/task-viewer.component.html index 012fc6497..180be1d91 100644 --- a/frontend/src/app/viewer/task-viewer.component.html +++ b/frontend/src/app/viewer/task-viewer.component.html @@ -27,10 +27,7 @@

{{ (taskChanged | async)?.name }} (Task is about to start!)

{{ (taskChanged | async)?.name }} ({{ toFormattedTime(timeLeft | async) }})

- +
diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index f6938fb3b..ff90ddaf2 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -68,10 +68,10 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { viewerState: BehaviorSubject = new BehaviorSubject(ViewerState.VIEWER_UNKNOWN); /** Reference to the current {@link ApiHintContent}. */ - currentTaskHint: Observable; + currentTaskHint: Observable; /** Reference to the current {@link ApiTargetContent}. */ - currentTaskTarget: Observable; + currentTaskTarget: Observable; /** The subscription associated with the current viewer state. */ viewerStateSubscription: Subscription; From a706cf3658dc667520c91f6f35d8aca641c10d0f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 15 Feb 2023 14:01:56 +0100 Subject: [PATCH 142/498] Fixed conversion of image-based hint to path. --- .../dres/data/model/template/task/DbHint.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index b70f590b0..1f86b454a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -10,12 +10,12 @@ import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.FilteringContext.le import kotlinx.dnq.simple.requireIf import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files import java.nio.file.Paths -import java.nio.file.StandardOpenOption import java.util.* /** @@ -116,19 +116,20 @@ class DbHint(entity: Entity) : XdEntity(entity) { */ fun toQueryContentElement(config: Config): ApiContentElement { val content = when (this.type) { - DbHintType.IMAGE, - DbHintType.VIDEO -> { - val path = if (this.item != null) { - Paths.get(config.cachePath, this.item!!.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)) - } else if (this.path != null) { - Paths.get(this.path!!) + DbHintType.IMAGE -> { + val filePath = this.item?.pathToOriginal() ?: this.path?.let { Paths.get(it) } + if (filePath != null && Files.exists(filePath)) { + Base64.getEncoder().encodeToString(Files.readAllBytes(filePath)) } else { - throw IllegalArgumentException("A hint of type ${this.type.description} must have a valid media item or external path.") + null } - if (Files.exists(path)) { - Base64.getEncoder().encodeToString(Files.readAllBytes(path)) + } + DbHintType.VIDEO -> { + val filePath = this.item?.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)?.let { Paths.get(config.cachePath, it) } ?: this.path?.let { Paths.get(it) } + if (Files.exists(filePath)) { + Base64.getEncoder().encodeToString(Files.readAllBytes(filePath)) } else { - "" + null } } DbHintType.EMPTY -> "" From 19c3d4d4b497d86238f2bbd8880d7cfda7dfd4f2 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 15 Feb 2023 20:20:16 +0100 Subject: [PATCH 143/498] Fixed inconsistent use of paths for cached video segments --- .../rest/handler/evaluation/admin/CreateEvaluationHandler.kt | 2 +- .../main/kotlin/dev/dres/data/model/template/task/DbHint.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 77ed6deca..0ed8053fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -92,7 +92,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs val cachePath = this.cacheLocation.resolve(cacheName) if (!Files.exists(cachePath)) { logger.warn("Query video file for item ${item.name} not found; rendering to $cachePath") - FFmpegUtil.extractSegment(item, it.second, cachePath) + FFmpegUtil.extractSegment(item, it.second, this.cacheLocation) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index 1f86b454a..c38686697 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -15,6 +15,7 @@ import kotlinx.dnq.simple.requireIf import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files +import java.nio.file.Path import java.nio.file.Paths import java.util.* @@ -115,6 +116,7 @@ class DbHint(entity: Entity) : XdEntity(entity) { * @throws IOException */ fun toQueryContentElement(config: Config): ApiContentElement { + val cacheLocation: Path = Paths.get(config.cachePath, "tasks") val content = when (this.type) { DbHintType.IMAGE -> { val filePath = this.item?.pathToOriginal() ?: this.path?.let { Paths.get(it) } @@ -125,7 +127,7 @@ class DbHint(entity: Entity) : XdEntity(entity) { } } DbHintType.VIDEO -> { - val filePath = this.item?.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)?.let { Paths.get(config.cachePath, it) } ?: this.path?.let { Paths.get(it) } + val filePath = this.item?.cachedItemName(this.temporalRangeStart, this.temporalRangeEnd)?.let { cacheLocation.resolve(it) } ?: this.path?.let { Paths.get(it) } if (Files.exists(filePath)) { Base64.getEncoder().encodeToString(Files.readAllBytes(filePath)) } else { From 8a6e20795cf401bd19ad8b2138cb51b4c1382a53 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 16 Feb 2023 10:06:55 +0100 Subject: [PATCH 144/498] Ongoing evaluation runs are now once again resumed when starting-up DRES. --- .../run/InteractiveSynchronousEvaluation.kt | 9 ++++++--- .../src/main/kotlin/dev/dres/run/RunExecutor.kt | 17 ++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 65fa2f1c5..637e12d5c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -11,6 +11,7 @@ import dev.dres.run.audit.DbAuditLogger import dev.dres.run.filter.SubmissionFilter import kotlinx.dnq.query.* import java.lang.IndexOutOfBoundsException +import java.util.LinkedList /** * Represents a concrete, interactive and synchronous [Run] of a [DbEvaluationTemplate]. @@ -43,9 +44,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu }) /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ - override val tasks = this.evaluation.tasks.asSequence().map { - ISTaskRun(it) - }.toMutableList() + override val tasks = LinkedList() /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ private val templates = this.description.tasks.asSequence().map { it.templateId }.toList() @@ -58,6 +57,10 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu val currentTask: AbstractInteractiveTask? get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex] } + init { + /* Load all ongoing tasks. */ + this.evaluation.tasks.asSequence().map { ISTaskRun(it) }.toMutableList() + } /** * Returns the [TemplateId] this [InteractiveSynchronousEvaluation] is currently pointing to. diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 96fe66de4..3ffbc6801 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -7,10 +7,8 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType +import dev.dres.data.model.run.* import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.run.InteractiveAsynchronousEvaluation -import dev.dres.data.model.run.InteractiveSynchronousEvaluation -import dev.dres.data.model.run.NonInteractiveEvaluation import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.run.validation.interfaces.JudgementValidator @@ -68,11 +66,16 @@ object RunExecutor : Consumer { * @param store The shared [TransientEntityStore]. */ fun init(store: TransientEntityStore) { - /* TODO: Schedule runs that have not ended - * this.runs.filter { !it.hasEnded }.forEach { //TODO needs more distinction - schedule(it) + store.transactional { + DbEvaluation.filter { (it.started) ne null and (it.ended eq null) }.asSequence().forEach { + this.schedule(when (it.type) { + DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(it) + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(it, emptyMap()) /* TODO: Team map. */ + DbEvaluationType.NON_INTERACTIVE -> TODO() + else -> throw IllegalStateException("Unsupported evaluation type ${it.type}.") + }, store) + } } - */ } /** From 8b10d9513fd3636db7519c6372af967490a9ea09 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 16 Feb 2023 10:20:18 +0100 Subject: [PATCH 145/498] Fixed AdjustDurationHandler --- .../rest/handler/evaluation/admin/AdjustDurationHandler.kt | 2 +- doc/oas-client.json | 4 ++-- doc/oas.json | 4 ++-- frontend/src/app/run/run-admin-view.component.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index a8c392660..dc22ff89e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -29,7 +29,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi summary = "Adjusts the duration of a running task. This is a method for admins.", path = "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}", operationId = OpenApiOperation.AUTO_GENERATE, - methods = [HttpMethod.POST], + methods = [HttpMethod.PATCH], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), OpenApiParam("duration", Int::class, "Duration to add. Can be negative.", required = true, allowEmptyValue = false) diff --git a/doc/oas-client.json b/doc/oas-client.json index 4f570b74c..bb9936179 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -274,10 +274,10 @@ } }, "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "post" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", "parameters" : [ { "name" : "evaluationId", "in" : "path", diff --git a/doc/oas.json b/doc/oas.json index 84d3ba7e9..8486ccbe9 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -936,10 +936,10 @@ } }, "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "post" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", "parameters" : [ { "name" : "evaluationId", "in" : "path", diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index 860d66270..843bfbb75 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -238,7 +238,7 @@ export class RunAdminViewComponent { public adjustDuration(duration: number) { this.runId - .pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdAdjustByDuration(id, duration))) + .pipe(switchMap((id) => this.runAdminService.patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration(id, duration))) .subscribe( (r) => { this.update.next(); From 19ff87bc784d325f296b0318c10a0a5b84de5ca6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 16 Feb 2023 10:37:00 +0100 Subject: [PATCH 146/498] Fixed resumption of evaluation from database: - Running tasks are now force ended - TaskStatus is now correctly reconstructed. --- .../dev/dres/data/model/run/AbstractTask.kt | 12 ++++++++---- .../src/main/kotlin/dev/dres/run/RunExecutor.kt | 16 +++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 5326fd283..5dabe2fe7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -54,7 +54,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Setter requires active database transaction! */ - final override var started: Long? = null + final override var started: Long? = task.started protected set(value) { field = value this.task.started = value /* Update backing database field. */ @@ -65,7 +65,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Setter requires active database transaction! */ - final override var ended: Long? = null + final override var ended: Long? = task.ended protected set(value) { field = value this.task.ended = value /* Update backing database field. */ @@ -81,8 +81,12 @@ abstract class AbstractTask(task: DbTask): TaskRun { /** The current status of this [AbstractTask]. This is a transient property. */ @Volatile - final override var status: TaskStatus = TaskStatus.CREATED - protected set + final override var status: TaskStatus = when { + this.started != null && this.ended != null -> TaskStatus.ENDED + this.started != null && this.ended == null -> TaskStatus.RUNNING + else -> TaskStatus.CREATED + } + protected set /** The [SubmissionFilter] used to filter [DbSubmission]s. */ abstract val filter: SubmissionFilter diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 3ffbc6801..a72f3d575 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -67,12 +67,18 @@ object RunExecutor : Consumer { */ fun init(store: TransientEntityStore) { store.transactional { - DbEvaluation.filter { (it.started) ne null and (it.ended eq null) }.asSequence().forEach { - this.schedule(when (it.type) { - DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(it) - DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(it, emptyMap()) /* TODO: Team map. */ + DbEvaluation.filter { (it.started) ne null and (it.ended eq null) }.asSequence().forEach {e -> + + /* Force-end tasks that are still running. */ + e.tasks.filter { t -> (t.ended eq null) }.asSequence().forEach { t -> + t.ended = System.currentTimeMillis() + } + + this.schedule(when (e.type) { + DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(e) + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(e, emptyMap()) /* TODO: Team map. */ DbEvaluationType.NON_INTERACTIVE -> TODO() - else -> throw IllegalStateException("Unsupported evaluation type ${it.type}.") + else -> throw IllegalStateException("Unsupported evaluation type ${e.type}.") }, store) } } From e470696889edcfb7ef7df6b3345d82ace4f57bd5 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 16 Feb 2023 10:53:16 +0100 Subject: [PATCH 147/498] Fixed deprecated OpenApi configuration. --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index de522b6b5..576569922 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -37,8 +37,11 @@ import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* import io.javalin.http.staticfiles.Location import io.javalin.community.ssl.SSLPlugin +import io.javalin.openapi.OpenApiInfo +import io.javalin.openapi.plugin.DefinitionConfiguration import io.javalin.openapi.plugin.OpenApiConfiguration import io.javalin.openapi.plugin.OpenApiPlugin +import io.javalin.openapi.plugin.OpenApiPluginConfiguration import io.javalin.openapi.plugin.swagger.SwaggerConfiguration import io.javalin.openapi.plugin.swagger.SwaggerPlugin import jetbrains.exodus.database.TransientEntityStore @@ -46,6 +49,8 @@ import org.eclipse.jetty.server.* import org.eclipse.jetty.util.thread.QueuedThreadPool import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory +import java.util.function.BiConsumer +import java.util.function.Consumer /** * This is a singleton instance of the RESTful API @@ -197,19 +202,19 @@ object RestApi { it.plugins.register( OpenApiPlugin( - OpenApiConfiguration().apply { - this.info.title = "DRES API" - this.info.version = DRES.VERSION - this.info.description = "API for DRES (Distributed Retrieval Evaluation Server), Version ${DRES.VERSION}" - this.documentationPath = "/swagger-docs" - } + OpenApiPluginConfiguration() + .withDocumentationPath("/swagger-docs") + .withDefinitionConfiguration { _, u -> + u.withOpenApiInfo { t -> + t.title = "DRES API" + t.version = DRES.VERSION + t.description = "API for DRES (Distributed Retrieval Evaluation Server), Version ${DRES.VERSION}" + } + } ) ) - it.plugins.register( - ClientOpenApiPlugin() - ) - + it.plugins.register(ClientOpenApiPlugin()) it.plugins.register( SwaggerPlugin( SwaggerConfiguration().apply { @@ -219,8 +224,6 @@ object RestApi { ) ) -// it.plugins.register(ClientSwaggerPlugin()) - it.http.defaultContentType = "application/json" it.http.prefer405over404 = true it.jetty.server { setupHttpServer() } @@ -245,20 +248,15 @@ object RestApi { //check for session cookie val cookieId = ctx.cookie(AccessManager.SESSION_COOKIE_NAME) - if (cookieId != null) { - //update cookie lifetime - ctx.cookie(AccessManager.SESSION_COOKIE_NAME, cookieId, AccessManager.SESSION_COOKIE_LIFETIME) - //store id in attribute for later use - ctx.attribute("session", cookieId) + ctx.cookie(AccessManager.SESSION_COOKIE_NAME, cookieId, AccessManager.SESSION_COOKIE_LIFETIME) //update cookie lifetime + ctx.attribute("session", cookieId) //store id in attribute for later use } //check for query parameter val paramId = ctx.queryParam("session") - if (paramId != null) { - //store id in attribute for later use - ctx.attribute("session", paramId) + ctx.attribute("session", paramId) //store id in attribute for later use } //logging From 7fe00ff83945ae9649fceb2035d3ab3c33e1c250 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 17 Feb 2023 10:26:10 +0100 Subject: [PATCH 148/498] Time code can now be specified with either frames or milliseconds see #367 --- .../data/model/media/PlayableMediaItem.kt | 24 ----------------- .../data/model/media/time/TemporalPoint.kt | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 30 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt deleted file mode 100644 index 592af22c1..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/media/PlayableMediaItem.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.dres.data.model.media - -import com.fasterxml.jackson.annotation.JsonIgnore -import java.time.Duration - - -/** - * Interface for [DbMediaItem]s that allow for playback and thus have a notion of duration and frames. - * - * @author Ralph Gasser - * @version 1.0 - */ -interface PlayableMediaItem { - /** Duration of the [PlayableMediaItem] in milliseconds. */ - val durationMs: Long - - /** [Duration] of the [PlayableMediaItem]. */ - val duration: Duration - @JsonIgnore - get() = Duration.ofMillis(this.durationMs) - - /** Frame per second for this [PlayableMediaItem]. */ - val fps: Float -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt index 278682db5..e12fc1e09 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt @@ -1,7 +1,6 @@ package dev.dres.data.model.media.time import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.PlayableMediaItem import java.lang.IllegalArgumentException /** @@ -43,18 +42,21 @@ sealed class TemporalPoint { data class Timecode(val timecode: String, val fps: Float) : TemporalPoint(){ companion object { - private val timecodeRegex = "^\\s*(?:(?:(?:(\\d+):)?([0-5]?\\d):)?([0-5]?\\d):)?(\\d+)\\s*\$".toRegex() + private val timecodeFrameRegex = "^\\s*(?:(?:(?:(\\d+):)?([0-5]?\\d):)?([0-5]?\\d):)?(\\d+)\\s*\$".toRegex() + private val timecodeMsRegex = "^\\s*(?:(?:(\\d+):)?([0-5]?\\d):)?([0-5]?\\d)\\.(\\d{1,3})\\s*\$".toRegex() private const val msPerHour: Long = 3_600_000 private const val msPerMinute: Long = 60_000 /** - * Transforms a time code of the form HH:MM:SS:FF to milliseconds + * Transforms a time code of the form HH:MM:SS:FF or HH:MM:SS.mmm to milliseconds * @return time in milliseconds or null if the input is not a valid time code */ - fun timeCodeToMilliseconds(timecode: String, fps: Float = 24.0f): Long? { + fun timeCodeToMilliseconds(timecode: String, fps: Float = 24.0f): Long? = parseFrameTimecode(timecode, fps) ?: parseMsTimecode(timecode, fps) - val matches = timecodeRegex.matchEntire(timecode) ?: return null + private fun parseFrameTimecode(timecode: String, fps: Float = 24.0f): Long? { + + val matches = timecodeFrameRegex.matchEntire(timecode) ?: return null val hours = matches.groups[1]?.value?.toLong() ?: 0 val minutes = matches.groups[2]?.value?.toLong() ?: 0 @@ -62,9 +64,21 @@ sealed class TemporalPoint { val frames = matches.groups[4]?.value?.toLong() ?: 0 return hours * msPerHour + minutes * msPerMinute + seconds * 1000 + (1000 * frames / fps).toLong() + } - fun timeCodeToMilliseconds(timecode: String, item: PlayableMediaItem): Long? = timeCodeToMilliseconds(timecode, item.fps) + private fun parseMsTimecode(timecode: String, fps: Float = 24.0f): Long? { + + val matches = timecodeMsRegex.matchEntire(timecode) ?: return null + + val hours = matches.groups[1]?.value?.toLong() ?: 0 + val minutes = matches.groups[2]?.value?.toLong() ?: 0 + val seconds = matches.groups[3]?.value?.toLong() ?: 0 + val ms = matches.groups[4]?.value?.toLong() ?: 0 + + return hours * msPerHour + minutes * msPerMinute + seconds * 1000 + ms + + } } private val millisecond: Long = timeCodeToMilliseconds(timecode, fps) ?: throw IllegalArgumentException("'$timecode' is not a valid time code of the form HH:MM:SS:FF") From dc08cb683a294b02141e4fe7b73fbc8fbbe75bf9 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 17 Feb 2023 15:55:04 +0100 Subject: [PATCH 149/498] Some data model restructuring towards submissions --- backend/src/main/kotlin/dev/dres/DRES.kt | 6 ++---- .../handler/judgement/DequeueJudgementHandler.kt | 3 ++- .../rest/handler/judgement/DequeueVoteHandler.kt | 3 ++- .../api/rest/types/evaluation/ApiAnswerSet.kt | 15 ++++++++++----- .../api/rest/types/evaluation/ApiSubmission.kt | 4 +++- .../dev/dres/data/model/submissions/AnswerSet.kt | 5 ++++- .../dres/data/model/submissions/DbAnswerSet.kt | 9 ++++++++- .../dres/data/model/submissions/DbSubmission.kt | 7 ++++++- .../dev/dres/data/model/submissions/Submission.kt | 2 ++ .../kotlin/dev/dres/run/audit/DbAuditLogger.kt | 14 +++++++------- .../dev/dres/run/filter/CorrectPerTeamFilter.kt | 2 +- .../dres/run/filter/CorrectPerTeamItemFilter.kt | 2 +- .../dres/run/filter/CorrectPerTeamMemberFilter.kt | 2 +- .../dres/run/filter/MaximumTotalPerTeamFilter.kt | 2 +- .../dres/run/filter/MaximumWrongPerTeamFilter.kt | 2 +- 15 files changed, 51 insertions(+), 27 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 92227b0c2..4d6e5ef82 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -22,10 +22,7 @@ import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.DbEvaluationType import dev.dres.data.model.run.DbTask -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.* import dev.dres.run.RunExecutor import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor @@ -129,6 +126,7 @@ object DRES { DbTeamAggregator, DbTeamGroup, DbUser, + DbAnswer, DbAnswerSet, DbAnswerType, DbVerdictStatus, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 6893c3738..04bf1de08 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.submissions.AnswerType +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbAnswerType import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken @@ -49,7 +50,7 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa do { val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break val next = validator.next(ctx.sessionToken()!!) ?: break - val taskDescription = next.second.task.template.textualDescription() + val taskDescription = next.second.task().template.textualDescription() when (next.second.answers().firstOrNull()?.type()) { AnswerType.TEXT -> { val text = next.second.answers().firstOrNull()?.text ?: continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 661c56487..270548b77 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.submissions.AnswerType +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.Context @@ -47,7 +48,7 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( do { val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break val next = validator.nextSubmissionToVoteOn() ?: break - val taskDescription = next.task.template.textualDescription() + val taskDescription = next.task().template.textualDescription() when (next.answers().firstOrNull()?.type()) { AnswerType.TEXT -> { val text = next.answers().firstOrNull()?.text ?: continue diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index 61e7f9039..c67830fd1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -1,11 +1,13 @@ package dev.dres.api.rest.types.evaluation import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.Task +import dev.dres.data.model.run.TaskId import dev.dres.data.model.submissions.* -import dev.dres.data.model.template.interfaces.EvaluationTemplate import kotlinx.dnq.query.addAll +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first /** * The RESTful API equivalent for the type of [ApiAnswerSet]. @@ -16,16 +18,18 @@ import kotlinx.dnq.query.addAll */ data class ApiAnswerSet( var status: ApiVerdictStatus, + override val taskId: TaskId, val answers: List ) : AnswerSet { - @JsonIgnore - override lateinit var task: Task - @JsonIgnore override lateinit var submission: ApiSubmission internal set + override fun task(): Task { + TODO("Not yet implemented") + } + override fun answers(): Sequence = answers.asSequence() override fun status(): VerdictStatus = VerdictStatus.fromApi(this.status) @@ -36,6 +40,7 @@ data class ApiAnswerSet( override fun toDb(): DbAnswerSet { return DbAnswerSet.new { this.status = this@ApiAnswerSet.status.toDb() + this.task = DbTask.filter { taskId eq this@ApiAnswerSet.taskId }.first() this.answers.addAll( this@ApiAnswerSet.answers.map { it.toDb() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index c6faa64d6..ab41ec371 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.types.evaluation import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.User +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission @@ -28,7 +29,8 @@ data class ApiSubmission( val teamName: String, val memberName: String, val answers: List, - override val timestamp: Long = System.currentTimeMillis() + override val timestamp: Long = System.currentTimeMillis(), + override val evaluationId: EvaluationId ) : Submission { init { diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index 6ef364ede..73784c81f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -1,13 +1,16 @@ package dev.dres.data.model.submissions import dev.dres.data.model.run.Task +import dev.dres.data.model.run.TaskId interface AnswerSet { //TODO - val task: Task + val taskId: TaskId val submission: Submission + fun task(): Task + fun answers() : Sequence fun status() : VerdictStatus diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index cda2ae54d..896d8de1f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -3,6 +3,8 @@ package dev.dres.data.model.submissions import dev.dres.api.rest.types.evaluation.ApiAnswerSet import dev.dres.data.model.PersistentEntity import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.Task +import dev.dres.data.model.run.TaskId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -24,7 +26,11 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { override var submission: DbSubmission by xdParent(DbSubmission::answerSets) /** The [DbTask] this [DbAnswerSet] belongs to. */ - override var task: DbTask by xdParent(DbTask::answerSets) + var task: DbTask by xdParent(DbTask::answerSets) + + override fun task(): Task = task + override val taskId: TaskId + get() = task.taskId val answers by xdChildren1_N(DbAnswer::answerSet) @@ -46,6 +52,7 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { */ fun toApi(): ApiAnswerSet = ApiAnswerSet( status = this.status.toApi(), + taskId = this.taskId, answers = this.answers.asSequence().map { it.toApi() }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 9dd07050a..6ac7f78f5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -4,11 +4,13 @@ import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.first import kotlinx.dnq.simple.min @@ -43,6 +45,8 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { override val memberId: UserId get() = this.user.userId + override val evaluationId: EvaluationId + get() = this.answerSets.first().task.evaluation.evaluationId /** The [DbAnswerSet]s that make-up this [DbSubmission]. For batched submissions, more than one verdict can be possible. */ val answerSets by xdChildren1_N(DbAnswerSet::submission) @@ -65,6 +69,7 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { memberId = this.user.id, memberName = this.user.username, timestamp = this.timestamp, - answers = if (blind) { emptyList() } else { this.answerSets.asSequence().map { it.toApi() }.toList()} + answers = if (blind) { emptyList() } else { this.answerSets.asSequence().map { it.toApi() }.toList()}, + evaluationId = this.evaluationId ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 89281f24a..21e6c847c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.submissions import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.team.TeamId typealias SubmissionId = String @@ -11,6 +12,7 @@ interface Submission { val timestamp: Long val teamId: TeamId val memberId: UserId + val evaluationId: EvaluationId fun answerSets(): Sequence diff --git a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt index 1c0f5fe37..85b03595d 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt @@ -132,12 +132,12 @@ object DbAuditLogger { this.type = DbAuditLogType.SUBMISSION this.source = api this.submissionId = submission.submissionId - this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId - this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ + this.evaluationId = submission.evaluationId + this.taskId = submission.answerSets().first().taskId /* TODO: Multiple verdicts. */ this.session = sessionToken this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.answerSets().first().task.evaluation.evaluationId, submission.answerSets().first().task.taskId, submission)) + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.evaluationId, submission.answerSets().first().taskId, submission)) } /** @@ -151,8 +151,8 @@ object DbAuditLogger { this.type = DbAuditLogType.SUBMISSION_VALIDATION this.source = DbAuditLogSource.INTERNAL this.submissionId = submission.submissionId - this.evaluationId = submission.answerSets().first().task.evaluation.evaluationId - this.taskId = submission.answerSets().first().task.taskId /* TODO: Multiple verdicts. */ + this.evaluationId = submission.evaluationId + this.taskId = submission.answerSets().first().taskId /* TODO: Multiple verdicts. */ this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets().first().status()}" /* TODO: Here name, there ID. Why? */ } } @@ -188,8 +188,8 @@ object DbAuditLogger { this.type = DbAuditLogType.PREPARE_JUDGEMENT this.source = DbAuditLogSource.INTERNAL this.submissionId = answerSet.submission.submissionId - this.evaluationId = answerSet.task.evaluation.evaluationId - this.taskId = answerSet.task.taskId + this.evaluationId = "" //FIXME lookup? + this.taskId = answerSet.taskId this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${answerSet.status()}" } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index c4716c228..dc924da95 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -28,7 +28,7 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "1").toIntOrNull() ?: 1) override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> - answer.task.answerSets().filter { + answer.task().answerSets().filter { (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId }.count() < limit } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index 3f6467f3b..2d8d21c8a 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -18,7 +18,7 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { override fun test(submission: Submission): Boolean { val submittedItems = submission.answerSets().flatMap { it.answers() }.mapNotNull { it.item }.toSet() return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { taskAnswerSets -> + answerSet.task().answerSets().filter { taskAnswerSets -> (taskAnswerSets.status() == VerdictStatus.CORRECT) && taskAnswerSets.submission.teamId == submission.teamId && taskAnswerSets.answers().any { it.item in submittedItems } }.count() < this.limit } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index d1d20947f..c3542c21f 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -27,7 +27,7 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member." override fun test(submission: Submission): Boolean { return submission.answerSets().all { answer -> - answer.task.answerSets().filter { + answer.task().answerSets().filter { (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId && it.submission.memberId == submission.memberId }.count() < limit } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index a5c0177aa..f211a64c1 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -22,7 +22,7 @@ class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi */ override fun test(submission: Submission): Boolean { return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { it.submission.teamId == submission.teamId }.count() < max + answerSet.task().answerSets().filter { it.submission.teamId == submission.teamId }.count() < max } } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 8b695eebb..159bd21f6 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -28,7 +28,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi */ override fun test(submission: Submission): Boolean { return submission.answerSets().all { answerSet -> - answerSet.task.answerSets().filter { (it.submission.teamId == submission.teamId) and (it.status() == VerdictStatus.WRONG) }.count() < max + answerSet.task().answerSets().filter { (it.submission.teamId == submission.teamId) and (it.status() == VerdictStatus.WRONG) }.count() < max } } } From 890cd77f3c5b69b7dafbb604de8e3abef263450e Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 19 Feb 2023 13:43:46 +0100 Subject: [PATCH 150/498] Added security configuration to OpenAPI --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 11 +- doc/oas-client.json | 185 ++++++-------- doc/oas.json | 239 ++++++++---------- 3 files changed, 181 insertions(+), 254 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 576569922..6d867338f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -37,11 +37,9 @@ import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* import io.javalin.http.staticfiles.Location import io.javalin.community.ssl.SSLPlugin +import io.javalin.openapi.CookieAuth import io.javalin.openapi.OpenApiInfo -import io.javalin.openapi.plugin.DefinitionConfiguration -import io.javalin.openapi.plugin.OpenApiConfiguration -import io.javalin.openapi.plugin.OpenApiPlugin -import io.javalin.openapi.plugin.OpenApiPluginConfiguration +import io.javalin.openapi.plugin.* import io.javalin.openapi.plugin.swagger.SwaggerConfiguration import io.javalin.openapi.plugin.swagger.SwaggerPlugin import jetbrains.exodus.database.TransientEntityStore @@ -210,7 +208,12 @@ object RestApi { t.version = DRES.VERSION t.description = "API for DRES (Distributed Retrieval Evaluation Server), Version ${DRES.VERSION}" } + u.withSecurity(SecurityComponentConfiguration() + .withSecurityScheme("CookieAuth", CookieAuth(AccessManager.SESSION_COOKIE_NAME)) + ) } + + ) ) diff --git a/doc/oas-client.json b/doc/oas-client.json index bb9936179..b572b956b 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2749,10 +2749,10 @@ } }, "/api/v2/template/{templateId}" : { - "get" : { + "delete" : { "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -2770,7 +2770,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -2809,10 +2809,10 @@ "deprecated" : false, "security" : [ ] }, - "delete" : { + "get" : { "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -2830,7 +2830,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } @@ -3071,11 +3071,21 @@ } }, "/api/v2/user" : { - "get" : { + "post" : { "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3087,6 +3097,16 @@ } } }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "500" : { "description" : "Server Error", "content" : { @@ -3101,21 +3121,11 @@ "deprecated" : false, "security" : [ ] }, - "post" : { + "get" : { "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3127,16 +3137,6 @@ } } }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "500" : { "description" : "Server Error", "content" : { @@ -3245,14 +3245,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3260,6 +3260,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3271,8 +3281,18 @@ } } }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { - "description" : "If the user could not be found.", + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3295,14 +3315,14 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User ID", + "description" : "User's UID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -3310,16 +3330,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3331,18 +3341,8 @@ } } }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -3940,20 +3940,20 @@ "status" : { "$ref" : "#/components/schemas/ApiVerdictStatus" }, + "taskId" : { + "type" : "string" + }, "answers" : { "type" : "array", "items" : { "$ref" : "#/components/schemas/ApiAnswer" } }, - "task" : { - "$ref" : "#/components/schemas/Task" - }, "submission" : { "$ref" : "#/components/schemas/ApiSubmission" } }, - "required" : [ "status", "answers", "task", "submission" ] + "required" : [ "status", "taskId", "answers", "submission" ] }, "ApiAnswerType" : { "type" : "string", @@ -4059,9 +4059,12 @@ "timestamp" : { "type" : "integer", "format" : "int64" + }, + "evaluationId" : { + "type" : "string" } }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp" ] + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] }, "ApiSubmissionInfo" : { "type" : "object", @@ -4674,50 +4677,6 @@ }, "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, - "Task" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - }, - "evaluation" : { - "$ref" : "#/components/schemas/Evaluation" - }, - "template" : { - "$ref" : "#/components/schemas/TaskTemplate" - } - }, - "required" : [ "taskId", "evaluation", "template" ] - }, - "Evaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - } - }, - "required" : [ "evaluationId" ] - }, - "TaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - } - }, - "required" : [ "templateId" ] - }, "RunManagerStatus" : { "type" : "string", "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] diff --git a/doc/oas.json b/doc/oas.json index 8486ccbe9..9801d3651 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -244,10 +244,10 @@ } }, "/api/v2/collection" : { - "patch" : { + "post" : { "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -294,10 +294,10 @@ "deprecated" : false, "security" : [ ] }, - "post" : { + "patch" : { "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -381,10 +381,10 @@ } }, "/api/v2/collection/{collectionId}" : { - "get" : { + "delete" : { "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -402,13 +402,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -417,8 +417,8 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -431,10 +431,10 @@ "deprecated" : false, "security" : [ ] }, - "delete" : { + "get" : { "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -452,13 +452,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -467,8 +467,8 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4036,10 +4036,10 @@ } }, "/api/v2/template/{templateId}" : { - "get" : { + "delete" : { "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4057,7 +4057,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -4096,10 +4096,10 @@ "deprecated" : false, "security" : [ ] }, - "delete" : { + "get" : { "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4117,7 +4117,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } @@ -4358,11 +4358,21 @@ } }, "/api/v2/user" : { - "get" : { + "post" : { "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4374,6 +4384,16 @@ } } }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "500" : { "description" : "Server Error", "content" : { @@ -4388,21 +4408,11 @@ "deprecated" : false, "security" : [ ] }, - "post" : { + "get" : { "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4414,16 +4424,6 @@ } } }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "500" : { "description" : "Server Error", "content" : { @@ -4592,14 +4592,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -4607,6 +4607,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4618,8 +4628,18 @@ } } }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { - "description" : "If the user could not be found.", + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4642,14 +4662,14 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User ID", + "description" : "User's UID", "required" : false, "deprecated" : false, "allowEmptyValue" : false, @@ -4657,16 +4677,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4678,18 +4688,8 @@ } } }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -5287,20 +5287,20 @@ "status" : { "$ref" : "#/components/schemas/ApiVerdictStatus" }, + "taskId" : { + "type" : "string" + }, "answers" : { "type" : "array", "items" : { "$ref" : "#/components/schemas/ApiAnswer" } }, - "task" : { - "$ref" : "#/components/schemas/Task" - }, "submission" : { "$ref" : "#/components/schemas/ApiSubmission" } }, - "required" : [ "status", "answers", "task", "submission" ] + "required" : [ "status", "taskId", "answers", "submission" ] }, "ApiAnswerType" : { "type" : "string", @@ -5406,9 +5406,12 @@ "timestamp" : { "type" : "integer", "format" : "int64" + }, + "evaluationId" : { + "type" : "string" } }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp" ] + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] }, "ApiSubmissionInfo" : { "type" : "object", @@ -6021,50 +6024,6 @@ }, "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] }, - "Task" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - }, - "evaluation" : { - "$ref" : "#/components/schemas/Evaluation" - }, - "template" : { - "$ref" : "#/components/schemas/TaskTemplate" - } - }, - "required" : [ "taskId", "evaluation", "template" ] - }, - "Evaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - } - }, - "required" : [ "evaluationId" ] - }, - "TaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - } - }, - "required" : [ "templateId" ] - }, "RunManagerStatus" : { "type" : "string", "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] @@ -6074,8 +6033,14 @@ "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] } }, - "securitySchemes" : { } + "securitySchemes" : { + "CookieAuth" : { + "name" : "SESSIONID", + "in" : "cookie", + "type" : "apiKey" + } + } }, "servers" : [ ], - "security" : null + "security" : [ ] } \ No newline at end of file From 2854796482767ebe1041fb7c87b6d41460da9e27 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 19 Feb 2023 16:01:24 +0100 Subject: [PATCH 151/498] LegacySubmissionHandler now generates ApiSubmission, since submission filters need to be applied before persisting --- .../submission/LegacySubmissionHandler.kt | 275 +++++++++++++----- 1 file changed, 197 insertions(+), 78 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 76fbc75e6..cc57fdb74 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -4,7 +4,7 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus @@ -43,7 +43,8 @@ import java.nio.file.Paths * @author Loris Sauter * @version 2.0.0 */ -class LegacySubmissionHandler(private val store: TransientEntityStore, private val config: Config): GetRestHandler, AccessManagedRestHandler { +class LegacySubmissionHandler(private val store: TransientEntityStore, private val config: Config) : + GetRestHandler, AccessManagedRestHandler { /** [LegacySubmissionHandler] requires [ApiRole.PARTICIPANT]. */ override val permittedRoles = setOf(ApiRole.PARTICIPANT) @@ -64,32 +65,66 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v const val PARAMETER_NAME_TEXT = "text" } - @OpenApi(summary = "Endpoint to accept submissions", - path = "/api/v2/submit", + @OpenApi( + summary = "Endpoint to accept submissions", + path = "/api/v2/submit", operationId = OpenApiOperation.AUTO_GENERATE, - queryParams = [ - OpenApiParam(PARAMETER_NAME_COLLECTION, String::class, "Collection identifier. Optional, in which case the default collection for the run will be considered.", allowEmptyValue = true), - OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), - OpenApiParam(PARAMETER_NAME_TEXT, String::class, "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_FRAME, Int::class, "Frame number for media with temporal progression (e.g., video).", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_SHOT, Int::class, "Shot number for media with temporal progression (e.g., video).", allowEmptyValue = true, required = false), - OpenApiParam(PARAMETER_NAME_TIMECODE, String::class, "Timecode for media with temporal progression (e.g,. video).", allowEmptyValue = true, required = false), - OpenApiParam("session", String::class, "Session Token") - ], - tags = ["Submission"], - responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), - OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), - OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) - ], + queryParams = [ + OpenApiParam( + PARAMETER_NAME_COLLECTION, + String::class, + "Collection identifier. Optional, in which case the default collection for the run will be considered.", + allowEmptyValue = true + ), + OpenApiParam(PARAMETER_NAME_ITEM, String::class, "Identifier for the actual media object or media file."), + OpenApiParam( + PARAMETER_NAME_TEXT, + String::class, + "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + allowEmptyValue = true, + required = false + ), + OpenApiParam( + PARAMETER_NAME_FRAME, + Int::class, + "Frame number for media with temporal progression (e.g., video).", + allowEmptyValue = true, + required = false + ), + OpenApiParam( + PARAMETER_NAME_SHOT, + Int::class, + "Shot number for media with temporal progression (e.g., video).", + allowEmptyValue = true, + required = false + ), + OpenApiParam( + PARAMETER_NAME_TIMECODE, + String::class, + "Timecode for media with temporal progression (e.g,. video).", + allowEmptyValue = true, + required = false + ), + OpenApiParam("session", String::class, "Session Token") + ], + tags = ["Submission"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), + OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) + ], methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { - val (s,r) = this.store.transactional { - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val (s, r) = this.store.transactional { + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( + 401, + "Authorization required.", + ctx + ) val run = getEligibleRunManager(userId, ctx) val time = System.currentTimeMillis() val submission = toSubmission(userId, run, time, ctx) @@ -101,7 +136,11 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) } catch (e: IllegalRunStateException) { logger.info("Submission was received while run manager was not accepting submissions.") - throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + throw ErrorStatusException( + 400, + "Run manager is in wrong state and cannot accept any more submission.", + ctx + ) } catch (e: IllegalTeamIdException) { logger.info("Submission with unknown team id '${rac.teamId}' was received.") throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) @@ -109,21 +148,30 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v DbAuditLogger.submission(submission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS)) { //pre-generate preview - generatePreview(submission.answerSets.first()) + generatePreview(submission.answerSets().first()) } submission to result } - logger.info("Submission ${s.id} received status $r.") + logger.info("Submission ${s.submissionId} received status $r.") return when (r) { VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") - VerdictStatus.WRONG -> SuccessfulSubmissionsStatus(ApiVerdictStatus.WRONG, "Submission incorrect! Try again") + VerdictStatus.WRONG -> SuccessfulSubmissionsStatus( + ApiVerdictStatus.WRONG, + "Submission incorrect! Try again" + ) + VerdictStatus.INDETERMINATE -> { ctx.status(202) /* HTTP Accepted. */ SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") } - VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(ApiVerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") + + VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus( + ApiVerdictStatus.UNDECIDABLE, + "Submission undecidable. Try again!" + ) + else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } } @@ -132,12 +180,21 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v * Returns the [InteractiveRunManager] that is eligible for the given [UserId] and [Context] */ private fun getEligibleRunManager(userId: UserId, ctx: Context): InteractiveRunManager { - val managers = AccessManager.getRunManagerForUser(userId).filterIsInstance(InteractiveRunManager::class.java).filter { - val rac = RunActionContext.runActionContext(ctx, it) - it.currentTask(rac)?.isRunning == true - } - if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) - if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) + val managers = + AccessManager.getRunManagerForUser(userId).filterIsInstance(InteractiveRunManager::class.java).filter { + val rac = RunActionContext.runActionContext(ctx, it) + it.currentTask(rac)?.isRunning == true + } + if (managers.isEmpty()) throw ErrorStatusException( + 404, + "There is currently no eligible competition with an active task.", + ctx + ) + if (managers.size > 1) throw ErrorStatusException( + 409, + "More than one possible competition found: ${managers.joinToString { it.template.name }}", + ctx + ) return managers.first() } @@ -151,7 +208,12 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v * @param submissionTime Time of the submission. * @param ctx The HTTP [Context] */ - private fun toSubmission(userId: UserId, runManager: InteractiveRunManager, submissionTime: Long, ctx: Context): DbSubmission { + private fun toSubmission( + userId: UserId, + runManager: InteractiveRunManager, + submissionTime: Long, + ctx: Context + ): ApiSubmission { val map = ctx.queryParamMap() /* Find team that the user belongs to. */ @@ -161,91 +223,137 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v ?: throw ErrorStatusException(404, "No team for user '$userId' could be found.", ctx) val rac = RunActionContext.runActionContext(ctx, runManager) + val answerSets = mutableListOf() + /* Create new submission. */ - val submission = DbSubmission.new { - this.user = user - this.team = team - this.timestamp = submissionTime - } + val submission = ApiSubmission( + teamId = team.teamId, + memberId = user.userId, + teamName = team.name, + memberName = user.username, + answers = answerSets, + evaluationId = runManager.id + ) /* If text is supplied, it supersedes other parameters */ val textParam = map[PARAMETER_NAME_TEXT]?.first() val itemParam = map[PARAMETER_NAME_ITEM]?.first() val currentTaskId = runManager.currentTask(rac)?.id - val task = DbTask.query(DbTask::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException(404, "No active task for ID '$currentTaskId' could be found.", ctx) + val task = DbTask.query(DbTask::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException( + 404, + "No active task for ID '$currentTaskId' could be found.", + ctx + ) - /* Create Verdict. */ - val answerSet = DbAnswerSet.new { - this.status = DbVerdictStatus.INDETERMINATE - this.task = task - } - submission.answerSets.add(answerSet) - if (textParam != null) { - answerSet.answers.firstOrNull()?.type = DbAnswerType.TEXT - answerSet.answers.firstOrNull()?.text = textParam - return submission + val answer = if (textParam != null) { + ApiAnswer( + type = ApiAnswerType.TEXT, + text = textParam, + item = null, + start = null, + end = null + ) } else if (itemParam != null) { - val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ - val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) - val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)).firstOrNull() + val collection = + runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ + val mapToSegment = + runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) + val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)) + .firstOrNull() ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) - val range: Pair? = when { + val range: Pair? = when { map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { val time = TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) - ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) + ?: throw ErrorStatusException( + 400, + "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", + ctx + ) time.first to time.second } + map.containsKey(PARAMETER_NAME_FRAME) && item.type == DbMediaType.VIDEO -> { val fps = item.fps ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") val time = TemporalPoint.Frame.toMilliseconds( map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() - ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), + ?: throw ErrorStatusException( + 400, + "Parameter '$PARAMETER_NAME_FRAME' must be a number.", + ctx + ), fps ) if (mapToSegment) { TimeUtil.timeToSegment(time, item.segments.toList()) - ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + ?: throw ErrorStatusException( + 400, + "No matching segments found for item '${item.name}'.", + ctx + ) } else { time to time } } + map.containsKey(PARAMETER_NAME_TIMECODE) -> { val fps = item.fps ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") val time = TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, fps) - ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) - if (mapToSegment) { + ?: throw ErrorStatusException( + 400, + "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", + ctx + ) + if (mapToSegment) { TimeUtil.timeToSegment(time, item.segments.toList()) - ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + ?: throw ErrorStatusException( + 400, + "No matching segments found for item '${item.name}'.", + ctx + ) } else { time to time } } + else -> null } - val answer = DbAnswer.new() - /* Assign information to submission. */ if (range != null) { - answer.item = item - answer.type = DbAnswerType.TEMPORAL - answer.start = range.first - answer.end = range.second + ApiAnswer( + type = ApiAnswerType.TEMPORAL, + item = item.toApi(), + start = range.first, + end = range.second, + text = null + ) } else { - answer.item = item - answer.type = DbAnswerType.ITEM + ApiAnswer( + type = ApiAnswerType.ITEM, + item = item.toApi(), + start = null, + end = null, + text = null + ) } - answerSet.answers.add(answer) - } else { throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) } + /* Create AnswerSet. */ + answerSets.add( + ApiAnswerSet( + status = ApiVerdictStatus.INDETERMINATE, + taskId = task.taskId, + answers = listOf(answer) + ) + ) + return submission } @@ -254,13 +362,24 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v * * @param answerSet The [DbAnswerSet] to generate preview for. */ - private fun generatePreview(answerSet: DbAnswerSet) { - if (answerSet.answers.firstOrNull()?.type != DbAnswerType.TEMPORAL) return - if (answerSet.answers.firstOrNull()?.item == null) return - val destinationPath = Paths.get(this.config.cachePath, "previews", answerSet.answers.firstOrNull()?.item!!.collection.name, answerSet.answers.firstOrNull()?.item!!.name, "${answerSet.answers.firstOrNull()?.start}.jpg") - if (Files.exists(destinationPath)){ + private fun generatePreview(answerSet: AnswerSet) { + if (answerSet.answers().firstOrNull()?.type() != AnswerType.TEMPORAL) return + if (answerSet.answers().firstOrNull()?.item == null) return + val item = DbMediaItem.query((DbMediaItem::id eq answerSet.answers().firstOrNull()?.item!!.id)).firstOrNull() ?: return + val destinationPath = Paths.get( + this.config.cachePath, + "previews", + item.collection.name, + item.name, + "${answerSet.answers().firstOrNull()?.start}.jpg" + ) + if (Files.exists(destinationPath)) { return } - FFmpegUtil.extractFrame(answerSet.answers.firstOrNull()?.item!!.pathToOriginal(), answerSet.answers.firstOrNull()?.start!!, destinationPath) + FFmpegUtil.extractFrame( + item.pathToOriginal(), + answerSet.answers().firstOrNull()?.start!!, + destinationPath + ) } } From 1e686c7a7d2e01b852b6b6325c97122473607289 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 19 Feb 2023 16:01:33 +0100 Subject: [PATCH 152/498] Fixed tests --- .../run/score/scorer/AvsTaskScorerTest.kt | 53 +++---- .../run/score/scorer/KisTaskScorerTest.kt | 32 ++-- .../score/scorer/LegacyAvsTaskScorerTest.kt | 138 +++++++++--------- 3 files changed, 114 insertions(+), 109 deletions(-) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index fd21b9fee..e16eae1ce 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -29,9 +29,10 @@ class AvsTaskScorerTest { dummyVideoItems = list } - private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long) = listOf( + private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long, taskId: String) = listOf( ApiAnswerSet( status, + taskId, listOf( ApiAnswer( ApiAnswerType.TEMPORAL, @@ -64,7 +65,7 @@ class AvsTaskScorerTest { fun onlyTeamOneWithAllEqualsOneCorrect(){ val taskStart = 100_000L val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 1000, "task2" ), ) val scores = this.scorer.computeScores(subs, TaskContext("task2", teams, 100_000, defaultTaskDuration)) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -77,9 +78,9 @@ class AvsTaskScorerTest { fun allTeamsWithAllEqualsOneCorrect(){ val taskStart = 100_000L val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 1000, "task2"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 2000, "task2"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 3000, "task2") ) val scores = this.scorer.computeScores(subs, TaskContext("task2", teams, 100_000, defaultTaskDuration)) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -92,9 +93,9 @@ class AvsTaskScorerTest { fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ val taskStart = 100_000L val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000), taskStart + 2000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 3000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStart + 1000, "task3"), + ApiSubmission(teams[0], teams[0], "user1", "team2", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000, "task3"), taskStart + 2000, "task3"), + ApiSubmission(teams[1], teams[1], "user2", "team3", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStart + 3000, "task3"), ) val scores = this.scorer.computeScores(subs, TaskContext("task3", teams, 100_000, defaultTaskDuration)) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -108,33 +109,33 @@ class AvsTaskScorerTest { val taskStart = 100_000L val subs = sequenceOf( /* Team One: All correct */ - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000), taskStart + 2000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStart + 3000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStart + 1000, "task4"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000, "task4"), taskStart + 2000, "task4"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStart + 3000, "task4"), /* Team Two: One correct, One correct with one wrong */ - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000), taskStart + 2000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000), taskStart + 3000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStart + 1000, "task4"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStart + 2000, "task4"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStart + 3000, "task4"), /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ /* v1 */ - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStart + 1000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStart + 1000, "task4"), /* v2 */ - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000), taskStart + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000), taskStart + 3000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStart + 2000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStart + 3000, "task4"), /* v3 */ - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000), taskStart + 4000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000), taskStart + 5000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStart + 6000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000, "task4"), taskStart + 4000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000, "task4"), taskStart + 5000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStart + 6000, "task4"), /* v4 */ - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStart + 7000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000), taskStart + 8000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000), taskStart + 9000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000, "task4"), taskStart + 7000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000, "task4"), taskStart + 8000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000, "task4"), taskStart + 9000, "task4"), /* v5 */ - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000), taskStart + 10000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000), taskStart + 11000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000), taskStart + 12000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000, "task4"), taskStart + 10000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000, "task4"), taskStart + 11000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000, "task4"), taskStart + 12000, "task4"), ) val scores = this.scorer.computeScores(subs, TaskContext("task4", teams, 100_000, defaultTaskDuration)) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index d837976ac..26a8d81f1 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -24,6 +24,7 @@ class KisTaskScorerTest { private val wrongAnswer = listOf( ApiAnswerSet( ApiVerdictStatus.WRONG, + "task", listOf( ApiAnswer( ApiAnswerType.ITEM, @@ -37,6 +38,7 @@ class KisTaskScorerTest { private val correctAnswer = listOf( ApiAnswerSet( ApiVerdictStatus.CORRECT, + "task", listOf( ApiAnswer( ApiAnswerType.ITEM, @@ -66,9 +68,9 @@ class KisTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", wrongAnswer, taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", wrongAnswer, taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", wrongAnswer, taskStartTime + 1000, "task2"), + ApiSubmission(teams[1], teams[1], "user2", "team1", "user2", wrongAnswer, taskStartTime + 2000, "task2"), + ApiSubmission(teams[2], teams[2], "user3", "team1", "user3", wrongAnswer, taskStartTime + 3000, "task2") ) val scores = this.scorer.computeScores(submissions, TaskContext("task2", teams, taskStartTime, defaultTaskDuration)) @@ -82,7 +84,7 @@ class KisTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime, "task3"), ) val scores = this.scorer.computeScores(submissions, TaskContext("task3", teams, taskStartTime, defaultTaskDuration)) @@ -96,7 +98,7 @@ class KisTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000)), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000), "task4"), ) val scores = @@ -109,7 +111,7 @@ class KisTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task5"), ) val scores = @@ -123,19 +125,19 @@ class KisTaskScorerTest { val submissions = sequenceOf( //incorrect submissions - ApiSubmission(teams[0], teams[0], "user1", "user1", wrongAnswer, taskStartTime + 1), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", wrongAnswer, taskStartTime + 1, "task6"), - ApiSubmission(teams[1], teams[1], "user2", "user2", wrongAnswer, taskStartTime + 2), - ApiSubmission(teams[1], teams[1], "user2", "user2", wrongAnswer, taskStartTime + 3), + ApiSubmission(teams[1], teams[1], "user2", "team2","user2", wrongAnswer, taskStartTime + 2, "task6"), + ApiSubmission(teams[1], teams[1], "user2", "team2","user2", wrongAnswer, taskStartTime + 3, "task6"), - ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 4), - ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 5), - ApiSubmission(teams[2], teams[2], "user3", "user3", wrongAnswer, taskStartTime + 6), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", wrongAnswer, taskStartTime + 4, "task6"), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", wrongAnswer, taskStartTime + 5, "task6"), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", wrongAnswer, taskStartTime + 6, "task6"), //correct submissions at 1/2 the task time - ApiSubmission(teams[0], teams[0], "user1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), - ApiSubmission(teams[1], teams[1], "user2", "user2", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), - ApiSubmission(teams[2], teams[2], "user3", "user3", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2)), + ApiSubmission(teams[0], teams[0], "user1", "team1","user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), + ApiSubmission(teams[1], teams[1], "user2", "team2","user2", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), ) val scores = this.scorer.computeScores(submissions, TaskContext("task6", teams, taskStartTime, defaultTaskDuration)) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index 02fe642b1..1ed177caf 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -44,6 +44,7 @@ class LegacyAvsTaskScorerTest { private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem) = listOf( ApiAnswerSet( status, + "task", listOf( ApiAnswer( ApiAnswerType.ITEM, @@ -57,6 +58,7 @@ class LegacyAvsTaskScorerTest { private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long) = listOf( ApiAnswerSet( status, + "task", listOf( ApiAnswer( ApiAnswerType.TEMPORAL, @@ -87,9 +89,9 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000, "task2"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 2000, "task2"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 3000, "task2") ) val scores = this.scorer.computeScores(submissions, TaskContext("task2", teams, 100_000, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -101,9 +103,9 @@ class LegacyAvsTaskScorerTest { fun allWrongDifferentImages() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[1]), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[2]), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000, "task3"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[1]), taskStartTime + 2000, "task3"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[2]), taskStartTime + 3000, "task3") ) val scores = this.scorer.computeScores(submissions, TaskContext("task3", teams, 100_000, defaultTaskDuration)) assertEquals(0.0, scores[teams[0]]) @@ -115,9 +117,9 @@ class LegacyAvsTaskScorerTest { fun allSameImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task4"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 2000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 3000, "task4") ) val scores = this.scorer.computeScores(submissions, TaskContext("task4", teams, 100_000, defaultTaskDuration)) assertEquals(100.0, scores[teams[0]]) @@ -129,9 +131,9 @@ class LegacyAvsTaskScorerTest { fun allDifferentImageAllCorrect() { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task5"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000, "task5"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000, "task5") ) val scores = this.scorer.computeScores(submissions, TaskContext("task5", teams, 100_000, defaultTaskDuration)) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) @@ -150,19 +152,19 @@ class LegacyAvsTaskScorerTest { */ //3 out of 4 correct - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[3]), taskStartTime + 4000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task6"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000, "task6"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000, "task6"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[3]), taskStartTime + 4000, "task6"), //1 out of 3 correct - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[4]), taskStartTime + 2000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task6"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[4]), taskStartTime + 2000, "task6"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000, "task6"), //1 out of 2 correct - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[3]), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[3]), taskStartTime + 2000, "task6"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000, "task6"), ) @@ -203,9 +205,9 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task7"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 2000, "task7"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 3000, "task7") ) val scores = this.scorer.computeScores(submissions, TaskContext("task7", teams, 100_000, defaultTaskDuration)) @@ -220,9 +222,9 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 30_000, 40_000), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 50_000, 60_000), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task8"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 30_000, 40_000), taskStartTime + 2000, "task8"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 50_000, 60_000), taskStartTime + 3000, "task8") ) val scores = this.scorer.computeScores(submissions, TaskContext("task8", teams, 100_000, defaultTaskDuration)) @@ -237,9 +239,9 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[3], 50_000, 60_000), taskStartTime + 3000) + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task9"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStartTime + 2000, "task9"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[3], 50_000, 60_000), taskStartTime + 3000, "task9") ) val scores = this.scorer.computeScores(submissions, TaskContext("task9", teams, 100_000, defaultTaskDuration)) @@ -254,17 +256,17 @@ class LegacyAvsTaskScorerTest { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task10"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task10"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task10"), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task10"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task10"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task10"), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task10"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000, "task10"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000, "task10"), ) val scores = this.scorer.computeScores(submissions, TaskContext("task10", teams, 100_000, defaultTaskDuration)) @@ -278,21 +280,21 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task11"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task11"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task11"), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task11"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task11"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task11"), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task11"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000, "task11"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000, "task11"), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 3000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 1000, "task11"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 2000, "task11"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 3000, "task11"), ) val scores = this.scorer.computeScores(submissions, TaskContext("task11", teams, 100_000, defaultTaskDuration)) @@ -309,47 +311,47 @@ class LegacyAvsTaskScorerTest { val submissions = sequenceOf( //team 1 //gets merged to one - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task12"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000, "task12"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000, "task12"), //plus 2 independent correct ones - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000), - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 3_000, 4_000), taskStartTime + 5000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000, "task12"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 3_000, 4_000), taskStartTime + 5000, "task12"), //and an incorrect one directly next to it - ApiSubmission(teams[0], teams[0], "user1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 4_001, 5_000), taskStartTime + 6000), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 4_001, 5_000), taskStartTime + 6000, "task12"), //c = 5, q(c) = 3, i = 1 //team 2 //the center part is missing, so it's counted as two in the quantization - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task12"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000, "task12"), //another correct one, same as team 1 - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000, "task12"), //and two wrong ones - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 6000), - ApiSubmission(teams[1], teams[1], "user2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 6000), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 6000, "task12"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 6000, "task12"), //c = 3, q(c) = 3, i = 2 //team 3 - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task12"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000, "task12"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000, "task12"), //another correct one, same as team 1 - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000, "task12"), //and two wrong ones - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 4000), - ApiSubmission(teams[2], teams[2], "user3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 4000), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 4000, "task12"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 4000, "task12"), //c = 4, q(c) = 2, i = 2 From d2de6ec5bae79ee7055a1465c39081dbcd568bf3 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 20 Feb 2023 11:21:17 +0100 Subject: [PATCH 153/498] WiP Working on reusable UI components --- .../actionable-dynamic-table.component.html | 33 ++++++++ .../actionable-dynamic-table.component.scss | 0 .../actionable-dynamic-table.component.ts | 84 +++++++++++++++++++ .../dynamic-table/dynamic-table.component.ts | 4 +- frontend/src/app/shared/shared.module.ts | 2 + 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html create mode 100644 frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.scss create mode 100644 frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html new file mode 100644 index 000000000..c601c9bb4 --- /dev/null +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -0,0 +1,33 @@ +
+

{{title}}

+
+ +
+ + + + + + + +
{{column.header}} + + + + + + + +
+ {{element[column.property}} +
+
+
diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.scss b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts new file mode 100644 index 000000000..c4a9cc774 --- /dev/null +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts @@ -0,0 +1,84 @@ +import {Component, Input, OnInit, ViewChild} from '@angular/core'; +import {MatTable} from '@angular/material/table'; + +export class ActionableDynamicTableColumnDefinition { + property: string; + type: ActionableDynamicTableColumnType; + header: string; + + action?: ActionableDynamicTableActionType +} + +export enum ActionableDynamicTableColumnType { + TEXT = 'text', + NUMBER = 'number', + ACTION = 'action' +} + +export enum ActionableDynamicTableActionType{ + EDIT= 'edit', + REMOVE = 'remove' +} + +export interface ActionableDynamicTableHandler{ + add(): T; + edit(obj: T); + beforeRemove(obj: T): boolean; +} + +@Component({ + selector: 'app-actionable-dynamic-table', + templateUrl: './actionable-dynamic-table.component.html', + styleUrls: ['./actionable-dynamic-table.component.scss'] +}) +export class ActionableDynamicTable { + + @Input() + public dataSource: Array; + + @Input() + public columnSchema: ActionableDynamicTableColumnDefinition[]; + + @Input() + public displayedColumns: string[]; + + @Input() + public title: string; + + @Input() + public trackByProperty?: string; + + private handler: ActionableDynamicTableHandler; + + @ViewChild('table') + table: MatTable + + constructor() { } + + public setHandler(handler: ActionableDynamicTableHandler){ + this.handler = handler; + } + + onEdit(element: T){ + if(this.handler){ + this.handler.edit(element); + } + } + + onAdd(){ + if(this.handler){ + const newElement = this.handler.add(); + this.dataSource.push(newElement); + } + } + + onRemove(element: T){ + if(this.handler){ + if(this.handler.beforeRemove(element)){ + this.dataSource.splice(this.dataSource.indexOf(element), 1); + } + } + } + + +} diff --git a/frontend/src/app/shared/dynamic-table/dynamic-table.component.ts b/frontend/src/app/shared/dynamic-table/dynamic-table.component.ts index e7b19cdd1..679ccb1ec 100644 --- a/frontend/src/app/shared/dynamic-table/dynamic-table.component.ts +++ b/frontend/src/app/shared/dynamic-table/dynamic-table.component.ts @@ -11,10 +11,10 @@ export interface ColumnDefinition { templateUrl: './dynamic-table.component.html', styleUrls: ['./dynamic-table.component.scss'] }) -export class DynamicTableComponent { +export class DynamicTableComponent { @Input() - public dataSource; + public dataSource: Array; @Input() public columnSchema: ColumnDefinition[]; diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 7feeac511..43d84a993 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -12,6 +12,7 @@ import { ConfirmationDialogComponent } from './confirmation-dialog/confirmation- import { MatDialogModule } from '@angular/material/dialog'; import { DynamicTableComponent } from './dynamic-table/dynamic-table.component'; import {MatTableModule} from '@angular/material/table'; +import { ActionableDynamicTable } from './actionable-dynamic-table/actionable-dynamic-table.component'; @NgModule({ declarations: [ @@ -21,6 +22,7 @@ import {MatTableModule} from '@angular/material/table'; UploadJsonButtonComponent, ConfirmationDialogComponent, DynamicTableComponent, + ActionableDynamicTable, ], exports: [ BackButtonComponent, From 16bfd035d57e813989759c520d4fb3ad33f98886 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 20 Feb 2023 16:22:36 +0100 Subject: [PATCH 154/498] Fixed swagger ui not working --- .../src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt | 3 ++- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt index 3ecb4df37..583fdee3f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt @@ -7,4 +7,5 @@ class ClientSwaggerPlugin : SwaggerPlugin( SwaggerConfiguration().apply { this.documentationPath = "/client-oas" this.uiPath = "/swagger-client" - }) \ No newline at end of file + this.version = "4.10.3" + }) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 6d867338f..41fa627f9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -221,6 +221,7 @@ object RestApi { it.plugins.register( SwaggerPlugin( SwaggerConfiguration().apply { + this.version = "4.10.3" this.documentationPath = "/swagger-docs" this.uiPath = "/swagger-ui" } From 003fe851d89fcad285b34fed5a5f328e9b45452b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 20 Feb 2023 16:40:52 +0100 Subject: [PATCH 155/498] Fixed incorrect path of legacy submission handler in openapi spec --- .../submission/LegacySubmissionHandler.kt | 2 +- doc/oas-client.json | 417 +++++++++------ doc/oas.json | 477 ++++++++++++------ 3 files changed, 607 insertions(+), 289 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index cc57fdb74..417450c82 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -67,7 +67,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v @OpenApi( summary = "Endpoint to accept submissions", - path = "/api/v2/submit", + path = "/api/v1/submit", operationId = OpenApiOperation.AUTO_GENERATE, queryParams = [ OpenApiParam( diff --git a/doc/oas-client.json b/doc/oas-client.json index b572b956b..5ada8141c 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -10,6 +10,153 @@ "version" : "1.0" }, "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/client/evaluation/currentTask/{evaluationId}" : { "get" : { "tags" : [ "Evaluation Client" ], @@ -26,6 +173,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -68,6 +218,9 @@ "summary" : "Lists an overview of all evaluation runs visible to the current client.", "operationId" : "getApiV2ClientEvaluationList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -113,6 +266,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -175,6 +331,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -300,6 +459,9 @@ "format" : "int32" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -362,6 +524,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -496,6 +661,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -558,6 +726,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -695,6 +866,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -747,6 +921,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -799,6 +976,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -864,6 +1044,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -916,6 +1099,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -979,6 +1165,9 @@ "format" : "int32" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1031,6 +1220,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1083,6 +1275,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1158,6 +1353,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1210,6 +1408,9 @@ "summary" : "Lists an overview of all evaluations visible to the current user.", "operationId" : "getApiV2EvaluationInfoList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1245,6 +1446,9 @@ "summary" : "Lists an overview of all evaluation visible to the current user.", "operationId" : "getApiV2EvaluationStateList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1300,6 +1504,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1362,6 +1569,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1516,6 +1726,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1598,6 +1811,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1735,6 +1951,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1797,6 +2016,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1863,6 +2085,9 @@ "format" : "int64" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1938,6 +2163,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2000,6 +2228,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2072,6 +2303,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2137,6 +2371,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2385,6 +2622,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2459,6 +2699,9 @@ "summary" : "Returns an overview of the server properties.", "operationId" : "getApiV2StatusInfo", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2491,157 +2734,16 @@ "summary" : "Returns the current time on the server.", "operationId" : "getApiV2StatusTime", "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } + "requestBody" : { + "required" : false }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV2Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/CurrentTime" } } } @@ -2719,6 +2821,9 @@ "summary" : "Lists an overview of all available competitions with basic information about their content.", "operationId" : "getApiV2TemplateList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2764,6 +2869,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2824,6 +2932,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2956,6 +3067,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3021,6 +3135,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3126,6 +3243,9 @@ "summary" : "Get information about the current user.", "operationId" : "getApiV2User", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3168,6 +3288,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3210,6 +3333,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3330,6 +3456,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", diff --git a/doc/oas.json b/doc/oas.json index 9801d3651..dc6bcced1 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -10,12 +10,162 @@ "version" : "2.0.0" }, "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/audit/info" : { "get" : { "tags" : [ "Audit" ], "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", "operationId" : "getApiV2AuditInfo", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "The audit log info.", @@ -70,6 +220,9 @@ "format" : "int32" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "The audit logs", @@ -127,6 +280,9 @@ "format" : "int64" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "The audit logs", @@ -172,6 +328,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -214,6 +373,9 @@ "summary" : "Lists an overview of all evaluation runs visible to the current client.", "operationId" : "getApiV2ClientEvaluationList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -351,6 +513,9 @@ "summary" : "Lists all available media collections with basic information about their content.", "operationId" : "getApiV2CollectionList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -396,6 +561,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -446,6 +614,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -498,6 +669,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -648,6 +822,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -713,6 +890,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -775,6 +955,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -837,6 +1020,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -962,6 +1148,9 @@ "format" : "int32" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1024,6 +1213,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1158,6 +1350,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1220,6 +1415,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1357,6 +1555,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1409,6 +1610,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1461,6 +1665,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1526,6 +1733,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1578,6 +1788,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1641,6 +1854,9 @@ "format" : "int32" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1693,6 +1909,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1745,6 +1964,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1820,6 +2042,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1872,6 +2097,9 @@ "summary" : "Lists an overview of all evaluations visible to the current user.", "operationId" : "getApiV2EvaluationInfoList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1907,6 +2135,9 @@ "summary" : "Lists an overview of all evaluation visible to the current user.", "operationId" : "getApiV2EvaluationStateList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1962,6 +2193,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2024,6 +2258,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2178,6 +2415,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2260,6 +2500,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2397,6 +2640,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2462,6 +2708,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2524,6 +2773,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2590,6 +2842,9 @@ "format" : "int64" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2665,6 +2920,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2727,6 +2985,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2799,6 +3060,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2864,6 +3128,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2936,6 +3203,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3177,6 +3447,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3261,6 +3534,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3323,6 +3599,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3427,6 +3706,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3482,6 +3764,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3554,6 +3839,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3626,6 +3914,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3691,6 +3982,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3746,6 +4040,9 @@ "summary" : "Returns an overview of the server properties.", "operationId" : "getApiV2StatusInfo", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3778,157 +4075,16 @@ "summary" : "Returns the current time on the server.", "operationId" : "getApiV2StatusTime", "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } + "requestBody" : { + "required" : false }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV2Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "$ref" : "#/components/schemas/CurrentTime" } } } @@ -4006,6 +4162,9 @@ "summary" : "Lists an overview of all available competitions with basic information about their content.", "operationId" : "getApiV2TemplateList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4051,6 +4210,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4111,6 +4273,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4243,6 +4408,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4308,6 +4476,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4413,6 +4584,9 @@ "summary" : "Get information about the current user.", "operationId" : "getApiV2User", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4445,6 +4619,9 @@ "summary" : "Lists all available users.", "operationId" : "getApiV2UserList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4480,6 +4657,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4512,6 +4692,9 @@ "summary" : "Get details of all current user sessions", "operationId" : "getApiV2UserSessionActiveList", "parameters" : [ ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4557,6 +4740,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4677,6 +4863,9 @@ "type" : "string" } } ], + "requestBody" : { + "required" : false + }, "responses" : { "200" : { "description" : "OK", From 207ec55a735fdc966f66ccaff339adac3c6f6ff2 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 20 Feb 2023 16:47:33 +0100 Subject: [PATCH 156/498] Added closing bracket which was found by @lucaro --- .../src/app/shared/dynamic-table/dynamic-table.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/shared/dynamic-table/dynamic-table.component.html b/frontend/src/app/shared/dynamic-table/dynamic-table.component.html index edfa3fe65..ee97d576f 100644 --- a/frontend/src/app/shared/dynamic-table/dynamic-table.component.html +++ b/frontend/src/app/shared/dynamic-table/dynamic-table.component.html @@ -2,7 +2,7 @@ {{column.header}} - {{element[column.property}} + {{element[column.property]}} From 523b3d9879a89b18bf45642a62e59889f6b2c79a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 21 Feb 2023 17:16:31 +0100 Subject: [PATCH 157/498] Regenerated OpenAPI spec after fix in generator --- doc/oas-client.json | 129 ------------------------------ doc/oas.json | 189 -------------------------------------------- 2 files changed, 318 deletions(-) diff --git a/doc/oas-client.json b/doc/oas-client.json index 5ada8141c..9dc05d610 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -88,9 +88,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -173,9 +170,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -218,9 +212,6 @@ "summary" : "Lists an overview of all evaluation runs visible to the current client.", "operationId" : "getApiV2ClientEvaluationList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -266,9 +257,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -331,9 +319,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -459,9 +444,6 @@ "format" : "int32" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -524,9 +506,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -661,9 +640,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -726,9 +702,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -866,9 +839,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -921,9 +891,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -976,9 +943,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1044,9 +1008,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1099,9 +1060,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1165,9 +1123,6 @@ "format" : "int32" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1220,9 +1175,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1275,9 +1227,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1353,9 +1302,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1408,9 +1354,6 @@ "summary" : "Lists an overview of all evaluations visible to the current user.", "operationId" : "getApiV2EvaluationInfoList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1446,9 +1389,6 @@ "summary" : "Lists an overview of all evaluation visible to the current user.", "operationId" : "getApiV2EvaluationStateList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1504,9 +1444,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1569,9 +1506,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1726,9 +1660,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1811,9 +1742,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1951,9 +1879,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2016,9 +1941,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2085,9 +2007,6 @@ "format" : "int64" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2163,9 +2082,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2228,9 +2144,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2303,9 +2216,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2371,9 +2281,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2622,9 +2529,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2699,9 +2603,6 @@ "summary" : "Returns an overview of the server properties.", "operationId" : "getApiV2StatusInfo", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2734,9 +2635,6 @@ "summary" : "Returns the current time on the server.", "operationId" : "getApiV2StatusTime", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2821,9 +2719,6 @@ "summary" : "Lists an overview of all available competitions with basic information about their content.", "operationId" : "getApiV2TemplateList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2869,9 +2764,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2932,9 +2824,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3067,9 +2956,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3135,9 +3021,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3243,9 +3126,6 @@ "summary" : "Get information about the current user.", "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3288,9 +3168,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3333,9 +3210,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3456,9 +3330,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", diff --git a/doc/oas.json b/doc/oas.json index dc6bcced1..8ed92257f 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -88,9 +88,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -163,9 +160,6 @@ "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", "operationId" : "getApiV2AuditInfo", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "The audit log info.", @@ -220,9 +214,6 @@ "format" : "int32" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "The audit logs", @@ -280,9 +271,6 @@ "format" : "int64" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "The audit logs", @@ -328,9 +316,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -373,9 +358,6 @@ "summary" : "Lists an overview of all evaluation runs visible to the current client.", "operationId" : "getApiV2ClientEvaluationList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -513,9 +495,6 @@ "summary" : "Lists all available media collections with basic information about their content.", "operationId" : "getApiV2CollectionList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -561,9 +540,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -614,9 +590,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -669,9 +642,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -822,9 +792,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -890,9 +857,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -955,9 +919,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1020,9 +981,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1148,9 +1106,6 @@ "format" : "int32" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1213,9 +1168,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1350,9 +1302,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1415,9 +1364,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1555,9 +1501,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1610,9 +1553,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1665,9 +1605,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1733,9 +1670,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1788,9 +1722,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1854,9 +1785,6 @@ "format" : "int32" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1909,9 +1837,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -1964,9 +1889,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2042,9 +1964,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2097,9 +2016,6 @@ "summary" : "Lists an overview of all evaluations visible to the current user.", "operationId" : "getApiV2EvaluationInfoList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2135,9 +2051,6 @@ "summary" : "Lists an overview of all evaluation visible to the current user.", "operationId" : "getApiV2EvaluationStateList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2193,9 +2106,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2258,9 +2168,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2415,9 +2322,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2500,9 +2404,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2640,9 +2541,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2708,9 +2606,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2773,9 +2668,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2842,9 +2734,6 @@ "format" : "int64" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2920,9 +2809,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -2985,9 +2871,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3060,9 +2943,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3128,9 +3008,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3203,9 +3080,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3447,9 +3321,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3534,9 +3405,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3599,9 +3467,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3706,9 +3571,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3764,9 +3626,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3839,9 +3698,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3914,9 +3770,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3982,9 +3835,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4040,9 +3890,6 @@ "summary" : "Returns an overview of the server properties.", "operationId" : "getApiV2StatusInfo", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4075,9 +3922,6 @@ "summary" : "Returns the current time on the server.", "operationId" : "getApiV2StatusTime", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4162,9 +4006,6 @@ "summary" : "Lists an overview of all available competitions with basic information about their content.", "operationId" : "getApiV2TemplateList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4210,9 +4051,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4273,9 +4111,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4408,9 +4243,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4476,9 +4308,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4584,9 +4413,6 @@ "summary" : "Get information about the current user.", "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4619,9 +4445,6 @@ "summary" : "Lists all available users.", "operationId" : "getApiV2UserList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4657,9 +4480,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4692,9 +4512,6 @@ "summary" : "Get details of all current user sessions", "operationId" : "getApiV2UserSessionActiveList", "parameters" : [ ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4740,9 +4557,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4863,9 +4677,6 @@ "type" : "string" } } ], - "requestBody" : { - "required" : false - }, "responses" : { "200" : { "description" : "OK", From fe2cb2d0912136fb703b68101216e315ce35ce58 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 11:22:12 +0100 Subject: [PATCH 158/498] Fixed submission mechanism for InteractiveSynchronousRunManager. --- .../handler/evaluation/admin/CreateEvaluationHandler.kt | 3 --- .../dev/dres/api/rest/types/evaluation/ApiAnswer.kt | 1 - .../dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt | 9 +++++---- .../dev/dres/api/rest/types/evaluation/ApiSubmission.kt | 4 ++-- .../src/main/kotlin/dev/dres/data/model/run/DbTask.kt | 8 ++++---- .../dev/dres/data/model/submissions/DbAnswerSet.kt | 8 ++++---- .../dev/dres/run/InteractiveSynchronousRunManager.kt | 2 +- .../dres/run/validation/MediaItemsSubmissionValidator.kt | 3 +-- 8 files changed, 17 insertions(+), 21 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 0ed8053fd..aa549230d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -108,9 +108,6 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs } /* Try to flush change prior to scheduling it. */ - if (!tx.flush()) { - throw ErrorStatusException(500, "Failed to store new evaluation.", ctx) - } RunExecutor.schedule(when (message.type) { ApiEvaluationType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ ApiEvaluationType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt index 910bd2b84..c31b46aec 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt @@ -15,7 +15,6 @@ data class ApiAnswer( override fun toDb(): DbAnswer { return DbAnswer.new { this.type = this@ApiAnswer.type.toDb() - } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index c67830fd1..29f7a4653 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -8,6 +8,7 @@ import dev.dres.data.model.submissions.* import kotlinx.dnq.query.addAll import kotlinx.dnq.query.filter import kotlinx.dnq.query.first +import kotlinx.dnq.query.firstOrNull /** * The RESTful API equivalent for the type of [ApiAnswerSet]. @@ -26,9 +27,9 @@ data class ApiAnswerSet( override lateinit var submission: ApiSubmission internal set - override fun task(): Task { - TODO("Not yet implemented") - } + override fun task(): Task = DbTask.filter { + it.id eq this@ApiAnswerSet.taskId + }.firstOrNull() ?: throw IllegalStateException("The specified task ${this.taskId} does not exist in the database. This is a programmer's error!") override fun answers(): Sequence = answers.asSequence() override fun status(): VerdictStatus = VerdictStatus.fromApi(this.status) @@ -40,7 +41,7 @@ data class ApiAnswerSet( override fun toDb(): DbAnswerSet { return DbAnswerSet.new { this.status = this@ApiAnswerSet.status.toDb() - this.task = DbTask.filter { taskId eq this@ApiAnswerSet.taskId }.first() + this.task = DbTask.filter { it.id eq this@ApiAnswerSet.taskId }.first() this.answers.addAll( this@ApiAnswerSet.answers.map { it.toDb() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index ab41ec371..695144c6b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -45,8 +45,8 @@ data class ApiSubmission( return DbSubmission.new { this.id = this@ApiSubmission.submissionId this.timestamp = this@ApiSubmission.timestamp - this.team = DbTeam.filter { teamId eq this@ApiSubmission.teamId }.first() - this.user = DbUser.filter { id eq this@ApiSubmission.memberId }.first() + this.team = DbTeam.filter { it.id eq this@ApiSubmission.teamId }.first() + this.user = DbUser.filter { it.id eq this@ApiSubmission.memberId }.first() this.answerSets.addAll( this@ApiSubmission.answers.map { it.toDb() } ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index 38fda8efd..5486cc642 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -27,6 +27,9 @@ class DbTask(entity: Entity) : PersistentEntity(entity), Task { get() = this.id set(value) { this.id = value } + /** The [DbEvaluation] this [DbTask] belongs to. */ + override var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) + /** Timestamp of when this [DbEvaluation] started. */ override var started by xdNullableLongProp() @@ -39,11 +42,8 @@ class DbTask(entity: Entity) : PersistentEntity(entity), Task { /** Link to a [DbTeam] this [DbTask] was created for. Can be NULL!*/ var team by xdLink0_1(DbTeam) - /** The [DbEvaluation] this [DbTask] belongs to. */ - override var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) - /** List of [DbSubmission]s received by this [DbTask]. */ - val answerSets by xdChildren0_N(DbAnswerSet::task) + val answerSets by xdLink0_N(DbAnswerSet::task) override fun answerSets(): Sequence = answerSets.asSequence() //TODO can this be sorted by submission timestamp? diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 896d8de1f..c76f7543d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -19,14 +19,14 @@ import kotlinx.dnq.query.asSequence class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { companion object : XdNaturalEntityType() - /** The [DbVerdictStatus] of this [DbAnswerSet]. */ - var status: DbVerdictStatus by xdLink1(DbVerdictStatus) - /** The [DbSubmission] this [DbAnswerSet] belongs to. */ override var submission: DbSubmission by xdParent(DbSubmission::answerSets) + /** The [DbVerdictStatus] of this [DbAnswerSet]. */ + var status: DbVerdictStatus by xdLink1(DbVerdictStatus) + /** The [DbTask] this [DbAnswerSet] belongs to. */ - var task: DbTask by xdParent(DbTask::answerSets) + var task: DbTask by xdLink1(DbTask::answerSets) override fun task(): Task = task override val taskId: TaskId diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 2c79abab6..5e3246bed 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -454,7 +454,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch task.postSubmission(submission) /** Checks for the presence of the [DbTaskOption.PROLONG_ON_SUBMISSION] and applies it. */ - if (task.template.taskGroup.type.options.filter { it eq DbTaskOption.PROLONG_ON_SUBMISSION }.any()) { + if (task.template.taskGroup.type.options.contains(DbTaskOption.PROLONG_ON_SUBMISSION)) { this.prolongOnSubmit(context, submission) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index f8046fbc1..dedea931e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -26,8 +26,7 @@ class MediaItemsSubmissionValidator(private val items : Set) : Subm */ override fun validate(submission: Submission) { submission.answerSets().forEach { answerSet -> - - if (answerSet.answers().any { it.item == null || it.item !in this.items} ) { + if (answerSet.answers().any { it.item == null || it.item !in this.items} ) { /* TODO: This doesn't work cause we're comparing DbMediaItem with ApiMediaItem. */ answerSet.status(VerdictStatus.WRONG) } else { answerSet.status(VerdictStatus.CORRECT) From e38ede7ff874523b6a7502a13823dc7245abaeb2 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 11:22:32 +0100 Subject: [PATCH 159/498] Fixed typo --- .../actionable-dynamic-table.component.html | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index c601c9bb4..d9f68b123 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -10,20 +10,16 @@

{{title}}

- -
- {{element[column.property}} + {{ element[column.property] }}
From f5d421468c7e26eda6806503cdf1653b47529c5e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 11:22:51 +0100 Subject: [PATCH 160/498] Updated dependencies. --- frontend/package-lock.json | 18953 +++++++---------------------------- frontend/package.json | 2 +- frontend/yarn.lock | 4367 ++++---- 3 files changed, 6185 insertions(+), 17137 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dc7d5901d..9ef0094fd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,7 +1,7 @@ { "name": "dres-frontend", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -22,7 +22,7 @@ "@angular/platform-browser-dynamic": "13.2.x", "@angular/router": "13.2.x", "angularx-qrcode": "13.0.x", - "apexcharts": "3.33.x", + "apexcharts": "3.37.x", "ng-apexcharts": "1.7.x", "ngx-color-picker": "12.0.x", "rxjs": "6.5.x", @@ -71,24 +71,25 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.1.tgz", - "integrity": "sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", + "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/resolve-uri": "^3.0.3", + "sourcemap-codec": "1.4.8" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@angular-devkit/architect": { - "version": "0.1302.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.3.tgz", - "integrity": "sha512-0m8jMKrFfIqsYt33zTUwSmyekyfuS67hna08RQ6USjzWQSE3z4S8ulCUARSjM6AzdMblX+whfy56nJUpT17NSA==", + "version": "0.1302.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.6.tgz", + "integrity": "sha512-NztzorUMfwJeRaT7SY00Y8WSqc2lQYuF11yNoyEm7Dae3V7VZ28rW2Z9RwibP27rYQL0RjSMaz2wKITHX2vOAw==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.2.3", + "@angular-devkit/core": "13.2.6", "rxjs": "6.6.7" }, "engines": { @@ -116,15 +117,15 @@ "dev": true }, "node_modules/@angular-devkit/build-angular": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.2.3.tgz", - "integrity": "sha512-cZ2gRcMRgW3t1WCeP+2D/wmr2M+BR/RICAh0wL9irIdypWAzIFt3Z2+2R/HmgAAxoEkdUMIfB9AnkYmwRVgFeA==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.2.6.tgz", + "integrity": "sha512-Y2ojy6xbZ0kwScppcutLHBP8eW0qNOjburTISSBU/L5l/9FOeZ1E7yAreKuVu/qibZiLbSJfAhk+SLwhRHFSSQ==", "dev": true, "dependencies": { "@ampproject/remapping": "1.1.1", - "@angular-devkit/architect": "0.1302.3", - "@angular-devkit/build-webpack": "0.1302.3", - "@angular-devkit/core": "13.2.3", + "@angular-devkit/architect": "0.1302.6", + "@angular-devkit/build-webpack": "0.1302.6", + "@angular-devkit/core": "13.2.6", "@babel/core": "7.16.12", "@babel/generator": "7.16.8", "@babel/helper-annotate-as-pure": "7.16.7", @@ -135,7 +136,7 @@ "@babel/runtime": "7.16.7", "@babel/template": "7.16.7", "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.2.3", + "@ngtools/webpack": "13.2.6", "ansi-colors": "4.1.1", "babel-loader": "8.2.3", "babel-plugin-istanbul": "6.1.1", @@ -146,7 +147,7 @@ "core-js": "3.20.3", "critters": "0.0.16", "css-loader": "6.5.1", - "esbuild-wasm": "0.14.14", + "esbuild-wasm": "0.14.22", "glob": "7.2.0", "https-proxy-agent": "5.0.0", "inquirer": "8.2.0", @@ -154,7 +155,7 @@ "karma-source-map-support": "1.4.0", "less": "4.1.2", "less-loader": "10.2.0", - "license-webpack-plugin": "4.0.1", + "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.0", "mini-css-extract-plugin": "2.5.3", "minimatch": "3.0.4", @@ -176,7 +177,7 @@ "source-map-support": "0.5.21", "stylus": "0.56.0", "stylus-loader": "6.2.0", - "terser": "5.10.0", + "terser": "5.11.0", "text-table": "0.2.0", "tree-kill": "1.2.2", "tslib": "2.3.1", @@ -192,7 +193,7 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.14.14" + "esbuild": "0.14.22" }, "peerDependencies": { "@angular/compiler-cli": "^13.0.0", @@ -225,84 +226,6 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", - "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", - "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -321,34 +244,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -356,12 +251,12 @@ "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1302.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1302.3.tgz", - "integrity": "sha512-+JYH1lWU0UOjaWYxpoR2VLsdcb6nG9Gv+M1gH+kT0r2sAKOFaHnrksbOvca3EhDoaMa2b9LSGEE0OcSHWnN+eQ==", + "version": "0.1302.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1302.6.tgz", + "integrity": "sha512-TYEh2n9tPe932rEIgdiSpojOqtDppW2jzb/empVqCkLF7WUZsXKvTanttZC34L6R2VD6SAGWhb6JDg75ghUVYA==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1302.3", + "@angular-devkit/architect": "0.1302.6", "rxjs": "6.6.7" }, "engines": { @@ -393,9 +288,9 @@ "dev": true }, "node_modules/@angular-devkit/core": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz", - "integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.6.tgz", + "integrity": "sha512-8h2mWdBTN/dYwZuzKMg2IODlOWMdbJcpQG4XVrkk9ejCPP+3aX5Aa3glCe/voN6eBNiRfs8YDM0jxmpN2aWVtg==", "dev": true, "dependencies": { "ajv": "8.9.0", @@ -438,12 +333,12 @@ "dev": true }, "node_modules/@angular-devkit/schematics": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.3.tgz", - "integrity": "sha512-+dyC4iKV0huvpjiuz4uyjLNK3FsCIp/Ghv5lXvhG6yok/dCAubsJItJOxi6G16aVCzG/E9zbsDfm9fNMyVOkgQ==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.6.tgz", + "integrity": "sha512-mPgSqdnZRuPSMeUA+T+mwVCrq2yhXpcYm1/Rjbhy09CyHs4wSrFv21WHCrE6shlvXpcmwr0n+I0DIeagAPmjUA==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.2.3", + "@angular-devkit/core": "13.2.6", "jsonc-parser": "3.0.0", "magic-string": "0.25.7", "ora": "5.4.1", @@ -522,25 +417,6 @@ "typescript": "*" } }, - "node_modules/@angular-eslint/eslint-plugin-template/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template/node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, "node_modules/@angular-eslint/schematics": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", @@ -557,18 +433,6 @@ "@angular/cli": ">= 13.0.0 < 14.0.0" } }, - "node_modules/@angular-eslint/schematics/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, "node_modules/@angular-eslint/template-parser": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", @@ -618,9 +482,9 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@angular/animations": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.2.2.tgz", - "integrity": "sha512-qX8LAMuCJaueHBVyuwKtqunx96G0Dr26k7y5Z03VTcscYst4Ib4V2d4i5dwn3HS82DehFdO86cm3Hi2PqE/qww==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.2.7.tgz", + "integrity": "sha512-FthGqRPQ1AOcOx/NIW65xeFYkQZJ7PpXcX59Kt+qkoUzngAQEY+UUpOteG52tmL0iZSVwOCjtxRFi9w4heVgEg==", "dependencies": { "tslib": "^2.3.0" }, @@ -628,18 +492,18 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "13.2.2" + "@angular/core": "13.2.7" } }, "node_modules/@angular/animations/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/cdk": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.2.2.tgz", - "integrity": "sha512-cT5DIaz+NI9IGb3X61Wh26+L6zdRcOXT1BP37iRbK2Qa2qM8/0VNeK6hrBBIblyoHKR/WUmRlS8XYf6mmArpZw==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.2.6.tgz", + "integrity": "sha512-epuXmaHqfukwPsYvIksbuHLXDtb6GALV2Vgv6W2asj4TorD584CeQTs0EcdPGmCzhGUYI8U8QV63WOxu9YFcNA==", "dependencies": { "tslib": "^2.3.0" }, @@ -653,21 +517,21 @@ } }, "node_modules/@angular/cdk/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/cli": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.3.tgz", - "integrity": "sha512-QsakxpdQuO67u4fQNuOASqabYUO9gJb/5CpUGpWbuBzru0/9CMEF1CtXoF4EoDiwa5sJMirz3SJMKhtzFlv1cQ==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.6.tgz", + "integrity": "sha512-xIjEaQI5sWemXXc7GXLm4u9UL5sjtrQL/y1PJvvk/Jsa8+kIT+MutOfZfC7zcdAh9fqHd8mokH3guFV8BJdFxA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@angular-devkit/architect": "0.1302.3", - "@angular-devkit/core": "13.2.3", - "@angular-devkit/schematics": "13.2.3", - "@schematics/angular": "13.2.3", + "@angular-devkit/architect": "0.1302.6", + "@angular-devkit/core": "13.2.6", + "@angular-devkit/schematics": "13.2.6", + "@schematics/angular": "13.2.6", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.3.3", @@ -694,9 +558,9 @@ } }, "node_modules/@angular/common": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.2.2.tgz", - "integrity": "sha512-56C/bheNLKtTCyQUZCiYtKbBIZN9jj6rjFILPtJCGls3cBCxp7t9tIdoLiQG/wVQRmaxdj1ioLT+sCWz7mLtQw==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.2.7.tgz", + "integrity": "sha512-gSkv9aMz5q2ynIqSwgp5HEVVwlmpMYGVZFNZnEnezGY96Hza0eXlb/AYdqO7S3VQVvx+FXpvXP/eq/SsCw7rFA==", "dependencies": { "tslib": "^2.3.0" }, @@ -704,19 +568,19 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "13.2.2", + "@angular/core": "13.2.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/common/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/compiler": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.2.2.tgz", - "integrity": "sha512-XXQtB0/e7pR2LPrHmpEiTU72SX4xxHGy91vYWIj1JCjSn0fYF7vtHzSJPXDvkbnkNow/PXXzJJYaU1ctdMZPcA==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.2.7.tgz", + "integrity": "sha512-auRMo+k+xCQmIBkZ5UgkAAmhbpcoOUWQrJN2PQnPl88DPquui3tXC4R6RANpWCu59oT8m29FQMviHcN3ZTFl6Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -725,16 +589,16 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.2.tgz", - "integrity": "sha512-tuOIcEEKVIht+mKrj0rtX3I8gc+ByPjzpCZhFQRggxM6xbKJIToO1zERbEGKrZ+sUJ6BB5KLvscDy+Pddy3b8w==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.7.tgz", + "integrity": "sha512-EZFWHyC2PO3ECEgX/WTMaTEvH4isvtw8E/l+48YHvINeHoxPGF7Or8qEeu/lvGlXMzBd89QH1ohutfz93vNz+g==", "dev": true, "dependencies": { - "@babel/core": "^7.8.6", + "@babel/core": "^7.17.2", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", "dependency-graph": "^0.11.0", - "magic-string": "^0.25.0", + "magic-string": "^0.26.0", "reflect-metadata": "^0.1.2", "semver": "^7.0.0", "sourcemap-codec": "^1.4.8", @@ -750,25 +614,132 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/compiler": "13.2.2", + "@angular/compiler": "13.2.7", "typescript": ">=4.4.2 <4.6" } }, + "node_modules/@angular/compiler-cli/node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.0", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/magic-string": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", + "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@angular/compiler-cli/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "node_modules/@angular/compiler/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/core": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.2.2.tgz", - "integrity": "sha512-zpctw0BxIVOsRFnckchK15SD1L8tzhf5GzwIDaM6+VylDQj1uYkm8mvAjJTQZyUuApomoFet2Rfj7XQPV+cNSQ==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.2.7.tgz", + "integrity": "sha512-6J6C2ymIy+cZudock25BLEZFckhqFfXOSZw5YBIoAJuntV4hckLNfRTn/dxn5qmWF/Vw60NKdhV367YYejz6Gg==", "dependencies": { "tslib": "^2.3.0" }, @@ -781,9 +752,9 @@ } }, "node_modules/@angular/core/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/flex-layout": { "version": "13.0.0-beta.38", @@ -802,14 +773,14 @@ } }, "node_modules/@angular/flex-layout/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/forms": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.2.2.tgz", - "integrity": "sha512-T61W4Ay9X9qhxjc6lLqpNFeHrGKwg2mqdsZ3zIm/c7oKo37mgl9TB5kkrtnS+205r3N2hF4ICnGFZ4a/egUP/g==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.2.7.tgz", + "integrity": "sha512-Did5ShmHTu52cljmNtMxUBUGEYHJ/FV5ZpKhbI7sd/VSFhXp9KWYUbfma0m7+CUJMGmpt6bmDaN0G2WS8Es1LQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -817,36 +788,36 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "13.2.2", - "@angular/core": "13.2.2", - "@angular/platform-browser": "13.2.2", + "@angular/common": "13.2.7", + "@angular/core": "13.2.7", + "@angular/platform-browser": "13.2.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/forms/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/language-service": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.2.2.tgz", - "integrity": "sha512-2P5+wRsbHgpI2rVeFwnsLWxyntUiw8kG9Tqh5BkVDqtQovbYtzFiaMkf5TFz/g938JBBgeRQzvXr1kQhEidAWQ==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.2.7.tgz", + "integrity": "sha512-hkZ1du/XxG6efri3FSOPc7JBwWHgHUAwI8dAUC+xFL3ZcVhFMPwrouMo8y4w/P5RctCO0ls4dDxThn/uDEZBQA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.15.0 || >=16.10.0" } }, "node_modules/@angular/material": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-13.2.2.tgz", - "integrity": "sha512-YAjPp2+/wuEOPfkAxdRVdbWHiK4P3DgMZa9qP/NizN2lTXNrftEfD09ZlPIFMZRnnExezJ2LnO7eyELpc1VSKg==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-13.2.6.tgz", + "integrity": "sha512-/h5wa/tXE0DMIIEQX+rozFkUWHYUWg1Xf1R2tXUFLslLQ0KRCGyNo225Sv/1wrxXHxfrML787lA9ex4p90Feqw==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^13.0.0 || ^14.0.0-0", - "@angular/cdk": "13.2.2", + "@angular/cdk": "13.2.6", "@angular/common": "^13.0.0 || ^14.0.0-0", "@angular/core": "^13.0.0 || ^14.0.0-0", "@angular/forms": "^13.0.0 || ^14.0.0-0", @@ -855,14 +826,14 @@ } }, "node_modules/@angular/material/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/platform-browser": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.2.2.tgz", - "integrity": "sha512-M7gWC8fFCPc/CRcHCzqe/j7WzwAUMeKt9vwlK633XnesHBoqZdYgbb3YHHc6WPVU0YI09Nb/Hm5sezEKmjUmPg==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.2.7.tgz", + "integrity": "sha512-3rpeS2n+mfey9FqJg/NQKPiyHC47vgldWXmuz5FmOCHrOY54AaFfoiwQcdxzh6Lxx/CUVm0TlOS8S/xI9iEqXw==", "dependencies": { "tslib": "^2.3.0" }, @@ -870,9 +841,9 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/animations": "13.2.2", - "@angular/common": "13.2.2", - "@angular/core": "13.2.2" + "@angular/animations": "13.2.7", + "@angular/common": "13.2.7", + "@angular/core": "13.2.7" }, "peerDependenciesMeta": { "@angular/animations": { @@ -881,9 +852,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.2.2.tgz", - "integrity": "sha512-lj6xwat0StLp+ROFqXU62upwHQhlxaQi0djhrS+DGKUK0Xu9bkBeaSCfBFgS78jPm1SwL8Xztu9/vuDAHLRrqw==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.2.7.tgz", + "integrity": "sha512-3tKiUohQ8wl4hp1zYLKvMJ7GVYpg2K5dRrihtUKkJk8xUv3iuTUI0wbNCrUDZkrWc0GMhnQNXwE22gd+hKjfKg==", "dependencies": { "tslib": "^2.3.0" }, @@ -891,26 +862,26 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "13.2.2", - "@angular/compiler": "13.2.2", - "@angular/core": "13.2.2", - "@angular/platform-browser": "13.2.2" + "@angular/common": "13.2.7", + "@angular/compiler": "13.2.7", + "@angular/core": "13.2.7", + "@angular/platform-browser": "13.2.7" } }, "node_modules/@angular/platform-browser-dynamic/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/platform-browser/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@angular/router": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.2.2.tgz", - "integrity": "sha512-dt2b9/kGJAkmOqUmUD3aKlp4pGpdqLwB0zmhUYF3ktNEcQaPf4ZjWT/4jhy09gFL+TKOHG5OQW9GxBbhWI4bSg==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.2.7.tgz", + "integrity": "sha512-VzEFKyUE8CR23IbmAjmcSFY6pa4NsjaaTqT4mDYhzFeYc7R0s58Ow9d4Fy+0sWX6rzys01rcVNCg+ifJAnwYZA==", "dependencies": { "tslib": "^2.3.0" }, @@ -918,16 +889,16 @@ "node": "^12.20.0 || ^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "13.2.2", - "@angular/core": "13.2.2", - "@angular/platform-browser": "13.2.2", + "@angular/common": "13.2.7", + "@angular/core": "13.2.7", + "@angular/platform-browser": "13.2.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/router/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/@assemblyscript/loader": { "version": "0.10.1", @@ -936,47 +907,47 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", - "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", - "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.0.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", + "@babel/generator": "^7.16.8", "@babel/helper-compilation-targets": "^7.16.7", "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.17.2", - "@babel/parser": "^7.17.0", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0" + "semver": "^6.3.0", + "source-map": "^0.5.0" }, "engines": { "node": ">=6.9.0" @@ -995,13 +966,22 @@ "semver": "bin/semver.js" } }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0", + "@babel/types": "^7.16.8", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -1031,27 +1011,28 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "dev": true, "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", "semver": "^6.3.0" }, "engines": { @@ -1071,18 +1052,19 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.17.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz", - "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1091,14 +1073,26 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", - "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", + "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.3.1" }, "engines": { "node": ">=6.9.0" @@ -1107,16 +1101,26 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", - "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2", @@ -1136,251 +1140,331 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", - "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.0.tgz", + "integrity": "sha512-eD/JQ21IG2i1FraJnTMbUarAUkA7G988ofehG5MDCRXaUU91rEBJuCeSoou2Sk1y4RbLYXzqEg1QLwEmRU4qcQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", - "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -1389,9 +1473,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz", + "integrity": "sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1401,12 +1485,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1416,14 +1500,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -1450,13 +1534,13 @@ } }, "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1466,13 +1550,13 @@ } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", - "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -1483,12 +1567,12 @@ } }, "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1499,12 +1583,12 @@ } }, "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1515,12 +1599,12 @@ } }, "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1531,12 +1615,12 @@ } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1547,12 +1631,12 @@ } }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -1563,12 +1647,12 @@ } }, "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -1579,16 +1663,16 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", - "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" + "@babel/plugin-transform-parameters": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -1598,12 +1682,12 @@ } }, "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -1614,13 +1698,13 @@ } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -1631,13 +1715,13 @@ } }, "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", - "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.10", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1647,14 +1731,14 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1664,14 +1748,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-proposal-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=4" @@ -1858,12 +1954,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1890,12 +1986,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1905,12 +2001,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1920,18 +2016,19 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" }, "engines": { @@ -1941,13 +2038,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -1956,13 +2066,27 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", - "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1972,13 +2096,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1988,12 +2112,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2003,13 +2127,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2019,12 +2143,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -2034,14 +2158,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2051,12 +2175,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2066,12 +2190,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2081,14 +2205,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -2098,15 +2221,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", + "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -2116,16 +2238,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -2135,13 +2256,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2151,12 +2272,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -2166,12 +2288,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2181,13 +2303,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2197,12 +2319,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -2212,12 +2334,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2227,12 +2349,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "dev": true, "dependencies": { - "regenerator-transform": "^0.14.2" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" @@ -2242,12 +2365,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2286,12 +2409,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2301,13 +2424,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -2317,12 +2440,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2332,12 +2455,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2347,12 +2470,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2362,12 +2485,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2377,13 +2500,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -2505,6 +2628,12 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, "node_modules/@babel/runtime": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", @@ -2518,18 +2647,24 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.0.tgz", - "integrity": "sha512-G5FaGZOWORq9zthDjIrjib5XlcddeqLbIiDO3YQsut6j7aGf76xn0umUC/pA6+nApk3hQJF4JzLzg5PCl6ewJg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz", + "integrity": "sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==", "dev": true, "dependencies": { - "core-js-pure": "^3.20.2", - "regenerator-runtime": "^0.13.4" + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, "node_modules/@babel/template": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", @@ -2545,19 +2680,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.0.tgz", + "integrity": "sha512-Xdt2P1H4LKTO8ApPfnO1KmzYMFpp7D/EinoXzLYN/cHcBNrVCAkAtGUcXnHXrl/VGktureU6fkQrHSBE2URfoA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.0", + "@babel/types": "^7.21.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2565,19 +2700,58 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.0.tgz", + "integrity": "sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cordobo/qrcode": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@cordobo/qrcode/-/qrcode-1.5.0.tgz", @@ -2594,9 +2768,9 @@ } }, "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.1.0.tgz", - "integrity": "sha512-DO76V3295AqhjJZvgeaDP5GAGAat4g6wYfF8X+1n+76MpJat8ffY5bCJ9eSUqFY71nImxXgaDTRYJcRnA9oo7g==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -2608,6 +2782,23 @@ "postcss": "^8.3" } }, + "node_modules/@csstools/selector-specificity": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4", + "postcss-selector-parser": "^6.0.10" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", @@ -2618,15 +2809,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.4.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -2635,6 +2826,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { @@ -2660,9 +2854,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2692,6 +2886,18 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2705,26 +2911,51 @@ } }, "node_modules/@gar/promisify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", - "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/object-schema": { + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", @@ -2755,35 +2986,81 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@ngtools/webpack": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.3.tgz", - "integrity": "sha512-wooUZiV92QyoeFxkhqIwH/cfiAAAn+l8fEEuaaEIfJtpjpbShvvlboEVsqb28soeGiFJfLcmsZM3mUFgsG4QBQ==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.6.tgz", + "integrity": "sha512-N8SvRV91+/57TcAfbghc0k0tKCukw/7KqbDaLPAQTGFekJ4xMGT3elMzOyBXTH3Hvp5HL8/hiBt2tG04qiMf+w==", "dev": true, "engines": { "node": "^12.20.0 || ^14.15.0 || >=16.10.0", @@ -2857,32 +3134,23 @@ "which": "^2.0.2" } }, - "node_modules/@npmcli/git/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } + "node_modules/@npmcli/git/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/@npmcli/installed-package-contents": { "version": "1.0.7", @@ -2914,18 +3182,6 @@ "node": ">=10" } }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@npmcli/node-gyp": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", @@ -2954,21 +3210,21 @@ } }, "node_modules/@nrwl/cli": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.1.7.tgz", - "integrity": "sha512-HVvqYwefizcoVc4xrTgfIC8nfMA9cx5NiaIbZFDIZv12NsdalpA6a2BmmV8Zck+QQSGEHrhTZyt1AqDhA1t+6A==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-15.7.2.tgz", + "integrity": "sha512-A/72FAW1e0ku8YB/PaCqN9BpVvciO83MS5F5bvX5PA8xCNqe1+iXp/5T2ASnN2lB9zR3fQJmvR7mHKTKQlqQQQ==", "dev": true, "dependencies": { - "nx": "14.1.7" + "nx": "15.7.2" } }, "node_modules/@nrwl/cli/node_modules/@nrwl/tao": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.1.7.tgz", - "integrity": "sha512-7jzPqEwElMiTKCZm2iuqC5aPIs+IwocWR6Tl1JhC1eK2PWskHOhdgTQM186lfXwPKWH4NJAE3eZFqtECnGDCJg==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-15.7.2.tgz", + "integrity": "sha512-srx9heMIt/QIyuqfewiVYbRpFcD/2pHkTkrEEUKspPd25kzAL2adcAITQKVCHI7/VS2sPdDR67pVsGQPZFBMRQ==", "dev": true, "dependencies": { - "nx": "14.1.7" + "nx": "15.7.2" }, "bin": { "tao": "index.js" @@ -2989,10 +3245,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@nrwl/cli/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/@nrwl/cli/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -3005,6 +3267,29 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@nrwl/cli/node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nrwl/cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/@nrwl/cli/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3039,6 +3324,20 @@ "node": ">=8" } }, + "node_modules/@nrwl/cli/node_modules/fs-extra": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/@nrwl/cli/node_modules/glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -3065,10 +3364,49 @@ "node": ">=8" } }, + "node_modules/@nrwl/cli/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@nrwl/cli/node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/@nrwl/cli/node_modules/lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@nrwl/cli/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nrwl/cli/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -3078,19 +3416,20 @@ } }, "node_modules/@nrwl/cli/node_modules/nx": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/nx/-/nx-14.1.7.tgz", - "integrity": "sha512-Q10PVQ70TFg9HY6KQKJ316B42sVtxmFk/VUdC7ZHKxzDm/JeYf6OaDarQOUZaFZp/EL2brtEsrlWSMTWmk3r4w==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/nx/-/nx-15.7.2.tgz", + "integrity": "sha512-VRb+CZCji3G4ikdMAGoh6TeU9Q6n5atRwqRSFhUX63er8zhlMvWHLskPMZC4q/81edo/E7RhbmEVUD5MB0JoeA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@nrwl/cli": "14.1.7", - "@nrwl/tao": "14.1.7", + "@nrwl/cli": "15.7.2", + "@nrwl/tao": "15.7.2", "@parcel/watcher": "2.0.4", - "@swc-node/register": "^1.4.2", - "@swc/core": "^1.2.173", - "chalk": "4.1.0", - "chokidar": "^3.5.1", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "^3.0.0-rc.18", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.0.0", + "chalk": "^4.1.0", "cli-cursor": "3.1.0", "cli-spinners": "2.6.1", "cliui": "^7.0.2", @@ -3099,27 +3438,51 @@ "fast-glob": "3.2.7", "figures": "3.2.0", "flat": "^5.0.2", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.0", "glob": "7.1.4", "ignore": "^5.0.4", - "jsonc-parser": "3.0.0", - "minimatch": "3.0.4", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "3.0.5", "npm-run-path": "^4.0.1", "open": "^8.4.0", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", "semver": "7.3.4", "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", "tar-stream": "~2.2.0", "tmp": "~0.2.1", - "tsconfig-paths": "^3.9.0", + "tsconfig-paths": "^4.1.2", "tslib": "^2.3.0", "v8-compile-cache": "2.3.0", - "yargs": "^17.4.0", - "yargs-parser": "21.0.1" + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" }, "bin": { "nx": "bin/nx.js" + }, + "optionalDependencies": { + "@nrwl/nx-darwin-arm64": "15.7.2", + "@nrwl/nx-darwin-x64": "15.7.2", + "@nrwl/nx-linux-arm-gnueabihf": "15.7.2", + "@nrwl/nx-linux-arm64-gnu": "15.7.2", + "@nrwl/nx-linux-arm64-musl": "15.7.2", + "@nrwl/nx-linux-x64-gnu": "15.7.2", + "@nrwl/nx-linux-x64-musl": "15.7.2", + "@nrwl/nx-win32-arm64-msvc": "15.7.2", + "@nrwl/nx-win32-x64-msvc": "15.7.2" + }, + "peerDependencies": { + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } } }, "node_modules/@nrwl/cli/node_modules/semver": { @@ -3149,24 +3512,27 @@ "node": ">=8" } }, - "node_modules/@nrwl/cli/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/@nrwl/cli/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/@nrwl/cli/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@nrwl/cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, "engines": { - "node": ">=8.17.0" + "node": ">=12" } }, - "node_modules/@nrwl/cli/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, "node_modules/@nrwl/devkit": { "version": "13.1.3", "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", @@ -3181,6 +3547,18 @@ "tslib": "^2.0.0" } }, + "node_modules/@nrwl/devkit/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nrwl/devkit/node_modules/semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -3196,33 +3574,183 @@ "node": ">=10" } }, - "node_modules/@nrwl/tao": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", - "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", + "node_modules/@nrwl/devkit/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@nrwl/nx-darwin-arm64": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.7.2.tgz", + "integrity": "sha512-F82exjuqkAkElSTxEcTFeLMhHpbGiccfTQh2VjXMS+ONldxM+Kd7atJjtUG8wKNXfg0lxxjjAdnzLy3iBuN/HQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "chalk": "4.1.0", - "enquirer": "~2.3.6", - "fs-extra": "^9.1.0", - "jsonc-parser": "3.0.0", - "nx": "13.1.3", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", - "semver": "7.3.4", - "tmp": "~0.2.1", - "tslib": "^2.0.0", - "yargs-parser": "20.0.0" - }, - "bin": { - "tao": "index.js" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@nrwl/tao/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/@nrwl/nx-darwin-x64": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.7.2.tgz", + "integrity": "sha512-MNT7Bxz6yhoVLCgGpR0NtVkj20SER1CbrCaY7tmsKVNY9iA/EOZhz9qa3LeA1KZ4lw8Gpi2vD42mOngn7Mwr7w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-linux-arm-gnueabihf": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.7.2.tgz", + "integrity": "sha512-QGyPkYnZ9LnUnuCzrP50bwsMJ9n6r8K2bNC1sQQwioijY+4MHNL+bMTOGWc8+lYBP7Ju3gpTqozGV3FQVkaM2w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-linux-arm64-gnu": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.7.2.tgz", + "integrity": "sha512-HqufFVIvuunfChEFGkIhsLhhQjWLTFcCH2aQBSNesHpm6AhFVRGyokNu+PT6NNobr+BTrqJMocBqNQR1uvSyRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-linux-arm64-musl": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.7.2.tgz", + "integrity": "sha512-9B8q6I/OVyQuYe+Yg2wNyxza/CsbvejIUsrK3QGGWUwHlkklqOSmUOHyTrcyMHUSped6CWPyKdIywngYOQzltQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-linux-x64-gnu": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.7.2.tgz", + "integrity": "sha512-8/6WtQn4derYKUWu5SxWWM+1dGihSZXMhDW9l/sXOr/qbMZu3XBmM2XZSguw/+p9gEVHcMmN0+D+Cai+q6/vDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-linux-x64-musl": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.7.2.tgz", + "integrity": "sha512-c5SbqYZZBeBHhH5E30xwb4cHzCMVa/GQMCyTpZgsS/AHAPHbdkv+pO6bxxALvLPTyimcub7V+xbLCL7rgALzyw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-win32-arm64-msvc": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.7.2.tgz", + "integrity": "sha512-gWD/+gSO3XBma8PHX1Dp86fM6EcntHFfa7n/BISwDFkZ19MfV/gK6HbO847fkD6I34/IcDM/z1PsFwoIpTeoow==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/nx-win32-x64-msvc": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.7.2.tgz", + "integrity": "sha512-ARE4qGPgk+e+pSm0uPhHan5UCRtwNYc5ddVNS88NFrVoDTPm5MxYLGdvLnshWWio/Bx526FcwUMSCBWSW8HIFw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nrwl/tao": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", + "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", + "dev": true, + "dependencies": { + "chalk": "4.1.0", + "enquirer": "~2.3.6", + "fs-extra": "^9.1.0", + "jsonc-parser": "3.0.0", + "nx": "13.1.3", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "semver": "7.3.4", + "tmp": "~0.2.1", + "tslib": "^2.0.0", + "yargs-parser": "20.0.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/tao/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3267,21 +3795,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/@nrwl/tao/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@nrwl/tao/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3291,6 +3804,18 @@ "node": ">=8" } }, + "node_modules/@nrwl/tao/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@nrwl/tao/node_modules/semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -3318,26 +3843,11 @@ "node": ">=8" } }, - "node_modules/@nrwl/tao/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/@nrwl/tao/node_modules/yargs-parser": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", - "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", - "dev": true, - "engines": { - "node": ">=10" - } + "node_modules/@nrwl/tao/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/@parcel/watcher": { "version": "2.0.4", @@ -3358,13 +3868,13 @@ } }, "node_modules/@schematics/angular": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.3.tgz", - "integrity": "sha512-jloooGC7eco9AKxlIMMqFRptJYzZ0jNRBStWOp2dCISg6rmOKqpxbsHLtYFQIT1PnlomSxtKDAgYGQMDi9zhXw==", + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.6.tgz", + "integrity": "sha512-8NzHMX9+FSgaB0lJYxlTJv9OcBuolwZJqo9M/yX3RPSqSHghA33jWwgVbV551hBJOpbVEePerG1DQkIC99DXKA==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.2.3", - "@angular-devkit/schematics": "13.2.3", + "@angular-devkit/core": "13.2.6", + "@angular-devkit/schematics": "13.2.6", "jsonc-parser": "3.0.0" }, "engines": { @@ -3373,421 +3883,88 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@socket.io/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">= 6" } }, - "node_modules/@swc-node/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.9.0.tgz", - "integrity": "sha512-vRnvsMtL9OxybA/Wun1ZhlDvB6MNs4Zujnina0VKdGk+yI6s87KUhdTcbAY6dQMZhQTLFiC1Lnv/BuwCKcCEug==", + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dev": true, "dependencies": { - "@swc/core": "^1.2.172" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@swc-node/register": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.5.1.tgz", - "integrity": "sha512-6IL5s4QShKGs08qAeNou3rDA3gbp2WHk6fo0XnJXQn/aC9k6FnVBbj/thGOIEDtgNhC/DKpZT8tCY1LpQnOZFg==", + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", "dev": true, "dependencies": { - "@swc-node/core": "^1.9.0", - "@swc-node/sourcemap-support": "^0.2.0", - "colorette": "^2.0.16", - "debug": "^4.3.4", - "pirates": "^4.0.5", - "tslib": "^2.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "typescript": ">= 4.3" + "@types/node": "*" } }, - "node_modules/@swc-node/register/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "@types/node": "*" } }, - "node_modules/@swc-node/register/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/@swc-node/sourcemap-support": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.2.0.tgz", - "integrity": "sha512-FNrxdI6XMYfoNt81L8eFKEm1d8P82I1nPwS3MrnBGzZoMWB+seQhQK+iN6M5RreJxXbfZw5lF86LRjHEQeGMqg==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", "dev": true, "dependencies": { - "source-map-support": "^0.5.21" + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "node_modules/@swc-node/sourcemap-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@types/node": "*" } }, - "node_modules/@swc-node/sourcemap-support/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/@types/eslint": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", + "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/@swc/core": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.189.tgz", - "integrity": "sha512-S5cKX4ECMSfW78DLFgnlilJZgjrFRYwPslrrwpLl3gpwh+Qo72/Mhn71u7G/5xXW+T/xW5GwPccHfCk+k72uUg==", - "dev": true, - "bin": { - "swcx": "run_swcx.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-android-arm-eabi": "1.2.189", - "@swc/core-android-arm64": "1.2.189", - "@swc/core-darwin-arm64": "1.2.189", - "@swc/core-darwin-x64": "1.2.189", - "@swc/core-freebsd-x64": "1.2.189", - "@swc/core-linux-arm-gnueabihf": "1.2.189", - "@swc/core-linux-arm64-gnu": "1.2.189", - "@swc/core-linux-arm64-musl": "1.2.189", - "@swc/core-linux-x64-gnu": "1.2.189", - "@swc/core-linux-x64-musl": "1.2.189", - "@swc/core-win32-arm64-msvc": "1.2.189", - "@swc/core-win32-ia32-msvc": "1.2.189", - "@swc/core-win32-x64-msvc": "1.2.189" - } - }, - "node_modules/@swc/core-android-arm-eabi": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.189.tgz", - "integrity": "sha512-0kN3Le6QzFFz+Lc6a/tf/RkJXubWwWaHxF4c0bVm4AKIFf4nRlUCEqEkjdVaZvL92rpBMHaEEBuIIz3T8DqTTQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-android-arm64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.189.tgz", - "integrity": "sha512-smsb+YkDw2OKwg66Z63E/G4NlFApDbsiOPmZXFZbtZbNBD9v+wnk6WVA//XR1bdUI9VbzNKlMPKJxQTE685QDw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.189.tgz", - "integrity": "sha512-OGjZRkTulKirJMLYbW9etb59lA9ueDXVwYRVD9SrNh8tRMTf0Nq+SUT/C3LVhBBGC4KSdWOzBAYbDTTdsnY++Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.189.tgz", - "integrity": "sha512-BEcxnBHx51514Voe2dn/y1y5H9VNyw7Zpp9+mPekZqx5o/onPD5wZ1ZfAsPrA4UlvM3v16u6ITE/cLawJ/GdAQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-freebsd-x64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.189.tgz", - "integrity": "sha512-B6g2NWeh2iw6WPOaM19Uj3VE4se6alT265kWibLUshjcofRfnYT1lNhhkrF1D0EVnpC8I96I/xXNQo4Am9z4zQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.189.tgz", - "integrity": "sha512-6WhPG9pyN5AahQTVQk8MoN1I9Z/Ytfqizuie1wV7mW8FMNmMkiJvBekKtE6ftxu80Hqa34r86WfEwmJKm5MqYw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.189.tgz", - "integrity": "sha512-frJTGnxsDe7h2d7BtnBivOdOJTtabkQIIZmHd4JHqafcoekI6teyveIax17axLyimvWl278yTo3hf7ePagD/eg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.189.tgz", - "integrity": "sha512-27K38LoZpME5lojDJIUNo7zdTDwAKLm0BMQ7HXWcYOyiDAekhSidI+SrBWxCfLzfuClhFu6/VE3E7j32VFJsiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.189.tgz", - "integrity": "sha512-Ha5oJKOyQm9w7+e+WdRm4ypijzEmglWZGtgBR6vV6ViqqHcTBAU4nG87ex7y7AS9p+Cbc6EOSR9X1qIB8KxtbA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.189.tgz", - "integrity": "sha512-/p5yXa9HEzpVEuE4ivkW1IvwyYu6fT+L2OvVEs5oXIba80F0Wjy7InWqaa83gwrdMH+bXV6loG8LzZUZu/lpjA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.189.tgz", - "integrity": "sha512-o/1ueM6/sifNjYnO6NMEXB895spVfJs5oQIPxQG9vJ/4zWLw8YmAx+u1xJY+XGyK6gnroHt7yPiS87qWdbeF6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.189.tgz", - "integrity": "sha512-YDwRkzykaf+dw5Z7u189cC/Tttkn2NVV84hrGL3LbVuh7wT5PaDhZs4Yz4unZQSlPV12olmZWgNr/i27h5wlpg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.189.tgz", - "integrity": "sha512-Nge8Z/ZkAp5p5No50yBDpBG7+ZYaVWGSuwtPj6OJe7orzvDCEm9GgcVE6J9GEjbclSWlCH8B8lUe17GaKRZHbg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", - "dev": true - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", - "dev": true - }, - "node_modules/@types/eslint": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", - "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@types/estree": "*", + "@types/json-schema": "*" } }, "node_modules/@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -3795,27 +3972,27 @@ } }, "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dev": true, "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", "dev": true, "dependencies": { "@types/node": "*", @@ -3824,9 +4001,9 @@ } }, "node_modules/@types/http-proxy": { - "version": "1.17.8", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", - "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", "dev": true, "dependencies": { "@types/node": "*" @@ -3848,21 +4025,15 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, "node_modules/@types/minimist": { @@ -3890,9 +4061,9 @@ "dev": true }, "node_modules/@types/prettier": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", - "integrity": "sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", "dev": true }, "node_modules/@types/q": { @@ -3914,15 +4085,15 @@ "dev": true }, "node_modules/@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, "node_modules/@types/selenium-webdriver": { - "version": "3.0.19", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.19.tgz", - "integrity": "sha512-OFUilxQg+rWL2FMxtmIgCkUDlJB6pskkpvmew7yeXfzzsOBb5rc+y2+DjHm+r3r1ZPPcJefK3DveNSYWGiy68g==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.20.tgz", + "integrity": "sha512-6d8Q5fqS9DWOXEhMDiF6/2FjyHdmP/jSTAUyeQR7QwrFeNmYyzmvGxD5aLIHL445HjWgibs0eAig+KPnbaesXA==", "dev": true }, "node_modules/@types/serve-index": { @@ -3935,12 +4106,12 @@ } }, "node_modules/@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", "dev": true, "dependencies": { - "@types/mime": "^1", + "@types/mime": "*", "@types/node": "*" } }, @@ -3954,9 +4125,9 @@ } }, "node_modules/@types/ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", "dev": true, "dependencies": { "@types/node": "*" @@ -3995,34 +4166,13 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", + "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", "dev": true, "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", - "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.17.0" + "@typescript-eslint/utils": "5.17.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4105,27 +4255,6 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/@typescript-eslint/types": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", @@ -4166,65 +4295,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/@typescript-eslint/utils": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", @@ -4430,10 +4500,47 @@ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.39", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.39.tgz", + "integrity": "sha512-BsD4zq3EVmaHqlynXTceNuEFAtrfToV4fI9GA54moKlWZL4Eb2eXrhgf1jV2nMYx18SZxYO4Jc5Kf1sCDNRjOg==", + "dev": true, + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/@zkochan/js-yaml": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", + "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@zkochan/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, "node_modules/abbrev": { @@ -4456,9 +4563,9 @@ } }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4499,9 +4606,9 @@ } }, "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -4513,12 +4620,12 @@ } }, "node_modules/adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", "dev": true, "engines": { - "node": ">=0.3.0" + "node": ">=6.0" } }, "node_modules/agent-base": { @@ -4534,9 +4641,9 @@ } }, "node_modules/agentkeepalive": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz", - "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", "dev": true, "dependencies": { "debug": "^4.1.0", @@ -4547,6 +4654,15 @@ "node": ">= 8.0.0" } }, + "node_modules/agentkeepalive/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -4606,9 +4722,9 @@ } }, "node_modules/angularx-qrcode": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-13.0.3.tgz", - "integrity": "sha512-KzalrRy2MczdApSq29k2TBOml6taQ7NqdgZDNgPSQBd5/27+l3xHkOkhhDOmGruSyCcT2KdB8qeOT/6K48c+xw==", + "version": "13.0.15", + "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-13.0.15.tgz", + "integrity": "sha512-4gNfG6JNfKVBAPEBOQ685tmFA4UnOzqhhlmeCsbFy7pX9QGm7KRFTfujhpNav+bEDGTMyRpPhvLFnsRaOWWH6g==", "dependencies": { "@cordobo/qrcode": "1.5.0", "tslib": "^2.3.0" @@ -4618,9 +4734,9 @@ } }, "node_modules/angularx-qrcode/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/ansi-colors": { "version": "4.1.1", @@ -4659,11 +4775,12 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/ansi-styles": { @@ -4679,9 +4796,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -4692,9 +4809,9 @@ } }, "node_modules/apexcharts": { - "version": "3.33.1", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.33.1.tgz", - "integrity": "sha512-5aVzrgJefd8EH4w7oRmuOhA3+cxJxQg27cYg3ANVGvPCOB4AY3mVVNtFHRFaIq7bv8ws4GRaA9MWfzoWQw3MPQ==", + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.37.0.tgz", + "integrity": "sha512-0mg1gDKUo3JG00Q//LK0jEXBS6OLjpuglqZ8ec9cqfA5oP8owopD9n5EhfARbWROb5o8GSPzFuohTJiCm2ecWw==", "dependencies": { "svg.draggable.js": "^2.2.2", "svg.easing.js": "^2.0.0", @@ -4711,16 +4828,16 @@ "dev": true }, "node_modules/are-we-there-yet": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", - "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "dev": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/arg": { @@ -4738,11 +4855,18 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/argparse/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } }, "node_modules/array-flatten": { "version": "2.1.2", @@ -4751,15 +4875,12 @@ "dev": true }, "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/array-uniq": { @@ -4808,13 +4929,10 @@ } }, "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true }, "node_modules/asynckit": { "version": "0.4.0", @@ -4844,14 +4962,24 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", - "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], "dependencies": { - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001297", - "fraction.js": "^4.1.2", + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -4862,10 +4990,6 @@ "engines": { "node": "^10 || ^12 || >=14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.1.0" } @@ -4880,9 +5004,40 @@ } }, "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "dev": true + }, + "node_modules/axios": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz", + "integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, "node_modules/babel-loader": { @@ -4905,9 +5060,9 @@ } }, "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -4917,9 +5072,9 @@ } }, "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -4930,15 +5085,6 @@ "node": ">=4.0.0" } }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -4956,13 +5102,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", - "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" }, "peerDependencies": { @@ -4979,12 +5125,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", - "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1", + "@babel/helper-define-polyfill-provider": "^0.3.2", "core-js-compat": "^3.21.0" }, "peerDependencies": { @@ -5098,24 +5244,27 @@ } }, "node_modules/body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "dependencies": { - "bytes": "3.1.1", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/debug": { @@ -5176,26 +5325,31 @@ } }, "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/browserstack": { @@ -5284,9 +5438,9 @@ "dev": true }, "node_modules/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "engines": { "node": ">= 0.8" @@ -5321,18 +5475,24 @@ "node": ">= 10" } }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -5382,9 +5542,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001450", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", - "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "version": "1.0.30001457", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", + "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", "dev": true, "funding": [ { @@ -5502,9 +5662,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "dev": true, "engines": { "node": ">=6" @@ -5589,13 +5749,16 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/clone": { @@ -5621,16 +5784,16 @@ "node": ">=6" } }, - "node_modules/clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "dependencies": { - "is-regexp": "^2.0.0" + "isobject": "^3.0.1" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, "node_modules/color-convert": { @@ -5658,15 +5821,15 @@ } }, "node_modules/colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, "node_modules/colors": { @@ -5691,10 +5854,13 @@ } }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } }, "node_modules/common-tags": { "version": "1.8.2", @@ -5711,12 +5877,6 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -5771,6 +5931,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5834,43 +6000,20 @@ "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/cookie": { "version": "0.4.2", @@ -5986,6 +6129,18 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/core-js": { "version": "3.20.3", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", @@ -5999,33 +6154,22 @@ } }, "node_modules/core-js-compat": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz", - "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==", + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.28.0.tgz", + "integrity": "sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg==", "dev": true, "dependencies": { - "browserslist": "^4.19.1", - "semver": "7.0.0" + "browserslist": "^4.21.5" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/core-js-pure": { - "version": "3.22.6", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.6.tgz", - "integrity": "sha512-u5yG2VL6NKXz9BZHr9RAm6eWD1DTNjG7jJnJgLGR+Im0whdPcPXqwqxd+dcUrZvpvPan5KMgn/3pI+Q/aGqPOA==", - "deprecated": "core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure.", + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.28.0.tgz", + "integrity": "sha512-DSOVleA9/v3LNj/vFxAPfUHttKTzrB2RXhAPvR5TPXn4vrra3Z2ssytvRyt8eruJwAfwAiFADEbrjcRdcvPLQQ==", "dev": true, "hasInstallScript": true, "funding": { @@ -6053,9 +6197,9 @@ } }, "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", @@ -6172,21 +6316,6 @@ "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -6217,9 +6346,9 @@ } }, "node_modules/css-functions-list": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", - "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.1.0.tgz", + "integrity": "sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==", "dev": true, "engines": { "node": ">=12.22" @@ -6285,14 +6414,14 @@ } }, "node_modules/css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, "dependencies": { "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" }, @@ -6301,9 +6430,9 @@ } }, "node_modules/css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, "engines": { "node": ">= 6" @@ -6358,9 +6487,9 @@ } }, "node_modules/date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", "dev": true, "engines": { "node": ">=4.0" @@ -6393,9 +6522,9 @@ } }, "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, "dependencies": { "decamelize": "^1.1.0", @@ -6403,21 +6532,24 @@ }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/decamelize-keys/node_modules/map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true, "engines": { "node": ">=0.10" @@ -6459,12 +6591,15 @@ } }, "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-lazy-prop": { @@ -6477,15 +6612,19 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/del": { @@ -6506,6 +6645,35 @@ "node": ">=0.10.0" } }, + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/del/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -6534,12 +6702,12 @@ "dev": true }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/dependency-graph": { @@ -6552,10 +6720,14 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-it": { "version": "4.0.1", @@ -6630,6 +6802,12 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/dns-packet/node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, "node_modules/dns-txt": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", @@ -6664,9 +6842,9 @@ } }, "node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "dependencies": { "domelementtype": "^2.0.1", @@ -6678,9 +6856,9 @@ } }, "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, "funding": [ { @@ -6690,9 +6868,9 @@ ] }, "node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, "dependencies": { "domelementtype": "^2.2.0" @@ -6727,6 +6905,12 @@ "node": ">=10" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -6765,9 +6949,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.68", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", - "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==", + "version": "1.4.305", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.305.tgz", + "integrity": "sha512-WETy6tG0CT5gm1O+xCbyapWNsCcmIvrn4NHViIGYo2AT8FV2qUCXdaB+WqYxSv/vS5mFqhBYnfZAAkVArjBmUg==", "dev": true }, "node_modules/emoji-regex": { @@ -6831,9 +7015,9 @@ } }, "node_modules/engine.io": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", - "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -6844,29 +7028,26 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", - "ws": "~8.2.3" + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" }, "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", - "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", "dev": true, - "dependencies": { - "@socket.io/base64-arraybuffer": "~1.0.2" - }, "engines": { "node": ">=10.0.0" } }, "node_modules/enhanced-resolve": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", - "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -6962,40 +7143,44 @@ } }, "node_modules/esbuild": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.14.tgz", - "integrity": "sha512-aiK4ddv+uui0k52OqSHu4xxu+SzOim7Rlz4i25pMEiC8rlnGU0HJ9r+ZMfdWL5bzifg+nhnn7x4NSWTeehYblg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.22.tgz", + "integrity": "sha512-CjFCFGgYtbFOPrwZNJf7wsuzesx8kqwAffOlbYcFDLFuUtP8xloK1GH+Ai13Qr0RZQf9tE7LMTHJ2iVGJ1SKZA==", "dev": true, "hasInstallScript": true, "optional": true, "bin": { "esbuild": "bin/esbuild" }, + "engines": { + "node": ">=12" + }, "optionalDependencies": { - "esbuild-android-arm64": "0.14.14", - "esbuild-darwin-64": "0.14.14", - "esbuild-darwin-arm64": "0.14.14", - "esbuild-freebsd-64": "0.14.14", - "esbuild-freebsd-arm64": "0.14.14", - "esbuild-linux-32": "0.14.14", - "esbuild-linux-64": "0.14.14", - "esbuild-linux-arm": "0.14.14", - "esbuild-linux-arm64": "0.14.14", - "esbuild-linux-mips64le": "0.14.14", - "esbuild-linux-ppc64le": "0.14.14", - "esbuild-linux-s390x": "0.14.14", - "esbuild-netbsd-64": "0.14.14", - "esbuild-openbsd-64": "0.14.14", - "esbuild-sunos-64": "0.14.14", - "esbuild-windows-32": "0.14.14", - "esbuild-windows-64": "0.14.14", - "esbuild-windows-arm64": "0.14.14" + "esbuild-android-arm64": "0.14.22", + "esbuild-darwin-64": "0.14.22", + "esbuild-darwin-arm64": "0.14.22", + "esbuild-freebsd-64": "0.14.22", + "esbuild-freebsd-arm64": "0.14.22", + "esbuild-linux-32": "0.14.22", + "esbuild-linux-64": "0.14.22", + "esbuild-linux-arm": "0.14.22", + "esbuild-linux-arm64": "0.14.22", + "esbuild-linux-mips64le": "0.14.22", + "esbuild-linux-ppc64le": "0.14.22", + "esbuild-linux-riscv64": "0.14.22", + "esbuild-linux-s390x": "0.14.22", + "esbuild-netbsd-64": "0.14.22", + "esbuild-openbsd-64": "0.14.22", + "esbuild-sunos-64": "0.14.22", + "esbuild-windows-32": "0.14.22", + "esbuild-windows-64": "0.14.22", + "esbuild-windows-arm64": "0.14.22" } }, "node_modules/esbuild-android-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.14.tgz", - "integrity": "sha512-be/Uw6DdpQiPfula1J4bdmA+wtZ6T3BRCZsDMFB5X+k0Gp8TIh9UvmAcqvKNnbRAafSaXG3jPCeXxDKqnc8hFQ==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.22.tgz", + "integrity": "sha512-k1Uu4uC4UOFgrnTj2zuj75EswFSEBK+H6lT70/DdS4mTAOfs2ECv2I9ZYvr3w0WL0T4YItzJdK7fPNxcPw6YmQ==", "cpu": [ "arm64" ], @@ -7003,12 +7188,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-darwin-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.14.tgz", - "integrity": "sha512-BEexYmjWafcISK8cT6O98E3TfcLuZL8DKuubry6G54n2+bD4GkoRD6HYUOnCkfl2p7jodA+s4369IjSFSWjtHg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.22.tgz", + "integrity": "sha512-d8Ceuo6Vw6HM3fW218FB6jTY6O3r2WNcTAU0SGsBkXZ3k8SDoRLd3Nrc//EqzdgYnzDNMNtrWegK2Qsss4THhw==", "cpu": [ "x64" ], @@ -7016,12 +7204,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-darwin-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.14.tgz", - "integrity": "sha512-tnBKm41pDOB1GtZ8q/w26gZlLLRzVmP8fdsduYjvM+yFD7E2DLG4KbPAqFMWm4Md9B+DitBglP57FY7AznxbTg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.22.tgz", + "integrity": "sha512-YAt9Tj3SkIUkswuzHxkaNlT9+sg0xvzDvE75LlBo4DI++ogSgSmKNR6B4eUhU5EUUepVXcXdRIdqMq9ppeRqfw==", "cpu": [ "arm64" ], @@ -7029,12 +7220,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-freebsd-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.14.tgz", - "integrity": "sha512-Q9Rx6sgArOHalQtNwAaIzJ6dnQ8A+I7f/RsQsdkS3JrdzmnlFo8JEVofTmwVQLoIop7OKUqIVOGP4PoQcwfVMA==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.22.tgz", + "integrity": "sha512-ek1HUv7fkXMy87Qm2G4IRohN+Qux4IcnrDBPZGXNN33KAL0pEJJzdTv0hB/42+DCYWylSrSKxk3KUXfqXOoH4A==", "cpu": [ "x64" ], @@ -7042,12 +7236,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.14.tgz", - "integrity": "sha512-TJvq0OpLM7BkTczlyPIphcvnwrQwQDG1HqxzoYePWn26SMUAlt6wrLnEvxdbXAvNvDLVzG83kA+JimjK7aRNBA==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.22.tgz", + "integrity": "sha512-zPh9SzjRvr9FwsouNYTqgqFlsMIW07O8mNXulGeQx6O5ApgGUBZBgtzSlBQXkHi18WjrosYfsvp5nzOKiWzkjQ==", "cpu": [ "arm64" ], @@ -7055,12 +7252,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-32": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.14.tgz", - "integrity": "sha512-h/CrK9Baimt5VRbu8gqibWV7e1P9l+mkanQgyOgv0Ng3jHT1NVFC9e6rb1zbDdaJVmuhWX5xVliUA5bDDCcJeg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.22.tgz", + "integrity": "sha512-SnpveoE4nzjb9t2hqCIzzTWBM0RzcCINDMBB67H6OXIuDa4KqFqaIgmTchNA9pJKOVLVIKd5FYxNiJStli21qg==", "cpu": [ "ia32" ], @@ -7068,12 +7268,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.14.tgz", - "integrity": "sha512-IC+wAiIg/egp5OhQp4W44D9PcBOH1b621iRn1OXmlLzij9a/6BGr9NMIL4CRwz4j2kp3WNZu5sT473tYdynOuQ==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.22.tgz", + "integrity": "sha512-Zcl9Wg7gKhOWWNqAjygyqzB+fJa19glgl2JG7GtuxHyL1uEnWlpSMytTLMqtfbmRykIHdab797IOZeKwk5g0zg==", "cpu": [ "x64" ], @@ -7081,12 +7284,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-arm": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.14.tgz", - "integrity": "sha512-gxpOaHOPwp7zSmcKYsHrtxabScMqaTzfSQioAMUaB047YiMuDBzqVcKBG8OuESrYkGrL9DDljXr/mQNg7pbdaQ==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.22.tgz", + "integrity": "sha512-soPDdbpt/C0XvOOK45p4EFt8HbH5g+0uHs5nUKjHVExfgR7du734kEkXR/mE5zmjrlymk5AA79I0VIvj90WZ4g==", "cpu": [ "arm" ], @@ -7094,12 +7300,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.14.tgz", - "integrity": "sha512-6QVul3RI4M5/VxVIRF/I5F+7BaxzR3DfNGoqEVSCZqUbgzHExPn+LXr5ly1C7af2Kw4AHpo+wDqx8A4ziP9avw==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.22.tgz", + "integrity": "sha512-8q/FRBJtV5IHnQChO3LHh/Jf7KLrxJ/RCTGdBvlVZhBde+dk3/qS9fFsUy+rs3dEi49aAsyVitTwlKw1SUFm+A==", "cpu": [ "arm64" ], @@ -7107,12 +7316,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-mips64le": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.14.tgz", - "integrity": "sha512-4Jl5/+xoINKbA4cesH3f4R+q0vltAztZ6Jm8YycS8lNhN1pgZJBDxWfI6HUMIAdkKlIpR1PIkA9aXQgZ8sxFAg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.22.tgz", + "integrity": "sha512-SiNDfuRXhGh1JQLLA9JPprBgPVFOsGuQ0yDfSPTNxztmVJd8W2mX++c4FfLpAwxuJe183mLuKf7qKCHQs5ZnBQ==", "cpu": [ "mips64el" ], @@ -7120,12 +7332,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.14.tgz", - "integrity": "sha512-BitW37GxeebKxqYNl4SVuSdnIJAzH830Lr6Mkq3pBHXtzQay0vK+IeOR/Ele1GtNVJ+/f8wYM53tcThkv5SC5w==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.22.tgz", + "integrity": "sha512-6t/GI9I+3o1EFm2AyN9+TsjdgWCpg2nwniEhjm2qJWtJyJ5VzTXGUU3alCO3evopu8G0hN2Bu1Jhz2YmZD0kng==", "cpu": [ "ppc64" ], @@ -7133,12 +7348,31 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.22.tgz", + "integrity": "sha512-AyJHipZKe88sc+tp5layovquw5cvz45QXw5SaDgAq2M911wLHiCvDtf/07oDx8eweCyzYzG5Y39Ih568amMTCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-linux-s390x": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.14.tgz", - "integrity": "sha512-vLj6p76HOZG3wfuTr5MyO3qW5iu8YdhUNxuY+tx846rPo7GcKtYSPMusQjeVEfZlJpSYoR+yrNBBxq+qVF9zrw==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.22.tgz", + "integrity": "sha512-Sz1NjZewTIXSblQDZWEFZYjOK6p8tV6hrshYdXZ0NHTjWE+lwxpOpWeElUGtEmiPcMT71FiuA9ODplqzzSxkzw==", "cpu": [ "s390x" ], @@ -7146,12 +7380,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-netbsd-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.14.tgz", - "integrity": "sha512-fn8looXPQhpVqUyCBWUuPjesH+yGIyfbIQrLKG05rr1Kgm3rZD/gaYrd3Wpmf5syVZx70pKZPvdHp8OTA+y7cQ==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.22.tgz", + "integrity": "sha512-TBbCtx+k32xydImsHxvFgsOCuFqCTGIxhzRNbgSL1Z2CKhzxwT92kQMhxort9N/fZM2CkRCPPs5wzQSamtzEHA==", "cpu": [ "x64" ], @@ -7159,12 +7396,15 @@ "optional": true, "os": [ "netbsd" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-openbsd-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.14.tgz", - "integrity": "sha512-HdAnJ399pPff3SKbd8g+P4o5znseni5u5n5rJ6Z7ouqOdgbOwHe2ofZbMow17WMdNtz1IyOZk2Wo9Ve6/lZ4Rg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.22.tgz", + "integrity": "sha512-vK912As725haT313ANZZZN+0EysEEQXWC/+YE4rQvOQzLuxAQc2tjbzlAFREx3C8+uMuZj/q7E5gyVB7TzpcTA==", "cpu": [ "x64" ], @@ -7172,12 +7412,15 @@ "optional": true, "os": [ "openbsd" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-sunos-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.14.tgz", - "integrity": "sha512-bmDHa99ulsGnYlh/xjBEfxoGuC8CEG5OWvlgD+pF7bKKiVTbtxqVCvOGEZeoDXB+ja6AvHIbPxrEE32J+m5nqQ==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", + "integrity": "sha512-/mbJdXTW7MTcsPhtfDsDyPEOju9EOABvCjeUU2OJ7fWpX/Em/H3WYDa86tzLUbcVg++BScQDzqV/7RYw5XNY0g==", "cpu": [ "x64" ], @@ -7185,24 +7428,27 @@ "optional": true, "os": [ "sunos" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-wasm": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.14.tgz", - "integrity": "sha512-qTjK4MWnYtQHCMGg2qDUqeFYXfVvYq5qJkQTIsOV4VZCknoYePVaDTG9ygEB9Ct0kc0DWs7IrS6Ja+GjY62Kzw==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.22.tgz", + "integrity": "sha512-FOSAM29GN1fWusw0oLMv6JYhoheDIh5+atC72TkJKfIUMID6yISlicoQSd9gsNSFsNBvABvtE2jR4JB1j4FkFw==", "dev": true, "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/esbuild-windows-32": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.14.tgz", - "integrity": "sha512-6tVooQcxJCNenPp5GHZBs/RLu31q4B+BuF4MEoRxswT+Eq2JGF0ZWDRQwNKB8QVIo3t6Svc5wNGez+CwKNQjBg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.22.tgz", + "integrity": "sha512-1vRIkuvPTjeSVK3diVrnMLSbkuE36jxA+8zGLUOrT4bb7E/JZvDRhvtbWXWaveUc/7LbhaNFhHNvfPuSw2QOQg==", "cpu": [ "ia32" ], @@ -7210,12 +7456,15 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-windows-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.14.tgz", - "integrity": "sha512-kl3BdPXh0/RD/dad41dtzj2itMUR4C6nQbXQCyYHHo4zoUoeIXhpCrSl7BAW1nv5EFL8stT1V+TQVXGZca5A2A==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.22.tgz", + "integrity": "sha512-AxjIDcOmx17vr31C5hp20HIwz1MymtMjKqX4qL6whPj0dT9lwxPexmLj6G1CpR3vFhui6m75EnBEe4QL82SYqw==", "cpu": [ "x64" ], @@ -7223,12 +7472,15 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/esbuild-windows-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.14.tgz", - "integrity": "sha512-dCm1wTOm6HIisLanmybvRKvaXZZo4yEVrHh1dY0v582GThXJOzuXGja1HIQgV09RpSHYRL3m4KoUBL00l6SWEg==", + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.22.tgz", + "integrity": "sha512-5wvQ+39tHmRhNpu2Fx04l7QfeK3mQ9tKzDqqGR8n/4WUxsFxnVLfDRBGirIfk4AfWlxk60kqirlODPoT5LqMUg==", "cpu": [ "arm64" ], @@ -7236,7 +7488,10 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=12" + } }, "node_modules/escalade": { "version": "3.1.1", @@ -7262,13 +7517,15 @@ } }, "node_modules/eslint": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", - "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -7278,18 +7535,21 @@ "eslint-scope": "^7.1.1", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", + "espree": "^9.4.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -7300,8 +7560,7 @@ "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -7314,9 +7573,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -7326,15 +7585,15 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=12.0.0" }, "peerDependencies": { "eslint": ">=7.28.0", @@ -7500,6 +7759,22 @@ "node": ">=4.0" } }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7513,9 +7788,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -7554,6 +7829,63 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7579,17 +7911,20 @@ } }, "node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "dependencies": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -7606,9 +7941,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", + "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -7718,18 +8053,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "dependencies": { - "clone-regexp": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -7740,38 +8063,39 @@ } }, "node_modules/express": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", - "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dev": true, "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.1", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.1", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.6", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -7786,10 +8110,34 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/express/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true, "engines": { "node": ">= 0.6" @@ -7804,31 +8152,53 @@ "ms": "2.0.0" } }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, "node_modules/extend": { "version": "3.0.2", @@ -7850,6 +8220,18 @@ "node": ">=4" } }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -7872,9 +8254,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -7896,19 +8278,22 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -7972,9 +8357,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -8028,6 +8413,18 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -8081,15 +8478,15 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -8139,9 +8536,9 @@ } }, "node_modules/fraction.js": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz", - "integrity": "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", "dev": true, "engines": { "node": "*" @@ -8167,17 +8564,18 @@ "dev": true }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/fs-minipass": { @@ -8227,27 +8625,35 @@ "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gauge": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", - "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1", "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", - "signal-exit": "^3.0.0", + "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "wide-align": "^1.1.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/gensync": { @@ -8268,14 +8674,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8290,18 +8696,6 @@ "node": ">=8.0.0" } }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -8393,6 +8787,18 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -8403,32 +8809,41 @@ } }, "node_modules/globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globjoin": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, "node_modules/handle-thing": { @@ -8533,13 +8948,25 @@ "node": ">=4" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8595,6 +9022,24 @@ "node": ">=10" } }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -8622,6 +9067,12 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8632,9 +9083,9 @@ } }, "node_modules/html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, "node_modules/html-escaper": { @@ -8655,10 +9106,89 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "node_modules/http-deceiver": { @@ -8668,25 +9198,34 @@ "dev": true }, "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", + "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" } }, "node_modules/http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, "node_modules/http-proxy": { @@ -8718,9 +9257,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz", - "integrity": "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, "dependencies": { "@types/http-proxy": "^1.17.8", @@ -8741,6 +9280,18 @@ } } }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -8887,9 +9438,9 @@ "dev": true }, "node_modules/immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", + "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==", "dev": true }, "node_modules/import-fresh": { @@ -9059,9 +9610,9 @@ } }, "node_modules/inquirer/node_modules/rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -9080,15 +9631,15 @@ } }, "node_modules/inquirer/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, "node_modules/ipaddr.js": { @@ -9135,9 +9686,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -9186,11 +9737,15 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { @@ -9250,7 +9805,7 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-inside": { + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", @@ -9262,26 +9817,29 @@ "node": ">=0.10.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } @@ -9302,15 +9860,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -9366,9 +9915,9 @@ "dev": true }, "node_modules/isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "engines": { "node": ">= 8.0.0" @@ -9408,9 +9957,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -9545,9 +10094,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -9590,12 +10139,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jake/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, "node_modules/jake/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9733,6 +10276,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9797,7 +10350,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json-stringify-safe": { @@ -9807,13 +10360,10 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -9864,15 +10414,15 @@ } }, "node_modules/jszip": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", - "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" + "setimmediate": "^1.0.5" } }, "node_modules/jszip/node_modules/readable-stream": { @@ -9890,6 +10440,12 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/jszip/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -9900,15 +10456,15 @@ } }, "node_modules/karma": { - "version": "6.3.16", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", - "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", + "version": "6.3.20", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", + "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==", "dev": true, "dependencies": { + "@colors/colors": "1.5.0", "body-parser": "^1.19.0", "braces": "^3.0.2", "chokidar": "^3.5.1", - "colors": "1.4.0", "connect": "^3.7.0", "di": "^0.0.1", "dom-serialize": "^2.2.1", @@ -9924,7 +10480,7 @@ "qjobs": "^1.2.0", "range-parser": "^1.2.1", "rimraf": "^3.0.2", - "socket.io": "^4.2.0", + "socket.io": "^4.4.1", "source-map": "^0.6.1", "tmp": "^0.2.1", "ua-parser-js": "^0.7.30", @@ -9938,14 +10494,26 @@ } }, "node_modules/karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", "dev": true, "dependencies": { "which": "^1.2.1" } }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/karma-coverage-istanbul-reporter": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", @@ -9963,9 +10531,9 @@ } }, "node_modules/karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz", + "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==", "dev": true, "dependencies": { "jasmine-core": "^3.6.0" @@ -9997,23 +10565,27 @@ "source-map-support": "^0.5.5" } }, - "node_modules/karma-source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/karma-source-map-support/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, "node_modules/karma/node_modules/source-map": { @@ -10025,18 +10597,6 @@ "node": ">=0.10.0" } }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, "node_modules/karma/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -10074,18 +10634,18 @@ } }, "node_modules/klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", "dev": true }, "node_modules/less": { @@ -10192,9 +10752,9 @@ } }, "node_modules/less/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "node_modules/levn": { @@ -10211,9 +10771,9 @@ } }, "node_modules/license-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-SQum9mg3BgnY5BK+2KYl4W7pk9b26Q8tW2lTsO6tidD0/Ds9ksdXvp3ip2s9LqDjj5gtBMyWRfOPZptWj4PfCg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", "dev": true, "dependencies": { "webpack-sources": "^3.0.0" @@ -10237,9 +10797,9 @@ } }, "node_modules/lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", "dev": true, "engines": { "node": ">=10" @@ -10252,24 +10812,24 @@ "dev": true }, "node_modules/lint-staged": { - "version": "12.4.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz", - "integrity": "sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.5.0.tgz", + "integrity": "sha512-BKLUjWDsKquV/JuIcoQW4MSAI3ggwEImF1+sB4zaKvyVx1wBk3FsG7UK9bpnmBTN1pm7EH2BBcMwINJzCRv12g==", "dev": true, "dependencies": { "cli-truncate": "^3.1.0", "colorette": "^2.0.16", - "commander": "^8.3.0", - "debug": "^4.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", "execa": "^5.1.1", - "lilconfig": "2.0.4", - "listr2": "^4.0.1", - "micromatch": "^4.0.4", + "lilconfig": "2.0.5", + "listr2": "^4.0.5", + "micromatch": "^4.0.5", "normalize-path": "^3.0.0", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "pidtree": "^0.5.0", "string-argv": "^0.3.1", - "supports-color": "^9.2.1", + "supports-color": "^9.2.2", "yaml": "^1.10.2" }, "bin": { @@ -10282,19 +10842,27 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "node_modules/lint-staged/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">= 12" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/lint-staged/node_modules/supports-color": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", - "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.3.1.tgz", + "integrity": "sha512-knBY82pjmnIzK3NifMo3RxEIRD9E0kIzV4BKcyTZ9+9kWgLMxd4PrsTSMoFQUabgRBbF8KOLRDCyKgNV+iK44Q==", "dev": true, "engines": { "node": ">=12" @@ -10379,11 +10947,20 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/listr2/node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "dev": true, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -10403,15 +10980,15 @@ } }, "node_modules/listr2/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, "engines": { "node": ">=6.11.5" @@ -10459,7 +11036,7 @@ "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "node_modules/log-symbols": { @@ -10599,6 +11176,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -10631,25 +11217,42 @@ } }, "node_modules/log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.8.0.tgz", + "integrity": "sha512-g+V8gZyurIexrOvWQ+AcZsIvuK/lBnx2argejZxL4gVZ4Hq02kUYH6WZOnqxgBml+zzQZYdaEoTN84B6Hzm8Fg==", "dev": true, "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", "rfdc": "^1.3.0", - "streamroller": "^3.0.2" + "streamroller": "^3.1.5" }, "engines": { "node": ">=8.0" } }, + "node_modules/log4js/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/loglevel": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", - "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", "dev": true, "engines": { "node": ">= 0.6.0" @@ -10662,7 +11265,7 @@ "node_modules/loglevel-colored-level-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", "dev": true, "dependencies": { "chalk": "^1.1.3", @@ -10706,7 +11309,7 @@ "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -10718,22 +11321,19 @@ "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/magic-string": { @@ -10802,6 +11402,24 @@ "node": ">= 10" } }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -10834,12 +11452,12 @@ } }, "node_modules/memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", + "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", "dev": true, "dependencies": { - "fs-monkey": "1.0.3" + "fs-monkey": "^1.0.3" }, "engines": { "node": ">= 4.0.0" @@ -10948,21 +11566,21 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -11031,9 +11649,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -11043,10 +11661,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minimist-options": { "version": "4.1.0", @@ -11062,19 +11683,10 @@ "node": ">= 6" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { "yallist": "^4.0.0" @@ -11158,6 +11770,12 @@ "node": ">=8" } }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -11171,16 +11789,22 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/ms": { @@ -11229,7 +11853,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/needle": { @@ -11276,23 +11900,23 @@ "dev": true }, "node_modules/ng-apexcharts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.0.tgz", - "integrity": "sha512-HSLPHrSH4CYn6crU8RsbZzuecKoXZ7a8i0cGdq8yDZ9DwaEauiJl5WWCWKajHLbwloVEdqoAsKN04TcvIP0ulQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.4.tgz", + "integrity": "sha512-XpykR8qos14i2ly/QrmGkQIUQfqaZAe2+7tDg5An1akws3VwYg6O643g9LwdTIG3YeWlkKtt4zxYJpEJF3uLUg==", "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@angular/common": ">=13.0.0", "@angular/core": ">=13.0.0", - "apexcharts": "^3.31.0", + "apexcharts": "^3.36.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/ngx-color-picker": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.0.tgz", - "integrity": "sha512-SY5KoZka/uq2MNhUAKfJXQjjS2TFvKDJHbsCxfnjKjS/VHx8VVeTJpnt5wuuewzRzLxfOm5y2Fw8/HTPEPtRkA==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.1.tgz", + "integrity": "sha512-ODLePeqN7dnhTmjHGJrxFHaEXb3o2vH4n8qUzkUAIFmz5vOOxCCEYY4/bg+5VK62PonaK7j0fFf8AjnwrqhFHQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -11303,9 +11927,9 @@ } }, "node_modules/ngx-color-picker/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/nice-napi": { "version": "1.0.2", @@ -11329,9 +11953,9 @@ "dev": true }, "node_modules/node-forge": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", - "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, "engines": { "node": ">= 6.13.0" @@ -11362,9 +11986,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -11372,25 +11996,10 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, "node_modules/nopt": { @@ -11529,6 +12138,33 @@ "node": "^12.13.0 || ^14.15.0 || >=16" } }, + "node_modules/npm-registry-fetch/node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -11538,6 +12174,63 @@ "node": ">= 10" } }, + "node_modules/npm-registry-fetch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -11553,40 +12246,118 @@ } }, "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.3.1.tgz", - "integrity": "sha512-nX1x4qUrKqwbIAhv4s9et4FIUVzNOpeY07bsjGUy8gwJrXH/wScImSQqXErmo/b2jZY2r0mohbLA9zVj7u1cNw==", - "deprecated": "Please update to latest patch version to fix memory leak https://github.com/isaacs/node-lru-cache/issues/227", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.17.0.tgz", + "integrity": "sha512-zSxlVVwOabhVyTi6E8gYv2cr6bXK+8ifYz5/uyJb9feXX6NACVDwY4p5Ut3WC3Ivo/QhpARHU3iujx2xGAYHbQ==", "dev": true, "engines": { "node": ">=12" } }, "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.0.2.tgz", - "integrity": "sha512-JSFLK53NJP22FL/eAGOyKsWbc2G3v+toPMD7Dq9PJKQCvK0i3t8hGkKxe+3YZzwYa+c0kxRHu7uxH3fvO+rsaA==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "dependencies": { - "agentkeepalive": "^4.2.0", - "cacache": "^15.3.0", + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^7.3.1", + "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.4.1", + "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.1.1", - "ssri": "^8.0.1" + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm-registry-fetch/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-registry-fetch/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-registry-fetch/node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/npm-run-path": { @@ -11602,24 +12373,24 @@ } }, "node_modules/npmlog": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", - "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "dev": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", - "gauge": "^4.0.0", + "gauge": "^4.0.3", "set-blocking": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "dependencies": { "boolbase": "^1.0.0" @@ -11659,9 +12430,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", - "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11692,34 +12463,16 @@ "node": ">= 0.4" } }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { "ee-first": "1.1.1" @@ -11940,12 +12693,12 @@ } }, "node_modules/p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "dev": true, "dependencies": { - "@types/retry": "^0.12.0", + "@types/retry": "0.12.0", "retry": "^0.13.1" }, "engines": { @@ -12003,18 +12756,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16" } }, - "node_modules/pacote/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -12241,15 +12982,6 @@ "node": ">=0.10.0" } }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/piscina": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", @@ -12285,19 +13017,28 @@ } }, "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "engines": { "node": ">= 0.12.0" } }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/portfinder/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -12307,6 +13048,18 @@ "ms": "^2.1.1" } }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/postcss": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", @@ -12326,21 +13079,28 @@ } }, "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", - "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.2" + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.0.2" + "postcss": "^8.2" } }, "node_modules/postcss-color-functional-notation": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.2.tgz", - "integrity": "sha512-DXVtwUhIk4f49KK5EGuEdgx4Gnyj6+t2jBSEmxvpIK9QI40tWrpS2Pua8Q7iIZWBrki2QOaeUdEaLPPa91K0RQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12348,14 +13108,18 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-color-hex-alpha": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz", - "integrity": "sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12363,14 +13127,18 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { "postcss": "^8.4" } }, "node_modules/postcss-color-rebeccapurple": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", - "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12378,26 +13146,37 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.3" + "postcss": "^8.2" } }, "node_modules/postcss-custom-media": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", - "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=10.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.3" } }, "node_modules/postcss-custom-properties": { - "version": "12.1.4", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.4.tgz", - "integrity": "sha512-i6AytuTCoDLJkWN/MtAIGriJz3j7UX6bV7Z5t+KgFz+dwZS15/mlTJY1S0kRizlk6ba0V8u8hN50Fz5Nm7tdZw==", + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12405,59 +13184,76 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-custom-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", - "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.4" }, "engines": { - "node": ">=10.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.1.2" + "postcss": "^8.3" } }, "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz", - "integrity": "sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" }, "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-double-position-gradients": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.5.tgz", - "integrity": "sha512-XiZzvdxLOWZwtt/1GgHJYGoD9scog/DD/yI5dcvPrXNdNDEv7T53/6tL7ikl+EM3jcerII5/XIQzd1UHOdTi2w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", "dev": true, "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-env-function": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.5.tgz", - "integrity": "sha512-gPUJc71ji9XKyl0WSzAalBeEA/89kU+XpffpPxSaaaZ1c48OL36r1Ep5R6+9XAPkIiDlSvVAwP4io12q/vTcvA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12509,21 +13305,48 @@ } }, "node_modules/postcss-gap-properties": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", - "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", "dev": true, "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" + } + }, + "node_modules/postcss-html": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.5.0.tgz", + "integrity": "sha512-kCMRWJRHKicpA166kc2lAVUGxDZL324bkj/pVOb6RhjB0Z5Krl7mN0AsVkBhVIRZZirY0lyQXG38HCVaoKVNoA==", + "dev": true, + "peer": true, + "dependencies": { + "htmlparser2": "^8.0.0", + "js-tokens": "^8.0.0", + "postcss": "^8.4.0", + "postcss-safe-parser": "^6.0.0" + }, + "engines": { + "node": "^12 || >=14" } }, + "node_modules/postcss-html/node_modules/js-tokens": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.1.tgz", + "integrity": "sha512-3AGrZT6tuMm1ZWWn9mLXh7XMfi2YtiLNPALCVxBCiUVq0LD1OQMxV/AdS/s7rLJU5o9i/jBZw/N4vXXL5dm29A==", + "dev": true, + "peer": true + }, "node_modules/postcss-image-set-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz", - "integrity": "sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12531,8 +13354,12 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-import": { @@ -12562,9 +13389,9 @@ } }, "node_modules/postcss-lab-function": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.1.0.tgz", - "integrity": "sha512-59uHN/2wRaOd7whDyeaJ82E0kncIEeJkwcmvXFPNus8v1YMhtv2IUo9OtOAncn7sifZVMRsyoPlhxwckTjn4cQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", "dev": true, "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", @@ -12573,8 +13400,12 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-loader": { @@ -12626,7 +13457,7 @@ "node_modules/postcss-media-query-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true }, "node_modules/postcss-modules-extract-imports": { @@ -12689,30 +13520,42 @@ } }, "node_modules/postcss-nesting": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz", - "integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.8" + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" }, "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.3" + "postcss": "^8.2" } }, "node_modules/postcss-overflow-shorthand": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", - "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-page-break": { @@ -12725,9 +13568,9 @@ } }, "node_modules/postcss-place": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.4.tgz", - "integrity": "sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" @@ -12735,8 +13578,12 @@ "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-preset-env": { @@ -12787,18 +13634,22 @@ } }, "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.1.tgz", - "integrity": "sha512-JRoLFvPEX/1YTPxRxp1JO4WxBVXJYrSY7NHeak5LImwJ+VobFMwYDQHvfTXEpcn+7fYIeGkC29zYFhFWIZD8fg==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" }, "engines": { "node": "^12 || ^14 || >=16" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, "peerDependencies": { - "postcss": "^8.4" + "postcss": "^8.2" } }, "node_modules/postcss-replace-overflow-wrap": { @@ -12813,7 +13664,7 @@ "node_modules/postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", "dev": true }, "node_modules/postcss-safe-parser": { @@ -12832,22 +13683,6 @@ "postcss": "^8.3.3" } }, - "node_modules/postcss-scss": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", - "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", - "dev": true, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, "node_modules/postcss-selector-not": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", @@ -12861,9 +13696,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -12962,15 +13797,6 @@ "ansi-styles": "^3.2.0" } }, - "node_modules/pretty-format/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -13111,6 +13937,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/protractor/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, "node_modules/protractor/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -13245,6 +14089,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -13253,15 +14103,15 @@ "optional": true }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -13287,10 +14137,13 @@ } }, "node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" }, @@ -13346,13 +14199,13 @@ } }, "node_modules/raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { - "bytes": "3.1.1", - "http-errors": "1.8.1", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -13511,9 +14364,9 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -13529,9 +14382,9 @@ "dev": true }, "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" @@ -13544,13 +14397,14 @@ "dev": true }, "node_modules/regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -13572,32 +14426,26 @@ } }, "node_modules/regexpu-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", - "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.1.tgz", + "integrity": "sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ==", "dev": true, "dependencies": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { "node": ">=4" } }, - "node_modules/regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", - "dev": true - }, "node_modules/regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "dependencies": { "jsesc": "~0.5.0" @@ -13692,7 +14540,7 @@ "node_modules/require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", "dev": true }, "node_modules/requires-port": { @@ -13744,9 +14592,9 @@ } }, "node_modules/resolve-url-loader/node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -13877,10 +14725,24 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -14086,12 +14948,12 @@ } }, "node_modules/selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", "dev": true, "dependencies": { - "node-forge": "^1.2.0" + "node-forge": "^1" }, "engines": { "node": ">=10" @@ -14112,25 +14974,43 @@ "node": ">=10" } }, - "node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "dev": true, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" @@ -14169,10 +15049,19 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -14205,6 +15094,15 @@ "ms": "2.0.0" } }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-index/node_modules/http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -14239,15 +15137,15 @@ "dev": true }, "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" @@ -14259,14 +15157,11 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true }, "node_modules/setprototypeof": { "version": "1.2.0", @@ -14307,6 +15202,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -14314,15 +15223,12 @@ "dev": true }, "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/slice-ansi": { @@ -14342,9 +15248,9 @@ } }, "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "engines": { "node": ">=12" @@ -14353,18 +15259,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -14376,36 +15270,38 @@ } }, "node_modules/socket.io": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", - "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", - "dev": true + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "dependencies": { + "ws": "~8.11.0" + } }, "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", "dev": true, "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -14424,12 +15320,12 @@ } }, "node_modules/socks": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", - "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dev": true, "dependencies": { - "ip": "^1.1.5", + "ip": "^2.0.0", "smart-buffer": "^4.2.0" }, "engines": { @@ -14438,14 +15334,14 @@ } }, "node_modules/socks-proxy-agent": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", - "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", "dev": true, "dependencies": { "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { "node": ">= 10" @@ -14514,18 +15410,19 @@ } }, "node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { - "source-map": "^0.5.6" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, "node_modules/source-map-support/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" @@ -14565,9 +15462,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "node_modules/spdy": { @@ -14600,14 +15497,11 @@ "wbuf": "^1.7.3" } }, - "node_modules/specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "dev": true, - "bin": { - "specificity": "bin/specificity" - } + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, "node_modules/sshpk": { "version": "1.17.0", @@ -14656,19 +15550,68 @@ } }, "node_modules/streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", "dev": true, "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" }, "engines": { "node": ">=8.0" } }, + "node_modules/streamroller/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -14678,26 +15621,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -14720,6 +15643,14 @@ "node": ">=8" } }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -14731,10 +15662,18 @@ "node": ">=8" } }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" @@ -14773,58 +15712,73 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strong-log-transformer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + }, + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", "dev": true }, "node_modules/stylelint": { - "version": "14.8.3", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.3.tgz", - "integrity": "sha512-aLpskXwSgFEBYbFRKA/BfuyYMGuXNtn2t5GqoffNPSezvw97x/vVNWcZNF0+cwt+LBjfvyq9/MRE3OjInGRgNA==", + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", + "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", "dev": true, "dependencies": { + "@csstools/selector-specificity": "^2.0.2", "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", - "css-functions-list": "^3.0.1", + "colord": "^2.9.3", + "cosmiconfig": "^7.1.0", + "css-functions-list": "^3.1.0", "debug": "^4.3.4", - "execall": "^2.0.0", - "fast-glob": "^3.2.11", - "fastest-levenshtein": "^1.0.12", + "fast-glob": "^3.2.12", + "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.2.0", - "ignore": "^5.2.0", + "ignore": "^5.2.1", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", + "known-css-properties": "^0.26.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.13", + "postcss": "^8.4.19", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.10", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", - "specificity": "^0.4.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "style-search": "^0.1.0", - "supports-hyperlinks": "^2.2.0", + "supports-hyperlinks": "^2.3.0", "svg-tags": "^1.0.0", - "table": "^6.8.0", + "table": "^6.8.1", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.1" + "write-file-atomic": "^4.0.2" }, "bin": { "stylelint": "bin/stylelint.js" @@ -14838,22 +15792,25 @@ } }, "node_modules/stylelint-config-html": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", - "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz", + "integrity": "sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==", "dev": true, "engines": { "node": "^12 || >=14" }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, "peerDependencies": { "postcss-html": "^1.0.0", "stylelint": ">=14.0.0" } }, "node_modules/stylelint-config-prettier": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", - "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz", + "integrity": "sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==", "dev": true, "bin": { "stylelint-config-prettier": "bin/check.js", @@ -14863,7 +15820,7 @@ "node": ">= 12" }, "peerDependencies": { - "stylelint": ">=11.0.0" + "stylelint": ">= 11.x < 15" } }, "node_modules/stylelint-config-prettier-scss": { @@ -14908,6 +15865,53 @@ "stylelint": "^14.0.0" } }, + "node_modules/stylelint-config-recommended-scss/node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "peer": true, + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/stylelint-config-recommended-scss/node_modules/postcss-scss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + } + ], + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.19" + } + }, "node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", @@ -14970,9 +15974,9 @@ } }, "node_modules/stylelint-scss": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", - "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.4.0.tgz", + "integrity": "sha512-Qy66a+/30aylFhPmUArHhVsHOun1qrO93LGT15uzLuLjWS7hKDfpFm34mYo1ndR4MCo8W4bEZM1+AlJRJORaaw==", "dev": true, "dependencies": { "lodash": "^4.17.21", @@ -14982,16 +15986,7 @@ "postcss-value-parser": "^4.1.0" }, "peerDependencies": { - "stylelint": "^14.5.1" - } - }, - "node_modules/stylelint/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" + "stylelint": "^14.5.1 || ^15.0.0" } }, "node_modules/stylelint/node_modules/balanced-match": { @@ -15017,39 +16012,19 @@ } } }, - "node_modules/stylelint/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylelint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "node_modules/stylelint/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, "node_modules/stylelint/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "dev": true, "funding": [ { @@ -15070,15 +16045,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/stylelint/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/stylus": { "version": "0.56.0", "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", @@ -15134,9 +16100,9 @@ } }, "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, "dependencies": { "has-flag": "^4.0.0", @@ -15182,7 +16148,7 @@ "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "node_modules/svg.draggable.js": { @@ -15278,9 +16244,9 @@ } }, "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -15326,6 +16292,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/table/node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -15353,20 +16328,20 @@ } }, "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, "node_modules/tar-stream": { @@ -15385,24 +16360,28 @@ "node": ">=6" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/tar/node_modules/minipass": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.0.tgz", + "integrity": "sha512-ExlilAIS7zJ2EWUMaVXi14H+FnZ18kr17kFkGemMqBx6jW0m8P6XfqwYVPEG53ENlgsED+alVP9ZxC3JzkK23Q==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, "engines": { - "node": ">=10" + "node": ">=8" } }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz", + "integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==", "dev": true, "dependencies": { + "acorn": "^8.5.0", "commander": "^2.20.0", "source-map": "~0.7.2", "source-map-support": "~0.5.20" @@ -15412,27 +16391,19 @@ }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "acorn": "^8.5.0" - }, - "peerDependenciesMeta": { - "acorn": { - "optional": true - } } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "engines": { "node": ">= 10.13.0" @@ -15481,6 +16452,12 @@ "ajv": "^6.9.1" } }, + "node_modules/terser-webpack-plugin/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -15505,34 +16482,30 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.4.tgz", + "integrity": "sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/terser/node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -15566,15 +16539,15 @@ "dev": true }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "dependencies": { - "os-tmpdir": "~1.0.2" + "rimraf": "^3.0.0" }, "engines": { - "node": ">=0.6.0" + "node": ">=8.17.0" } }, "node_modules/to-fast-properties": { @@ -15660,47 +16633,18 @@ "typescript": ">=2.0" } }, - "node_modules/ts-node/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ts-node/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", + "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", "dev": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=6" } }, "node_modules/tslib": { @@ -15708,6 +16652,27 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -15764,9 +16729,9 @@ } }, "node_modules/typed-assert": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.8.tgz", - "integrity": "sha512-5NkbXZUlmCE73Fs7gvkp1XXJWHYetPkg60QnQ2NXQmBYNFxbBr2zA8GCtaH4K2s2WhOmSlgiSTmrjrcm5tnM5g==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", "dev": true }, "node_modules/typescript": { @@ -15783,9 +16748,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", "dev": true, "funding": [ { @@ -15824,18 +16789,18 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "engines": { "node": ">=4" @@ -15877,6 +16842,32 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -16014,9 +17005,9 @@ } }, "node_modules/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -16058,12 +17049,12 @@ } }, "node_modules/webdriver-manager": { - "version": "12.1.8", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz", - "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==", + "version": "12.1.9", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz", + "integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==", "dev": true, "dependencies": { - "adm-zip": "^0.4.9", + "adm-zip": "^0.5.2", "chalk": "^1.1.1", "del": "^2.2.0", "glob": "^7.0.3", @@ -16316,19 +17307,10 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/webpack-dev-server/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/webpack-dev-server/node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "dev": true, "dependencies": { "globby": "^11.0.1", @@ -16347,26 +17329,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-dev-server/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webpack-dev-server/node_modules/is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -16376,15 +17338,6 @@ "node": ">=6" } }, - "node_modules/webpack-dev-server/node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", @@ -16404,15 +17357,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-dev-server/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/webpack-dev-server/node_modules/strip-ansi": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", @@ -16471,6 +17415,12 @@ } } }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, "node_modules/webpack/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -16544,15 +17494,18 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/which-module": { @@ -16638,22 +17591,22 @@ "dev": true }, "node_modules/write-file-atomic": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", - "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -16702,9 +17655,9 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "node_modules/yaml": { @@ -16717,26 +17670,35 @@ } }, "node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", + "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { "node": ">=12" } @@ -16750,12419 +17712,30 @@ "node": ">=6" } }, - "node_modules/zone.js": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", - "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", - "dependencies": { - "tslib": "^2.0.0" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.1.tgz", - "integrity": "sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" - } - }, - "@angular-devkit/architect": { - "version": "0.1302.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.3.tgz", - "integrity": "sha512-0m8jMKrFfIqsYt33zTUwSmyekyfuS67hna08RQ6USjzWQSE3z4S8ulCUARSjM6AzdMblX+whfy56nJUpT17NSA==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.3", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/build-angular": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.2.3.tgz", - "integrity": "sha512-cZ2gRcMRgW3t1WCeP+2D/wmr2M+BR/RICAh0wL9irIdypWAzIFt3Z2+2R/HmgAAxoEkdUMIfB9AnkYmwRVgFeA==", - "dev": true, - "requires": { - "@ampproject/remapping": "1.1.1", - "@angular-devkit/architect": "0.1302.3", - "@angular-devkit/build-webpack": "0.1302.3", - "@angular-devkit/core": "13.2.3", - "@babel/core": "7.16.12", - "@babel/generator": "7.16.8", - "@babel/helper-annotate-as-pure": "7.16.7", - "@babel/plugin-proposal-async-generator-functions": "7.16.8", - "@babel/plugin-transform-async-to-generator": "7.16.8", - "@babel/plugin-transform-runtime": "7.16.10", - "@babel/preset-env": "7.16.11", - "@babel/runtime": "7.16.7", - "@babel/template": "7.16.7", - "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.2.3", - "ansi-colors": "4.1.1", - "babel-loader": "8.2.3", - "babel-plugin-istanbul": "6.1.1", - "browserslist": "^4.9.1", - "cacache": "15.3.0", - "circular-dependency-plugin": "5.2.2", - "copy-webpack-plugin": "10.2.1", - "core-js": "3.20.3", - "critters": "0.0.16", - "css-loader": "6.5.1", - "esbuild": "0.14.14", - "esbuild-wasm": "0.14.14", - "glob": "7.2.0", - "https-proxy-agent": "5.0.0", - "inquirer": "8.2.0", - "jsonc-parser": "3.0.0", - "karma-source-map-support": "1.4.0", - "less": "4.1.2", - "less-loader": "10.2.0", - "license-webpack-plugin": "4.0.1", - "loader-utils": "3.2.0", - "mini-css-extract-plugin": "2.5.3", - "minimatch": "3.0.4", - "open": "8.4.0", - "ora": "5.4.1", - "parse5-html-rewriting-stream": "6.0.1", - "piscina": "3.2.0", - "postcss": "8.4.5", - "postcss-import": "14.0.2", - "postcss-loader": "6.2.1", - "postcss-preset-env": "7.2.3", - "regenerator-runtime": "0.13.9", - "resolve-url-loader": "5.0.0", - "rxjs": "6.6.7", - "sass": "1.49.0", - "sass-loader": "12.4.0", - "semver": "7.3.5", - "source-map-loader": "3.0.1", - "source-map-support": "0.5.21", - "stylus": "0.56.0", - "stylus-loader": "6.2.0", - "terser": "5.10.0", - "text-table": "0.2.0", - "tree-kill": "1.2.2", - "tslib": "2.3.1", - "webpack": "5.67.0", - "webpack-dev-middleware": "5.3.0", - "webpack-dev-server": "4.7.3", - "webpack-merge": "5.8.0", - "webpack-subresource-integrity": "5.1.0" - }, - "dependencies": { - "@ampproject/remapping": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", - "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" - } - }, - "@babel/core": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", - "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "@angular-devkit/build-webpack": { - "version": "0.1302.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1302.3.tgz", - "integrity": "sha512-+JYH1lWU0UOjaWYxpoR2VLsdcb6nG9Gv+M1gH+kT0r2sAKOFaHnrksbOvca3EhDoaMa2b9LSGEE0OcSHWnN+eQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1302.3", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/core": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.3.tgz", - "integrity": "sha512-/47RA8qmWzeS60xSdaprIn1MiSv0Iw83t0M9/ENH7irFS5vMAq62NCcwiWXH59pZmvvLbF+7xy/RgYUZLr4nHQ==", - "dev": true, - "requires": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/schematics": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.3.tgz", - "integrity": "sha512-+dyC4iKV0huvpjiuz4uyjLNK3FsCIp/Ghv5lXvhG6yok/dCAubsJItJOxi6G16aVCzG/E9zbsDfm9fNMyVOkgQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.3", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-eslint/builder": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-13.2.0.tgz", - "integrity": "sha512-7V/bJwkMX+1OzxNUMxiOGdfdksjBmzCdPfxNEHXrT4la+vQQxg1oaIsgx74OkDqFTBv6NJO4PK8Flm9/QE4CDw==", - "dev": true, - "requires": { - "@nrwl/devkit": "13.1.3" - } - }, - "@angular-eslint/bundled-angular-compiler": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.2.0.tgz", - "integrity": "sha512-ZA9JPERpeSo+G/bmp8GS/WjBbYkPDVzN6IINHz9SVdv//LWE58yymFFjRabHJx46iAEOe8P0CoKduuJWtEvNrQ==", - "dev": true - }, - "@angular-eslint/eslint-plugin": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-13.2.0.tgz", - "integrity": "sha512-8qscIBc4montFQ52+XfBk7qR675oXV8mvRpjDh3cTfIZCzV6CSbbbH2iLp/9egnn0Pgy2ZUIsWgcsbK4W3+4bw==", - "dev": true, - "requires": { - "@angular-eslint/utils": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0" - } - }, - "@angular-eslint/eslint-plugin-template": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-13.2.0.tgz", - "integrity": "sha512-Ej9bNGbpf8oaQGglPVWAQkBSIHhQv0FeJ/vVnB2fhHUoK9BbkGUYpjAFSSRlnMhl6ktPWKCX3yJiW+F4uIUTIw==", - "dev": true, - "requires": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0", - "aria-query": "^4.2.2", - "axobject-query": "^2.2.0" - }, - "dependencies": { - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - } - } - }, - "@angular-eslint/schematics": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", - "integrity": "sha512-wxpfZ1L8Twa9Jfwqis7fY6jZvORqAv1k+9m1P6+6/l8gIm/8RYwD44o9Qm2MGt7YClnVthpCDhvfprcoKq6zgA==", - "dev": true, - "requires": { - "@angular-eslint/eslint-plugin": "13.2.0", - "@angular-eslint/eslint-plugin-template": "13.2.0", - "ignore": "5.2.0", - "strip-json-comments": "3.1.1", - "tmp": "0.2.1" - }, - "dependencies": { - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - } - } - }, - "@angular-eslint/template-parser": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", - "integrity": "sha512-dv+bBFzvA4q/KiEDpO7BDUmB1EpVW0P0sX6OSs2eRzViEuXztkatQ4XOmgzuxP0+E1+zl/mK3F7uBDNXiaah9g==", - "dev": true, - "requires": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "eslint-scope": "^5.1.0" - } - }, - "@angular-eslint/utils": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-13.2.0.tgz", - "integrity": "sha512-ywkk+pVLaDLwzNKUcc/xWtJC4Bkm+qRrMOR8ZX3q84E5RTvIvc8IcPFBE4ey3lnGe+nE44OEGuLedCo9vn1Meg==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0" - } - }, - "@angular-slider/ngx-slider": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@angular-slider/ngx-slider/-/ngx-slider-2.0.4.tgz", - "integrity": "sha512-EccMcGlb2bJJWikXZBjRwdWgRiYYmWd3UCgf8l3KAlyqPAxPVgxI73wqipp4/nZwidq53a9s3OB+KC79enfM2A==", - "requires": { - "detect-passive-events": "^2.0.3", - "rxjs": "^6.5.2", - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@angular/animations": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.2.2.tgz", - "integrity": "sha512-qX8LAMuCJaueHBVyuwKtqunx96G0Dr26k7y5Z03VTcscYst4Ib4V2d4i5dwn3HS82DehFdO86cm3Hi2PqE/qww==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/cdk": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.2.2.tgz", - "integrity": "sha512-cT5DIaz+NI9IGb3X61Wh26+L6zdRcOXT1BP37iRbK2Qa2qM8/0VNeK6hrBBIblyoHKR/WUmRlS8XYf6mmArpZw==", - "requires": { - "parse5": "^5.0.0", - "tslib": "^2.3.0" + "engines": { + "node": ">=10" }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/cli": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.3.tgz", - "integrity": "sha512-QsakxpdQuO67u4fQNuOASqabYUO9gJb/5CpUGpWbuBzru0/9CMEF1CtXoF4EoDiwa5sJMirz3SJMKhtzFlv1cQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1302.3", - "@angular-devkit/core": "13.2.3", - "@angular-devkit/schematics": "13.2.3", - "@schematics/angular": "13.2.3", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.3.3", - "ini": "2.0.0", - "inquirer": "8.2.0", - "jsonc-parser": "3.0.0", - "npm-package-arg": "8.1.5", - "npm-pick-manifest": "6.1.1", - "open": "8.4.0", - "ora": "5.4.1", - "pacote": "12.0.3", - "resolve": "1.22.0", - "semver": "7.3.5", - "symbol-observable": "4.0.0", - "uuid": "8.3.2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@angular/common": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.2.2.tgz", - "integrity": "sha512-56C/bheNLKtTCyQUZCiYtKbBIZN9jj6rjFILPtJCGls3cBCxp7t9tIdoLiQG/wVQRmaxdj1ioLT+sCWz7mLtQw==", - "requires": { - "tslib": "^2.3.0" - }, + "node_modules/zone.js": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", + "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/compiler": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.2.2.tgz", - "integrity": "sha512-XXQtB0/e7pR2LPrHmpEiTU72SX4xxHGy91vYWIj1JCjSn0fYF7vtHzSJPXDvkbnkNow/PXXzJJYaU1ctdMZPcA==", - "requires": { "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/compiler-cli": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.2.tgz", - "integrity": "sha512-tuOIcEEKVIht+mKrj0rtX3I8gc+ByPjzpCZhFQRggxM6xbKJIToO1zERbEGKrZ+sUJ6BB5KLvscDy+Pddy3b8w==", - "dev": true, - "requires": { - "@babel/core": "^7.8.6", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.11.0", - "magic-string": "^0.25.0", - "reflect-metadata": "^0.1.2", - "semver": "^7.0.0", - "sourcemap-codec": "^1.4.8", - "tslib": "^2.3.0", - "yargs": "^17.2.1" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } } }, - "@angular/core": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.2.2.tgz", - "integrity": "sha512-zpctw0BxIVOsRFnckchK15SD1L8tzhf5GzwIDaM6+VylDQj1uYkm8mvAjJTQZyUuApomoFet2Rfj7XQPV+cNSQ==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/flex-layout": { - "version": "13.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-13.0.0-beta.38.tgz", - "integrity": "sha512-kcWb7CcoHbvw7fjo/knizWVmSSmvaTnr8v1ML6zOdxu1PK9UPPOcOS8RTm6fy61zoC2LABivP1/6Z2jF5XfpdQ==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/forms": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.2.2.tgz", - "integrity": "sha512-T61W4Ay9X9qhxjc6lLqpNFeHrGKwg2mqdsZ3zIm/c7oKo37mgl9TB5kkrtnS+205r3N2hF4ICnGFZ4a/egUP/g==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/language-service": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.2.2.tgz", - "integrity": "sha512-2P5+wRsbHgpI2rVeFwnsLWxyntUiw8kG9Tqh5BkVDqtQovbYtzFiaMkf5TFz/g938JBBgeRQzvXr1kQhEidAWQ==", - "dev": true - }, - "@angular/material": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-13.2.2.tgz", - "integrity": "sha512-YAjPp2+/wuEOPfkAxdRVdbWHiK4P3DgMZa9qP/NizN2lTXNrftEfD09ZlPIFMZRnnExezJ2LnO7eyELpc1VSKg==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/platform-browser": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.2.2.tgz", - "integrity": "sha512-M7gWC8fFCPc/CRcHCzqe/j7WzwAUMeKt9vwlK633XnesHBoqZdYgbb3YHHc6WPVU0YI09Nb/Hm5sezEKmjUmPg==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/platform-browser-dynamic": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.2.2.tgz", - "integrity": "sha512-lj6xwat0StLp+ROFqXU62upwHQhlxaQi0djhrS+DGKUK0Xu9bkBeaSCfBFgS78jPm1SwL8Xztu9/vuDAHLRrqw==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@angular/router": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.2.2.tgz", - "integrity": "sha512-dt2b9/kGJAkmOqUmUD3aKlp4pGpdqLwB0zmhUYF3ktNEcQaPf4ZjWT/4jhy09gFL+TKOHG5OQW9GxBbhWI4bSg==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true - }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/compat-data": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", - "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", - "dev": true - }, - "@babel/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", - "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.0.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.17.2", - "@babel/parser": "^7.17.0", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.17.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz", - "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", - "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", - "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", - "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" - } - }, - "@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" - } - }, - "@babel/helpers": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", - "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0" - } - }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", - "dev": true - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", - "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", - "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", - "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.10", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", - "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", - "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/preset-env": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", - "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/runtime-corejs3": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.0.tgz", - "integrity": "sha512-G5FaGZOWORq9zthDjIrjib5XlcddeqLbIiDO3YQsut6j7aGf76xn0umUC/pA6+nApk3hQJF4JzLzg5PCl6ewJg==", - "dev": true, - "requires": { - "core-js-pure": "^3.20.2", - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@cordobo/qrcode": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@cordobo/qrcode/-/qrcode-1.5.0.tgz", - "integrity": "sha512-aZ5n3MYw10t4v68EGvRGE1DL7iWfAiTUy4MSZRoqjHTRYdjX40sYgJf48NZa6zZeXVuJOEB/1Ni9KzS+C/EC0w==", - "requires": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^17.3.1" - } - }, - "@csstools/postcss-progressive-custom-properties": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.1.0.tgz", - "integrity": "sha512-DO76V3295AqhjJZvgeaDP5GAGAat4g6wYfF8X+1n+76MpJat8ffY5bCJ9eSUqFY71nImxXgaDTRYJcRnA9oo7g==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@discoveryjs/json-ext": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", - "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@gar/promisify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", - "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@ngtools/webpack": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.3.tgz", - "integrity": "sha512-wooUZiV92QyoeFxkhqIwH/cfiAAAn+l8fEEuaaEIfJtpjpbShvvlboEVsqb28soeGiFJfLcmsZM3mUFgsG4QBQ==", - "dev": true, - "requires": {} - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "dev": true, - "requires": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "@npmcli/git": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", - "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", - "dev": true, - "requires": { - "@npmcli/promise-spawn": "^1.3.2", - "lru-cache": "^6.0.0", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^6.1.1", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@npmcli/installed-package-contents": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", - "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", - "dev": true, - "requires": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "@npmcli/node-gyp": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", - "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", - "dev": true - }, - "@npmcli/promise-spawn": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", - "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", - "dev": true, - "requires": { - "infer-owner": "^1.0.4" - } - }, - "@npmcli/run-script": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", - "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", - "dev": true, - "requires": { - "@npmcli/node-gyp": "^1.0.2", - "@npmcli/promise-spawn": "^1.3.2", - "node-gyp": "^8.2.0", - "read-package-json-fast": "^2.0.1" - } - }, - "@nrwl/cli": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.1.7.tgz", - "integrity": "sha512-HVvqYwefizcoVc4xrTgfIC8nfMA9cx5NiaIbZFDIZv12NsdalpA6a2BmmV8Zck+QQSGEHrhTZyt1AqDhA1t+6A==", - "dev": true, - "requires": { - "nx": "14.1.7" - }, - "dependencies": { - "@nrwl/tao": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.1.7.tgz", - "integrity": "sha512-7jzPqEwElMiTKCZm2iuqC5aPIs+IwocWR6Tl1JhC1eK2PWskHOhdgTQM186lfXwPKWH4NJAE3eZFqtECnGDCJg==", - "dev": true, - "requires": { - "nx": "14.1.7" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "nx": { - "version": "14.1.7", - "resolved": "https://registry.npmjs.org/nx/-/nx-14.1.7.tgz", - "integrity": "sha512-Q10PVQ70TFg9HY6KQKJ316B42sVtxmFk/VUdC7ZHKxzDm/JeYf6OaDarQOUZaFZp/EL2brtEsrlWSMTWmk3r4w==", - "dev": true, - "requires": { - "@nrwl/cli": "14.1.7", - "@nrwl/tao": "14.1.7", - "@parcel/watcher": "2.0.4", - "@swc-node/register": "^1.4.2", - "@swc/core": "^1.2.173", - "chalk": "4.1.0", - "chokidar": "^3.5.1", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^7.0.2", - "dotenv": "~10.0.0", - "enquirer": "~2.3.6", - "fast-glob": "3.2.7", - "figures": "3.2.0", - "flat": "^5.0.2", - "fs-extra": "^10.1.0", - "glob": "7.1.4", - "ignore": "^5.0.4", - "jsonc-parser": "3.0.0", - "minimatch": "3.0.4", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", - "semver": "7.3.4", - "string-width": "^4.2.3", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^3.9.0", - "tslib": "^2.3.0", - "v8-compile-cache": "2.3.0", - "yargs": "^17.4.0", - "yargs-parser": "21.0.1" - } - }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - } - } - }, - "@nrwl/devkit": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", - "integrity": "sha512-TAAsZJvVc/obeH0rZKY6miVhyM2GHGb8qIWp9MAIdLlXf4VDcNC7rxwb5OrGVSwuTTjqGYBGPUx0yEogOOJthA==", - "dev": true, - "requires": { - "@nrwl/tao": "13.1.3", - "ejs": "^3.1.5", - "ignore": "^5.0.4", - "rxjs": "^6.5.4", - "semver": "7.3.4", - "tslib": "^2.0.0" - }, - "dependencies": { - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@nrwl/tao": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", - "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", - "dev": true, - "requires": { - "chalk": "4.1.0", - "enquirer": "~2.3.6", - "fs-extra": "^9.1.0", - "jsonc-parser": "3.0.0", - "nx": "13.1.3", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", - "semver": "7.3.4", - "tmp": "~0.2.1", - "tslib": "^2.0.0", - "yargs-parser": "20.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "yargs-parser": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", - "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", - "dev": true - } - } - }, - "@parcel/watcher": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", - "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", - "dev": true, - "requires": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" - } - }, - "@schematics/angular": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.3.tgz", - "integrity": "sha512-jloooGC7eco9AKxlIMMqFRptJYzZ0jNRBStWOp2dCISg6rmOKqpxbsHLtYFQIT1PnlomSxtKDAgYGQMDi9zhXw==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.3", - "@angular-devkit/schematics": "13.2.3", - "jsonc-parser": "3.0.0" - } - }, - "@socket.io/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", - "dev": true - }, - "@swc-node/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.9.0.tgz", - "integrity": "sha512-vRnvsMtL9OxybA/Wun1ZhlDvB6MNs4Zujnina0VKdGk+yI6s87KUhdTcbAY6dQMZhQTLFiC1Lnv/BuwCKcCEug==", - "dev": true, - "requires": { - "@swc/core": "^1.2.172" - } - }, - "@swc-node/register": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.5.1.tgz", - "integrity": "sha512-6IL5s4QShKGs08qAeNou3rDA3gbp2WHk6fo0XnJXQn/aC9k6FnVBbj/thGOIEDtgNhC/DKpZT8tCY1LpQnOZFg==", - "dev": true, - "requires": { - "@swc-node/core": "^1.9.0", - "@swc-node/sourcemap-support": "^0.2.0", - "colorette": "^2.0.16", - "debug": "^4.3.4", - "pirates": "^4.0.5", - "tslib": "^2.4.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - } - } - }, - "@swc-node/sourcemap-support": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.2.0.tgz", - "integrity": "sha512-FNrxdI6XMYfoNt81L8eFKEm1d8P82I1nPwS3MrnBGzZoMWB+seQhQK+iN6M5RreJxXbfZw5lF86LRjHEQeGMqg==", - "dev": true, - "requires": { - "source-map-support": "^0.5.21" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "@swc/core": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.189.tgz", - "integrity": "sha512-S5cKX4ECMSfW78DLFgnlilJZgjrFRYwPslrrwpLl3gpwh+Qo72/Mhn71u7G/5xXW+T/xW5GwPccHfCk+k72uUg==", - "dev": true, - "requires": { - "@swc/core-android-arm-eabi": "1.2.189", - "@swc/core-android-arm64": "1.2.189", - "@swc/core-darwin-arm64": "1.2.189", - "@swc/core-darwin-x64": "1.2.189", - "@swc/core-freebsd-x64": "1.2.189", - "@swc/core-linux-arm-gnueabihf": "1.2.189", - "@swc/core-linux-arm64-gnu": "1.2.189", - "@swc/core-linux-arm64-musl": "1.2.189", - "@swc/core-linux-x64-gnu": "1.2.189", - "@swc/core-linux-x64-musl": "1.2.189", - "@swc/core-win32-arm64-msvc": "1.2.189", - "@swc/core-win32-ia32-msvc": "1.2.189", - "@swc/core-win32-x64-msvc": "1.2.189" - } - }, - "@swc/core-android-arm-eabi": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.189.tgz", - "integrity": "sha512-0kN3Le6QzFFz+Lc6a/tf/RkJXubWwWaHxF4c0bVm4AKIFf4nRlUCEqEkjdVaZvL92rpBMHaEEBuIIz3T8DqTTQ==", - "dev": true, - "optional": true - }, - "@swc/core-android-arm64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-android-arm64/-/core-android-arm64-1.2.189.tgz", - "integrity": "sha512-smsb+YkDw2OKwg66Z63E/G4NlFApDbsiOPmZXFZbtZbNBD9v+wnk6WVA//XR1bdUI9VbzNKlMPKJxQTE685QDw==", - "dev": true, - "optional": true - }, - "@swc/core-darwin-arm64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.189.tgz", - "integrity": "sha512-OGjZRkTulKirJMLYbW9etb59lA9ueDXVwYRVD9SrNh8tRMTf0Nq+SUT/C3LVhBBGC4KSdWOzBAYbDTTdsnY++Q==", - "dev": true, - "optional": true - }, - "@swc/core-darwin-x64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.2.189.tgz", - "integrity": "sha512-BEcxnBHx51514Voe2dn/y1y5H9VNyw7Zpp9+mPekZqx5o/onPD5wZ1ZfAsPrA4UlvM3v16u6ITE/cLawJ/GdAQ==", - "dev": true, - "optional": true - }, - "@swc/core-freebsd-x64": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.189.tgz", - "integrity": "sha512-B6g2NWeh2iw6WPOaM19Uj3VE4se6alT265kWibLUshjcofRfnYT1lNhhkrF1D0EVnpC8I96I/xXNQo4Am9z4zQ==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm-gnueabihf": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.189.tgz", - "integrity": "sha512-6WhPG9pyN5AahQTVQk8MoN1I9Z/Ytfqizuie1wV7mW8FMNmMkiJvBekKtE6ftxu80Hqa34r86WfEwmJKm5MqYw==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-gnu": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.189.tgz", - "integrity": "sha512-frJTGnxsDe7h2d7BtnBivOdOJTtabkQIIZmHd4JHqafcoekI6teyveIax17axLyimvWl278yTo3hf7ePagD/eg==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-musl": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.189.tgz", - "integrity": "sha512-27K38LoZpME5lojDJIUNo7zdTDwAKLm0BMQ7HXWcYOyiDAekhSidI+SrBWxCfLzfuClhFu6/VE3E7j32VFJsiA==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-gnu": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.189.tgz", - "integrity": "sha512-Ha5oJKOyQm9w7+e+WdRm4ypijzEmglWZGtgBR6vV6ViqqHcTBAU4nG87ex7y7AS9p+Cbc6EOSR9X1qIB8KxtbA==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-musl": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.189.tgz", - "integrity": "sha512-/p5yXa9HEzpVEuE4ivkW1IvwyYu6fT+L2OvVEs5oXIba80F0Wjy7InWqaa83gwrdMH+bXV6loG8LzZUZu/lpjA==", - "dev": true, - "optional": true - }, - "@swc/core-win32-arm64-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.189.tgz", - "integrity": "sha512-o/1ueM6/sifNjYnO6NMEXB895spVfJs5oQIPxQG9vJ/4zWLw8YmAx+u1xJY+XGyK6gnroHt7yPiS87qWdbeF6w==", - "dev": true, - "optional": true - }, - "@swc/core-win32-ia32-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.189.tgz", - "integrity": "sha512-YDwRkzykaf+dw5Z7u189cC/Tttkn2NVV84hrGL3LbVuh7wT5PaDhZs4Yz4unZQSlPV12olmZWgNr/i27h5wlpg==", - "dev": true, - "optional": true - }, - "@swc/core-win32-x64-msvc": { - "version": "1.2.189", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.189.tgz", - "integrity": "sha512-Nge8Z/ZkAp5p5No50yBDpBG7+ZYaVWGSuwtPj6OJe7orzvDCEm9GgcVE6J9GEjbclSWlCH8B8lUe17GaKRZHbg==", - "dev": true, - "optional": true - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", - "dev": true - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", - "dev": true - }, - "@types/eslint": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", - "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/http-proxy": { - "version": "1.17.8", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", - "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/jasmine": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz", - "integrity": "sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", - "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", - "dev": true, - "requires": { - "@types/jasmine": "*" - } - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/node": { - "version": "12.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.7.tgz", - "integrity": "sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/prettier": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.1.tgz", - "integrity": "sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "3.0.19", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.19.tgz", - "integrity": "sha512-OFUilxQg+rWL2FMxtmIgCkUDlJB6pskkpvmew7yeXfzzsOBb5rc+y2+DjHm+r3r1ZPPcJefK3DveNSYWGiy68g==", - "dev": true - }, - "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", - "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.17.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "debug": "^4.3.2" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - } - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "dependencies": { - "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - } - } - }, - "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "agentkeepalive": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz", - "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "angularx-qrcode": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-13.0.3.tgz", - "integrity": "sha512-KzalrRy2MczdApSq29k2TBOml6taQ7NqdgZDNgPSQBd5/27+l3xHkOkhhDOmGruSyCcT2KdB8qeOT/6K48c+xw==", - "requires": { - "@cordobo/qrcode": "1.5.0", - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "apexcharts": { - "version": "3.33.1", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.33.1.tgz", - "integrity": "sha512-5aVzrgJefd8EH4w7oRmuOhA3+cxJxQg27cYg3ANVGvPCOB4AY3mVVNtFHRFaIq7bv8ws4GRaA9MWfzoWQw3MPQ==", - "requires": { - "svg.draggable.js": "^2.2.2", - "svg.easing.js": "^2.0.0", - "svg.filter.js": "^2.0.2", - "svg.pathmorphing.js": "^0.1.3", - "svg.resize.js": "^1.4.3", - "svg.select.js": "^3.0.1" - } - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "are-we-there-yet": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", - "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - } - } - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", - "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", - "dev": true, - "requires": { - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001297", - "fraction.js": "^4.1.2", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, - "babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", - "dev": true, - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", - "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", - "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.21.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", - "dev": true, - "requires": { - "bytes": "3.1.1", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", - "type-is": "~1.6.18" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - } - }, - "browserstack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", - "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", - "dev": true - }, - "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", - "dev": true - }, - "cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "dev": true, - "requires": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "caniuse-lite": { - "version": "1.0.30001450", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", - "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "circular-dependency-plugin": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", - "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "requires": {} - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true - }, - "cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "requires": { - "is-regexp": "^2.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", - "dev": true - }, - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", - "dev": true, - "requires": { - "is-what": "^3.14.1" - } - }, - "copy-webpack-plugin": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.1.tgz", - "integrity": "sha512-nr81NhCAIpAWXGCK5thrKmfCQ6GDY0L5RN0U+BnIn/7Us55+UCex5ANNsNKmIVtDRnk0Ecf+/kzp9SUVrrBMLg==", - "dev": true, - "requires": { - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.1", - "globby": "^12.0.2", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "dependencies": { - "array-union": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", - "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globby": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", - "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", - "dev": true, - "requires": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.9", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "core-js": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true - }, - "core-js-compat": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz", - "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==", - "dev": true, - "requires": { - "browserslist": "^4.19.1", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-js-pure": { - "version": "3.22.6", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.6.tgz", - "integrity": "sha512-u5yG2VL6NKXz9BZHr9RAm6eWD1DTNjG7jJnJgLGR+Im0whdPcPXqwqxd+dcUrZvpvPan5KMgn/3pI+Q/aGqPOA==", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "css-functions-list": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", - "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", - "dev": true - }, - "css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" - } - }, - "css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true, - "requires": {} - }, - "css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "dev": true - }, - "cssdb": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", - "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - } - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", - "dev": true - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true - }, - "dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", - "dev": true - }, - "detect-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz", - "integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug==" - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "detect-passive-events": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-passive-events/-/detect-passive-events-2.0.3.tgz", - "integrity": "sha512-QN/1X65Axis6a9D8qg8Py9cwY/fkWAmAH/edTbmLMcv4m5dboLJ7LcAi8CfaCON2tjk904KwKX/HTdsHC6yeRg==", - "requires": { - "detect-it": "^4.0.1" - } - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "dijkstrajs": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", - "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, - "dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dev": true, - "requires": { - "jake": "^10.8.5" - } - }, - "electron-to-chromium": { - "version": "1.4.68", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", - "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "encode-utf8": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true - }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "optional": true, - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", - "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", - "dev": true, - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", - "ws": "~8.2.3" - } - }, - "engine.io-parser": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", - "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", - "dev": true, - "requires": { - "@socket.io/base64-arraybuffer": "~1.0.2" - } - }, - "enhanced-resolve": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", - "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", - "dev": true - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - }, - "env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true - }, - "err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dev": true, - "optional": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "esbuild": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.14.tgz", - "integrity": "sha512-aiK4ddv+uui0k52OqSHu4xxu+SzOim7Rlz4i25pMEiC8rlnGU0HJ9r+ZMfdWL5bzifg+nhnn7x4NSWTeehYblg==", - "dev": true, - "optional": true, - "requires": { - "esbuild-android-arm64": "0.14.14", - "esbuild-darwin-64": "0.14.14", - "esbuild-darwin-arm64": "0.14.14", - "esbuild-freebsd-64": "0.14.14", - "esbuild-freebsd-arm64": "0.14.14", - "esbuild-linux-32": "0.14.14", - "esbuild-linux-64": "0.14.14", - "esbuild-linux-arm": "0.14.14", - "esbuild-linux-arm64": "0.14.14", - "esbuild-linux-mips64le": "0.14.14", - "esbuild-linux-ppc64le": "0.14.14", - "esbuild-linux-s390x": "0.14.14", - "esbuild-netbsd-64": "0.14.14", - "esbuild-openbsd-64": "0.14.14", - "esbuild-sunos-64": "0.14.14", - "esbuild-windows-32": "0.14.14", - "esbuild-windows-64": "0.14.14", - "esbuild-windows-arm64": "0.14.14" - } - }, - "esbuild-android-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.14.tgz", - "integrity": "sha512-be/Uw6DdpQiPfula1J4bdmA+wtZ6T3BRCZsDMFB5X+k0Gp8TIh9UvmAcqvKNnbRAafSaXG3jPCeXxDKqnc8hFQ==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.14.tgz", - "integrity": "sha512-BEexYmjWafcISK8cT6O98E3TfcLuZL8DKuubry6G54n2+bD4GkoRD6HYUOnCkfl2p7jodA+s4369IjSFSWjtHg==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.14.tgz", - "integrity": "sha512-tnBKm41pDOB1GtZ8q/w26gZlLLRzVmP8fdsduYjvM+yFD7E2DLG4KbPAqFMWm4Md9B+DitBglP57FY7AznxbTg==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.14.tgz", - "integrity": "sha512-Q9Rx6sgArOHalQtNwAaIzJ6dnQ8A+I7f/RsQsdkS3JrdzmnlFo8JEVofTmwVQLoIop7OKUqIVOGP4PoQcwfVMA==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.14.tgz", - "integrity": "sha512-TJvq0OpLM7BkTczlyPIphcvnwrQwQDG1HqxzoYePWn26SMUAlt6wrLnEvxdbXAvNvDLVzG83kA+JimjK7aRNBA==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.14.tgz", - "integrity": "sha512-h/CrK9Baimt5VRbu8gqibWV7e1P9l+mkanQgyOgv0Ng3jHT1NVFC9e6rb1zbDdaJVmuhWX5xVliUA5bDDCcJeg==", - "dev": true, - "optional": true - }, - "esbuild-linux-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.14.tgz", - "integrity": "sha512-IC+wAiIg/egp5OhQp4W44D9PcBOH1b621iRn1OXmlLzij9a/6BGr9NMIL4CRwz4j2kp3WNZu5sT473tYdynOuQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.14.tgz", - "integrity": "sha512-gxpOaHOPwp7zSmcKYsHrtxabScMqaTzfSQioAMUaB047YiMuDBzqVcKBG8OuESrYkGrL9DDljXr/mQNg7pbdaQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.14.tgz", - "integrity": "sha512-6QVul3RI4M5/VxVIRF/I5F+7BaxzR3DfNGoqEVSCZqUbgzHExPn+LXr5ly1C7af2Kw4AHpo+wDqx8A4ziP9avw==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.14.tgz", - "integrity": "sha512-4Jl5/+xoINKbA4cesH3f4R+q0vltAztZ6Jm8YycS8lNhN1pgZJBDxWfI6HUMIAdkKlIpR1PIkA9aXQgZ8sxFAg==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.14.tgz", - "integrity": "sha512-BitW37GxeebKxqYNl4SVuSdnIJAzH830Lr6Mkq3pBHXtzQay0vK+IeOR/Ele1GtNVJ+/f8wYM53tcThkv5SC5w==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.14.tgz", - "integrity": "sha512-vLj6p76HOZG3wfuTr5MyO3qW5iu8YdhUNxuY+tx846rPo7GcKtYSPMusQjeVEfZlJpSYoR+yrNBBxq+qVF9zrw==", - "dev": true, - "optional": true - }, - "esbuild-netbsd-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.14.tgz", - "integrity": "sha512-fn8looXPQhpVqUyCBWUuPjesH+yGIyfbIQrLKG05rr1Kgm3rZD/gaYrd3Wpmf5syVZx70pKZPvdHp8OTA+y7cQ==", - "dev": true, - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.14.tgz", - "integrity": "sha512-HdAnJ399pPff3SKbd8g+P4o5znseni5u5n5rJ6Z7ouqOdgbOwHe2ofZbMow17WMdNtz1IyOZk2Wo9Ve6/lZ4Rg==", - "dev": true, - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.14.tgz", - "integrity": "sha512-bmDHa99ulsGnYlh/xjBEfxoGuC8CEG5OWvlgD+pF7bKKiVTbtxqVCvOGEZeoDXB+ja6AvHIbPxrEE32J+m5nqQ==", - "dev": true, - "optional": true - }, - "esbuild-wasm": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.14.tgz", - "integrity": "sha512-qTjK4MWnYtQHCMGg2qDUqeFYXfVvYq5qJkQTIsOV4VZCknoYePVaDTG9ygEB9Ct0kc0DWs7IrS6Ja+GjY62Kzw==", - "dev": true - }, - "esbuild-windows-32": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.14.tgz", - "integrity": "sha512-6tVooQcxJCNenPp5GHZBs/RLu31q4B+BuF4MEoRxswT+Eq2JGF0ZWDRQwNKB8QVIo3t6Svc5wNGez+CwKNQjBg==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.14.tgz", - "integrity": "sha512-kl3BdPXh0/RD/dad41dtzj2itMUR4C6nQbXQCyYHHo4zoUoeIXhpCrSl7BAW1nv5EFL8stT1V+TQVXGZca5A2A==", - "dev": true, - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.14.14", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.14.tgz", - "integrity": "sha512-dCm1wTOm6HIisLanmybvRKvaXZZo4yEVrHh1dY0v582GThXJOzuXGja1HIQgV09RpSHYRL3m4KoUBL00l6SWEg==", - "dev": true, - "optional": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "eslint": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", - "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, - "requires": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true - }, - "eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "requires": { - "clone-regexp": "^2.1.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "express": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", - "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", - "dev": true, - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.6", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true - }, - "fraction.js": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz", - "integrity": "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==", - "dev": true - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gauge": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", - "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "globjoin": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", - "dev": true - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", - "dev": true, - "requires": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true - }, - "hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true - }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, - "http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "http-proxy-middleware": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz", - "integrity": "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==", - "dev": true, - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "ignore-walk": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", - "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "dev": true, - "optional": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, - "import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true - }, - "inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.2.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA==", - "dev": true - }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "dev": true - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dev": true, - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", - "dev": true, - "requires": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "dependencies": { - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", - "dev": true - } - } - }, - "jasmine-core": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.8.0.tgz", - "integrity": "sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg==", - "dev": true - }, - "jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", - "dev": true, - "requires": { - "colors": "1.4.0" - } - }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", - "dev": true - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "jszip": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", - "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "karma": { - "version": "6.3.16", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", - "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", - "dev": true, - "requires": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "colors": "1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.2.0", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, - "karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "requires": { - "which": "^1.2.1" - } - }, - "karma-coverage-istanbul-reporter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", - "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^3.0.2", - "minimatch": "^3.0.4" - } - }, - "karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", - "dev": true, - "requires": { - "jasmine-core": "^3.6.0" - } - }, - "karma-jasmine-html-reporter": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", - "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", - "dev": true, - "requires": {} - }, - "karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dev": true, - "requires": { - "source-map-support": "^0.5.5" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", - "dev": true - }, - "known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", - "dev": true - }, - "less": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", - "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", - "dev": true, - "requires": { - "copy-anything": "^2.0.1", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^2.5.2", - "parse-node-version": "^1.0.1", - "source-map": "~0.6.0", - "tslib": "^2.3.0" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "optional": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "less-loader": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz", - "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==", - "dev": true, - "requires": { - "klona": "^2.0.4" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "license-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-SQum9mg3BgnY5BK+2KYl4W7pk9b26Q8tW2lTsO6tidD0/Ds9ksdXvp3ip2s9LqDjj5gtBMyWRfOPZptWj4PfCg==", - "dev": true, - "requires": { - "webpack-sources": "^3.0.0" - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "lint-staged": { - "version": "12.4.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz", - "integrity": "sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==", - "dev": true, - "requires": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.16", - "commander": "^8.3.0", - "debug": "^4.3.3", - "execa": "^5.1.1", - "lilconfig": "2.0.4", - "listr2": "^4.0.1", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.0", - "pidtree": "^0.5.0", - "string-argv": "^0.3.1", - "supports-color": "^9.2.1", - "yaml": "^1.10.2" - }, - "dependencies": { - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true - }, - "supports-color": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", - "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", - "dev": true - } - } - }, - "listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.5", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - } - } - }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true - }, - "loader-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", - "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", - "dev": true, - "requires": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", - "rfdc": "^1.3.0", - "streamroller": "^3.0.2" - } - }, - "loglevel": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", - "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", - "dev": true - }, - "loglevel-colored-level-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "loglevel": "^1.4.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "dev": true, - "requires": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - } - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "mathml-tag-names": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", - "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true - }, - "memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", - "dev": true, - "requires": { - "fs-monkey": "1.0.3" - } - }, - "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "dependencies": { - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", - "integrity": "sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==", - "dev": true, - "requires": { - "schema-utils": "^4.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "dependencies": { - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - } - } - }, - "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "dev": true, - "requires": { - "encoding": "^0.1.12", - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - } - }, - "minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", - "dev": true, - "requires": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "needle": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", - "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", - "dev": true, - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "ng-apexcharts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.0.tgz", - "integrity": "sha512-HSLPHrSH4CYn6crU8RsbZzuecKoXZ7a8i0cGdq8yDZ9DwaEauiJl5WWCWKajHLbwloVEdqoAsKN04TcvIP0ulQ==", - "requires": { - "tslib": "^2.0.0" - } - }, - "ngx-color-picker": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.0.tgz", - "integrity": "sha512-SY5KoZka/uq2MNhUAKfJXQjjS2TFvKDJHbsCxfnjKjS/VHx8VVeTJpnt5wuuewzRzLxfOm5y2Fw8/HTPEPtRkA==", - "requires": { - "tslib": "^2.3.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "nice-napi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", - "dev": true, - "optional": true, - "requires": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" - } - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true - }, - "node-forge": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", - "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", - "dev": true - }, - "node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", - "dev": true - }, - "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true - }, - "npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-install-checks": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", - "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", - "dev": true, - "requires": { - "semver": "^7.1.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "npm-package-arg": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", - "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "semver": "^7.3.4", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", - "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", - "dev": true, - "requires": { - "glob": "^7.1.6", - "ignore-walk": "^4.0.1", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", - "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", - "dev": true, - "requires": { - "npm-install-checks": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^8.1.2", - "semver": "^7.3.4" - } - }, - "npm-registry-fetch": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", - "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", - "dev": true, - "requires": { - "make-fetch-happen": "^10.0.1", - "minipass": "^3.1.6", - "minipass-fetch": "^1.4.1", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^8.1.5" - }, - "dependencies": { - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true - }, - "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "lru-cache": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.3.1.tgz", - "integrity": "sha512-nX1x4qUrKqwbIAhv4s9et4FIUVzNOpeY07bsjGUy8gwJrXH/wScImSQqXErmo/b2jZY2r0mohbLA9zVj7u1cNw==", - "dev": true - }, - "make-fetch-happen": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.0.2.tgz", - "integrity": "sha512-JSFLK53NJP22FL/eAGOyKsWbc2G3v+toPMD7Dq9PJKQCvK0i3t8hGkKxe+3YZzwYa+c0kxRHu7uxH3fvO+rsaA==", - "dev": true, - "requires": { - "agentkeepalive": "^4.2.0", - "cacache": "^15.3.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.3.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.4.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.1.1", - "ssri": "^8.0.1" - } - } - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "npmlog": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", - "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", - "dev": true, - "requires": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.0", - "set-blocking": "^2.0.0" - } - }, - "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "nx": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/nx/-/nx-13.1.3.tgz", - "integrity": "sha512-clM0NQhQKYkqcNz2E3uYRMLwhp2L/9dBhJhQi9XBX4IAyA2gWAomhRIlLm5Xxg3g4h1xwSpP3eJ5t89VikY8Pw==", - "dev": true, - "requires": { - "@nrwl/cli": "*" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", - "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", - "dev": true - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" - }, - "dependencies": { - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true - } - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pacote": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", - "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", - "dev": true, - "requires": { - "@npmcli/git": "^2.1.0", - "@npmcli/installed-package-contents": "^1.0.6", - "@npmcli/promise-spawn": "^1.2.0", - "@npmcli/run-script": "^2.0.0", - "cacache": "^15.0.5", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.3", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^3.0.0", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^12.0.0", - "promise-retry": "^2.0.1", - "read-package-json-fast": "^2.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.1.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true - }, - "parse5-html-rewriting-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", - "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", - "dev": true, - "requires": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" - }, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } - } - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - }, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } - } - }, - "parse5-sax-parser": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", - "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - }, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pidtree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", - "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "piscina": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", - "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", - "dev": true, - "requires": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0", - "nice-napi": "^1.0.2" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "postcss": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", - "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", - "dev": true, - "requires": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" - } - }, - "postcss-attribute-case-insensitive": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", - "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.2" - } - }, - "postcss-color-functional-notation": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.2.tgz", - "integrity": "sha512-DXVtwUhIk4f49KK5EGuEdgx4Gnyj6+t2jBSEmxvpIK9QI40tWrpS2Pua8Q7iIZWBrki2QOaeUdEaLPPa91K0RQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-hex-alpha": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz", - "integrity": "sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-rebeccapurple": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", - "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-custom-media": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", - "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true, - "requires": {} - }, - "postcss-custom-properties": { - "version": "12.1.4", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.4.tgz", - "integrity": "sha512-i6AytuTCoDLJkWN/MtAIGriJz3j7UX6bV7Z5t+KgFz+dwZS15/mlTJY1S0kRizlk6ba0V8u8hN50Fz5Nm7tdZw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-custom-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", - "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-dir-pseudo-class": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz", - "integrity": "sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "postcss-double-position-gradients": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.5.tgz", - "integrity": "sha512-XiZzvdxLOWZwtt/1GgHJYGoD9scog/DD/yI5dcvPrXNdNDEv7T53/6tL7ikl+EM3jcerII5/XIQzd1UHOdTi2w==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-env-function": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.5.tgz", - "integrity": "sha512-gPUJc71ji9XKyl0WSzAalBeEA/89kU+XpffpPxSaaaZ1c48OL36r1Ep5R6+9XAPkIiDlSvVAwP4io12q/vTcvA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "requires": {} - }, - "postcss-gap-properties": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", - "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", - "dev": true, - "requires": {} - }, - "postcss-image-set-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz", - "integrity": "sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-import": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", - "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "requires": {} - }, - "postcss-lab-function": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.1.0.tgz", - "integrity": "sha512-59uHN/2wRaOd7whDyeaJ82E0kncIEeJkwcmvXFPNus8v1YMhtv2IUo9OtOAncn7sifZVMRsyoPlhxwckTjn4cQ==", - "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "dev": true, - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - } - }, - "postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "requires": {} - }, - "postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "requires": {} - }, - "postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", - "dev": true - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} - }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-nesting": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz", - "integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "postcss-overflow-shorthand": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", - "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", - "dev": true, - "requires": {} - }, - "postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "requires": {} - }, - "postcss-place": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.4.tgz", - "integrity": "sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-preset-env": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", - "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", - "dev": true, - "requires": { - "autoprefixer": "^10.4.2", - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001299", - "css-blank-pseudo": "^3.0.2", - "css-has-pseudo": "^3.0.3", - "css-prefers-color-scheme": "^6.0.2", - "cssdb": "^5.0.0", - "postcss-attribute-case-insensitive": "^5.0.0", - "postcss-color-functional-notation": "^4.2.1", - "postcss-color-hex-alpha": "^8.0.2", - "postcss-color-rebeccapurple": "^7.0.2", - "postcss-custom-media": "^8.0.0", - "postcss-custom-properties": "^12.1.2", - "postcss-custom-selectors": "^6.0.0", - "postcss-dir-pseudo-class": "^6.0.3", - "postcss-double-position-gradients": "^3.0.4", - "postcss-env-function": "^4.0.4", - "postcss-focus-visible": "^6.0.3", - "postcss-focus-within": "^5.0.3", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.2", - "postcss-image-set-function": "^4.0.4", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.0.3", - "postcss-logical": "^5.0.3", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.2", - "postcss-overflow-shorthand": "^3.0.2", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.3", - "postcss-pseudo-class-any-link": "^7.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^5.0.0" - } - }, - "postcss-pseudo-class-any-link": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.1.tgz", - "integrity": "sha512-JRoLFvPEX/1YTPxRxp1JO4WxBVXJYrSY7NHeak5LImwJ+VobFMwYDQHvfTXEpcn+7fYIeGkC29zYFhFWIZD8fg==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "requires": {} - }, - "postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", - "dev": true - }, - "postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "requires": {} - }, - "postcss-scss": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.4.tgz", - "integrity": "sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==", - "dev": true, - "requires": {} - }, - "postcss-selector-not": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", - "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", - "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", - "dev": true - }, - "prettier-eslint": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-14.1.0.tgz", - "integrity": "sha512-K0TRVaAUXtI5xz1ZaVZfvGMmunDNyIGXFkE845hVl6FzSxzRN9E03YmK3IiapcRFv3w4PyAL25LIPsy2sRz2tw==", - "dev": true, - "requires": { - "@types/eslint": "^8.4.2", - "@types/prettier": "^2.6.0", - "@typescript-eslint/parser": "^5.10.0", - "common-tags": "^1.4.0", - "dlv": "^1.1.0", - "eslint": "^8.7.0", - "indent-string": "^4.0.0", - "lodash.merge": "^4.6.0", - "loglevel-colored-level-prefix": "^1.0.0", - "prettier": "^2.5.1", - "pretty-format": "^23.0.1", - "require-relative": "^0.8.7", - "typescript": "^4.5.4", - "vue-eslint-parser": "^8.0.1" - } - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true - }, - "pretty-format": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", - "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - } - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "requires": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - } - }, - "protractor": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", - "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", - "dev": true, - "requires": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.1.7", - "yargs": "^15.3.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - } - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "optional": true - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", - "dev": true - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", - "dev": true, - "requires": { - "bytes": "3.1.1", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "requires": { - "pify": "^2.3.0" - } - }, - "read-package-json-fast": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", - "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "regexpu-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", - "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - } - }, - "regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", - "dev": true - }, - "regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true - } - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", - "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", - "dev": true, - "requires": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" - }, - "dependencies": { - "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "rxjs-for-await": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", - "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true, - "requires": {} - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sass": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.0.tgz", - "integrity": "sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw==", - "dev": true, - "requires": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - } - }, - "sass-loader": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", - "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", - "dev": true, - "requires": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - } - }, - "saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true - }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, - "selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", - "dev": true, - "requires": { - "node-forge": "^1.2.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - }, - "slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true - } - } - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true - }, - "socket.io": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", - "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" - } - }, - "socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", - "dev": true - }, - "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "dev": true, - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - } - }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "socks": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", - "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", - "dev": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.2.0" - } - }, - "socks-proxy-agent": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", - "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", - "dev": true, - "requires": { - "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true - }, - "source-map-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", - "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - } - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "dev": true - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true - }, - "streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", - "dev": true, - "requires": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "style-search": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", - "dev": true - }, - "stylelint": { - "version": "14.8.3", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.8.3.tgz", - "integrity": "sha512-aLpskXwSgFEBYbFRKA/BfuyYMGuXNtn2t5GqoffNPSezvw97x/vVNWcZNF0+cwt+LBjfvyq9/MRE3OjInGRgNA==", - "dev": true, - "requires": { - "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", - "css-functions-list": "^3.0.1", - "debug": "^4.3.4", - "execall": "^2.0.0", - "fast-glob": "^3.2.11", - "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.2.0", - "ignore": "^5.2.0", - "import-lazy": "^4.0.0", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", - "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.13", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "specificity": "^0.4.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", - "supports-hyperlinks": "^2.2.0", - "svg-tags": "^1.0.0", - "table": "^6.8.0", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.1" - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "stylelint-config-html": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz", - "integrity": "sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ==", - "dev": true, - "requires": {} - }, - "stylelint-config-prettier": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", - "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", - "dev": true, - "requires": {} - }, - "stylelint-config-prettier-scss": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-prettier-scss/-/stylelint-config-prettier-scss-0.0.1.tgz", - "integrity": "sha512-lBAYG9xYOh2LeWEPC/64xeUxwOTnQ8nDyBijQoWoJb10/bMGrUwnokpt8jegGck2Vbtxh6XGwH63z5qBcVHreQ==", - "dev": true, - "requires": { - "stylelint-config-prettier": ">=9.0.3" - } - }, - "stylelint-config-recommended": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", - "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", - "dev": true, - "requires": {} - }, - "stylelint-config-recommended-scss": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", - "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", - "dev": true, - "requires": { - "postcss-scss": "^4.0.2", - "stylelint-config-recommended": "^6.0.0", - "stylelint-scss": "^4.0.0" - }, - "dependencies": { - "stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "requires": {} - } - } - }, - "stylelint-config-scss": { - "version": "1.0.0-security", - "resolved": "https://registry.npmjs.org/stylelint-config-scss/-/stylelint-config-scss-1.0.0-security.tgz", - "integrity": "sha512-8Pgul2mNpzTeK2KCuyV5RcDC1BgzWzU7dCLVJWuxpkKgmxrMqCvjqgyosaKbAVZy2AiaMU0zfHBt7prg7/NaxA==", - "dev": true - }, - "stylelint-config-standard": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", - "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", - "dev": true, - "requires": { - "stylelint-config-recommended": "^7.0.0" - } - }, - "stylelint-config-standard-scss": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz", - "integrity": "sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==", - "dev": true, - "requires": { - "stylelint-config-recommended-scss": "^5.0.2", - "stylelint-config-standard": "^24.0.0" - }, - "dependencies": { - "stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "requires": {} - }, - "stylelint-config-standard": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", - "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", - "dev": true, - "requires": { - "stylelint-config-recommended": "^6.0.0" - } - } - } - }, - "stylelint-scss": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", - "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", - "dev": true, - "requires": { - "lodash": "^4.17.21", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0" - } - }, - "stylus": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", - "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", - "dev": true, - "requires": { - "css": "^3.0.0", - "debug": "^4.3.2", - "glob": "^7.1.6", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "source-map": "^0.7.3" - } - }, - "stylus-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.2.0.tgz", - "integrity": "sha512-5dsDc7qVQGRoc6pvCL20eYgRUxepZ9FpeK28XhdXaIPP6kXr6nI1zAAKFQgP5OBkOfKaURp4WUpJzspg1f01Gg==", - "dev": true, - "requires": { - "fast-glob": "^3.2.7", - "klona": "^2.0.4", - "normalize-path": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "svg.draggable.js": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", - "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", - "requires": { - "svg.js": "^2.0.1" - } - }, - "svg.easing.js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", - "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", - "requires": { - "svg.js": ">=2.3.x" - } - }, - "svg.filter.js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", - "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", - "requires": { - "svg.js": "^2.2.5" - } - }, - "svg.js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", - "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" - }, - "svg.pathmorphing.js": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", - "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", - "requires": { - "svg.js": "^2.4.0" - } - }, - "svg.resize.js": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", - "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", - "requires": { - "svg.js": "^2.6.5", - "svg.select.js": "^2.1.2" - }, - "dependencies": { - "svg.select.js": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", - "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", - "requires": { - "svg.js": "^2.2.5" - } - } - } - }, - "svg.select.js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", - "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", - "requires": { - "svg.js": "^2.6.5" - } - }, - "symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true - }, - "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", - "dev": true, - "requires": { - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typed-assert": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.8.tgz", - "integrity": "sha512-5NkbXZUlmCE73Fs7gvkp1XXJWHYetPkg60QnQ2NXQmBYNFxbBr2zA8GCtaH4K2s2WhOmSlgiSTmrjrcm5tnM5g==", - "dev": true - }, - "typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", - "dev": true - }, - "vue-eslint-parser": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", - "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", - "dev": true, - "requires": { - "debug": "^4.3.2", - "eslint-scope": "^7.0.0", - "eslint-visitor-keys": "^3.1.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.5" - }, - "dependencies": { - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - } - }, - "webdriver-manager": { - "version": "12.1.8", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz", - "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==", - "dev": true, - "requires": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - } - } - }, - "webpack": { - "version": "5.67.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", - "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "webpack-dev-middleware": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", - "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", - "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.2.2", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "webpack-dev-server": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", - "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", - "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/serve-index": "^1.9.1", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", - "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "portfinder": "^1.0.28", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "webpack-subresource-integrity": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", - "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", - "dev": true, - "requires": { - "typed-assert": "^1.0.8" - } - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", - "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, - "requires": {} - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - } - }, - "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "zone.js": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", - "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", - "requires": { - "tslib": "^2.0.0" - } + "node_modules/zone.js/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } } diff --git a/frontend/package.json b/frontend/package.json index 1c3327285..e488e3c92 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "@angular/platform-browser-dynamic": "13.2.x", "@angular/router": "13.2.x", "angularx-qrcode": "13.0.x", - "apexcharts": "3.33.x", + "apexcharts": "3.37.x", "ng-apexcharts": "1.7.x", "ngx-color-picker": "12.0.x", "rxjs": "6.5.x", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0a9285394..e90a65d36 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -15,33 +15,34 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.0.0": - version: 2.1.1 - resolution: "@ampproject/remapping@npm:2.1.1" +"@ampproject/remapping@npm:^2.2.0": + version: 2.2.0 + resolution: "@ampproject/remapping@npm:2.2.0" dependencies: - "@jridgewell/trace-mapping": ^0.3.0 - checksum: cc5cf29833e2b8bdf420821a6e027a35cf6a48605df64ae5095b55cb722581b236554fc8f920138e6da3f7a99ec99273b07ebe2e2bb98b6a4a8d8e33648cac77 + "@jridgewell/gen-mapping": ^0.1.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: d74d170d06468913921d72430259424b7e4c826b5a7d39ff839a29d547efb97dc577caa8ba3fb5cf023624e9af9d09651afc3d4112a45e2050328abc9b3a2292 languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1302.3": - version: 0.1302.3 - resolution: "@angular-devkit/architect@npm:0.1302.3" +"@angular-devkit/architect@npm:0.1302.6": + version: 0.1302.6 + resolution: "@angular-devkit/architect@npm:0.1302.6" dependencies: - "@angular-devkit/core": 13.2.3 + "@angular-devkit/core": 13.2.6 rxjs: 6.6.7 - checksum: c9b85a71b5765bd319ff474825a18a510bfb0aeb8f632cdab258dc8fbba9a79b9db5c1802312f9b446614190c283d6527474dd30ca83405ce429ed0c559a847e + checksum: d6f1b706bc56bfc8c1ab4c25bff52714728895ac58ea52c6ad7d0ffa73e55326982f7f024ab2bb14cb7ba5a330b05e797df004f4404995a22c26163d106e9a42 languageName: node linkType: hard "@angular-devkit/build-angular@npm:13.2.x": - version: 13.2.3 - resolution: "@angular-devkit/build-angular@npm:13.2.3" + version: 13.2.6 + resolution: "@angular-devkit/build-angular@npm:13.2.6" dependencies: "@ampproject/remapping": 1.1.1 - "@angular-devkit/architect": 0.1302.3 - "@angular-devkit/build-webpack": 0.1302.3 - "@angular-devkit/core": 13.2.3 + "@angular-devkit/architect": 0.1302.6 + "@angular-devkit/build-webpack": 0.1302.6 + "@angular-devkit/core": 13.2.6 "@babel/core": 7.16.12 "@babel/generator": 7.16.8 "@babel/helper-annotate-as-pure": 7.16.7 @@ -52,7 +53,7 @@ __metadata: "@babel/runtime": 7.16.7 "@babel/template": 7.16.7 "@discoveryjs/json-ext": 0.5.6 - "@ngtools/webpack": 13.2.3 + "@ngtools/webpack": 13.2.6 ansi-colors: 4.1.1 babel-loader: 8.2.3 babel-plugin-istanbul: 6.1.1 @@ -63,8 +64,8 @@ __metadata: core-js: 3.20.3 critters: 0.0.16 css-loader: 6.5.1 - esbuild: 0.14.14 - esbuild-wasm: 0.14.14 + esbuild: 0.14.22 + esbuild-wasm: 0.14.22 glob: 7.2.0 https-proxy-agent: 5.0.0 inquirer: 8.2.0 @@ -72,7 +73,7 @@ __metadata: karma-source-map-support: 1.4.0 less: 4.1.2 less-loader: 10.2.0 - license-webpack-plugin: 4.0.1 + license-webpack-plugin: 4.0.2 loader-utils: 3.2.0 mini-css-extract-plugin: 2.5.3 minimatch: 3.0.4 @@ -94,7 +95,7 @@ __metadata: source-map-support: 0.5.21 stylus: 0.56.0 stylus-loader: 6.2.0 - terser: 5.10.0 + terser: 5.11.0 text-table: 0.2.0 tree-kill: 1.2.2 tslib: 2.3.1 @@ -128,26 +129,26 @@ __metadata: optional: true tailwindcss: optional: true - checksum: 4138cfbfa5e68984073294a69e6fb6b58df9712c42deba6aae75767e072df2a4e4e165b0d1590d2e72cdeea4e0eb77e87db336a8a579d6a403a0bd3ebdbb7729 + checksum: f5b041a64661bcb845d9f91d19397fa9f14760c4904266974c6d4a7c9db3360ae7f76e2dd1332a85e68fc3d1d0051964a40fc2fdfbfef5ff912a29b67ffca978 languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1302.3": - version: 0.1302.3 - resolution: "@angular-devkit/build-webpack@npm:0.1302.3" +"@angular-devkit/build-webpack@npm:0.1302.6": + version: 0.1302.6 + resolution: "@angular-devkit/build-webpack@npm:0.1302.6" dependencies: - "@angular-devkit/architect": 0.1302.3 + "@angular-devkit/architect": 0.1302.6 rxjs: 6.6.7 peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^4.0.0 - checksum: 827f35b1186093efda0be38e7b5b44634f59ba9203a6ba21dd9701af0cc487f55cf2bafc0a6729ac1534226cd9e9c97d9f35297517ac73304f67f19f9e74d6e6 + checksum: 9c8f7737211eeb5240a2abd0caf6a61a19065829d2b75f47faa9dd2ed48c117ad0572641701cddabc264201ec3c4c2037c139a4efb8514c991704f4ac030e0df languageName: node linkType: hard -"@angular-devkit/core@npm:13.2.3": - version: 13.2.3 - resolution: "@angular-devkit/core@npm:13.2.3" +"@angular-devkit/core@npm:13.2.6": + version: 13.2.6 + resolution: "@angular-devkit/core@npm:13.2.6" dependencies: ajv: 8.9.0 ajv-formats: 2.1.1 @@ -160,20 +161,20 @@ __metadata: peerDependenciesMeta: chokidar: optional: true - checksum: 523671d26806046aa2e81aac45dbd2a18afdd4a8803ad9bdf5883e818357dcbd31174a1439fb1c7dc1b1dbe5ce885b385cb03dae6e7034c87b3d4f58b25a5c88 + checksum: ec8589dbd7b2b1c444229be9915c6a1f5ddb3ee26dcc43612f930125d93c7b2be1aabba09e0ba46008883908cd908c7883e67d534313d979cb3ad86b30e06019 languageName: node linkType: hard -"@angular-devkit/schematics@npm:13.2.3": - version: 13.2.3 - resolution: "@angular-devkit/schematics@npm:13.2.3" +"@angular-devkit/schematics@npm:13.2.6": + version: 13.2.6 + resolution: "@angular-devkit/schematics@npm:13.2.6" dependencies: - "@angular-devkit/core": 13.2.3 + "@angular-devkit/core": 13.2.6 jsonc-parser: 3.0.0 magic-string: 0.25.7 ora: 5.4.1 rxjs: 6.6.7 - checksum: 98748b5a3456798b7e32362b8b67ece9c00757d154a2d95275a18989713bd66174bff158bd3db8b3b50f054c9f2d33e418e79067992bd3a6e1205c5bb9bac0f2 + checksum: 0aeade80952a08ddf461f6e3b8fb3643089e5691c5aecb3b330610a197cdb3b0b2f2032a131128d9355146ad6a46a10af2e986dd5eac7d93a7e89374e772a6da languageName: node linkType: hard @@ -281,19 +282,19 @@ __metadata: linkType: hard "@angular/animations@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/animations@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/animations@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.2 - checksum: 8b976929ca0db1ccb7a81e1769506edb7ceab16406bfdd936c30da51513b34234ecda8ad3d5a3d57b6e5cd39b4b3369990ce21cbbbe19d3f810eac7072e3e5f4 + "@angular/core": 13.2.7 + checksum: e444c814cdbfd2c1360e9f7f01d853baeccf17551eba178fd30b5c29b8e95386fe7d315ccc951b8f0c5d2cfa04091f5ecffecde71f6a2a3137c711218d4cae58 languageName: node linkType: hard "@angular/cdk@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/cdk@npm:13.2.2" + version: 13.2.6 + resolution: "@angular/cdk@npm:13.2.6" dependencies: parse5: ^5.0.0 tslib: ^2.3.0 @@ -304,18 +305,18 @@ __metadata: dependenciesMeta: parse5: optional: true - checksum: 940b3e9d43bc425cdf0b8211ea00601e0a5ca98fe3bb8dca6981ec0738690c56dddf34c79fb71890d0064643cccd1e18956506aefabf24c9729aa7a624741619 + checksum: 8cce0c7568bf7d83ec1b8f6be171f061531c9c0c681b94967f6345f33e7febf58b71266d1414ef71e16808229de03c3fa835cb032e043a494c398fffc62f7d24 languageName: node linkType: hard "@angular/cli@npm:13.2.x": - version: 13.2.3 - resolution: "@angular/cli@npm:13.2.3" + version: 13.2.6 + resolution: "@angular/cli@npm:13.2.6" dependencies: - "@angular-devkit/architect": 0.1302.3 - "@angular-devkit/core": 13.2.3 - "@angular-devkit/schematics": 13.2.3 - "@schematics/angular": 13.2.3 + "@angular-devkit/architect": 0.1302.6 + "@angular-devkit/core": 13.2.6 + "@angular-devkit/schematics": 13.2.6 + "@schematics/angular": 13.2.6 "@yarnpkg/lockfile": 1.1.0 ansi-colors: 4.1.1 debug: 4.3.3 @@ -333,65 +334,65 @@ __metadata: uuid: 8.3.2 bin: ng: bin/ng.js - checksum: b979a704ac248f6ee11d2b2bd57a86504be440afa8f07747b908080e32848d22aa220d3ca2580c50e4ec62e613d61a332ae5ee8fc15f4d3acea34fefa2585936 + checksum: 66765d8ead86045949dc3b8fe45577e9ae705354975da4e6bfdce51654102f313dde667f67ae2106cd79e810bec44192e4cca2f75a19522fac3561314a10d84c languageName: node linkType: hard "@angular/common@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/common@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/common@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.2 + "@angular/core": 13.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: c7a6eadf9b31c6bcffc5072f8c2159c1624fc87a2056f092d7c2bc2ab8b925c8ee9a099262a1dae7bead451a29ad5cea5ed42688a2c9f6c0b85c1302ed5dafd7 + checksum: dbf2c324b3e3a549312d5d196da02cb07854fc75c7db5afeeb5efd7d432dff0d9539b097a1f150a2e8e8291cc85f0405a3e5cf7ec7a888a86f6f66a2213e269e languageName: node linkType: hard "@angular/compiler-cli@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/compiler-cli@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/compiler-cli@npm:13.2.7" dependencies: - "@babel/core": ^7.8.6 + "@babel/core": ^7.17.2 chokidar: ^3.0.0 convert-source-map: ^1.5.1 dependency-graph: ^0.11.0 - magic-string: ^0.25.0 + magic-string: ^0.26.0 reflect-metadata: ^0.1.2 semver: ^7.0.0 sourcemap-codec: ^1.4.8 tslib: ^2.3.0 yargs: ^17.2.1 peerDependencies: - "@angular/compiler": 13.2.2 + "@angular/compiler": 13.2.7 typescript: ">=4.4.2 <4.6" bin: ng-xi18n: bundles/src/bin/ng_xi18n.js ngc: bundles/src/bin/ngc.js ngcc: bundles/ngcc/main-ngcc.js - checksum: ade19505c9ca98a1c68f266532eed47798ba979612bbfca2be3d16a23a6b6af4aeeaad6fe000bfcdb8fdaf7cf48de3192ebf39dc38707a2ce0112d7d8c6b4918 + checksum: 9c7f207285174bd2e5bbb52363eadbaf143bc1582dca44fa941fc8c29cefd580173a8aa9444f152dc9b96bd8a267415890cd4509a08b786211a14a3f85cf1254 languageName: node linkType: hard "@angular/compiler@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/compiler@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/compiler@npm:13.2.7" dependencies: tslib: ^2.3.0 - checksum: 338a0da25cbbf47bf09ed617fd948fa3565c39b971f92718ce119c14f287ee1191de898e0c9adeee774754c203f130e1ba87132353a5084b3ae69012a326e988 + checksum: 6d63ede056a8625d237e9895c2c532009b90b4a3b0ed232a84cea5a6005021c4cd91746fe38398af72c8b6794107386bba56e1b1c65163635bde5f325fb67674 languageName: node linkType: hard "@angular/core@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/core@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/core@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.11.4 - checksum: 801240fe14c4dbf4d1c5fb80418ecf067b8cf2741b5a3fb2a29c4e11aa2494c853a593a28f35c270fbc88979095bfbf0c985983e0cfec7ba1d5b52957d2538d5 + checksum: 7601052ae8c2206a6c26a1e1f92077cc2cf2c84aeed2562b68b3f45066adac1fb581622b175475e4979ca5aee5ce3bc9c9d5cb95325ca61b6546c5366d10adcc languageName: node linkType: hard @@ -411,84 +412,84 @@ __metadata: linkType: hard "@angular/forms@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/forms@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/forms@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.2 - "@angular/core": 13.2.2 - "@angular/platform-browser": 13.2.2 + "@angular/common": 13.2.7 + "@angular/core": 13.2.7 + "@angular/platform-browser": 13.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: bba5e568453db12a0857d0be1d7840e0f3d6755816dafc4320318ec38663e1b7e261291ef56fd8a08f5e595164fca6890d72d589e5064bd308b09a3a3a103896 + checksum: bea0ae8de61695b341846982821a7463ee9cded9f8afb1e3fd1c344935898682ce73743adfdadc4d2be075cb77fab2af2b1fe4ab4c5ec3cc8bd371c71c99876b languageName: node linkType: hard "@angular/language-service@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/language-service@npm:13.2.2" - checksum: 2eca2dd49dae04c57637e1f9f5f5da528d98823f31a899e8466974bae5e1b4ad6ea9b397094cda26667605f934948baf4dffed180f746f3d4ad3be7d430ebd6a + version: 13.2.7 + resolution: "@angular/language-service@npm:13.2.7" + checksum: 2054f85287c822143ba891659148dfa5934b8abe2186d5b8a7544d6e890e7cec7d8afaaeddd76c004cc4e4b45414b24d4902ef176aa1dfca9b28c0598246968f languageName: node linkType: hard "@angular/material@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/material@npm:13.2.2" + version: 13.2.6 + resolution: "@angular/material@npm:13.2.6" dependencies: tslib: ^2.3.0 peerDependencies: "@angular/animations": ^13.0.0 || ^14.0.0-0 - "@angular/cdk": 13.2.2 + "@angular/cdk": 13.2.6 "@angular/common": ^13.0.0 || ^14.0.0-0 "@angular/core": ^13.0.0 || ^14.0.0-0 "@angular/forms": ^13.0.0 || ^14.0.0-0 "@angular/platform-browser": ^13.0.0 || ^14.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - checksum: 0a6ab51fb588d2911f770b2abfdb7e1d55c28441b83e5650c29b0056855e553dcc364fd40a398fb06ab28f922cfdbc6257ad8822a3ec70f5a252c1e91a4d9355 + checksum: 0287d8b966271cf1cd4587859ec59124a2019c542bffc561251238221e4a31b9fb2dfe57c1e47263b8c195aa66175ae0f8bc4373c66db11299793ff1aee406a7 languageName: node linkType: hard "@angular/platform-browser-dynamic@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/platform-browser-dynamic@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/platform-browser-dynamic@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.2 - "@angular/compiler": 13.2.2 - "@angular/core": 13.2.2 - "@angular/platform-browser": 13.2.2 - checksum: 6dbf65da1460d855b623a4d1c10468241c0f0f5efcd3aab4f3b150d262c8f58e6a96f8e48732aacd9d8a930512c751af7d693c71d8c01a6cd8d2a21361540be7 + "@angular/common": 13.2.7 + "@angular/compiler": 13.2.7 + "@angular/core": 13.2.7 + "@angular/platform-browser": 13.2.7 + checksum: f8423844f0c63d618b8861e2911163c266ce972fb9ebcc8e0bb49d8a176e9abedb5019a9b300100d7d7f054784c343410dd80ac7f1e62946910e5484513cec7c languageName: node linkType: hard "@angular/platform-browser@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/platform-browser@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/platform-browser@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/animations": 13.2.2 - "@angular/common": 13.2.2 - "@angular/core": 13.2.2 + "@angular/animations": 13.2.7 + "@angular/common": 13.2.7 + "@angular/core": 13.2.7 peerDependenciesMeta: "@angular/animations": optional: true - checksum: 22472d8dba4151654d413b9d3e65d56ab15867007e6b838261ca46e48ac0794d770fea57149ccdbf8d973d787afcead0a4dd73121c0fd36a377ca38fa7e305ad + checksum: 92c9020c6cd0b3e61e251fff6d86d8173e8af26c95b11f82548a81afc5b305507bd366b2a107d7596477b9d82a1b39397bdd20cf39f5779902ccbbd1dba6a816 languageName: node linkType: hard "@angular/router@npm:13.2.x": - version: 13.2.2 - resolution: "@angular/router@npm:13.2.2" + version: 13.2.7 + resolution: "@angular/router@npm:13.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.2 - "@angular/core": 13.2.2 - "@angular/platform-browser": 13.2.2 + "@angular/common": 13.2.7 + "@angular/core": 13.2.7 + "@angular/platform-browser": 13.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: de28b31d2552b0551b3bc70f1e5a9e46effe7a421ece8c72a3708d0d0d53394feee4ca362f842d19fc909c08cfeeae3e8e36028e3b9c05274c7ffaf0480578f5 + checksum: ac3403e65a6eb420b040179e0ccb927744b654b0c335f58b7b0e144f53b0af5d5273284fdc143265c17a872680b94be304b61d031910f19375bafadd8b1389af languageName: node linkType: hard @@ -499,19 +500,19 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/code-frame@npm:7.16.7" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/code-frame@npm:7.18.6" dependencies: - "@babel/highlight": ^7.16.7 - checksum: db2f7faa31bc2c9cf63197b481b30ea57147a5fc1a6fab60e5d6c02cdfbf6de8e17b5121f99917b3dabb5eeb572da078312e70697415940383efc140d4e0808b + "@babel/highlight": ^7.18.6 + checksum: 195e2be3172d7684bf95cff69ae3b7a15a9841ea9d27d3c843662d50cdd7d6470fd9c8e64be84d031117e4a4083486effba39f9aef6bbb2c89f7f21bcfba33ba languageName: node linkType: hard -"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.16.4, @babel/compat-data@npm:^7.16.8": - version: 7.17.0 - resolution: "@babel/compat-data@npm:7.17.0" - checksum: fe5afaf529d107a223cd5937dace248464b6df1e9f4ea4031a5723e9571b46a4db1c4ff226bac6351148b1bc02ba1b39cb142662cd235aa99c1dda77882f8c9d +"@babel/compat-data@npm:^7.16.8, @babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5": + version: 7.21.0 + resolution: "@babel/compat-data@npm:7.21.0" + checksum: dbf632c532f9c75ba0be7d1dc9f6cd3582501af52f10a6b90415d634ec5878735bd46064c91673b10317af94d4cc99c4da5bd9d955978cdccb7905fc33291e4d languageName: node linkType: hard @@ -538,30 +539,30 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.12.3, @babel/core@npm:^7.8.6": - version: 7.17.2 - resolution: "@babel/core@npm:7.17.2" +"@babel/core@npm:^7.12.3, @babel/core@npm:^7.17.2": + version: 7.21.0 + resolution: "@babel/core@npm:7.21.0" dependencies: - "@ampproject/remapping": ^2.0.0 - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.17.0 - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helpers": ^7.17.2 - "@babel/parser": ^7.17.0 - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.17.0 - "@babel/types": ^7.17.0 + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.21.0 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-module-transforms": ^7.21.0 + "@babel/helpers": ^7.21.0 + "@babel/parser": ^7.21.0 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.0 + "@babel/types": ^7.21.0 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.1.2 + json5: ^2.2.2 semver: ^6.3.0 - checksum: 68ab3459f41b41feb5cb263937f15e418e1c46998d482d1b6dfe34f78064765466cfd5b10205c22fb16b69dbd1d46e7a3c26c067884ca4eb514b3dac1e09a57f + checksum: 357f4dd3638861ceebf6d95ff49ad8b902065ee8b7b352621deed5666c2a6d702a48ca7254dba23ecae2a0afb67d20f90db7dd645c3b75e35e72ad9776c671aa languageName: node linkType: hard -"@babel/generator@npm:7.16.8, @babel/generator@npm:^7.16.8": +"@babel/generator@npm:7.16.8": version: 7.16.8 resolution: "@babel/generator@npm:7.16.8" dependencies: @@ -572,18 +573,19 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.17.0": - version: 7.17.0 - resolution: "@babel/generator@npm:7.17.0" +"@babel/generator@npm:^7.16.8, @babel/generator@npm:^7.21.0": + version: 7.21.1 + resolution: "@babel/generator@npm:7.21.1" dependencies: - "@babel/types": ^7.17.0 + "@babel/types": ^7.21.0 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - source-map: ^0.5.0 - checksum: 2987dbebb484727a227f1ce3db90810320986cfb3ffd23e6d1d87f75bbd8e7871b5bc44252822d4d5f048a2d872a5702b2a9bf7bab7e07f087d7f306f0ea6c0a + checksum: 69085a211ff91a7a608ee3f86e6fcb9cf5e724b756d792a713b0c328a671cd3e423e1ef1b12533f366baba0616caffe0a7ba9d328727eab484de5961badbef00 languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:7.16.7, @babel/helper-annotate-as-pure@npm:^7.16.7": +"@babel/helper-annotate-as-pure@npm:7.16.7": version: 7.16.7 resolution: "@babel/helper-annotate-as-pure@npm:7.16.7" dependencies: @@ -592,222 +594,223 @@ __metadata: languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.16.7" +"@babel/helper-annotate-as-pure@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" dependencies: - "@babel/helper-explode-assignable-expression": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: 1784f19a57ecfafca8e5c2e0f3eac53451cb13a857cbe0ca0cd9670922228d099ef8c3dd8cd318e2d7bce316fdb2ece3e527c30f3ecd83706e37ab6beb0c60eb + "@babel/types": ^7.18.6 + checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-compilation-targets@npm:7.16.7" +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": + version: 7.18.9 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.9" dependencies: - "@babel/compat-data": ^7.16.4 - "@babel/helper-validator-option": ^7.16.7 - browserslist: ^4.17.5 + "@babel/helper-explode-assignable-expression": ^7.18.6 + "@babel/types": ^7.18.9 + checksum: b4bc214cb56329daff6cc18a7f7a26aeafb55a1242e5362f3d47fe3808421f8c7cd91fff95d6b9b7ccb67e14e5a67d944e49dbe026942bfcbfda19b1c72a8e72 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.16.7, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/helper-compilation-targets@npm:7.20.7" + dependencies: + "@babel/compat-data": ^7.20.5 + "@babel/helper-validator-option": ^7.18.6 + browserslist: ^4.21.3 + lru-cache: ^5.1.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 7238aaee78c011a42fb5ca92e5eff098752f7b314c2111d7bb9cdd58792fcab1b9c819b59f6a0851dc210dc09dc06b30d130a23982753e70eb3111bc65204842 + checksum: 8c32c873ba86e2e1805b30e0807abd07188acbe00ebb97576f0b09061cc65007f1312b589eccb4349c5a8c7f8bb9f2ab199d41da7030bf103d9f347dcd3a3cf4 languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.16.10, @babel/helper-create-class-features-plugin@npm:^7.16.7": - version: 7.17.1 - resolution: "@babel/helper-create-class-features-plugin@npm:7.17.1" +"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-create-class-features-plugin@npm:7.21.0" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-member-expression-to-functions": ^7.16.7 - "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/helper-replace-supers": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-member-expression-to-functions": ^7.21.0 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-replace-supers": ^7.20.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/helper-split-export-declaration": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0 - checksum: fb791071dcaa664640d7f1d041772c6b57a8a456720bf7cb21aa055845fad98c644cc7707f03aa94abe8720d19a7c69fd5984fe02fe57b7e99a69f77aa501fc8 + checksum: 3e781d91d1056ea9b3a0395f3017492594a8b86899119b4a1645227c31727b8bec9bc8f6b72e86b1c5cf2dd6690893d2e8c5baff4974c429e616ead089552a21 languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.16.7": - version: 7.17.0 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.0" +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.20.5": + version: 7.21.0 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.0" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - regexpu-core: ^5.0.1 + "@babel/helper-annotate-as-pure": ^7.18.6 + regexpu-core: ^5.3.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: eb66d9241544c705e9ce96d2d122b595ef52d926e6e031653e09af8a01050bd9d7e7fee168bf33a863342774d7d6a8cc7e8e9e5a45b955e9c01121c7a2d51708 + checksum: 63a6396a4e9444edc7e97617845583ea5cf059573d0b4cc566869f38576d543e37fde0edfcc21d6dfb7962ed241e909561714dc41c5213198bac04e0983b04f2 languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.1": - version: 0.3.1 - resolution: "@babel/helper-define-polyfill-provider@npm:0.3.1" +"@babel/helper-define-polyfill-provider@npm:^0.3.1, @babel/helper-define-polyfill-provider@npm:^0.3.2, @babel/helper-define-polyfill-provider@npm:^0.3.3": + version: 0.3.3 + resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" dependencies: - "@babel/helper-compilation-targets": ^7.13.0 - "@babel/helper-module-imports": ^7.12.13 - "@babel/helper-plugin-utils": ^7.13.0 - "@babel/traverse": ^7.13.0 + "@babel/helper-compilation-targets": ^7.17.7 + "@babel/helper-plugin-utils": ^7.16.7 debug: ^4.1.1 lodash.debounce: ^4.0.8 resolve: ^1.14.2 semver: ^6.1.2 peerDependencies: "@babel/core": ^7.4.0-0 - checksum: e3e93cb22febfc0449a210cdafb278e5e1a038af2ca2b02f5dee71c7a49e8ba26e469d631ee11a4243885961a62bb2e5b0a4deb3ec1d7918a33c953d05c3e584 + checksum: 8e3fe75513302e34f6d92bd67b53890e8545e6c5bca8fe757b9979f09d68d7e259f6daea90dc9e01e332c4f8781bda31c5fe551c82a277f9bc0bec007aed497c languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-environment-visitor@npm:7.16.7" - dependencies: - "@babel/types": ^7.16.7 - checksum: c03a10105d9ebd1fe632a77356b2e6e2f3c44edba9a93b0dc3591b6a66bd7a2e323dd9502f9ce96fc6401234abff1907aa877b6674f7826b61c953f7c8204bbe +"@babel/helper-environment-visitor@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-environment-visitor@npm:7.18.9" + checksum: b25101f6162ddca2d12da73942c08ad203d7668e06663df685634a8fde54a98bc015f6f62938e8554457a592a024108d45b8f3e651fd6dcdb877275b73cc4420 languageName: node linkType: hard -"@babel/helper-explode-assignable-expression@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-explode-assignable-expression@npm:7.16.7" +"@babel/helper-explode-assignable-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: ea2135ba36da6a2be059ebc8f10fbbb291eb0e312da54c55c6f50f9cbd8601e2406ec497c5e985f7c07a97f31b3bef9b2be8df53f1d53b974043eaf74fe54bbc + "@babel/types": ^7.18.6 + checksum: 225cfcc3376a8799023d15dc95000609e9d4e7547b29528c7f7111a0e05493ffb12c15d70d379a0bb32d42752f340233c4115bded6d299bc0c3ab7a12be3d30f languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-function-name@npm:7.16.7" +"@babel/helper-function-name@npm:^7.18.9, @babel/helper-function-name@npm:^7.19.0, @babel/helper-function-name@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-function-name@npm:7.21.0" dependencies: - "@babel/helper-get-function-arity": ^7.16.7 - "@babel/template": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: fc77cbe7b10cfa2a262d7a37dca575c037f20419dfe0c5d9317f589599ca24beb5f5c1057748011159149eaec47fe32338c6c6412376fcded68200df470161e1 - languageName: node - linkType: hard - -"@babel/helper-get-function-arity@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-get-function-arity@npm:7.16.7" - dependencies: - "@babel/types": ^7.16.7 - checksum: 25d969fb207ff2ad5f57a90d118f6c42d56a0171022e200aaa919ba7dc95ae7f92ec71cdea6c63ef3629a0dc962ab4c78e09ca2b437185ab44539193f796e0c3 + "@babel/template": ^7.20.7 + "@babel/types": ^7.21.0 + checksum: d63e63c3e0e3e8b3138fa47b0cd321148a300ef12b8ee951196994dcd2a492cc708aeda94c2c53759a5c9177fffaac0fd8778791286746f72a000976968daf4e languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-hoist-variables@npm:7.16.7" +"@babel/helper-hoist-variables@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-hoist-variables@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: 6ae1641f4a751cd9045346e3f61c3d9ec1312fd779ab6d6fecfe2a96e59a481ad5d7e40d2a840894c13b3fd6114345b157f9e3062fc5f1580f284636e722de60 + "@babel/types": ^7.18.6 + checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-member-expression-to-functions@npm:7.16.7" +"@babel/helper-member-expression-to-functions@npm:^7.20.7, @babel/helper-member-expression-to-functions@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-member-expression-to-functions@npm:7.21.0" dependencies: - "@babel/types": ^7.16.7 - checksum: e275378022278a7e7974a3f65566690f1804ac88c5f4e848725cf936f61cd1e2557e88cfb6cb4fea92ae5a95ad89d78dbccc9a53715d4363f84c9fd109272c18 + "@babel/types": ^7.21.0 + checksum: 49cbb865098195fe82ba22da3a8fe630cde30dcd8ebf8ad5f9a24a2b685150c6711419879cf9d99b94dad24cff9244d8c2a890d3d7ec75502cd01fe58cff5b5d languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-module-imports@npm:7.16.7" +"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-module-imports@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: ddd2c4a600a2e9a4fee192ab92bf35a627c5461dbab4af31b903d9ba4d6b6e59e0ff3499fde4e2e9a0eebe24906f00b636f8b4d9bd72ff24d50e6618215c3212 + "@babel/types": ^7.18.6 + checksum: f393f8a3b3304b1b7a288a38c10989de754f01d29caf62ce7c4e5835daf0a27b81f3ac687d9d2780d39685aae7b55267324b512150e7b2be967b0c493b6a1def languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-module-transforms@npm:7.16.7" +"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-module-transforms@npm:7.21.0" dependencies: - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-simple-access": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/helper-validator-identifier": ^7.16.7 - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: 6e930ce776c979f299cdbeaf80187f4ab086d75287b96ecc1c6896d392fcb561065f0d6219fc06fa79b4ceb4bbdc1a9847da8099aba9b077d0a9e583500fb673 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-simple-access": ^7.20.2 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-validator-identifier": ^7.19.1 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.0 + "@babel/types": ^7.21.0 + checksum: bd92d0b73c12dc2f37be906954c58cc3fbec74ba243731e1aa223063b422eef6b961ca7fe19737a073be18db298e1385d370df2e5781646b8c09ecebd7c847de languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-optimise-call-expression@npm:7.16.7" +"@babel/helper-optimise-call-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: 925feb877d5a30a71db56e2be498b3abbd513831311c0188850896c4c1ada865eea795dce5251a1539b0f883ef82493f057f84286dd01abccc4736acfafe15ea + "@babel/types": ^7.18.6 + checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.16.7 - resolution: "@babel/helper-plugin-utils@npm:7.16.7" - checksum: d08dd86554a186c2538547cd537552e4029f704994a9201d41d82015c10ed7f58f9036e8d1527c3760f042409163269d308b0b3706589039c5f1884619c6d4ce +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.20.2 + resolution: "@babel/helper-plugin-utils@npm:7.20.2" + checksum: f6cae53b7fdb1bf3abd50fa61b10b4470985b400cc794d92635da1e7077bb19729f626adc0741b69403d9b6e411cddddb9c0157a709cc7c4eeb41e663be5d74b languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/helper-remap-async-to-generator@npm:7.16.8" +"@babel/helper-remap-async-to-generator@npm:^7.16.8, @babel/helper-remap-async-to-generator@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-wrap-function": ^7.16.8 - "@babel/types": ^7.16.8 - checksum: 29282ee36872130085ca111539725abbf20210c2a1d674bee77f338a57c093c3154108d03a275f602e471f583bd2c7ae10d05534f87cbc22b95524fe2b569488 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-wrap-function": ^7.18.9 + "@babel/types": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-replace-supers@npm:7.16.7" +"@babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/helper-replace-supers@npm:7.20.7" dependencies: - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-member-expression-to-functions": ^7.16.7 - "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/traverse": ^7.16.7 - "@babel/types": ^7.16.7 - checksum: e5c0b6eb3dad8410a6255f93b580dde9b3c1564646c6ef751de59d5b2a65b5caa80cc9e568155f04bbae895ad0f54305c2e833dbd971a4f641f970c90b3d892b + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-member-expression-to-functions": ^7.20.7 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: b8e0087c9b0c1446e3c6f3f72b73b7e03559c6b570e2cfbe62c738676d9ebd8c369a708cf1a564ef88113b4330750a50232ee1131d303d478b7a5e65e46fbc7c languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-simple-access@npm:7.16.7" +"@babel/helper-simple-access@npm:^7.20.2": + version: 7.20.2 + resolution: "@babel/helper-simple-access@npm:7.20.2" dependencies: - "@babel/types": ^7.16.7 - checksum: 8d22c46c5ec2ead0686c4d5a3d1d12b5190c59be676bfe0d9d89df62b437b51d1a3df2ccfb8a77dded2e585176ebf12986accb6d45a18cff229eef3b10344f4b + "@babel/types": ^7.20.2 + checksum: ad1e96ee2e5f654ffee2369a586e5e8d2722bf2d8b028a121b4c33ebae47253f64d420157b9f0a8927aea3a9e0f18c0103e74fdd531815cf3650a0a4adca11a1 languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.16.0": - version: 7.16.0 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.16.0" +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": + version: 7.20.0 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" dependencies: - "@babel/types": ^7.16.0 - checksum: b9ed2896eb253e6a85f472b0d4098ed80403758ad1a4e34b02b11e8276e3083297526758b1a3e6886e292987266f10622d7dbced3508cc22b296a74903b41cfb + "@babel/types": ^7.20.0 + checksum: 34da8c832d1c8a546e45d5c1d59755459ffe43629436707079989599b91e8c19e50e73af7a4bd09c95402d389266731b0d9c5f69e372d8ebd3a709c05c80d7dd languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-split-export-declaration@npm:7.16.7" +"@babel/helper-split-export-declaration@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-split-export-declaration@npm:7.18.6" dependencies: - "@babel/types": ^7.16.7 - checksum: e10aaf135465c55114627951b79115f24bc7af72ecbb58d541d66daf1edaee5dde7cae3ec8c3639afaf74526c03ae3ce723444e3b5b3dc77140c456cd84bcaa1 + "@babel/types": ^7.18.6 + checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b languageName: node linkType: hard @@ -818,95 +821,88 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-validator-identifier@npm:7.16.7" - checksum: dbb3db9d184343152520a209b5684f5e0ed416109cde82b428ca9c759c29b10c7450657785a8b5c5256aa74acc6da491c1f0cf6b784939f7931ef82982051b69 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.19.1": +"@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": version: 7.19.1 resolution: "@babel/helper-validator-identifier@npm:7.19.1" checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/helper-validator-option@npm:7.16.7" - checksum: c5ccc451911883cc9f12125d47be69434f28094475c1b9d2ada7c3452e6ac98a1ee8ddd364ca9e3f9855fcdee96cdeafa32543ebd9d17fee7a1062c202e80570 +"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.18.6": + version: 7.21.0 + resolution: "@babel/helper-validator-option@npm:7.21.0" + checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/helper-wrap-function@npm:7.16.8" +"@babel/helper-wrap-function@npm:^7.18.9": + version: 7.20.5 + resolution: "@babel/helper-wrap-function@npm:7.20.5" dependencies: - "@babel/helper-function-name": ^7.16.7 - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.16.8 - "@babel/types": ^7.16.8 - checksum: d8aae4bacaf138d47dca1421ba82b41eac954cbb0ad17ab1c782825c6f2afe20076fbed926ab265967758336de5112d193a363128cd1c6967c66e0151174f797 + "@babel/helper-function-name": ^7.19.0 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.20.5 + "@babel/types": ^7.20.5 + checksum: 11a6fc28334368a193a9cb3ad16f29cd7603bab958433efc82ebe59fa6556c227faa24f07ce43983f7a85df826f71d441638442c4315e90a554fe0a70ca5005b languageName: node linkType: hard -"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.17.2": - version: 7.17.2 - resolution: "@babel/helpers@npm:7.17.2" +"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helpers@npm:7.21.0" dependencies: - "@babel/template": ^7.16.7 - "@babel/traverse": ^7.17.0 - "@babel/types": ^7.17.0 - checksum: 5fa06bbf59636314fb4098bb2e70cf488e0fb6989553438abab90356357b79976102ac129fb16fc8186893c79e0809de1d90e3304426d6fcdb1750da2b6dff9d + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.0 + "@babel/types": ^7.21.0 + checksum: 9370dad2bb665c551869a08ac87c8bdafad53dbcdce1f5c5d498f51811456a3c005d9857562715151a0f00b2e912ac8d89f56574f837b5689f5f5072221cdf54 languageName: node linkType: hard -"@babel/highlight@npm:^7.16.7": - version: 7.16.10 - resolution: "@babel/highlight@npm:7.16.10" +"@babel/highlight@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/highlight@npm:7.18.6" dependencies: - "@babel/helper-validator-identifier": ^7.16.7 + "@babel/helper-validator-identifier": ^7.18.6 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 1f1bdd752a90844f4efc22166a46303fb651ba0fd75a06daba3ebae2575ab3edc1da9827c279872a3aaf305f50a18473c5fa1966752726a2b253065fd4c0745e + checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 languageName: node linkType: hard -"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.0": - version: 7.17.0 - resolution: "@babel/parser@npm:7.17.0" +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.0": + version: 7.21.1 + resolution: "@babel/parser@npm:7.21.1" bin: parser: ./bin/babel-parser.js - checksum: d0ac5ffba0b234dde516f867edf5da5d92d6f841592b370ae3244cd7c8f27a7f5e3e3d4e90ca9c15ea58bc46823f1643f3f75b6eb9a9f676ae16e8b2365e922a + checksum: a564cff6dec4201a611d1f2ae5af8d7436ce80550e75a77ee72dca6b094df6188b5a4ccfae6a98c85991b56a51e6c48159e466cc5a374c7a37af706fcb5a6bc2 languageName: node linkType: hard "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0 - checksum: bbb0f82a4cf297bdbb9110eea570addd4b883fd1b61535558d849822b087aa340fe4e9c31f8a39b087595c8310b58d0f5548d6be0b72c410abefb23a5734b7bc + checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d languageName: node linkType: hard "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 - "@babel/plugin-proposal-optional-chaining": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/plugin-proposal-optional-chaining": ^7.20.7 peerDependencies: "@babel/core": ^7.13.0 - checksum: 81b372651a7d886a06596b02df7fb65ea90265a8bd60c9f0d5c1777590a598e6cccbdc3239033ee0719abf904813e69577eeb0ed5960b40e07978df023b17a6a + checksum: d610f532210bee5342f5b44a12395ccc6d904e675a297189bc1e401cc185beec09873da523466d7fec34ae1574f7a384235cba1ccc9fe7b89ba094167897c845 languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:7.16.8, @babel/plugin-proposal-async-generator-functions@npm:^7.16.8": +"@babel/plugin-proposal-async-generator-functions@npm:7.16.8": version: 7.16.8 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.8" dependencies: @@ -919,178 +915,192 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-async-generator-functions@npm:^7.16.8": + version: 7.20.7 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 + languageName: node + linkType: hard + "@babel/plugin-proposal-class-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3977e841e17b45b47be749b9a5b67b9e8b25ff0840f9fdad3f00cbcb35db4f5ff15f074939fe19b01207a29688c432cc2c682351959350834d62920b7881f803 + checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 languageName: node linkType: hard "@babel/plugin-proposal-class-static-block@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.16.7" + version: 7.21.0 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 "@babel/plugin-syntax-class-static-block": ^7.14.5 peerDependencies: "@babel/core": ^7.12.0 - checksum: 3b95b5137e089f0be17de667299ea2e28867b6310ab94219a5a89ac7675824e69f316d31930586142b9f432122ef3b98eb05fffdffae01b5587019ce9aab4ef3 + checksum: 236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b languageName: node linkType: hard "@babel/plugin-proposal-dynamic-import@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-dynamic-import@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-dynamic-import": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5992012484fb8bda1451369350e475091954ed414dd9ef8654a3c4daa2db0205d4f29c94f5d3dedfbc5a434996375c8304586904337d6af938ac0f27a0033e23 + checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f languageName: node linkType: hard "@babel/plugin-proposal-export-namespace-from@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.16.7" + version: 7.18.9 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5016079a5305c1c130fea587b42cdce501574739cfefa5b63469dbc1f32d436df0ff42fabf04089fe8b6a00f4ea7563869e944744b457e186c677995983cb166 + checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef languageName: node linkType: hard "@babel/plugin-proposal-json-strings@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-json-strings@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-json-strings": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ea6487918f8d88322ac2a4e5273be6163b0d84a34330c31cee346e23525299de3b4f753bc987951300a79f55b8f4b1971b24d04c0cdfcb7ceb4d636975c215e8 + checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 languageName: node linkType: hard "@babel/plugin-proposal-logical-assignment-operators@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c4cf18e10f900d40eaa471c4adce4805e67bd845f997a4b9d5653eced4e653187b9950843b2bf7eab6c0c3e753aba222b1d38888e3e14e013f87295c5b014f19 + checksum: cdd7b8136cc4db3f47714d5266f9e7b592a2ac5a94a5878787ce08890e97c8ab1ca8e94b27bfeba7b0f2b1549a026d9fc414ca2196de603df36fb32633bbdc19 languageName: node linkType: hard "@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bfafc2701697b5c763dbbb65dd97b56979bfb0922e35be27733699a837aeff22316313ddfdd0fb45129efa3f86617219b77110d05338bc4dca4385d8ce83dd19 + checksum: 949c9ddcdecdaec766ee610ef98f965f928ccc0361dd87cf9f88cf4896a6ccd62fce063d4494778e50da99dea63d270a1be574a62d6ab81cbe9d85884bf55a7d languageName: node linkType: hard "@babel/plugin-proposal-numeric-separator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-numeric-separator@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-numeric-separator": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8e2fb0b32845908c67f80bc637a0968e28a66727d7ffb22b9c801dc355d88e865dc24aec586b00c922c23833ae5d26301b443b53609ea73d8344733cd48a1eca + checksum: f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec languageName: node linkType: hard "@babel/plugin-proposal-object-rest-spread@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: - "@babel/compat-data": ^7.16.4 - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/compat-data": ^7.20.5 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-plugin-utils": ^7.20.2 "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-transform-parameters": ^7.16.7 + "@babel/plugin-transform-parameters": ^7.20.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2d3740e4df6d3f51d57862100c45c000104571aa98b7f798fdfc05ae0c12b9e7cc9b55f4a28612d626e29f3369a1481a0ee8a0241b23508b9d3da00c55f99d41 + checksum: 1329db17009964bc644484c660eab717cb3ca63ac0ab0f67c651a028d1bc2ead51dc4064caea283e46994f1b7221670a35cbc0b4beb6273f55e915494b5aa0b2 languageName: node linkType: hard "@babel/plugin-proposal-optional-catch-binding@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4a422bb19a23cf80a245c60bea7adbe5dac8ff3bc1a62f05d7155e1eb68d401b13339c94dfd1f3d272972feeb45746f30d52ca0f8d5c63edf6891340878403df + checksum: 7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-optional-chaining@npm:7.16.7" +"@babel/plugin-proposal-optional-chaining@npm:^7.16.7, @babel/plugin-proposal-optional-chaining@npm:^7.20.7": + version: 7.21.0 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e4a6c1ac7e6817b92a673ea52ab0b7dc1fb39d29fb0820cd414e10ae2cd132bd186b4238dcca881a29fc38fe9d38ed24fc111ba22ca20086481682d343f4f130 + checksum: 11c5449e01b18bb8881e8e005a577fa7be2fe5688e2382c8822d51f8f7005342a301a46af7b273b1f5645f9a7b894c428eee8526342038a275ef6ba4c8d8d746 languageName: node linkType: hard "@babel/plugin-proposal-private-methods@npm:^7.16.11": - version: 7.16.11 - resolution: "@babel/plugin-proposal-private-methods@npm:7.16.11" + version: 7.18.6 + resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.10 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b333e5aa91c265bb394a57b5f4ae1a34fc8ee73a8d75506b12df258d8b5342107cbd9261f95e606bd3264a5b023db77f1f95be30c2e526683916c57f793f7943 + checksum: 22d8502ee96bca99ad2c8393e8493e2b8d4507576dd054490fd8201a36824373440106f5b098b6d821b026c7e72b0424ff4aeca69ed5f42e48f029d3a156d5ad languageName: node linkType: hard "@babel/plugin-proposal-private-property-in-object@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.16.7" + version: 7.21.0 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-create-class-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 666d668f51d8c01aaf0dd87b27a83fc0392884d2c8e9d8e17b3b7011c0d348865dee94b44dc2d7070726e58e3b579728dc2588aaa8140d563f7390743ee90f0a + checksum: add881a6a836635c41d2710551fdf777e2c07c0b691bf2baacc5d658dd64107479df1038680d6e67c468bfc6f36fb8920025d6bac2a1df0a81b867537d40ae78 languageName: node linkType: hard "@babel/plugin-proposal-unicode-property-regex@npm:^7.16.7, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": - version: 7.16.7 - resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b8a33713d456183f0b7d011011e7bd932c08cc06216399a7b2015ab39284b511993dc10a89bbb15d1d728e6a2ef42ca08c3202619aa148cbd48052422ea3995 + checksum: a8575ecb7ff24bf6c6e94808d5c84bb5a0c6dd7892b54f09f4646711ba0ee1e1668032b3c43e3e1dfec2c5716c302e851ac756c1645e15882d73df6ad21ae951 languageName: node linkType: hard @@ -1249,17 +1259,17 @@ __metadata: linkType: hard "@babel/plugin-transform-arrow-functions@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2a6aa982c6fc80f4de7ccd973507ce5464fab129987cb6661136a7b9b6a020c2b329b912cbc46a68d39b5a18451ba833dcc8d1ca8d615597fec98624ac2add54 + checksum: b43cabe3790c2de7710abe32df9a30005eddb2050dadd5d122c6872f679e5710e410f1b90c8f99a2aff7b614cccfecf30e7fd310236686f60d3ed43fd80b9847 languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.16.8, @babel/plugin-transform-async-to-generator@npm:^7.16.8": +"@babel/plugin-transform-async-to-generator@npm:7.16.8": version: 7.16.8 resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.8" dependencies: @@ -1272,278 +1282,292 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.16.8": + version: 7.20.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" + dependencies: + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 591e9f75437bb32ebf9506d28d5c9659c66c0e8e0c19b12924d808d898e68309050aadb783ccd70bb4956555067326ecfa17a402bc77eb3ece3c6863d40b9016 + checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e languageName: node linkType: hard "@babel/plugin-transform-block-scoping@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-block-scoping@npm:7.16.7" + version: 7.21.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f93b5441af573fc274655f1707aeb4f67a43e926b58f56d89cc35a27877ae0bf198648603cbc19f442579489138f93c3838905895f109aa356996dbc3ed97a68 + checksum: 15aacaadbecf96b53a750db1be4990b0d89c7f5bc3e1794b63b49fb219638c1fd25d452d15566d7e5ddf5b5f4e1a0a0055c35c1c7aee323c7b114bf49f66f4b0 languageName: node linkType: hard "@babel/plugin-transform-classes@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-classes@npm:7.16.7" - dependencies: - "@babel/helper-annotate-as-pure": ^7.16.7 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-optimise-call-expression": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-replace-supers": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 + version: 7.21.0 + resolution: "@babel/plugin-transform-classes@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-replace-supers": ^7.20.7 + "@babel/helper-split-export-declaration": ^7.18.6 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 791526a1bf3c4659b94d619536e3181d3ad54887d50539066628c6e695789a3bb264dc1fbc8540169d62a222f623df54defb490c1811ae63bad1e3557d6b3bb0 + checksum: 088ae152074bd0e90f64659169255bfe50393e637ec8765cb2a518848b11b0299e66b91003728fd0a41563a6fdc6b8d548ece698a314fd5447f5489c22e466b7 languageName: node linkType: hard "@babel/plugin-transform-computed-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-computed-properties@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/template": ^7.20.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 28b17f7cfe643f45920b76dc040cab40d4e54eccf5074fba2658c484feacda9b4885b3854ffaf26292189783fdecc97211519c61831b6708fcbf739cfbcbf31c + checksum: be70e54bda8b469146459f429e5f2bd415023b87b2d5af8b10e48f465ffb02847a3ed162ca60378c004b82db848e4d62e90010d41ded7e7176b6d8d1c2911139 languageName: node linkType: hard "@babel/plugin-transform-destructuring@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-destructuring@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-transform-destructuring@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d1c2e15e7be2a7c57ac8ec4df06fbb706c7ecc872ab7bc2193606e6d6a01929b6d5a1bb41540e41180e42a5ce0e70dce22e7896cb6578dd581d554f77780971b + checksum: bd8affdb142c77662037215e37128b2110a786c92a67e1f00b38223c438c1610bd84cbc0386e9cd3479245ea811c5ca6c9838f49be4729b592159a30ce79add2 languageName: node linkType: hard "@babel/plugin-transform-dotall-regex@npm:^7.16.7, @babel/plugin-transform-dotall-regex@npm:^7.4.4": - version: 7.16.7 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 554570dddfd5bfd87ab307be520f69a3d4ed2d2db677c165971b400d4c96656d0c165b318e69f1735612dcd12e04c0ee257697dc26800e8a572ca73bc05fa0f4 + checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da languageName: node linkType: hard "@babel/plugin-transform-duplicate-keys@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.16.7" + version: 7.18.9 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b96f6e9f7b33a91ad0eb6b793e4da58b7a0108b58269109f391d57078d26e043b3872c95429b491894ae6400e72e44d9b744c9b112b8433c99e6969b767e30ed + checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe languageName: node linkType: hard "@babel/plugin-transform-exponentiation-operator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8082c79268f5b1552292bd3abbfed838a1131747e62000146e70670707b518602e907bbe3aef0fda824a2eebe995a9d897bd2336a039c5391743df01608673b0 + checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 languageName: node linkType: hard "@babel/plugin-transform-for-of@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-for-of@npm:7.16.7" + version: 7.21.0 + resolution: "@babel/plugin-transform-for-of@npm:7.21.0" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 35c9264ee4bef814818123d70afe8b2f0a85753a0a9dc7b73f93a71cadc5d7de852f1a3e300a7c69a491705805704611de1e2ccceb5686f7828d6bca2e5a7306 + checksum: 2f3f86ca1fab2929fcda6a87e4303d5c635b5f96dc9a45fd4ca083308a3020c79ac33b9543eb4640ef2b79f3586a00ab2d002a7081adb9e9d7440dce30781034 languageName: node linkType: hard "@babel/plugin-transform-function-name@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-function-name@npm:7.16.7" + version: 7.18.9 + resolution: "@babel/plugin-transform-function-name@npm:7.18.9" dependencies: - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4d97d0b84461cdd5d5aa2d010cdaf30f1f83a92a0dedd3686cbc7e90dc1249a70246f5bac0c1f3cd3f1dbfb03f7aac437776525a0c90cafd459776ea4fcc6bde + checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 languageName: node linkType: hard "@babel/plugin-transform-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-literals@npm:7.16.7" + version: 7.18.9 + resolution: "@babel/plugin-transform-literals@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a9565d999fc7a72a391ef843cf66028c38ca858537c7014d9ea8ea587a59e5f952d9754bdcca6ca0446e84653e297d417d4faedccb9e4221af1aa30f25d918e0 + checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 languageName: node linkType: hard "@babel/plugin-transform-member-expression-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fdf5b22abab2b770e69348ce7f99796c3e0e1e7ce266afdbe995924284704930fa989323bdbda7070db8adb45a72f39eaa1dbebf18b67fc44035ec00c6ae3300 + checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 languageName: node linkType: hard "@babel/plugin-transform-modules-amd@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-modules-amd@npm:7.16.7" + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" dependencies: - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - babel-plugin-dynamic-import-node: ^2.3.3 + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9ac251ee96183b10cf9b4ec8f9e8d52e14ec186a56103f6c07d0c69e99faa60391f6bac67da733412975e487bd36adb403e2fc99bae6b785bf1413e9d928bc71 + checksum: 23665c1c20c8f11c89382b588fb9651c0756d130737a7625baeaadbd3b973bc5bfba1303bedffa8fb99db1e6d848afb01016e1df2b69b18303e946890c790001 languageName: node linkType: hard "@babel/plugin-transform-modules-commonjs@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.16.8" + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.20.11" dependencies: - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-simple-access": ^7.16.7 - babel-plugin-dynamic-import-node: ^2.3.3 + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-simple-access": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c0ac00f5457e12cac7825b14725b6fc787bef78945181469ff79f07ef0fd7df021cb00fe1d3a9f35fc9bc92ae59e6e3fc9075a70b627dfe10e00d0907892aace + checksum: ddd0623e2ad4b5c0faaa0ae30d3407a3fa484d911c968ed33cfb1b339ac3691321c959db60b66dc136dbd67770fff586f7e48a7ce0d7d357f92d6ef6fb7ed1a7 languageName: node linkType: hard "@babel/plugin-transform-modules-systemjs@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.16.7" + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" dependencies: - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-validator-identifier": ^7.16.7 - babel-plugin-dynamic-import-node: ^2.3.3 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-validator-identifier": ^7.19.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2e50ae45a725eeafac5a9d30e07a5e17ab8dcf62c3528cf4efe444fc6f12cd3c4e42e911a9aa37abab169687a98b29a4418eeafcf2031f9917162ac36105cb1b + checksum: 4546c47587f88156d66c7eb7808e903cf4bb3f6ba6ac9bc8e3af2e29e92eb9f0b3f44d52043bfd24eb25fa7827fd7b6c8bfeac0cac7584e019b87e1ecbd0e673 languageName: node linkType: hard "@babel/plugin-transform-modules-umd@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-modules-umd@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" dependencies: - "@babel/helper-module-transforms": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-module-transforms": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d1433f8b0e0b3c9f892aa530f08fe3ba653a5e51fe1ed6034ac7d45d4d6f22c3ba99186b72e41ad9ce5d8dcf964104c3da2419f15fcdcf5ba05c5fda3ea2cefc + checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 languageName: node linkType: hard "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.16.8" + version: 7.20.5 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.20.5 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0 - checksum: 73e149f5ff690f5b8e3764a881e8e5240f12f394256e7d5217705d0cbeae074c3faff394783190fe1a41f9fc5a53b960b6021158b7e5174391b5fc38f4ba047a + checksum: 528c95fb1087e212f17e1c6456df041b28a83c772b9c93d2e407c9d03b72182b0d9d126770c1d6e0b23aab052599ceaf25ed6a2c0627f4249be34a83f6fae853 languageName: node linkType: hard "@babel/plugin-transform-new-target@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-new-target@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-new-target@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7410c3e68abc835f87a98d40269e65fb1a05c131decbb6721a80ed49a01bd0c53abb6b8f7f52d5055815509022790e1accca32e975c02f2231ac3cf13d8af768 + checksum: bd780e14f46af55d0ae8503b3cb81ca86dcc73ed782f177e74f498fff934754f9e9911df1f8f3bd123777eed7c1c1af4d66abab87c8daae5403e7719a6b845d1 languageName: node linkType: hard "@babel/plugin-transform-object-super@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-object-super@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-object-super@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-replace-supers": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-replace-supers": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 46e3c879f4a93e904f2ecf83233d40c48c832bdbd82a67cab1f432db9aa51702e40d9e51e5800613e12299974f90f4ed3869e1273dbca8642984266320c5f341 + checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-parameters@npm:7.16.7" +"@babel/plugin-transform-parameters@npm:^7.16.7, @babel/plugin-transform-parameters@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-transform-parameters@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4d6904376db82d0b35f0a6cce08f630daf8608d94e903d6c7aff5bd742b251651bd1f88cdf9f16cad98aba5fc7c61da8635199364865fad6367d5ae37cf56cc1 + checksum: 6ffe0dd9afb2d2b9bc247381aa2e95dd9997ff5568a0a11900528919a4e073ac68f46409431455badb8809644d47cff180045bc2b9700e3f36e3b23554978947 languageName: node linkType: hard "@babel/plugin-transform-property-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-property-literals@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b5674458991a9b0e8738989d70faa88c7f98ed3df923c119f1225069eed72fe5e0ce947b1adc91e378f5822fbdeb7a672f496fd1c75c4babcc88169e3a7c3229 + checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 languageName: node linkType: hard "@babel/plugin-transform-regenerator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-regenerator@npm:7.16.7" + version: 7.20.5 + resolution: "@babel/plugin-transform-regenerator@npm:7.20.5" dependencies: - regenerator-transform: ^0.14.2 + "@babel/helper-plugin-utils": ^7.20.2 + regenerator-transform: ^0.15.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 12b1f9a4f324027af69f49522fbe7feea2ac53285ca5c7e27a70de09f56c74938bfda8b09ac06e57fa1207e441f00efb7adbc462afc9be5e8abd0c2a07715e01 + checksum: 13164861e71fb23d84c6270ef5330b03c54d5d661c2c7468f28e21c4f8598558ca0c8c3cb1d996219352946e849d270a61372bc93c8fbe9676e78e3ffd0dea07 languageName: node linkType: hard "@babel/plugin-transform-reserved-words@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-reserved-words@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 00218a646e99a97c1f10b77c41c178ca1b91d0e6cf18dd4ca3c59b8a5ad721db04ef508f49be4cd0dcca7742490dbb145307b706a2dbea1917d5e5f7ba2f31b7 + checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c languageName: node linkType: hard @@ -1564,81 +1588,81 @@ __metadata: linkType: hard "@babel/plugin-transform-shorthand-properties@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ca381ecf8f48696512172deca40af46b1f64e3497186fdc2c9009286d8f06b468c4d61cdc392dc8b0c165298117dda67be9e2ff0e99d7691b0503f1240d4c62b + checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 languageName: node linkType: hard "@babel/plugin-transform-spread@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-spread@npm:7.16.7" + version: 7.20.7 + resolution: "@babel/plugin-transform-spread@npm:7.20.7" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6e961af1a70586bb72dd85e8296cee857c5dadd73225fccd0fe261c0d98652a82d69c65f3e9dc31ce019a12e9677262678479b96bd2d9140ddf6514618362828 + checksum: 8ea698a12da15718aac7489d4cde10beb8a3eea1f66167d11ab1e625033641e8b328157fd1a0b55dd6531933a160c01fc2e2e61132a385cece05f26429fd0cc2 languageName: node linkType: hard "@babel/plugin-transform-sticky-regex@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d59e20121ff0a483e29364eff8bb42cd8a0b7a3158141eea5b6f219227e5b873ea70f317f65037c0f557887a692ac993b72f99641a37ea6ec0ae8000bfab1343 + checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 languageName: node linkType: hard "@babel/plugin-transform-template-literals@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-template-literals@npm:7.16.7" + version: 7.18.9 + resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b55a519dd8b957247ebad3cab21918af5adca4f6e6c87819501cfe3d4d4bccda25bc296c7dfc8a30909b4ad905902aeb9d55ad955cb9f5cbc74b42dab32baa18 + checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 languageName: node linkType: hard "@babel/plugin-transform-typeof-symbol@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.16.7" + version: 7.18.9 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 739a8c439dacbd9af62cfbfa0a7cbc3f220849e5fc774e5ef708a09186689a724c41a1d11323e7d36588d24f5481c8b702c86ff7be8da2e2fed69bed0175f625 + checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 languageName: node linkType: hard "@babel/plugin-transform-unicode-escapes@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.16.7" + version: 7.18.10 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.18.10" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.18.9 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d10c3b5baa697ca2d9ecce2fd7705014d7e1ddd86ed684ccec378f7ad4d609ab970b5546d6cdbe242089ecfc7a79009d248cf4f8ee87d629485acfb20c0d9160 + checksum: f5baca55cb3c11bc08ec589f5f522d85c1ab509b4d11492437e45027d64ae0b22f0907bd1381e8d7f2a436384bb1f9ad89d19277314242c5c2671a0f91d0f9cd languageName: node linkType: hard "@babel/plugin-transform-unicode-regex@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.16.7" + version: 7.18.6 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ef7721cfb11b269809555b1c392732566c49f6ced58e0e990c0e81e58a934bbab3072dcbe92d3a20d60e3e41036ecf987bcc63a7cde90711a350ad774667e5e6 + checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e languageName: node linkType: hard @@ -1741,17 +1765,24 @@ __metadata: languageName: node linkType: hard +"@babel/regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "@babel/regjsgen@npm:0.8.0" + checksum: 89c338fee774770e5a487382170711014d49a68eb281e74f2b5eac88f38300a4ad545516a7786a8dd5702e9cf009c94c2f582d200f077ac5decd74c56b973730 + languageName: node + linkType: hard + "@babel/runtime-corejs3@npm:^7.10.2": - version: 7.18.0 - resolution: "@babel/runtime-corejs3@npm:7.18.0" + version: 7.21.0 + resolution: "@babel/runtime-corejs3@npm:7.21.0" dependencies: - core-js-pure: ^3.20.2 - regenerator-runtime: ^0.13.4 - checksum: 9890cd59456847d5c9da7a5a118ab7ae76d0eba7ea7fae62e802fa807f87ea7e960acc9be7af3282cbd9ca7e8930a7b566e348d83af3d21e78667cfabbaa595f + core-js-pure: ^3.25.1 + regenerator-runtime: ^0.13.11 + checksum: a47927671672b1e1644771458f804e03802303eeffcafd55f85cb121d3d3ca33032cc2fe68e086e3de6923049343d0aa599fc3eb3ad5749e30646e2a2ef6f11d languageName: node linkType: hard -"@babel/runtime@npm:7.16.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:7.16.7": version: 7.16.7 resolution: "@babel/runtime@npm:7.16.7" dependencies: @@ -1760,7 +1791,16 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:7.16.7, @babel/template@npm:^7.16.7": +"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": + version: 7.21.0 + resolution: "@babel/runtime@npm:7.21.0" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab + languageName: node + linkType: hard + +"@babel/template@npm:7.16.7": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" dependencies: @@ -1771,42 +1811,50 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.17.0": - version: 7.17.0 - resolution: "@babel/traverse@npm:7.17.0" +"@babel/template@npm:^7.16.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/template@npm:7.20.7" dependencies: - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.17.0 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.17.0 - "@babel/types": ^7.17.0 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: 9b7de053d8a29453fd7b9614a028d8dc811817f02948eaee02093274b67927a1cfb0513b521bc4a9328c9b6e5b021fd343b358c3526bbb6ee61ec078d4110c0c + "@babel/code-frame": ^7.18.6 + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e languageName: node linkType: hard -"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.4.4": - version: 7.17.0 - resolution: "@babel/types@npm:7.17.0" +"@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/traverse@npm:7.21.0" dependencies: - "@babel/helper-validator-identifier": ^7.16.7 - to-fast-properties: ^2.0.0 - checksum: 12e5a287986fe557188e87b2c5202223f1dc83d9239a196ab936fdb9f8c1eb0be717ff19f934b5fad4e29a75586d5798f74bed209bccea1c20376b9952056f0e + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.21.0 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/parser": ^7.21.0 + "@babel/types": ^7.21.0 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 99241b22db509d2f01a9af51bfab1d68e73cd3b66bbc2560f0f65e49880f68a05ead913e72a4e464152430a027f0c7822f126d6f1bcc3bc3e01ef8b8558a6dc6 languageName: node linkType: hard -"@babel/types@npm:^7.8.3": - version: 7.20.7 - resolution: "@babel/types@npm:7.20.7" +"@babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.21.0 + resolution: "@babel/types@npm:7.21.0" dependencies: "@babel/helper-string-parser": ^7.19.4 "@babel/helper-validator-identifier": ^7.19.1 to-fast-properties: ^2.0.0 - checksum: b39af241f0b72bba67fd6d0d23914f6faec8c0eba8015c181cbd5ea92e59fc91a52a1ab490d3520c7dbd19ddb9ebb76c476308f6388764f16d8201e37fae6811 + checksum: dbcdda202b3a2bfd59e4de880ce38652f1f8957893a9751be069ac86e47ad751222070fe6cd92220214d77973f1474e4e1111c16dc48199dfca1489c0ee8c0c5 + languageName: node + linkType: hard + +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 languageName: node linkType: hard @@ -1825,13 +1873,23 @@ __metadata: linkType: hard "@csstools/postcss-progressive-custom-properties@npm:^1.1.0": - version: 1.1.0 - resolution: "@csstools/postcss-progressive-custom-properties@npm:1.1.0" + version: 1.3.0 + resolution: "@csstools/postcss-progressive-custom-properties@npm:1.3.0" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.3 - checksum: d8bb27f1fd83d3b5f3740cd9c85063c63178c765acf4af59cef62f5af90c46f49e5048a37e274f614cfd6b37c4d653a43b67ffb391fde306829997936bc76a24 + checksum: e281845fde5b8a80d06ec20147bd74e96a9351bebbec5e5c3a6fb37ea30a597ff84172601786a8a270662f58f708b4a3bf8d822d6318023def9773d2f6589962 + languageName: node + linkType: hard + +"@csstools/selector-specificity@npm:^2.0.0, @csstools/selector-specificity@npm:^2.0.2": + version: 2.1.1 + resolution: "@csstools/selector-specificity@npm:2.1.1" + peerDependencies: + postcss: ^8.4 + postcss-selector-parser: ^6.0.10 + checksum: 392ab62732e93aa8cbea445bf3485c1acbbecc8ec087b200e06c9ddd2acf740fd1fe46abdacf813e7a50a95a60346377ee3eecb4e1fe3709582e2851430b376a languageName: node linkType: hard @@ -1842,45 +1900,45 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.3.0": - version: 1.3.0 - resolution: "@eslint/eslintrc@npm:1.3.0" +"@eslint/eslintrc@npm:^1.4.1": + version: 1.4.1 + resolution: "@eslint/eslintrc@npm:1.4.1" dependencies: ajv: ^6.12.4 debug: ^4.3.2 - espree: ^9.3.2 - globals: ^13.15.0 + espree: ^9.4.0 + globals: ^13.19.0 ignore: ^5.2.0 import-fresh: ^3.2.1 js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: a1e734ad31a8b5328dce9f479f185fd4fc83dd7f06c538e1fa457fd8226b89602a55cc6458cd52b29573b01cdfaf42331be8cfc1fec732570086b591f4ed6515 - languageName: node - linkType: hard - -"@gar/promisify@npm:^1.0.1": - version: 1.1.2 - resolution: "@gar/promisify@npm:1.1.2" - checksum: d05081e0887a49c178b75ee3067bd6ee086f73c154d121b854fb2e044e8a89cb1cbb6de3a0dd93a519b80f0531fda68b099dd7256205f7fbb3490324342f2217 + checksum: cd3e5a8683db604739938b1c1c8b77927dc04fce3e28e0c88e7f2cd4900b89466baf83dfbad76b2b9e4d2746abdd00dd3f9da544d3e311633d8693f327d04cd7 languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": +"@gar/promisify@npm:^1.0.1, @gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.9.2": - version: 0.9.5 - resolution: "@humanwhocodes/config-array@npm:0.9.5" +"@humanwhocodes/config-array@npm:^0.11.8": + version: 0.11.8 + resolution: "@humanwhocodes/config-array@npm:0.11.8" dependencies: "@humanwhocodes/object-schema": ^1.2.1 debug: ^4.1.1 - minimatch: ^3.0.4 - checksum: 8ba6281bc0590f6c6eadeefc14244b5a3e3f5903445aadd1a32099ed80e753037674026ce1b3c945ab93561bea5eb29e3c5bff67060e230c295595ba517a3492 + minimatch: ^3.0.5 + checksum: 0fd6b3c54f1674ce0a224df09b9c2f9846d20b9e54fabae1281ecfc04f2e6ad69bf19e1d6af6a28f88e8aa3990168b6cb9e1ef755868c3256a630605ec2cb1d3 + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 0fd22007db8034a2cdf2c764b140d37d9020bbfce8a49d3ec5c05290e77d4b0263b1b972b752df8c89e5eaa94073408f2b7d977aed131faf6cf396ebb5d7fb61 languageName: node linkType: hard @@ -1911,38 +1969,76 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.5 - resolution: "@jridgewell/resolve-uri@npm:3.0.5" - checksum: 1ee652b693da7979ac4007926cc3f0a32b657ffeb913e111f44e5b67153d94a2f28a1d560101cc0cf8087625468293a69a00f634a2914e1a6d0817ba2039a913 +"@jridgewell/gen-mapping@npm:^0.1.0": + version: 0.1.1 + resolution: "@jridgewell/gen-mapping@npm:0.1.1" + dependencies: + "@jridgewell/set-array": ^1.0.0 + "@jridgewell/sourcemap-codec": ^1.4.10 + checksum: 3bcc21fe786de6ffbf35c399a174faab05eb23ce6a03e8769569de28abbf4facc2db36a9ddb0150545ae23a8d35a7cf7237b2aa9e9356a7c626fb4698287d5cc + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/gen-mapping@npm:0.3.2" + dependencies: + "@jridgewell/set-array": ^1.0.1 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1832707a1c476afebe4d0fbbd4b9434fdb51a4c3e009ab1e9938648e21b7a97049fa6009393bdf05cab7504108413441df26d8a3c12193996e65493a4efb6882 languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.11 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.11" - checksum: 3b2afaf8400fb07a36db60e901fcce6a746cdec587310ee9035939d89878e57b2dec8173b0b8f63176f647efa352294049a53c49739098eb907ff81fec2547c8 +"@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": + version: 3.1.0 + resolution: "@jridgewell/resolve-uri@npm:3.1.0" + checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.0": - version: 0.3.4 - resolution: "@jridgewell/trace-mapping@npm:0.3.4" +"@jridgewell/set-array@npm:^1.0.0, @jridgewell/set-array@npm:^1.0.1": + version: 1.1.2 + resolution: "@jridgewell/set-array@npm:1.1.2" + checksum: 69a84d5980385f396ff60a175f7177af0b8da4ddb81824cb7016a9ef914eee9806c72b6b65942003c63f7983d4f39a5c6c27185bbca88eb4690b62075602e28e + languageName: node + linkType: hard + +"@jridgewell/source-map@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/source-map@npm:0.3.2" dependencies: - "@jridgewell/resolve-uri": ^3.0.3 - "@jridgewell/sourcemap-codec": ^1.4.10 - checksum: ab8bce84bbbc8c34f3ba8325ed926f8f2d3098983c10442a80c55764c4eb6e47d5b92d8ff20a0dd868c3e76a3535651fd8a0138182c290dbfc8396195685c37b + "@jridgewell/gen-mapping": ^0.3.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 languageName: node linkType: hard -"@ngtools/webpack@npm:13.2.3": - version: 13.2.3 - resolution: "@ngtools/webpack@npm:13.2.3" +"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": + version: 1.4.14 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" + checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.14, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.17 + resolution: "@jridgewell/trace-mapping@npm:0.3.17" + dependencies: + "@jridgewell/resolve-uri": 3.1.0 + "@jridgewell/sourcemap-codec": 1.4.14 + checksum: 9d703b859cff5cd83b7308fd457a431387db5db96bd781a63bf48e183418dd9d3d44e76b9e4ae13237f6abeeb25d739ec9215c1d5bfdd08f66f750a50074a339 + languageName: node + linkType: hard + +"@ngtools/webpack@npm:13.2.6": + version: 13.2.6 + resolution: "@ngtools/webpack@npm:13.2.6" peerDependencies: "@angular/compiler-cli": ^13.0.0 typescript: ">=4.4.3 <4.6" webpack: ^5.30.0 - checksum: 1d37be74c4f79b0f021f768e62bd670e7e4e800af835f51700cb00fc6753edefc0f20befe1760bb0c9c54f04203f90bdb2984a6bdc5d4a6c554f502a1c00b4d4 + checksum: 9dfc5d89393bb90b56fd78fb08da536a88ab23f43d772493cf78c8b9aad94f4b859cfd770aa520c3776a261444a999550467803bc074b69b66a7c3c7dc4170d6 languageName: node linkType: hard @@ -1963,7 +2059,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3": +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -2069,12 +2165,12 @@ __metadata: languageName: node linkType: hard -"@nrwl/cli@npm:*, @nrwl/cli@npm:14.1.7": - version: 14.1.7 - resolution: "@nrwl/cli@npm:14.1.7" +"@nrwl/cli@npm:*, @nrwl/cli@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/cli@npm:15.7.2" dependencies: - nx: 14.1.7 - checksum: 37a0a8535a282dd012ed7847249dd08bf45674bab0b560dfd02540076d4ba327c4c49fdd5561516d023eacc0237a3a93dd147f09d13494bd81e6e11975e64460 + nx: 15.7.2 + checksum: c59130679458e1572181d8b49ff3a755bba4e07f8fe9a4c0b20330a16105548381467c557a4f3dd09051cf6ae8bcea15c3f2ccd2dba9ad6d0dfec79de0b61b49 languageName: node linkType: hard @@ -2092,6 +2188,69 @@ __metadata: languageName: node linkType: hard +"@nrwl/nx-darwin-arm64@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-darwin-arm64@npm:15.7.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@nrwl/nx-darwin-x64@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-darwin-x64@npm:15.7.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@nrwl/nx-linux-arm-gnueabihf@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-linux-arm-gnueabihf@npm:15.7.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@nrwl/nx-linux-arm64-gnu@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-linux-arm64-gnu@npm:15.7.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@nrwl/nx-linux-arm64-musl@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-linux-arm64-musl@npm:15.7.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@nrwl/nx-linux-x64-gnu@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-linux-x64-gnu@npm:15.7.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@nrwl/nx-linux-x64-musl@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-linux-x64-musl@npm:15.7.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@nrwl/nx-win32-arm64-msvc@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-win32-arm64-msvc@npm:15.7.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@nrwl/nx-win32-x64-msvc@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/nx-win32-x64-msvc@npm:15.7.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nrwl/tao@npm:13.1.3": version: 13.1.3 resolution: "@nrwl/tao@npm:13.1.3" @@ -2113,14 +2272,14 @@ __metadata: languageName: node linkType: hard -"@nrwl/tao@npm:14.1.7": - version: 14.1.7 - resolution: "@nrwl/tao@npm:14.1.7" +"@nrwl/tao@npm:15.7.2": + version: 15.7.2 + resolution: "@nrwl/tao@npm:15.7.2" dependencies: - nx: 14.1.7 + nx: 15.7.2 bin: tao: index.js - checksum: ac5c58666975c34fb1c3be98936516b9dc030e51308fa0f980a600119a1faf0c77f1299124e4d9b5e1004b3ba2b45d5c331662bc3189419b90c62e85676f9dfa + checksum: cbf76f385bffb1bf1bde52d4ad950a8abd1ba9865f47afb67a58bb5dc5d7be0d21c5021a3332d2ab665d99b36ec1f08ee260c1289bb7e69783e69712145d2709 languageName: node linkType: hard @@ -2135,196 +2294,21 @@ __metadata: languageName: node linkType: hard -"@schematics/angular@npm:13.2.3": - version: 13.2.3 - resolution: "@schematics/angular@npm:13.2.3" +"@schematics/angular@npm:13.2.6": + version: 13.2.6 + resolution: "@schematics/angular@npm:13.2.6" dependencies: - "@angular-devkit/core": 13.2.3 - "@angular-devkit/schematics": 13.2.3 + "@angular-devkit/core": 13.2.6 + "@angular-devkit/schematics": 13.2.6 jsonc-parser: 3.0.0 - checksum: eab01246d34fa7a9af89017a95a921a3ec2c38d23e5e0efa3d33475030d91ff076fc1bfb68e282e116360e4c57080538facca9a596b00fb9c66cea6ab41ca26e - languageName: node - linkType: hard - -"@socket.io/base64-arraybuffer@npm:~1.0.2": - version: 1.0.2 - resolution: "@socket.io/base64-arraybuffer@npm:1.0.2" - checksum: fa3e58c7581643d0557969cd3bece20e198596df77968ff29ede6be329d488e65104bef900e68a67f39d8855abfa59baa2b08d96fb856504bd01cbdd8f52249c - languageName: node - linkType: hard - -"@swc-node/core@npm:^1.9.0": - version: 1.9.0 - resolution: "@swc-node/core@npm:1.9.0" - dependencies: - "@swc/core": ^1.2.172 - checksum: 6fb86ce83456443ecd1f5be423fcaa72a144dbc925b4db5a2213179be93c5bfb4d7d1572c05cd9052a2f06612285e1562ccb1f9f77a8c5829dfb46c0e3fa84a1 - languageName: node - linkType: hard - -"@swc-node/register@npm:^1.4.2": - version: 1.5.1 - resolution: "@swc-node/register@npm:1.5.1" - dependencies: - "@swc-node/core": ^1.9.0 - "@swc-node/sourcemap-support": ^0.2.0 - colorette: ^2.0.16 - debug: ^4.3.4 - pirates: ^4.0.5 - tslib: ^2.4.0 - peerDependencies: - typescript: ">= 4.3" - checksum: d4e4442e850bfd2416fcdf9b4a22afdcacde5bbb7c0ff21f5a3336ea3211c046805d7fadf8fbec1ab9f65b64eea07ef91cdc5d91792ba9c9045098bf8ba4c096 - languageName: node - linkType: hard - -"@swc-node/sourcemap-support@npm:^0.2.0": - version: 0.2.0 - resolution: "@swc-node/sourcemap-support@npm:0.2.0" - dependencies: - source-map-support: ^0.5.21 - checksum: b202ada28394f1b4127a98f3ad3cde9e46f0a3b1e9e80580c2ac7fd52d246b3bfe78dc8fc3e1ceb96bf4fc572687cc5d1cfc67ceb44aefb0fceaad40684d27c9 - languageName: node - linkType: hard - -"@swc/core-android-arm-eabi@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-android-arm-eabi@npm:1.2.189" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@swc/core-android-arm64@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-android-arm64@npm:1.2.189" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@swc/core-darwin-arm64@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-darwin-arm64@npm:1.2.189" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@swc/core-darwin-x64@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-darwin-x64@npm:1.2.189" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@swc/core-freebsd-x64@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-freebsd-x64@npm:1.2.189" - conditions: os=freebsd & cpu=x64 + checksum: f4f74d1d3ceee65374f49419ef5240a8aa8fb8df2eadf89f2f6af3bfc38b7d2b688bedb65e69200cfafceffbce790dfbb0855f1e0d408b330284c6e88da5dbc7 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.2.189" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@swc/core-linux-arm64-gnu@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-linux-arm64-gnu@npm:1.2.189" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-arm64-musl@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-linux-arm64-musl@npm:1.2.189" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@swc/core-linux-x64-gnu@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-linux-x64-gnu@npm:1.2.189" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-x64-musl@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-linux-x64-musl@npm:1.2.189" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@swc/core-win32-arm64-msvc@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-win32-arm64-msvc@npm:1.2.189" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@swc/core-win32-ia32-msvc@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-win32-ia32-msvc@npm:1.2.189" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@swc/core-win32-x64-msvc@npm:1.2.189": - version: 1.2.189 - resolution: "@swc/core-win32-x64-msvc@npm:1.2.189" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@swc/core@npm:^1.2.172, @swc/core@npm:^1.2.173": - version: 1.2.189 - resolution: "@swc/core@npm:1.2.189" - dependencies: - "@swc/core-android-arm-eabi": 1.2.189 - "@swc/core-android-arm64": 1.2.189 - "@swc/core-darwin-arm64": 1.2.189 - "@swc/core-darwin-x64": 1.2.189 - "@swc/core-freebsd-x64": 1.2.189 - "@swc/core-linux-arm-gnueabihf": 1.2.189 - "@swc/core-linux-arm64-gnu": 1.2.189 - "@swc/core-linux-arm64-musl": 1.2.189 - "@swc/core-linux-x64-gnu": 1.2.189 - "@swc/core-linux-x64-musl": 1.2.189 - "@swc/core-win32-arm64-msvc": 1.2.189 - "@swc/core-win32-ia32-msvc": 1.2.189 - "@swc/core-win32-x64-msvc": 1.2.189 - dependenciesMeta: - "@swc/core-android-arm-eabi": - optional: true - "@swc/core-android-arm64": - optional: true - "@swc/core-darwin-arm64": - optional: true - "@swc/core-darwin-x64": - optional: true - "@swc/core-freebsd-x64": - optional: true - "@swc/core-linux-arm-gnueabihf": - optional: true - "@swc/core-linux-arm64-gnu": - optional: true - "@swc/core-linux-arm64-musl": - optional: true - "@swc/core-linux-x64-gnu": - optional: true - "@swc/core-linux-x64-musl": - optional: true - "@swc/core-win32-arm64-msvc": - optional: true - "@swc/core-win32-ia32-msvc": - optional: true - "@swc/core-win32-x64-msvc": - optional: true - bin: - swcx: run_swcx.js - checksum: 606013d0cf9e49a9c0498c77d070464394e2430b82728b5157b210300bc2d0dc84a071b98554292c8694a0d4e295c4248aebfe25ddd022764a75b9030e9ddf90 +"@socket.io/component-emitter@npm:~3.1.0": + version: 3.1.0 + resolution: "@socket.io/component-emitter@npm:3.1.0" + checksum: db069d95425b419de1514dffe945cc439795f6a8ef5b9465715acf5b8b50798e2c91b8719cbf5434b3fe7de179d6cdcd503c277b7871cb3dd03febb69bdd50fa languageName: node linkType: hard @@ -2361,13 +2345,6 @@ __metadata: languageName: node linkType: hard -"@types/component-emitter@npm:^1.2.10": - version: 1.2.11 - resolution: "@types/component-emitter@npm:1.2.11" - checksum: 0e081c5f7a4b113af3732f67ad9ebb487d5c239d440d96938ff9a679d18bb9337a513638e12b5b02a7a921494eef18c5a4d78f1188bc43a12290edd74c42a9c7 - languageName: node - linkType: hard - "@types/connect-history-api-fallback@npm:^1.3.5": version: 1.3.5 resolution: "@types/connect-history-api-fallback@npm:1.3.5" @@ -2395,72 +2372,88 @@ __metadata: linkType: hard "@types/cors@npm:^2.8.12": - version: 2.8.12 - resolution: "@types/cors@npm:2.8.12" - checksum: 8c45f112c7d1d2d831b4b266f2e6ed33a1887a35dcbfe2a18b28370751fababb7cd045e745ef84a523c33a25932678097bf79afaa367c6cb3fa0daa7a6438257 + version: 2.8.13 + resolution: "@types/cors@npm:2.8.13" + dependencies: + "@types/node": "*" + checksum: 7ef197ea19d2e5bf1313b8416baa6f3fd6dd887fd70191da1f804f557395357dafd8bc8bed0ac60686923406489262a7c8a525b55748f7b2b8afa686700de907 languageName: node linkType: hard "@types/eslint-scope@npm:^3.7.0": - version: 3.7.3 - resolution: "@types/eslint-scope@npm:3.7.3" + version: 3.7.4 + resolution: "@types/eslint-scope@npm:3.7.4" dependencies: "@types/eslint": "*" "@types/estree": "*" - checksum: 6772b05e1b92003d1f295e81bc847a61f4fbe8ddab77ffa49e84ed3f9552513bdde677eb53ef167753901282857dd1d604d9f82eddb34a233495932b2dc3dc17 + checksum: ea6a9363e92f301cd3888194469f9ec9d0021fe0a397a97a6dd689e7545c75de0bd2153dfb13d3ab532853a278b6572c6f678ce846980669e41029d205653460 languageName: node linkType: hard "@types/eslint@npm:*, @types/eslint@npm:^8.4.2": - version: 8.4.2 - resolution: "@types/eslint@npm:8.4.2" + version: 8.21.1 + resolution: "@types/eslint@npm:8.21.1" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: e81268cdeb8d64d84af649344df88f064ece0f05db62072657c976b6162ffe16f94b6480a5367d627c629272c2d86d0ee8c24f7095e98f8e743b16f98500673b + checksum: 584068441e4000c7b41c8928274fdcc737bc62f564928c30eb64ec41bbdbac31612f9fedaf490bceab31ec8305e99615166428188ea345d58878394683086fae languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^0.0.50": +"@types/estree@npm:*": + version: 1.0.0 + resolution: "@types/estree@npm:1.0.0" + checksum: 910d97fb7092c6738d30a7430ae4786a38542023c6302b95d46f49420b797f21619cdde11fa92b338366268795884111c2eb10356e4bd2c8ad5b92941e9e6443 + languageName: node + linkType: hard + +"@types/estree@npm:^0.0.50": version: 0.0.50 resolution: "@types/estree@npm:0.0.50" checksum: 9a2b6a4a8c117f34d08fbda5e8f69b1dfb109f7d149b60b00fd7a9fb6ac545c078bc590aa4ec2f0a256d680cf72c88b3b28b60c326ee38a7bc8ee1ee95624922 languageName: node linkType: hard -"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.18": - version: 4.17.28 - resolution: "@types/express-serve-static-core@npm:4.17.28" +"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": + version: 4.17.33 + resolution: "@types/express-serve-static-core@npm:4.17.33" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" - checksum: 826489811a5b371c10f02443b4ca894ffc05813bfdf2b60c224f5c18ac9a30a2e518cb9ef9fdfcaa2a1bb17f8bfa4ed1859ccdb252e879c9276271b4ee2df5a9 + checksum: dce580d16b85f207445af9d4053d66942b27d0c72e86153089fa00feee3e96ae336b7bedb31ed4eea9e553c99d6dd356ed6e0928f135375d9f862a1a8015adf2 languageName: node linkType: hard "@types/express@npm:*": - version: 4.17.13 - resolution: "@types/express@npm:4.17.13" + version: 4.17.17 + resolution: "@types/express@npm:4.17.17" dependencies: "@types/body-parser": "*" - "@types/express-serve-static-core": ^4.17.18 + "@types/express-serve-static-core": ^4.17.33 "@types/qs": "*" "@types/serve-static": "*" - checksum: 12a2a0e6c4b993fc0854bec665906788aea0d8ee4392389d7a98a5de1eefdd33c9e1e40a91f3afd274011119c506f7b4126acb97fae62ae20b654974d44cba12 + checksum: 0196dacc275ac3ce89d7364885cb08e7fb61f53ca101f65886dbf1daf9b7eb05c0943e2e4bbd01b0cc5e50f37e0eea7e4cbe97d0304094411ac73e1b7998f4da languageName: node linkType: hard "@types/http-proxy@npm:^1.17.8": - version: 1.17.8 - resolution: "@types/http-proxy@npm:1.17.8" + version: 1.17.9 + resolution: "@types/http-proxy@npm:1.17.9" dependencies: "@types/node": "*" - checksum: 3b3d683498267096c8aca03652702243b1e087bc20e77a9abe74fdbee1c89c8283ee41c47d245cda2f422483b01980d70a1030b92a8ff24b280e0aa868241a8b + checksum: 7a6746d00729b2a9fe9f9dd3453430b099931df879ec8f7a7b5f07b1795f6d99b0512640c45a67390b1e4bacb9401e36824952aeeaf089feba8627a063cf8e00 + languageName: node + linkType: hard + +"@types/jasmine@npm:*": + version: 4.3.1 + resolution: "@types/jasmine@npm:4.3.1" + checksum: d31fc78f7e70ed4aed20fa59400be3de9f204c473a7755aba29df90eb7d9d19d4b04b821a07e30732e67ef6021c8191eb259211264e287c053832c1940ae6e71 languageName: node linkType: hard -"@types/jasmine@npm:*, @types/jasmine@npm:3.6.x": +"@types/jasmine@npm:3.6.x": version: 3.6.11 resolution: "@types/jasmine@npm:3.6.11" checksum: ccb4b749dc43b9ccb4365f36b14bdba8aac5ad7fdd00cc693695064acfbddb6b32fd2d59accd7e70b8f3a1eba69b49e8afa263e96f2b29aed565d0b911efe6c4 @@ -2477,23 +2470,16 @@ __metadata: linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": - version: 7.0.9 - resolution: "@types/json-schema@npm:7.0.9" - checksum: 259d0e25f11a21ba5c708f7ea47196bd396e379fddb79c76f9f4f62c945879dc21657904914313ec2754e443c5018ea8372362f323f30e0792897fdb2098a705 - languageName: node - linkType: hard - -"@types/json5@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/json5@npm:0.0.29" - checksum: e60b153664572116dfea673c5bda7778dbff150498f44f998e34b5886d8afc47f16799280e4b6e241c0472aef1bc36add771c569c68fc5125fc2ae519a3eb9ac + version: 7.0.11 + resolution: "@types/json-schema@npm:7.0.11" + checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d languageName: node linkType: hard -"@types/mime@npm:^1": - version: 1.3.2 - resolution: "@types/mime@npm:1.3.2" - checksum: 0493368244cced1a69cb791b485a260a422e6fcc857782e1178d1e6f219f1b161793e9f87f5fae1b219af0f50bee24fcbe733a18b4be8fdd07a38a8fb91146fd +"@types/mime@npm:*": + version: 3.0.1 + resolution: "@types/mime@npm:3.0.1" + checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 languageName: node linkType: hard @@ -2504,7 +2490,14 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:12.11.x, @types/node@npm:>=10.0.0": +"@types/node@npm:*, @types/node@npm:>=10.0.0": + version: 18.14.0 + resolution: "@types/node@npm:18.14.0" + checksum: d83fcf5e4ed544755dd9028f5cbb6b9d46235043159111bb2ad62223729aee581c0144a9f6df8ba73d74011db9ed4ebd7af2fd5e0996714e3beb508a5da8ac5c + languageName: node + linkType: hard + +"@types/node@npm:12.11.x": version: 12.11.7 resolution: "@types/node@npm:12.11.7" checksum: 7e60bb298fe80f85e3c4b26592d74983452f2eedc7a4bd65157c2059160685ff8ef26f670768ca4d20e41cd7195be2892bea9b483215faf8da1a737fd180e635 @@ -2526,9 +2519,9 @@ __metadata: linkType: hard "@types/prettier@npm:^2.6.0": - version: 2.6.1 - resolution: "@types/prettier@npm:2.6.1" - checksum: b25ec46d18129fa40c1a1f42feb7406e8f19901ba5261ba3c71600ad14996ae07b4f4b727a9b83da673948011e59d870fc519166f05b5d49e9ad39db1aea8c93 + version: 2.7.2 + resolution: "@types/prettier@npm:2.7.2" + checksum: b47d76a5252265f8d25dd2fe2a5a61dc43ba0e6a96ffdd00c594cb4fd74c1982c2e346497e3472805d97915407a09423804cc2110a0b8e1b22cffcab246479b7 languageName: node linkType: hard @@ -2553,17 +2546,17 @@ __metadata: languageName: node linkType: hard -"@types/retry@npm:^0.12.0": - version: 0.12.1 - resolution: "@types/retry@npm:0.12.1" - checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c +"@types/retry@npm:0.12.0": + version: 0.12.0 + resolution: "@types/retry@npm:0.12.0" + checksum: 61a072c7639f6e8126588bf1eb1ce8835f2cb9c2aba795c4491cf6310e013267b0c8488039857c261c387e9728c1b43205099223f160bb6a76b4374f741b5603 languageName: node linkType: hard "@types/selenium-webdriver@npm:^3.0.0": - version: 3.0.19 - resolution: "@types/selenium-webdriver@npm:3.0.19" - checksum: 81d5a4da6a73ad1bc53ae36de0a981919f21e9863777fb16198fd2c963040476d782fc0f41263f902b60ae07d49adacad87b86c0eff67a10b2e047b829462498 + version: 3.0.20 + resolution: "@types/selenium-webdriver@npm:3.0.20" + checksum: 6f96abc30b0dc00b833f3dd69dde561984f4d5aa5ad8b234e222d301e74be898b5acc01d4810bb012743ce4b1ae902605365900b3f7ad04c4f522d6e663c9fff languageName: node linkType: hard @@ -2577,12 +2570,12 @@ __metadata: linkType: hard "@types/serve-static@npm:*": - version: 1.13.10 - resolution: "@types/serve-static@npm:1.13.10" + version: 1.15.0 + resolution: "@types/serve-static@npm:1.15.0" dependencies: - "@types/mime": ^1 + "@types/mime": "*" "@types/node": "*" - checksum: eaca858739483e3ded254cad7d7a679dc2c8b3f52c8bb0cd845b3b7eb1984bde0371fdcb0a5c83aa12e6daf61b6beb762545021f520f08a1fe882a3fa4ea5554 + checksum: b6ac93d471fb0f53ddcac1f9b67572a09cd62806f7db5855244b28f6f421139626f24799392566e97d1ffc61b12f9de7f30380c39fcae3c8a161fe161d44edf2 languageName: node linkType: hard @@ -2596,11 +2589,11 @@ __metadata: linkType: hard "@types/ws@npm:^8.2.2": - version: 8.2.2 - resolution: "@types/ws@npm:8.2.2" + version: 8.5.4 + resolution: "@types/ws@npm:8.5.4" dependencies: "@types/node": "*" - checksum: 308957864b9a5a0378ac82f1b084fa31b1bbe85106fb0d84ed2b392e4829404f21ab6ab2c1eb782d556e59cd33d57c75ad2d0cedc4b9b9d0ca3b2311bc915578 + checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 languageName: node linkType: hard @@ -2638,7 +2631,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.17.0, @typescript-eslint/parser@npm:^5.10.0": +"@typescript-eslint/parser@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/parser@npm:5.17.0" dependencies: @@ -2655,6 +2648,23 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:^5.10.0": + version: 5.53.0 + resolution: "@typescript-eslint/parser@npm:5.53.0" + dependencies: + "@typescript-eslint/scope-manager": 5.53.0 + "@typescript-eslint/types": 5.53.0 + "@typescript-eslint/typescript-estree": 5.53.0 + debug: ^4.3.4 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 979e5d63793a9e64998b1f956ba0f00f8a2674db3a664fafce7b2433323f5248bd776af8305e2419d73a9d94c55176fee099abc5c153b4cc52e5765c725c1edd + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/scope-manager@npm:5.17.0" @@ -2665,6 +2675,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:5.53.0": + version: 5.53.0 + resolution: "@typescript-eslint/scope-manager@npm:5.53.0" + dependencies: + "@typescript-eslint/types": 5.53.0 + "@typescript-eslint/visitor-keys": 5.53.0 + checksum: 51f31dc01e95908611f402441f58404da80a338c0237b2b82f4a7b0b2e8868c4bfe8f7cf44b2567dd56533de609156a5d4ac54bb1f9f09c7014b99428aef2543 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/type-utils@npm:5.17.0" @@ -2688,6 +2708,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.53.0": + version: 5.53.0 + resolution: "@typescript-eslint/types@npm:5.53.0" + checksum: b0eaf23de4ab13697d4d2095838c959a3f410c30f0d19091e5ca08e62320c3cc3c72bcb631823fb6a4fbb31db0a059e386a0801244930d0a88a6a698e5f46548 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/typescript-estree@npm:5.17.0" @@ -2706,6 +2733,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:5.53.0": + version: 5.53.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.53.0" + dependencies: + "@typescript-eslint/types": 5.53.0 + "@typescript-eslint/visitor-keys": 5.53.0 + debug: ^4.3.4 + globby: ^11.1.0 + is-glob: ^4.0.3 + semver: ^7.3.7 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 6e119c8e4167c8495d728c5556a834545a9c064918dd5e7b79b0d836726f4f8e2a0297b0ac82bf2b71f1e5427552217d0b59d8fb1406fd79bd3bf91b75dca873 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/utils@npm:5.17.0" @@ -2732,6 +2777,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:5.53.0": + version: 5.53.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.53.0" + dependencies: + "@typescript-eslint/types": 5.53.0 + eslint-visitor-keys: ^3.3.0 + checksum: 090695883c15364c6f401e97f56b13db0f31c1114f3bd22562bd41734864d27f6a3c80de33957e9dedab2d5f94b0f4480ba3fde1d4574e74dca4593917b7b54a + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -2897,17 +2952,38 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/lockfile@npm:1.1.0": +"@yarnpkg/lockfile@npm:1.1.0, @yarnpkg/lockfile@npm:^1.1.0": version: 1.1.0 resolution: "@yarnpkg/lockfile@npm:1.1.0" checksum: 05b881b4866a3546861fee756e6d3812776ea47fa6eb7098f983d6d0eefa02e12b66c3fff931574120f196286a7ad4879ce02743c8bb2be36c6a576c7852083a languageName: node linkType: hard +"@yarnpkg/parsers@npm:^3.0.0-rc.18": + version: 3.0.0-rc.39 + resolution: "@yarnpkg/parsers@npm:3.0.0-rc.39" + dependencies: + js-yaml: ^3.10.0 + tslib: ^2.4.0 + checksum: b54fb3694bd09e09142d5a8d607240a8389ce3ccf44a70808ac770c178bb527948c3805a230b6fa55753f95574c93773a853a37ece978e323c9f2d229fd82252 + languageName: node + linkType: hard + +"@zkochan/js-yaml@npm:0.0.6": + version: 0.0.6 + resolution: "@zkochan/js-yaml@npm:0.0.6" + dependencies: + argparse: ^2.0.1 + bin: + js-yaml: bin/js-yaml.js + checksum: 51b81597a1d1d79c778b8fae48317eaad78d75223d0b7477ad2b35f47cf63b19504da430bb7a03b326e668b282874242cc123e323e57293be038684cb5e755f8 + languageName: node + linkType: hard + "abab@npm:^2.0.5": - version: 2.0.5 - resolution: "abab@npm:2.0.5" - checksum: 0ec951b46d5418c2c2f923021ec193eaebdb4e802ffd5506286781b454be722a13a8430f98085cd3e204918401d9130ec6cc8f5ae19be315b3a0e857d83196e1 + version: 2.0.6 + resolution: "abab@npm:2.0.6" + checksum: 6ffc1af4ff315066c62600123990d87551ceb0aafa01e6539da77b0f5987ac7019466780bf480f1787576d4385e3690c81ccc37cfda12819bf510b8ab47e5a3e languageName: node linkType: hard @@ -2918,7 +2994,7 @@ __metadata: languageName: node linkType: hard -"accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.7": +"accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" dependencies: @@ -2946,12 +3022,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.1": - version: 8.7.1 - resolution: "acorn@npm:8.7.1" +"acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.8.0": + version: 8.8.2 + resolution: "acorn@npm:8.8.2" bin: acorn: bin/acorn - checksum: aca0aabf98826717920ac2583fdcad0a6fbe4e583fdb6e843af2594e907455aeafe30b1e14f1757cd83ce1776773cf8296ffc3a4acf13f0bd3dfebcf1db6ae80 + checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 languageName: node linkType: hard @@ -2965,10 +3041,10 @@ __metadata: languageName: node linkType: hard -"adm-zip@npm:^0.4.9": - version: 0.4.16 - resolution: "adm-zip@npm:0.4.16" - checksum: 5ea46664d8b3b073fffeb7f934705fea288708745e708cffc1dd732ce3d2672cecd476b243f9d051892fd12952db2b6bd061975e1ff40057246f6d0cb6534a50 +"adm-zip@npm:^0.5.2": + version: 0.5.10 + resolution: "adm-zip@npm:0.5.10" + checksum: 07ed91cf6423bf5dca4ee63977bc7635e91b8d21829c00829d48dce4c6932e1b19e6cfcbe44f1931c956e68795ae97183fc775913883fa48ce88a1ac11fb2034 languageName: node linkType: hard @@ -2990,18 +3066,7 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.0": - version: 4.2.0 - resolution: "agentkeepalive@npm:4.2.0" - dependencies: - debug: ^4.1.0 - depd: ^1.1.2 - humanize-ms: ^1.2.1 - checksum: 89806f83ceebbcaabf6bd581a8dce4870910fd2a11f66df8f505b4cd4ce4ca5ab9e6eec8d11ce8531a6b60f6748b75b0775e0e2fa33871503ef00d535418a19a - languageName: node - linkType: hard - -"agentkeepalive@npm:^4.2.1": +"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": version: 4.2.1 resolution: "agentkeepalive@npm:4.2.1" dependencies: @@ -3056,7 +3121,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.9.0, ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": +"ajv@npm:8.9.0": version: 8.9.0 resolution: "ajv@npm:8.9.0" dependencies: @@ -3080,25 +3145,44 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": + version: 8.12.0 + resolution: "ajv@npm:8.12.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 + languageName: node + linkType: hard + "angularx-qrcode@npm:13.0.x": - version: 13.0.3 - resolution: "angularx-qrcode@npm:13.0.3" + version: 13.0.15 + resolution: "angularx-qrcode@npm:13.0.15" dependencies: "@cordobo/qrcode": 1.5.0 tslib: ^2.3.0 peerDependencies: "@angular/core": ^13.0.0 - checksum: e36ae1afc87a2ce3c5d25845f3c940956dfca8a4d0e53e33f5d085046e96bd5390288183e04aae9968597177d45b7138fe1a409c8be8b0a9743aea9543c7b173 + checksum: 53cdb02a041d9fb489f83c798b0be119a0331bd67d295bda112622a8eb435ba9fc151544f1c0e524659c15a5223bc76ca75f41b3fd2cfd9ea12de2c39f27f744 languageName: node linkType: hard -"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": +"ansi-colors@npm:4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 languageName: node linkType: hard +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e + languageName: node + linkType: hard + "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -3171,25 +3255,25 @@ __metadata: linkType: hard "ansi-styles@npm:^6.0.0": - version: 6.1.0 - resolution: "ansi-styles@npm:6.1.0" - checksum: 7a7f8528c07a9d20c3a92bccd2b6bc3bb4d26e5cb775c02826921477377bd495d615d61f710d56216344b6238d1d11ef2b0348e146c5b128715578bfb3217229 + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 languageName: node linkType: hard "anymatch@npm:~3.1.2": - version: 3.1.2 - resolution: "anymatch@npm:3.1.2" + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" dependencies: normalize-path: ^3.0.0 picomatch: ^2.0.4 - checksum: 985163db2292fac9e5a1e072bf99f1b5baccf196e4de25a0b0b81865ebddeb3b3eb4480734ef0a2ac8c002845396b91aa89121f5b84f93981a4658164a9ec6e9 + checksum: 3e044fd6d1d26545f235a9fe4d7a534e2029d8e59fa7fd9f2a6eb21230f6b5380ea1eaf55136e60cbf8e613544b3b766e7a6fa2102e2a3a117505466e3025dc2 languageName: node linkType: hard -"apexcharts@npm:3.33.x": - version: 3.33.1 - resolution: "apexcharts@npm:3.33.1" +"apexcharts@npm:3.37.x": + version: 3.37.0 + resolution: "apexcharts@npm:3.37.0" dependencies: svg.draggable.js: ^2.2.2 svg.easing.js: ^2.0.0 @@ -3197,7 +3281,7 @@ __metadata: svg.pathmorphing.js: ^0.1.3 svg.resize.js: ^1.4.3 svg.select.js: ^3.0.1 - checksum: 15f3fe73cb249ce33a8588333c282873046b114430f0a0e9350dd0cdbbc0cd6ad61142e7ce311d064b3385f26c2f3f996c7da333b4f55a2bf495c6081c6e2409 + checksum: 888df7fc7fc49292788466215b86893ed0bd7b4fa52d4c692e74816d6ecdc73d47921babe9576e6fe5afb636fc98cbc13b1790aa5aa61fed7c103f3fc74867a8 languageName: node linkType: hard @@ -3209,12 +3293,12 @@ __metadata: linkType: hard "are-we-there-yet@npm:^3.0.0": - version: 3.0.0 - resolution: "are-we-there-yet@npm:3.0.0" + version: 3.0.1 + resolution: "are-we-there-yet@npm:3.0.1" dependencies: delegates: ^1.0.0 readable-stream: ^3.6.0 - checksum: 348edfdd931b0b50868b55402c01c3f64df1d4c229ab6f063539a5025fd6c5f5bb8a0cab409bbed8d75d34762d22aa91b7c20b4204eb8177063158d9ba792981 + checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 languageName: node linkType: hard @@ -3325,19 +3409,19 @@ __metadata: languageName: node linkType: hard -"async@npm:^2.6.2": - version: 2.6.3 - resolution: "async@npm:2.6.3" +"async@npm:^2.6.4": + version: 2.6.4 + resolution: "async@npm:2.6.4" dependencies: lodash: ^4.17.14 - checksum: 5e5561ff8fca807e88738533d620488ac03a5c43fce6c937451f7e35f943d33ad06c24af3f681a48cca3d2b0002b3118faff0a128dc89438a9bf0226f712c499 + checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 languageName: node linkType: hard "async@npm:^3.2.3": - version: 3.2.3 - resolution: "async@npm:3.2.3" - checksum: c4bee57ab2249af3dc83ca3ef9acfa8e822c0d5e5aa41bae3eaf7f673648343cd64ecd7d26091ffd357f3f044428b17b5f00098494b6cf8b6b3e9681f0636ca1 + version: 3.2.4 + resolution: "async@npm:3.2.4" + checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 languageName: node linkType: hard @@ -3365,12 +3449,12 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.2": - version: 10.4.2 - resolution: "autoprefixer@npm:10.4.2" + version: 10.4.13 + resolution: "autoprefixer@npm:10.4.13" dependencies: - browserslist: ^4.19.1 - caniuse-lite: ^1.0.30001297 - fraction.js: ^4.1.2 + browserslist: ^4.21.4 + caniuse-lite: ^1.0.30001426 + fraction.js: ^4.2.0 normalize-range: ^0.1.2 picocolors: ^1.0.0 postcss-value-parser: ^4.2.0 @@ -3378,7 +3462,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: dbd13e641eaa7d7e3121769c22cc439222f1a9d0371a583d12300849de7287ece1e793767ff9902842dbfd56c4b7c19ed9fe1947c9f343ba2f4f3519dbddfdef + checksum: dcb1cb7ae96a3363d65d82e52f9a0a7d8c982256f6fd032d7e1ec311f099c23acfebfd517ff8e96bf93f716a66c4ea2b80c60aa19efd2f474ce434bd75ef7b79 languageName: node linkType: hard @@ -3390,9 +3474,20 @@ __metadata: linkType: hard "aws4@npm:^1.8.0": - version: 1.11.0 - resolution: "aws4@npm:1.11.0" - checksum: 5a00d045fd0385926d20ebebcfba5ec79d4482fe706f63c27b324d489a04c68edb0db99ed991e19eda09cb8c97dc2452059a34d97545cebf591d7a2b5a10999f + version: 1.12.0 + resolution: "aws4@npm:1.12.0" + checksum: 68f79708ac7c335992730bf638286a3ee0a645cf12575d557860100767c500c08b30e24726b9f03265d74116417f628af78509e1333575e9f8d52a80edfe8cbc + languageName: node + linkType: hard + +"axios@npm:^1.0.0": + version: 1.3.3 + resolution: "axios@npm:1.3.3" + dependencies: + follow-redirects: ^1.15.0 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: b734a4bc348e2fa27150a7d4289d783fa405feb3f79f8daf28fd05813a12c8525ae9d3854aafe7ba041b005a4a751a0ba3b923331ceed41296ae14c7e54e2f26 languageName: node linkType: hard @@ -3418,15 +3513,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-dynamic-import-node@npm:^2.3.3": - version: 2.3.3 - resolution: "babel-plugin-dynamic-import-node@npm:2.3.3" - dependencies: - object.assign: ^4.1.0 - checksum: c9d24415bcc608d0db7d4c8540d8002ac2f94e2573d2eadced137a29d9eab7e25d2cbb4bc6b9db65cf6ee7430f7dd011d19c911a9a778f0533b4a05ce8292c9b - languageName: node - linkType: hard - "babel-plugin-istanbul@npm:6.1.1": version: 6.1.1 resolution: "babel-plugin-istanbul@npm:6.1.1" @@ -3441,27 +3527,27 @@ __metadata: linkType: hard "babel-plugin-polyfill-corejs2@npm:^0.3.0": - version: 0.3.1 - resolution: "babel-plugin-polyfill-corejs2@npm:0.3.1" + version: 0.3.3 + resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" dependencies: - "@babel/compat-data": ^7.13.11 - "@babel/helper-define-polyfill-provider": ^0.3.1 + "@babel/compat-data": ^7.17.7 + "@babel/helper-define-polyfill-provider": ^0.3.3 semver: ^6.1.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ca873f14ccd6d2942013345a956de8165d0913556ec29756a748157140f5312f79eed487674e0ca562285880f05829b3712d72e1e4b412c2ce46bd6a50b4b975 + checksum: 7db3044993f3dddb3cc3d407bc82e640964a3bfe22de05d90e1f8f7a5cb71460011ab136d3c03c6c1ba428359ebf635688cd6205e28d0469bba221985f5c6179 languageName: node linkType: hard "babel-plugin-polyfill-corejs3@npm:^0.5.0": - version: 0.5.2 - resolution: "babel-plugin-polyfill-corejs3@npm:0.5.2" + version: 0.5.3 + resolution: "babel-plugin-polyfill-corejs3@npm:0.5.3" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.1 + "@babel/helper-define-polyfill-provider": ^0.3.2 core-js-compat: ^3.21.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2f3184c73f80f00ac876a5ebcad945fd8d2ae70e5f85b7ab6cc3bc69bc74025f4f7070de7abbb2a7274c78e130bd34fc13f4c85342da28205930364a1ef0aa21 + checksum: 9c6644a1b0afbe59e402827fdafc6f44994ff92c5b2f258659cbbfd228f7075dea49e95114af10e66d70f36cbde12ff1d81263eb67be749b3ef0e2c18cf3c16d languageName: node linkType: hard @@ -3556,21 +3642,43 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.19.1, body-parser@npm:^1.19.0": - version: 1.19.1 - resolution: "body-parser@npm:1.19.1" +"body-parser@npm:1.20.1": + version: 1.20.1 + resolution: "body-parser@npm:1.20.1" dependencies: - bytes: 3.1.1 + bytes: 3.1.2 content-type: ~1.0.4 debug: 2.6.9 - depd: ~1.1.2 - http-errors: 1.8.1 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 iconv-lite: 0.4.24 - on-finished: ~2.3.0 - qs: 6.9.6 - raw-body: 2.4.2 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: ~1.6.18 + unpipe: 1.0.0 + checksum: f1050dbac3bede6a78f0b87947a8d548ce43f91ccc718a50dd774f3c81f2d8b04693e52acf62659fad23101827dd318da1fb1363444ff9a8482b886a3e4a5266 + languageName: node + linkType: hard + +"body-parser@npm:^1.19.0": + version: 1.20.2 + resolution: "body-parser@npm:1.20.2" + dependencies: + bytes: 3.1.2 + content-type: ~1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 type-is: ~1.6.18 - checksum: 9197a300a6580b8723c7b6b1e22cebd5ba47cd4a6fd45c153350efcde79293869ddee8d17d95fb52724812d649d89d62775faab072608d3243a0cbb00582234e + unpipe: 1.0.0 + checksum: 14d37ec638ab5c93f6099ecaed7f28f890d222c650c69306872e00b9efa081ff6c596cd9afb9930656aae4d6c4e1c17537bea12bb73c87a217cb3cfea8896737 languageName: node linkType: hard @@ -3623,18 +3731,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.17.5, browserslist@npm:^4.19.1, browserslist@npm:^4.9.1": - version: 4.19.1 - resolution: "browserslist@npm:4.19.1" +"browserslist@npm:^4.14.5, browserslist@npm:^4.19.1, browserslist@npm:^4.21.3, browserslist@npm:^4.21.4, browserslist@npm:^4.21.5, browserslist@npm:^4.9.1": + version: 4.21.5 + resolution: "browserslist@npm:4.21.5" dependencies: - caniuse-lite: ^1.0.30001286 - electron-to-chromium: ^1.4.17 - escalade: ^3.1.1 - node-releases: ^2.0.1 - picocolors: ^1.0.0 + caniuse-lite: ^1.0.30001449 + electron-to-chromium: ^1.4.284 + node-releases: ^2.0.8 + update-browserslist-db: ^1.0.10 bin: browserslist: cli.js - checksum: c0777fd483691638fd6801e16c9d809e1d65f6d2b06db2e806654be51045cbab1452a89841a2c5caea2cbe19d621b4f1d391cffbb24512aa33280039ab345875 + checksum: 9755986b22e73a6a1497fd8797aedd88e04270be33ce66ed5d85a1c8a798292a65e222b0f251bafa1c2522261e237d73b08b58689d4920a607e5a53d56dc4706 languageName: node linkType: hard @@ -3685,14 +3792,14 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.1": - version: 3.1.1 - resolution: "bytes@npm:3.1.1" - checksum: 949ab99a385d6acf4d2c69f1afc618615dc905936e0b0b9aa94a9e94d722baaba44d6a0851536585a0892ae4d462b5a270ccb1b04c774640742cbde5538ca328 +"bytes@npm:3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: e4bcd3948d289c5127591fbedf10c0b639ccbf00243504e4e127374a15c3bc8eed0d28d4aaab08ff6f1cf2abc0cce6ba3085ed32f4f90e82a5683ce0014e1b6e languageName: node linkType: hard -"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0, cacache@npm:^15.3.0": +"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0": version: 15.3.0 resolution: "cacache@npm:15.3.0" dependencies: @@ -3779,10 +3886,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001286, caniuse-lite@npm:^1.0.30001297, caniuse-lite@npm:^1.0.30001299": - version: 1.0.30001450 - resolution: "caniuse-lite@npm:1.0.30001450" - checksum: 511b360bfc907b2e437699364cf96b83507bc45043926450056642332bcd6f65a1e72540c828534ae15e0ac906e3e9af46cb2bb84458dd580bc31478e9dce282 +"caniuse-lite@npm:^1.0.30001299, caniuse-lite@npm:^1.0.30001426, caniuse-lite@npm:^1.0.30001449": + version: 1.0.30001457 + resolution: "caniuse-lite@npm:1.0.30001457" + checksum: f311a7c5098681962402a86a0a367014ee91c3135395ee68bbfaf45caf0e36d581e42d7c5b1526ce99484a228e6cf5cf0e400678292c65f5a21512a3fc7a5fb6 languageName: node linkType: hard @@ -3902,13 +4009,20 @@ __metadata: languageName: node linkType: hard -"cli-spinners@npm:2.6.1, cli-spinners@npm:^2.5.0": +"cli-spinners@npm:2.6.1": version: 2.6.1 resolution: "cli-spinners@npm:2.6.1" checksum: 423409baaa7a58e5104b46ca1745fbfc5888bbd0b0c5a626e052ae1387060839c8efd512fb127e25769b3dc9562db1dc1b5add6e0b93b7ef64f477feb6416a45 languageName: node linkType: hard +"cli-spinners@npm:^2.5.0": + version: 2.7.0 + resolution: "cli-spinners@npm:2.7.0" + checksum: a9afaf73f58d1f951fb23742f503631b3cf513f43f4c7acb1b640100eb76bfa16efbcd1994d149ffc6603a6d75dd3d4a516a76f125f90dce437de9b16fd0ee6f + languageName: node + linkType: hard + "cli-truncate@npm:^2.1.0": version: 2.1.0 resolution: "cli-truncate@npm:2.1.0" @@ -3958,6 +4072,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.1 + wrap-ansi: ^7.0.0 + checksum: 79648b3b0045f2e285b76fb2e24e207c6db44323581e421c3acbd0e86454cba1b37aea976ab50195a49e7384b871e6dfb2247ad7dec53c02454ac6497394cb56 + languageName: node + linkType: hard + "clone-deep@npm:^4.0.1": version: 4.0.1 resolution: "clone-deep@npm:4.0.1" @@ -3969,15 +4094,6 @@ __metadata: languageName: node linkType: hard -"clone-regexp@npm:^2.1.0": - version: 2.2.0 - resolution: "clone-regexp@npm:2.2.0" - dependencies: - is-regexp: ^2.0.0 - checksum: 3624905a98920ad5c196080f4ea4379fa42b12f3b1d1272d958bb79c194508d2aec85160c25846f0016ca861a064316b213a565cf53b81a513047f89cf877803 - languageName: node - linkType: hard - "clone@npm:^1.0.2": version: 1.0.4 resolution: "clone@npm:1.0.4" @@ -4017,7 +4133,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.2": +"color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -4026,17 +4142,17 @@ __metadata: languageName: node linkType: hard -"colord@npm:^2.9.2": - version: 2.9.2 - resolution: "colord@npm:2.9.2" - checksum: 2aa6a9b3abbce74ba3c563886cfeb433ea0d7df5ad6f4a560005eddab1ddf7c0fc98f39b09b599767a19c86dd3837b77f66f036e479515d4b17347006dbd6d9f +"colord@npm:^2.9.3": + version: 2.9.3 + resolution: "colord@npm:2.9.3" + checksum: 95d909bfbcfd8d5605cbb5af56f2d1ce2b323990258fd7c0d2eb0e6d3bb177254d7fb8213758db56bb4ede708964f78c6b992b326615f81a18a6aaf11d64c650 languageName: node linkType: hard "colorette@npm:^2.0.10, colorette@npm:^2.0.16": - version: 2.0.16 - resolution: "colorette@npm:2.0.16" - checksum: cd55596a3a2d1071c1a28eee7fd8a5387593ff1bd10a3e8d0a6221499311fe34a9f2b9272d77c391e0e003dcdc8934fb2f8d106e7ef1f7516f8060c901d41a27 + version: 2.0.19 + resolution: "colorette@npm:2.0.19" + checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 languageName: node linkType: hard @@ -4047,7 +4163,7 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -4063,10 +4179,10 @@ __metadata: languageName: node linkType: hard -"commander@npm:^8.3.0": - version: 8.3.0 - resolution: "commander@npm:8.3.0" - checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 +"commander@npm:^9.3.0": + version: 9.5.0 + resolution: "commander@npm:9.5.0" + checksum: c7a3e27aa59e913b54a1bafd366b88650bc41d6651f0cbe258d4ff09d43d6a7394232a4dadd0bf518b3e696fdf595db1028a0d82c785b88bd61f8a440cecfade languageName: node linkType: hard @@ -4084,13 +4200,6 @@ __metadata: languageName: node linkType: hard -"component-emitter@npm:~1.3.0": - version: 1.3.0 - resolution: "component-emitter@npm:1.3.0" - checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b - languageName: node - linkType: hard - "compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -4141,7 +4250,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": +"console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed @@ -4157,19 +4266,17 @@ __metadata: languageName: node linkType: hard -"content-type@npm:~1.0.4": - version: 1.0.4 - resolution: "content-type@npm:1.0.4" - checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 +"content-type@npm:~1.0.4, content-type@npm:~1.0.5": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 languageName: node linkType: hard "convert-source-map@npm:^1.5.1, convert-source-map@npm:^1.7.0": - version: 1.8.0 - resolution: "convert-source-map@npm:1.8.0" - dependencies: - safe-buffer: ~5.1.1 - checksum: 985d974a2d33e1a2543ada51c93e1ba2f73eaed608dc39f229afc78f71dcc4c8b7d7c684aa647e3c6a3a204027444d69e53e169ce94e8d1fa8d7dee80c9c8fed + version: 1.9.0 + resolution: "convert-source-map@npm:1.9.0" + checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 languageName: node linkType: hard @@ -4180,10 +4287,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.4.1": - version: 0.4.1 - resolution: "cookie@npm:0.4.1" - checksum: bd7c47f5d94ab70ccdfe8210cde7d725880d2fcda06d8e375afbdd82de0c8d3b73541996e9ce57d35f67f672c4ee6d60208adec06b3c5fc94cebb85196084cf8 +"cookie@npm:0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 languageName: node linkType: hard @@ -4220,19 +4327,18 @@ __metadata: linkType: hard "core-js-compat@npm:^3.20.2, core-js-compat@npm:^3.21.0": - version: 3.21.0 - resolution: "core-js-compat@npm:3.21.0" + version: 3.28.0 + resolution: "core-js-compat@npm:3.28.0" dependencies: - browserslist: ^4.19.1 - semver: 7.0.0 - checksum: 7914d2f8a2f7c1b400e1c04c7560f4c96028bf23cec3cea6063ba594e38023cccbd38ad88af41c5d6b65450e97a989eb37598f609e3f7fbc6ebc1856d4195cbb + browserslist: ^4.21.5 + checksum: 41d1d58c99ce7ee7abd8cf070f4c07a8f2655dbed1777d90a26246dddd7fac68315d53d2192584c8621a5328e6fe1a10da39b6bf2666e90fd5c2ff3b8f24e874 languageName: node linkType: hard -"core-js-pure@npm:^3.20.2": - version: 3.22.6 - resolution: "core-js-pure@npm:3.22.6" - checksum: 90737229b00fb26b0896bf5d22351702e595db7c60258f76d58cbd6a0dcef37a45017c97dc91632d168e2242e6dfff3f442ebcea1090e6154462fac5f119bc2d +"core-js-pure@npm:^3.25.1": + version: 3.28.0 + resolution: "core-js-pure@npm:3.28.0" + checksum: 8bef96a435783ea7e62b2bd4d6cc3d427a7bfeb053954aadabb33b5dba14a85c6297f7638bba9676a144f9cd7a5a0185a576d41d67baaae15227a4c9982a8cef languageName: node linkType: hard @@ -4243,13 +4349,20 @@ __metadata: languageName: node linkType: hard -"core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": +"core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab languageName: node linkType: hard +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 + languageName: node + linkType: hard + "cors@npm:~2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" @@ -4260,16 +4373,16 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^7.0.0, cosmiconfig@npm:^7.0.1": - version: 7.0.1 - resolution: "cosmiconfig@npm:7.0.1" +"cosmiconfig@npm:^7.0.0, cosmiconfig@npm:^7.1.0": + version: 7.1.0 + resolution: "cosmiconfig@npm:7.1.0" dependencies: "@types/parse-json": ^4.0.0 import-fresh: ^3.2.1 parse-json: ^5.0.0 path-type: ^4.0.0 yaml: ^1.10.0 - checksum: 4be63e7117955fd88333d7460e4c466a90f556df6ef34efd59034d2463484e339666c41f02b523d574a797ec61f4a91918c5b89a316db2ea2f834e0d2d09465b + checksum: c53bf7befc1591b2651a22414a5e786cd5f2eeaa87f3678a3d49d6069835a9d8d1aef223728e98aa8fec9a95bf831120d245096db12abe019fecb51f5696c96f languageName: node linkType: hard @@ -4311,10 +4424,10 @@ __metadata: languageName: node linkType: hard -"css-functions-list@npm:^3.0.1": - version: 3.0.1 - resolution: "css-functions-list@npm:3.0.1" - checksum: 8998406f9c6508180dad621bf91cfbf4649d922a57e2e5dd3fd909a2855e29d2f82cf176bd89a7598237b9e989126f8520e21479f2da51f37b1491a38fe72061 +"css-functions-list@npm:^3.1.0": + version: 3.1.0 + resolution: "css-functions-list@npm:3.1.0" + checksum: 8a7c9d4ae57cb2f01500263e65a21372048d359ca7aa6430a32a736fe2a421decfebe45e579124b9a158ec68aba2eadcd733e568495a7698240d9607d31f681b languageName: node linkType: hard @@ -4361,22 +4474,22 @@ __metadata: linkType: hard "css-select@npm:^4.2.0": - version: 4.2.1 - resolution: "css-select@npm:4.2.1" + version: 4.3.0 + resolution: "css-select@npm:4.3.0" dependencies: boolbase: ^1.0.0 - css-what: ^5.1.0 - domhandler: ^4.3.0 + css-what: ^6.0.1 + domhandler: ^4.3.1 domutils: ^2.8.0 nth-check: ^2.0.1 - checksum: 6617193ec7c332217204c4ea371d332c6845603fda415e36032e7e9e18206d7c368a14e3c57532082314d2689955b01122aa1097c1c52b6c1cab7ad90970d3c6 + checksum: d6202736839194dd7f910320032e7cfc40372f025e4bf21ca5bf6eb0a33264f322f50ba9c0adc35dadd342d3d6fae5ca244779a4873afbfa76561e343f2058e0 languageName: node linkType: hard -"css-what@npm:^5.1.0": - version: 5.1.0 - resolution: "css-what@npm:5.1.0" - checksum: 0b75d1bac95c885c168573c85744a6c6843d8c33345f54f717218b37ea6296b0e99bb12105930ea170fd4a921990392a7c790c16c585c1d8960c49e2b7ec39f7 +"css-what@npm:^6.0.1": + version: 6.1.0 + resolution: "css-what@npm:6.1.0" + checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe languageName: node linkType: hard @@ -4423,10 +4536,10 @@ __metadata: languageName: node linkType: hard -"date-format@npm:^4.0.3": - version: 4.0.3 - resolution: "date-format@npm:4.0.3" - checksum: 8ae4d9de3532010169a89bc7b079342051ba3ec88552636aa677bfb53e8eb15113af8394679aea7d41367dc8bb6e9865da17f21ac2802202180b09d6e3f2339e +"date-format@npm:^4.0.14": + version: 4.0.14 + resolution: "date-format@npm:4.0.14" + checksum: dfe5139df6af5759b9dd3c007b899b3f60d45a9240ffeee6314ab74e6ab52e9b519a44ccf285888bdd6b626c66ee9b4c8a523075fa1140617b5beb1cbb9b18d1 languageName: node linkType: hard @@ -4439,7 +4552,19 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.3, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + +"debug@npm:4.3.3": version: 4.3.3 resolution: "debug@npm:4.3.3" dependencies: @@ -4451,7 +4576,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.1.0, debug@npm:^3.1.1, debug@npm:^3.2.6": +"debug@npm:^3.1.0, debug@npm:^3.2.6, debug@npm:^3.2.7": version: 3.2.7 resolution: "debug@npm:3.2.7" dependencies: @@ -4460,25 +4585,13 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - "decamelize-keys@npm:^1.1.0": - version: 1.1.0 - resolution: "decamelize-keys@npm:1.1.0" + version: 1.1.1 + resolution: "decamelize-keys@npm:1.1.1" dependencies: decamelize: ^1.1.0 map-obj: ^1.0.0 - checksum: 8bc5d32e035a072f5dffc1f1f3d26ca7ab1fb44a9cade34c97ab6cd1e62c81a87e718101e96de07d78cecda20a3fdb955df958e46671ccad01bb8dcf0de2e298 + checksum: fc645fe20b7bda2680bbf9481a3477257a7f9304b1691036092b97ab04c0ab53e3bf9fcc2d2ae382536568e402ec41fb11e1d4c3836a9abe2d813dd9ef4311e0 languageName: node linkType: hard @@ -4490,9 +4603,9 @@ __metadata: linkType: hard "decode-uri-component@npm:^0.2.0": - version: 0.2.0 - resolution: "decode-uri-component@npm:0.2.0" - checksum: f3749344ab9305ffcfe4bfe300e2dbb61fc6359e2b736812100a3b1b6db0a5668cba31a05e4b45d4d63dbf1a18dfa354cd3ca5bb3ededddabb8cd293f4404f94 + version: 0.2.2 + resolution: "decode-uri-component@npm:0.2.2" + checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 languageName: node linkType: hard @@ -4527,11 +4640,11 @@ __metadata: linkType: hard "defaults@npm:^1.0.3": - version: 1.0.3 - resolution: "defaults@npm:1.0.3" + version: 1.0.4 + resolution: "defaults@npm:1.0.4" dependencies: clone: ^1.0.2 - checksum: 96e2112da6553d376afd5265ea7cbdb2a3b45535965d71ab8bb1da10c8126d168fdd5268799625324b368356d21ba2a7b3d4ec50961f11a47b7feb9de3d4413e + checksum: 3a88b7a587fc076b84e60affad8b85245c01f60f38fc1d259e7ac1d89eb9ce6abb19e27215de46b98568dd5bc48471730b327637e6f20b0f1bc85cf00440c80a languageName: node linkType: hard @@ -4543,11 +4656,12 @@ __metadata: linkType: hard "define-properties@npm:^1.1.3": - version: 1.1.3 - resolution: "define-properties@npm:1.1.3" + version: 1.2.0 + resolution: "define-properties@npm:1.2.0" dependencies: - object-keys: ^1.0.12 - checksum: da80dba55d0cd76a5a7ab71ef6ea0ebcb7b941f803793e4e0257b384cb772038faa0c31659d244e82c4342edef841c1a1212580006a05a5068ee48223d787317 + has-property-descriptors: ^1.0.0 + object-keys: ^1.1.1 + checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 languageName: node linkType: hard @@ -4567,8 +4681,8 @@ __metadata: linkType: hard "del@npm:^6.0.0": - version: 6.0.0 - resolution: "del@npm:6.0.0" + version: 6.1.1 + resolution: "del@npm:6.1.1" dependencies: globby: ^11.0.1 graceful-fs: ^4.2.4 @@ -4578,7 +4692,7 @@ __metadata: p-map: ^4.0.0 rimraf: ^3.0.2 slash: ^3.0.0 - checksum: 5742891627e91aaf62385714025233f4664da28bc55b6ab825649dcdea4691fed3cf329a2b1913fd2d2612e693e99e08a03c84cac7f36ef54bacac9390520192 + checksum: 563288b73b8b19a7261c47fd21a330eeab6e2acd7c6208c49790dfd369127120dd7836cdf0c1eca216b77c94782a81507eac6b4734252d3bef2795cb366996b6 languageName: node linkType: hard @@ -4596,6 +4710,13 @@ __metadata: languageName: node linkType: hard +"depd@npm:2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a + languageName: node + linkType: hard + "depd@npm:^1.1.2, depd@npm:~1.1.2": version: 1.1.2 resolution: "depd@npm:1.1.2" @@ -4610,10 +4731,10 @@ __metadata: languageName: node linkType: hard -"destroy@npm:~1.0.4": - version: 1.0.4 - resolution: "destroy@npm:1.0.4" - checksum: da9ab4961dc61677c709da0c25ef01733042614453924d65636a7db37308fef8a24cd1e07172e61173d471ca175371295fbc984b0af5b2b4ff47cd57bd784c03 +"destroy@npm:1.2.0": + version: 1.2.0 + resolution: "destroy@npm:1.2.0" + checksum: 0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38 languageName: node linkType: hard @@ -4725,29 +4846,29 @@ __metadata: linkType: hard "dom-serializer@npm:^1.0.1": - version: 1.3.2 - resolution: "dom-serializer@npm:1.3.2" + version: 1.4.1 + resolution: "dom-serializer@npm:1.4.1" dependencies: domelementtype: ^2.0.1 domhandler: ^4.2.0 entities: ^2.0.0 - checksum: bff48714944d67b160db71ba244fb0f3fe72e77ef2ec8414e2eeb56f2d926e404a13456b8b83a5392e217ba47dec2ec0c368801b31481813e94d185276c3e964 + checksum: fbb0b01f87a8a2d18e6e5a388ad0f7ec4a5c05c06d219377da1abc7bb0f674d804f4a8a94e3f71ff15f6cb7dcfc75704a54b261db672b9b3ab03da6b758b0b22 languageName: node linkType: hard "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": - version: 2.2.0 - resolution: "domelementtype@npm:2.2.0" - checksum: 24cb386198640cd58aa36f8c987f2ea61859929106d06ffcc8f547e70cb2ed82a6dc56dcb8252b21fba1f1ea07df6e4356d60bfe57f77114ca1aed6828362629 + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 languageName: node linkType: hard -"domhandler@npm:^4.2.0, domhandler@npm:^4.3.0": - version: 4.3.0 - resolution: "domhandler@npm:4.3.0" +"domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": + version: 4.3.1 + resolution: "domhandler@npm:4.3.1" dependencies: domelementtype: ^2.2.0 - checksum: d2a2dbf40dd99abf936b65ad83c6b530afdb3605a87cad37a11b5d9220e68423ebef1b86c89e0f6d93ffaf315cc327cf1a988652e7a9a95cce539e3984f4c64d + checksum: 4c665ceed016e1911bf7d1dadc09dc888090b64dee7851cccd2fcf5442747ec39c647bb1cb8c8919f8bbdd0f0c625a6bafeeed4b2d656bbecdbae893f43ffaaa languageName: node linkType: hard @@ -4800,7 +4921,7 @@ __metadata: "@typescript-eslint/eslint-plugin": 5.17.0 "@typescript-eslint/parser": 5.17.0 angularx-qrcode: 13.0.x - apexcharts: 3.33.x + apexcharts: 3.37.x eslint: ^8.12.0 eslint-config-prettier: ^8.5.0 eslint-plugin-prettier: ^4.0.0 @@ -4833,6 +4954,13 @@ __metadata: languageName: unknown linkType: soft +"duplexer@npm:^0.1.1": + version: 0.1.2 + resolution: "duplexer@npm:0.1.2" + checksum: 62ba61a830c56801db28ff6305c7d289b6dc9f859054e8c982abd8ee0b0a14d2e9a8e7d086ffee12e868d43e2bbe8a964be55ddbd8c8957714c87373c7a4f9b0 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -4868,10 +4996,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.17": - version: 1.4.68 - resolution: "electron-to-chromium@npm:1.4.68" - checksum: d7654d07ab7c504a0683cf29715db227bdbd3e397ad3a41bad3d1e35e9f837447be2bc5dea54b3350d51be5c9c7b79756dcbe44903fbee5949d67d783e788acb +"electron-to-chromium@npm:^1.4.284": + version: 1.4.305 + resolution: "electron-to-chromium@npm:1.4.305" + checksum: 574599461f7a5283026fc55727f9396b92f4d67952d3d333fc8dbb89e3b7f1ff646588c027fe8822b13227018d870405aeda2648a7324169871bab77108c4f40 languageName: node linkType: hard @@ -4928,18 +5056,16 @@ __metadata: languageName: node linkType: hard -"engine.io-parser@npm:~5.0.0": - version: 5.0.3 - resolution: "engine.io-parser@npm:5.0.3" - dependencies: - "@socket.io/base64-arraybuffer": ~1.0.2 - checksum: 88d664420a441dd02db17d110f7bbbd9efe971747918150bf666b82ee138df596a2f5038f461c8a01864c83af67cb202548364e4174543f8c0bf5f4776ca6e0d +"engine.io-parser@npm:~5.0.3": + version: 5.0.6 + resolution: "engine.io-parser@npm:5.0.6" + checksum: e92255b5463593cafe6cdc90577f107b39056c9c9337a8ee3477cb274337da1fe4ff53e9b3ad59d0478878e1d55ab15e973e2a91d0334d25ea99d8d6f8032f26 languageName: node linkType: hard -"engine.io@npm:~6.1.0": - version: 6.1.2 - resolution: "engine.io@npm:6.1.2" +"engine.io@npm:~6.4.1": + version: 6.4.1 + resolution: "engine.io@npm:6.4.1" dependencies: "@types/cookie": ^0.4.1 "@types/cors": ^2.8.12 @@ -4949,19 +5075,19 @@ __metadata: cookie: ~0.4.1 cors: ~2.8.5 debug: ~4.3.1 - engine.io-parser: ~5.0.0 - ws: ~8.2.3 - checksum: bd98d6ce2b1e868e8ff0f65d7667a885b90bce62065d851ea0394a00c86686925be824ab91237151222d6a5acfd5610634f36966fa9b4c502e7cf362fbdf974a + engine.io-parser: ~5.0.3 + ws: ~8.11.0 + checksum: b3921c35911d18b851153b97c1ad49f24ae068f01ddc17cd4d40b47a581d1317a8a1ed62665f63d07d076366b926b08a185d672573fedd186ee3304f9fa542d2 languageName: node linkType: hard "enhanced-resolve@npm:^5.8.3": - version: 5.9.0 - resolution: "enhanced-resolve@npm:5.9.0" + version: 5.12.0 + resolution: "enhanced-resolve@npm:5.12.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 06435f52670229eb7fd8d92ea2d988a275f9a1b8c08b9023ba45767c6ed844bc87aa9c9c7e6d044eef99ad73fc3b066b78101163696e5c53121909e5bf8efe8b + checksum: bf3f787facaf4ce3439bef59d148646344e372bef5557f0d37ea8aa02c51f50a925cd1f07b8d338f18992c29f544ec235a8c64bcdb56030196c48832a5494174 languageName: node linkType: hard @@ -5045,163 +5171,171 @@ __metadata: languageName: node linkType: hard -"esbuild-android-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-android-arm64@npm:0.14.14" +"esbuild-android-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-android-arm64@npm:0.14.22" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-darwin-64@npm:0.14.14" +"esbuild-darwin-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-darwin-64@npm:0.14.22" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-darwin-arm64@npm:0.14.14" +"esbuild-darwin-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-darwin-arm64@npm:0.14.22" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-freebsd-64@npm:0.14.14" +"esbuild-freebsd-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-freebsd-64@npm:0.14.22" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-freebsd-arm64@npm:0.14.14" +"esbuild-freebsd-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-freebsd-arm64@npm:0.14.22" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-32@npm:0.14.14" +"esbuild-linux-32@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-32@npm:0.14.22" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-64@npm:0.14.14" +"esbuild-linux-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-64@npm:0.14.22" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-arm64@npm:0.14.14" +"esbuild-linux-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-arm64@npm:0.14.22" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-arm@npm:0.14.14" +"esbuild-linux-arm@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-arm@npm:0.14.22" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-mips64le@npm:0.14.14" +"esbuild-linux-mips64le@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-mips64le@npm:0.14.22" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-ppc64le@npm:0.14.14" +"esbuild-linux-ppc64le@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-ppc64le@npm:0.14.22" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-s390x@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-linux-s390x@npm:0.14.14" +"esbuild-linux-riscv64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-riscv64@npm:0.14.22" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"esbuild-linux-s390x@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-linux-s390x@npm:0.14.22" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-netbsd-64@npm:0.14.14" +"esbuild-netbsd-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-netbsd-64@npm:0.14.22" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-openbsd-64@npm:0.14.14" +"esbuild-openbsd-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-openbsd-64@npm:0.14.22" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-sunos-64@npm:0.14.14" +"esbuild-sunos-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-sunos-64@npm:0.14.22" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-wasm@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-wasm@npm:0.14.14" +"esbuild-wasm@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-wasm@npm:0.14.22" bin: esbuild: bin/esbuild - checksum: 047703c94952561da3e8ab58dbadf3581e0a71dfbd866eef6c2f89b58315aab90b7d0087b1b99882c5698a18fe1fa0103cdc5ab292751a039fc0cee14f0432b1 + checksum: 56a75d428e086440126132bb465f64ad33e40fd9ecf91dbdd4fe5b1e16ca65b30d8cec8f90eb538c99ad0c5f57dcb15df809cbe626ed6ea5106d723de985a53b languageName: node linkType: hard -"esbuild-windows-32@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-windows-32@npm:0.14.14" +"esbuild-windows-32@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-windows-32@npm:0.14.22" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-windows-64@npm:0.14.14" +"esbuild-windows-64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-windows-64@npm:0.14.22" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild-windows-arm64@npm:0.14.14" +"esbuild-windows-arm64@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild-windows-arm64@npm:0.14.22" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"esbuild@npm:0.14.14": - version: 0.14.14 - resolution: "esbuild@npm:0.14.14" - dependencies: - esbuild-android-arm64: 0.14.14 - esbuild-darwin-64: 0.14.14 - esbuild-darwin-arm64: 0.14.14 - esbuild-freebsd-64: 0.14.14 - esbuild-freebsd-arm64: 0.14.14 - esbuild-linux-32: 0.14.14 - esbuild-linux-64: 0.14.14 - esbuild-linux-arm: 0.14.14 - esbuild-linux-arm64: 0.14.14 - esbuild-linux-mips64le: 0.14.14 - esbuild-linux-ppc64le: 0.14.14 - esbuild-linux-s390x: 0.14.14 - esbuild-netbsd-64: 0.14.14 - esbuild-openbsd-64: 0.14.14 - esbuild-sunos-64: 0.14.14 - esbuild-windows-32: 0.14.14 - esbuild-windows-64: 0.14.14 - esbuild-windows-arm64: 0.14.14 +"esbuild@npm:0.14.22": + version: 0.14.22 + resolution: "esbuild@npm:0.14.22" + dependencies: + esbuild-android-arm64: 0.14.22 + esbuild-darwin-64: 0.14.22 + esbuild-darwin-arm64: 0.14.22 + esbuild-freebsd-64: 0.14.22 + esbuild-freebsd-arm64: 0.14.22 + esbuild-linux-32: 0.14.22 + esbuild-linux-64: 0.14.22 + esbuild-linux-arm: 0.14.22 + esbuild-linux-arm64: 0.14.22 + esbuild-linux-mips64le: 0.14.22 + esbuild-linux-ppc64le: 0.14.22 + esbuild-linux-riscv64: 0.14.22 + esbuild-linux-s390x: 0.14.22 + esbuild-netbsd-64: 0.14.22 + esbuild-openbsd-64: 0.14.22 + esbuild-sunos-64: 0.14.22 + esbuild-windows-32: 0.14.22 + esbuild-windows-64: 0.14.22 + esbuild-windows-arm64: 0.14.22 dependenciesMeta: esbuild-android-arm64: optional: true @@ -5225,6 +5359,8 @@ __metadata: optional: true esbuild-linux-ppc64le: optional: true + esbuild-linux-riscv64: + optional: true esbuild-linux-s390x: optional: true esbuild-netbsd-64: @@ -5241,7 +5377,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: af09b271777ee6e63900cd7fbbe6a8845992454f97febba514d29cc91c2b23c385d9450badf6a538f7c78714421c21121ec9859c086b78aebf748a52d9fa456a + checksum: 8b99a61203b289ff0ad2be1dfc78420d3f838384ddef6a302203e5a9d8e4913f2c152d58dd775be9ab15ce6c77ea9588934c0af27b25806be48d56472e661676 languageName: node linkType: hard @@ -5274,19 +5410,19 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.5.0": - version: 8.5.0 - resolution: "eslint-config-prettier@npm:8.5.0" + version: 8.6.0 + resolution: "eslint-config-prettier@npm:8.6.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: 0d0f5c32e7a0ad91249467ce71ca92394ccd343178277d318baf32063b79ea90216f4c81d1065d60f96366fdc60f151d4d68ae7811a58bd37228b84c2083f893 + checksum: ff0d0dfc839a556355422293428637e8d35693de58dabf8638bf0b6529131a109d0b2ade77521aa6e54573bb842d7d9d322e465dd73dd61c7590fa3834c3fa81 languageName: node linkType: hard "eslint-plugin-prettier@npm:^4.0.0": - version: 4.0.0 - resolution: "eslint-plugin-prettier@npm:4.0.0" + version: 4.2.1 + resolution: "eslint-plugin-prettier@npm:4.2.1" dependencies: prettier-linter-helpers: ^1.0.0 peerDependencies: @@ -5295,7 +5431,7 @@ __metadata: peerDependenciesMeta: eslint-config-prettier: optional: true - checksum: 03d69177a3c21fa2229c7e427ce604429f0b20ab7f411e2e824912f572a207c7f5a41fd1f0a95b9b8afe121e291c1b1f1dc1d44c7aad4b0837487f9c19f5210d + checksum: b9e839d2334ad8ec7a5589c5cb0f219bded260839a857d7a486997f9870e95106aa59b8756ff3f37202085ebab658de382b0267cae44c3a7f0eb0bcc03a4f6d6 languageName: node linkType: hard @@ -5345,11 +5481,13 @@ __metadata: linkType: hard "eslint@npm:^8.12.0, eslint@npm:^8.7.0": - version: 8.16.0 - resolution: "eslint@npm:8.16.0" + version: 8.34.0 + resolution: "eslint@npm:8.34.0" dependencies: - "@eslint/eslintrc": ^1.3.0 - "@humanwhocodes/config-array": ^0.9.2 + "@eslint/eslintrc": ^1.4.1 + "@humanwhocodes/config-array": ^0.11.8 + "@humanwhocodes/module-importer": ^1.0.1 + "@nodelib/fs.walk": ^1.2.8 ajv: ^6.10.0 chalk: ^4.0.0 cross-spawn: ^7.0.2 @@ -5359,18 +5497,21 @@ __metadata: eslint-scope: ^7.1.1 eslint-utils: ^3.0.0 eslint-visitor-keys: ^3.3.0 - espree: ^9.3.2 + espree: ^9.4.0 esquery: ^1.4.0 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 - functional-red-black-tree: ^1.0.1 - glob-parent: ^6.0.1 - globals: ^13.15.0 + find-up: ^5.0.0 + glob-parent: ^6.0.2 + globals: ^13.19.0 + grapheme-splitter: ^1.0.4 ignore: ^5.2.0 import-fresh: ^3.0.0 imurmurhash: ^0.1.4 is-glob: ^4.0.0 + is-path-inside: ^3.0.3 + js-sdsl: ^4.1.4 js-yaml: ^4.1.0 json-stable-stringify-without-jsonify: ^1.0.1 levn: ^0.4.1 @@ -5382,21 +5523,20 @@ __metadata: strip-ansi: ^6.0.1 strip-json-comments: ^3.1.0 text-table: ^0.2.0 - v8-compile-cache: ^2.0.3 bin: eslint: bin/eslint.js - checksum: 654a0200b49dc07280673fee13cdfb04326466790e031dfa9660b69fba3b1cf766a51504328f9de56bd18e6b5eb7578985cf29dc7f016c5ec851220ff9db95eb + checksum: 4e13e9eb05ac2248efbb6acae0b2325091235d5c47ba91a4775c7d6760778cbcd358a773ebd42f4629d2ad89e27c02f5d66eb1f737d75d9f5fc411454f83b2e5 languageName: node linkType: hard -"espree@npm:^9.0.0, espree@npm:^9.3.2": - version: 9.3.2 - resolution: "espree@npm:9.3.2" +"espree@npm:^9.0.0, espree@npm:^9.4.0": + version: 9.4.1 + resolution: "espree@npm:9.4.1" dependencies: - acorn: ^8.7.1 + acorn: ^8.8.0 acorn-jsx: ^5.3.2 eslint-visitor-keys: ^3.3.0 - checksum: 9a790d6779847051e87f70d720a0f6981899a722419e80c92ab6dee01e1ab83b8ce52d11b4dc96c2c490182efb5a4c138b8b0d569205bfe1cd4629e658e58c30 + checksum: 4d266b0cf81c7dfe69e542c7df0f246e78d29f5b04dda36e514eb4c7af117ee6cfbd3280e560571ed82ff6c9c3f0003c05b82583fc7a94006db7497c4fe4270e languageName: node linkType: hard @@ -5411,11 +5551,11 @@ __metadata: linkType: hard "esquery@npm:^1.4.0": - version: 1.4.0 - resolution: "esquery@npm:1.4.0" + version: 1.4.2 + resolution: "esquery@npm:1.4.2" dependencies: estraverse: ^5.1.0 - checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210 + checksum: 2f4ad89c5aafaca61cc2c15e256190f0d6deb4791cae6552d3cb4b1eb8867958cdf27a56aaa3272ff17435e3eaa19ee0d4129fac336ca6373d7354d7b5da7966 languageName: node linkType: hard @@ -5494,15 +5634,6 @@ __metadata: languageName: node linkType: hard -"execall@npm:^2.0.0": - version: 2.0.0 - resolution: "execall@npm:2.0.0" - dependencies: - clone-regexp: ^2.1.0 - checksum: d98ee3e33f6c9001e80970e927fb9f16c6a121d5e250b2f4d6764d4157974f58cbe88613bbf073db05d5342677012002c5de956f4f0c32d10d092b6ff03a085c - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -5511,40 +5642,41 @@ __metadata: linkType: hard "express@npm:^4.17.1": - version: 4.17.2 - resolution: "express@npm:4.17.2" + version: 4.18.2 + resolution: "express@npm:4.18.2" dependencies: - accepts: ~1.3.7 + accepts: ~1.3.8 array-flatten: 1.1.1 - body-parser: 1.19.1 + body-parser: 1.20.1 content-disposition: 0.5.4 content-type: ~1.0.4 - cookie: 0.4.1 + cookie: 0.5.0 cookie-signature: 1.0.6 debug: 2.6.9 - depd: ~1.1.2 + depd: 2.0.0 encodeurl: ~1.0.2 escape-html: ~1.0.3 etag: ~1.8.1 - finalhandler: ~1.1.2 + finalhandler: 1.2.0 fresh: 0.5.2 + http-errors: 2.0.0 merge-descriptors: 1.0.1 methods: ~1.1.2 - on-finished: ~2.3.0 + on-finished: 2.4.1 parseurl: ~1.3.3 path-to-regexp: 0.1.7 proxy-addr: ~2.0.7 - qs: 6.9.6 + qs: 6.11.0 range-parser: ~1.2.1 safe-buffer: 5.2.1 - send: 0.17.2 - serve-static: 1.14.2 + send: 0.18.0 + serve-static: 1.15.0 setprototypeof: 1.2.0 - statuses: ~1.5.0 + statuses: 2.0.1 type-is: ~1.6.18 utils-merge: 1.0.1 vary: ~1.1.2 - checksum: 1535d56d20e65a1a39b5f056c025dd635290a744478ac69cc47633aeb4b2ce51458f8eb4080cfb7ba47c853ba5cfd794d404cff822a25127f1556b726ec3914a + checksum: 3c4b9b076879442f6b968fe53d85d9f1eeacbb4f4c41e5f16cc36d77ce39a2b0d81b3f250514982110d815b2f7173f5561367f9110fcc541f9371948e8c8b037 languageName: node linkType: hard @@ -5566,13 +5698,20 @@ __metadata: languageName: node linkType: hard -"extsprintf@npm:1.3.0, extsprintf@npm:^1.2.0": +"extsprintf@npm:1.3.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 languageName: node linkType: hard +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -5600,16 +5739,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": - version: 3.2.11 - resolution: "fast-glob@npm:3.2.11" +"fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": + version: 3.2.12 + resolution: "fast-glob@npm:3.2.12" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 + checksum: 0b1990f6ce831c7e28c4d505edcdaad8e27e88ab9fa65eedadb730438cfc7cde4910d6c975d6b7b8dc8a73da4773702ebcfcd6e3518e73938bb1383badfe01c2 languageName: node linkType: hard @@ -5627,19 +5766,19 @@ __metadata: languageName: node linkType: hard -"fastest-levenshtein@npm:^1.0.12": - version: 1.0.12 - resolution: "fastest-levenshtein@npm:1.0.12" - checksum: e1a013698dd1d302c7a78150130c7d50bb678c2c2f8839842a796d66cc7cdf50ea6b3d7ca930b0c8e7e8c2cd84fea8ab831023b382f7aab6922c318c1451beab +"fastest-levenshtein@npm:^1.0.16": + version: 1.0.16 + resolution: "fastest-levenshtein@npm:1.0.16" + checksum: a78d44285c9e2ae2c25f3ef0f8a73f332c1247b7ea7fb4a191e6bb51aa6ee1ef0dfb3ed113616dcdc7023e18e35a8db41f61c8d88988e877cf510df8edafbc71 languageName: node linkType: hard "fastq@npm:^1.6.0": - version: 1.13.0 - resolution: "fastq@npm:1.13.0" + version: 1.15.0 + resolution: "fastq@npm:1.15.0" dependencies: reusify: ^1.0.4 - checksum: 32cf15c29afe622af187d12fc9cd93e160a0cb7c31a3bb6ace86b7dea3b28e7b72acde89c882663f307b2184e14782c6c664fa315973c03626c7d4bff070bb0b + checksum: 0170e6bfcd5d57a70412440b8ef600da6de3b2a6c5966aeaf0a852d542daff506a0ee92d6de7679d1de82e644bce69d7a574a6c93f0b03964b5337eed75ada1a languageName: node linkType: hard @@ -5688,7 +5827,7 @@ __metadata: languageName: node linkType: hard -"finalhandler@npm:1.1.2, finalhandler@npm:~1.1.2": +"finalhandler@npm:1.1.2": version: 1.1.2 resolution: "finalhandler@npm:1.1.2" dependencies: @@ -5703,6 +5842,21 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:1.2.0": + version: 1.2.0 + resolution: "finalhandler@npm:1.2.0" + dependencies: + debug: 2.6.9 + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + on-finished: 2.4.1 + parseurl: ~1.3.3 + statuses: 2.0.1 + unpipe: ~1.0.0 + checksum: 92effbfd32e22a7dff2994acedbd9bcc3aa646a3e919ea6a53238090e87097f8ef07cced90aa2cc421abdf993aefbdd5b00104d55c7c5479a8d00ed105b45716 + languageName: node + linkType: hard + "find-cache-dir@npm:^3.3.1": version: 3.3.2 resolution: "find-cache-dir@npm:3.3.2" @@ -5724,6 +5878,16 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: ^6.0.0 + path-exists: ^4.0.0 + checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 + languageName: node + linkType: hard + "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -5743,20 +5907,20 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.1.0, flatted@npm:^3.2.4": - version: 3.2.5 - resolution: "flatted@npm:3.2.5" - checksum: 3c436e9695ccca29620b4be5671dd72e5dd0a7500e0856611b7ca9bd8169f177f408c3b9abfa78dfe1493ee2d873e2c119080a8a9bee4e1a186a9e60ca6c89f1 +"flatted@npm:^3.1.0, flatted@npm:^3.2.7": + version: 3.2.7 + resolution: "flatted@npm:3.2.7" + checksum: 427633049d55bdb80201c68f7eb1cbd533e03eac541f97d3aecab8c5526f12a20ccecaeede08b57503e772c769e7f8680b37e8d482d1e5f8d7e2194687f9ea35 languageName: node linkType: hard -"follow-redirects@npm:^1.0.0": - version: 1.14.8 - resolution: "follow-redirects@npm:1.14.8" +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.0": + version: 1.15.2 + resolution: "follow-redirects@npm:1.15.2" peerDependenciesMeta: debug: optional: true - checksum: 40c67899c2e3149a27e8b6498a338ff27f39fe138fde8d7f0756cb44b073ba0bfec3d52af28f20c5bdd67263d564d0d8d7b5efefd431de95c18c42f7b4aef457 + checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 languageName: node linkType: hard @@ -5767,6 +5931,17 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0": + version: 4.0.0 + resolution: "form-data@npm:4.0.0" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.8 + mime-types: ^2.1.12 + checksum: 01135bf8675f9d5c61ff18e2e2932f719ca4de964e3be90ef4c36aacfc7b9cb2fceb5eca0b7e0190e3383fe51c5b37f4cb80b62ca06a99aaabfcfd6ac7c9328c + languageName: node + linkType: hard + "form-data@npm:~2.3.2": version: 2.3.3 resolution: "form-data@npm:2.3.3" @@ -5785,10 +5960,10 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.1.2": - version: 4.1.3 - resolution: "fraction.js@npm:4.1.3" - checksum: d00065afce4814998b6e42fd439bbed17edbd9616b134927dbd75ebe1b94d6eb0820c0ce0e2cf8f26100e552cb72aff83f4816ef90cb1b329b6d12a531a26aaa +"fraction.js@npm:^4.2.0": + version: 4.2.0 + resolution: "fraction.js@npm:4.2.0" + checksum: 8c76a6e21dedea87109d6171a0ac77afa14205794a565d71cb10d2925f629a3922da61bf45ea52dbc30bce4d8636dc0a27213a88cbd600eab047d82f9a3a94c5 languageName: node linkType: hard @@ -5806,14 +5981,25 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^10.0.0, fs-extra@npm:^10.1.0": - version: 10.1.0 - resolution: "fs-extra@npm:10.1.0" +"fs-extra@npm:^11.1.0": + version: 11.1.0 + resolution: "fs-extra@npm:11.1.0" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: dc94ab37096f813cc3ca12f0f1b5ad6744dfed9ed21e953d72530d103cea193c2f81584a39e9dee1bea36de5ee66805678c0dddc048e8af1427ac19c00fffc50 + checksum: 5ca476103fa1f5ff4a9b3c4f331548f8a3c1881edaae323a4415d3153b5dc11dc6a981c8d1dd93eec8367ceee27b53f8bd27eecbbf66ffcdd04927510c171e7f + languageName: node + linkType: hard + +"fs-extra@npm:^8.1.0": + version: 8.1.0 + resolution: "fs-extra@npm:8.1.0" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^4.0.0 + universalify: ^0.1.0 + checksum: bf44f0e6cea59d5ce071bba4c43ca76d216f89e402dc6285c128abc0902e9b8525135aa808adad72c9d5d218e9f4bcc63962815529ff2f684ad532172a284880 languageName: node linkType: hard @@ -5838,7 +6024,7 @@ __metadata: languageName: node linkType: hard -"fs-monkey@npm:1.0.3": +"fs-monkey@npm:^1.0.3": version: 1.0.3 resolution: "fs-monkey@npm:1.0.3" checksum: cf50804833f9b88a476911ae911fe50f61a98d986df52f890bd97e7262796d023698cb2309fa9b74fdd8974f04315b648748a0a8ee059e7d5257b293bfc409c0 @@ -5885,20 +6071,26 @@ __metadata: languageName: node linkType: hard -"gauge@npm:^4.0.0": - version: 4.0.0 - resolution: "gauge@npm:4.0.0" +"functions-have-names@npm:^1.2.2": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 + languageName: node + linkType: hard + +"gauge@npm:^4.0.3": + version: 4.0.4 + resolution: "gauge@npm:4.0.4" dependencies: - ansi-regex: ^5.0.1 aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.2 - console-control-strings: ^1.0.0 + color-support: ^1.1.3 + console-control-strings: ^1.1.0 has-unicode: ^2.0.1 - signal-exit: ^3.0.0 + signal-exit: ^3.0.7 string-width: ^4.2.3 strip-ansi: ^6.0.1 - wide-align: ^1.1.2 - checksum: 637b34c84f518defa89319dbef68211a24e9302182ad2a619e3be1be5b7dcf2a962c8359e889294af667440f4722e7e6e61671859e00bd8ec280a136ded89b25 + wide-align: ^1.1.5 + checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d languageName: node linkType: hard @@ -5916,14 +6108,14 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2": - version: 1.1.1 - resolution: "get-intrinsic@npm:1.1.1" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1": + version: 1.2.0 + resolution: "get-intrinsic@npm:1.2.0" dependencies: function-bind: ^1.1.1 has: ^1.0.3 - has-symbols: ^1.0.1 - checksum: a9fe2ca8fa3f07f9b0d30fb202bcd01f3d9b9b6b732452e79c48e79f7d6d8d003af3f9e38514250e3553fdc83c61650851cb6870832ac89deaaceb08e3721a17 + has-symbols: ^1.0.3 + checksum: 78fc0487b783f5c58cf2dccafc3ae656ee8d2d8062a8831ce4a95e7057af4587a1d4882246c033aca0a7b4965276f4802b45cc300338d1b77a73d3e3e3f4877d languageName: node linkType: hard @@ -5934,13 +6126,6 @@ __metadata: languageName: node linkType: hard -"get-stdin@npm:^8.0.0": - version: 8.0.0 - resolution: "get-stdin@npm:8.0.0" - checksum: 40128b6cd25781ddbd233344f1a1e4006d4284906191ed0a7d55ec2c1a3e44d650f280b2c9eeab79c03ac3037da80257476c0e4e5af38ddfb902d6ff06282d77 - languageName: node - linkType: hard - "get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -5966,7 +6151,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^6.0.1": +"glob-parent@npm:^6.0.1, glob-parent@npm:^6.0.2": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" dependencies: @@ -5996,7 +6181,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": +"glob@npm:7.2.0": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -6010,6 +6195,20 @@ __metadata: languageName: node linkType: hard +"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + "glob@npm:^8.0.1": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -6050,12 +6249,12 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.15.0": - version: 13.15.0 - resolution: "globals@npm:13.15.0" +"globals@npm:^13.19.0": + version: 13.20.0 + resolution: "globals@npm:13.20.0" dependencies: type-fest: ^0.20.2 - checksum: 383ade0873b2ab29ce6d143466c203ed960491575bc97406395e5c8434026fb02472ab2dfff5bc16689b8460269b18fda1047975295cd0183904385c51258bae + checksum: ad1ecf914bd051325faad281d02ea2c0b1df5d01bd94d368dcc5513340eac41d14b3c61af325768e3c7f8d44576e72780ec0b6f2d366121f8eec6e03c3a3b97a languageName: node linkType: hard @@ -6109,9 +6308,16 @@ __metadata: linkType: hard "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": - version: 4.2.9 - resolution: "graceful-fs@npm:4.2.9" - checksum: 68ea4e07ff2c041ada184f9278b830375f8e0b75154e3f080af6b70f66172fabb4108d19b3863a96b53fc068a310b9b6493d86d1291acc5f3861eb4b79d26ad6 + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + languageName: node + linkType: hard + +"grapheme-splitter@npm:^1.0.4": + version: 1.0.4 + resolution: "grapheme-splitter@npm:1.0.4" + checksum: 0c22ec54dee1b05cd480f78cf14f732cb5b108edc073572c4ec205df4cd63f30f8db8025afc5debc8835a8ddeacf648a1c7992fe3dcd6ad38f9a476d84906620 languageName: node linkType: hard @@ -6169,10 +6375,19 @@ __metadata: languageName: node linkType: hard -"has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2": - version: 1.0.2 - resolution: "has-symbols@npm:1.0.2" - checksum: 2309c426071731be792b5be43b3da6fb4ed7cbe8a9a6bcfca1862587709f01b33d575ce8f5c264c1eaad09fca2f9a8208c0a2be156232629daa2dd0c0740976b +"has-property-descriptors@npm:^1.0.0": + version: 1.0.0 + resolution: "has-property-descriptors@npm:1.0.0" + dependencies: + get-intrinsic: ^1.1.1 + checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 languageName: node linkType: hard @@ -6248,9 +6463,9 @@ __metadata: linkType: hard "html-entities@npm:^2.3.2": - version: 2.3.2 - resolution: "html-entities@npm:2.3.2" - checksum: 522d8d202df301ff51b517a379e642023ed5c81ea9fb5674ffad88cff386165733d00b6089d5c2fcc644e44777d6072017b6216d8fa40f271d3610420d00a886 + version: 2.3.3 + resolution: "html-entities@npm:2.3.3" + checksum: 92521501da8aa5f66fee27f0f022d6e9ceae62667dae93aa6a2f636afa71ad530b7fb24a18d4d6c124c9885970cac5f8a52dbf1731741161002816ae43f98196 languageName: node linkType: hard @@ -6269,9 +6484,9 @@ __metadata: linkType: hard "http-cache-semantics@npm:^4.1.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 languageName: node linkType: hard @@ -6282,16 +6497,16 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:1.8.1": - version: 1.8.1 - resolution: "http-errors@npm:1.8.1" +"http-errors@npm:2.0.0": + version: 2.0.0 + resolution: "http-errors@npm:2.0.0" dependencies: - depd: ~1.1.2 + depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 - statuses: ">= 1.5.0 < 2" + statuses: 2.0.1 toidentifier: 1.0.1 - checksum: d3c7e7e776fd51c0a812baff570bdf06fe49a5dc448b700ab6171b1250e4cf7db8b8f4c0b133e4bfe2451022a5790c1ca6c2cae4094dedd6ac8304a1267f91d2 + checksum: 9b0a3782665c52ce9dc658a0d1560bcb0214ba5699e4ea15aefb2a496e2ca83db03ebc42e1cce4ac1f413e4e0d2d736a3fd755772c556a9a06853ba2a0b7d920 languageName: node linkType: hard @@ -6308,9 +6523,9 @@ __metadata: linkType: hard "http-parser-js@npm:>=0.5.1": - version: 0.5.5 - resolution: "http-parser-js@npm:0.5.5" - checksum: 85e67f12d99d67565be6c82dd86d4cf71939825fdf9826e10047b2443460bfef13235859ca67c0235d54e553db242204ec813febc86f11f83ed8ebd3cd475b65 + version: 0.5.8 + resolution: "http-parser-js@npm:0.5.8" + checksum: 6bbdf2429858e8cf13c62375b0bfb6dc3955ca0f32e58237488bc86cd2378f31d31785fd3ac4ce93f1c74e0189cf8823c91f5cb061696214fd368d2452dc871d languageName: node linkType: hard @@ -6337,8 +6552,8 @@ __metadata: linkType: hard "http-proxy-middleware@npm:^2.0.0": - version: 2.0.3 - resolution: "http-proxy-middleware@npm:2.0.3" + version: 2.0.6 + resolution: "http-proxy-middleware@npm:2.0.6" dependencies: "@types/http-proxy": ^1.17.8 http-proxy: ^1.18.1 @@ -6350,7 +6565,7 @@ __metadata: peerDependenciesMeta: "@types/express": optional: true - checksum: f121d5515e3c4613b6e9299f03ab70021bf2b5794a4052bcd5218cc754a19b3375e4d3744f3e70c682d9a1a5e125c8769a211d883cd0a24455ef18a5a310abf5 + checksum: 2ee85bc878afa6cbf34491e972ece0f5be0a3e5c98a60850cf40d2a9a5356e1fc57aab6cff33c1fc37691b0121c3a42602d2b1956c52577e87a5b77b62ae1c3a languageName: node linkType: hard @@ -6376,7 +6591,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.0, https-proxy-agent@npm:^5.0.0": +"https-proxy-agent@npm:5.0.0": version: 5.0.0 resolution: "https-proxy-agent@npm:5.0.0" dependencies: @@ -6396,6 +6611,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: 6 + debug: 4 + checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -6464,13 +6689,20 @@ __metadata: languageName: node linkType: hard -"ignore@npm:5.2.0, ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.1.9, ignore@npm:^5.2.0": +"ignore@npm:5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 languageName: node linkType: hard +"ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.1.9, ignore@npm:^5.2.0, ignore@npm:^5.2.1": + version: 5.2.4 + resolution: "ignore@npm:5.2.4" + checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef + languageName: node + linkType: hard + "image-size@npm:~0.5.0": version: 0.5.5 resolution: "image-size@npm:0.5.5" @@ -6488,9 +6720,9 @@ __metadata: linkType: hard "immutable@npm:^4.0.0": - version: 4.0.0 - resolution: "immutable@npm:4.0.0" - checksum: 4b5e9181e4d5fa06728a481835ec09c86367e5d03268666c95b522b7644ab891098022e4479a43c4c81a68f2ed82f10751ce5d33e208d7b873b6e7f9dfaf4d87 + version: 4.2.4 + resolution: "immutable@npm:4.2.4" + checksum: 3be84eded37b05e65cad57bfba630bc1bf170c498b7472144bc02d2650cc9baef79daf03574a9c2e41d195ebb55a1c12c9b312f41ee324b653927b24ad8bcaa7 languageName: node linkType: hard @@ -6592,10 +6824,10 @@ __metadata: languageName: node linkType: hard -"ip@npm:^1.1.0, ip@npm:^1.1.5": - version: 1.1.5 - resolution: "ip@npm:1.1.5" - checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a +"ip@npm:^1.1.0": + version: 1.1.8 + resolution: "ip@npm:1.1.8" + checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb languageName: node linkType: hard @@ -6646,12 +6878,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1": - version: 2.8.1 - resolution: "is-core-module@npm:2.8.1" +"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": + version: 2.11.0 + resolution: "is-core-module@npm:2.11.0" dependencies: has: ^1.0.3 - checksum: 418b7bc10768a73c41c7ef497e293719604007f88934a6ffc5f7c78702791b8528102fb4c9e56d006d69361549b3d9519440214a74aefc7e0b79e5e4411d377f + checksum: f96fd490c6b48eb4f6d10ba815c6ef13f410b0ba6f7eb8577af51697de523e5f2cd9de1c441b51d27251bf0e4aebc936545e33a5d26d5d51f28d25698d4a8bab languageName: node linkType: hard @@ -6756,7 +6988,7 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.2": +"is-path-inside@npm:^3.0.2, is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 @@ -6803,13 +7035,6 @@ __metadata: languageName: node linkType: hard -"is-regexp@npm:^2.0.0": - version: 2.1.0 - resolution: "is-regexp@npm:2.1.0" - checksum: 502f8e09faddc2e360350d3fa88dfb3af47b3c8e0bea1d0fe9903a1265cb199547cc11c99e9ee27cb010f678f6b48e52e92273860b68f6339e463e034f21859c - languageName: node - linkType: hard - "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -6855,9 +7080,9 @@ __metadata: linkType: hard "isbinaryfile@npm:^4.0.8": - version: 4.0.8 - resolution: "isbinaryfile@npm:4.0.8" - checksum: 606e3bb648d1a0dee23459d1d937bb2560e66a5281ec7c9ff50e585402d73321ac268d0f34cb7393125b3ebc4c7962d39e50a01cdb8904b52fce08b7ccd2bf9f + version: 4.0.10 + resolution: "isbinaryfile@npm:4.0.10" + checksum: a6b28db7e23ac7a77d3707567cac81356ea18bd602a4f21f424f862a31d0e7ab4f250759c98a559ece35ffe4d99f0d339f1ab884ffa9795172f632ab8f88e686 languageName: node linkType: hard @@ -6897,15 +7122,15 @@ __metadata: linkType: hard "istanbul-lib-instrument@npm:^5.0.4": - version: 5.1.0 - resolution: "istanbul-lib-instrument@npm:5.1.0" + version: 5.2.1 + resolution: "istanbul-lib-instrument@npm:5.2.1" dependencies: "@babel/core": ^7.12.3 "@babel/parser": ^7.14.7 "@istanbuljs/schema": ^0.1.2 istanbul-lib-coverage: ^3.2.0 semver: ^6.3.0 - checksum: 8b82e733c69fe9f94d2e21f3e5760c9bedb110329aa75df4bd40df95f1cac3bf38767e43f35b125cc547ceca7376b72ce7d95cc5238b7e9088345c7b589233d3 + checksum: bf16f1803ba5e51b28bbd49ed955a736488381e09375d830e42ddeb403855b2006f850711d95ad726f2ba3f1ae8e7366de7e51d2b9ac67dc4d80191ef7ddf272 languageName: node linkType: hard @@ -6934,12 +7159,12 @@ __metadata: linkType: hard "istanbul-reports@npm:^3.0.2": - version: 3.1.4 - resolution: "istanbul-reports@npm:3.1.4" + version: 3.1.5 + resolution: "istanbul-reports@npm:3.1.5" dependencies: html-escaper: ^2.0.0 istanbul-lib-report: ^3.0.0 - checksum: 2132983355710c522f6b26808015cab9a0ee8b9f5ae0db0d3edeff40b886dd83cb670fb123cb7b32dbe59473d7c00cdde2ba6136bc0acdb20a865fccea64dfe1 + checksum: 7867228f83ed39477b188ea07e7ccb9b4f5320b6f73d1db93a0981b7414fa4ef72d3f80c4692c442f90fc250d9406e71d8d7ab65bb615cb334e6292b73192b89 languageName: node linkType: hard @@ -6957,13 +7182,20 @@ __metadata: languageName: node linkType: hard -"jasmine-core@npm:3.8.x, jasmine-core@npm:^3.6.0": +"jasmine-core@npm:3.8.x": version: 3.8.0 resolution: "jasmine-core@npm:3.8.0" checksum: 6490373bd96d4d2a3c74214d307f8c37394370b1e892b9f203d4c5eda56941d53f84aabee0e65538b6921a190710618f1e73af44bd1d93e6c6e6c3f5c4bbfab6 languageName: node linkType: hard +"jasmine-core@npm:^3.6.0": + version: 3.99.1 + resolution: "jasmine-core@npm:3.99.1" + checksum: 4e4a89739d99e471b86c7ccc4c5c244a77cc6d1e17b2b0d87d81266b8415697354d8873f7e764790a10661744f73a753a6e9bcd9b3e48c66a0c9b8a092b071b7 + languageName: node + linkType: hard + "jasmine-core@npm:~2.8.0": version: 2.8.0 resolution: "jasmine-core@npm:2.8.0" @@ -7011,6 +7243,13 @@ __metadata: languageName: node linkType: hard +"js-sdsl@npm:^4.1.4": + version: 4.3.0 + resolution: "js-sdsl@npm:4.3.0" + checksum: ce908257cf6909e213af580af3a691a736f5ee8b16315454768f917a682a4ea0c11bde1b241bbfaecedc0eb67b72101b2c2df2ffaed32aed5d539fca816f054e + languageName: node + linkType: hard + "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -7018,26 +7257,26 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.13.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" +"js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" dependencies: - argparse: ^1.0.7 - esprima: ^4.0.0 + argparse: ^2.0.1 bin: js-yaml: bin/js-yaml.js - checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c + checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a languageName: node linkType: hard -"js-yaml@npm:^4.1.0": - version: 4.1.0 - resolution: "js-yaml@npm:4.1.0" +"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1": + version: 3.14.1 + resolution: "js-yaml@npm:3.14.1" dependencies: - argparse: ^2.0.1 + argparse: ^1.0.7 + esprima: ^4.0.0 bin: js-yaml: bin/js-yaml.js - checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a + checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c languageName: node linkType: hard @@ -7116,24 +7355,22 @@ __metadata: linkType: hard "json5@npm:^1.0.1": - version: 1.0.1 - resolution: "json5@npm:1.0.1" + version: 1.0.2 + resolution: "json5@npm:1.0.2" dependencies: minimist: ^1.2.0 bin: json5: lib/cli.js - checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 + checksum: 866458a8c58a95a49bef3adba929c625e82532bcff1fe93f01d29cb02cac7c3fe1f4b79951b7792c2da9de0b32871a8401a6e3c5b36778ad852bf5b8a61165d7 languageName: node linkType: hard -"json5@npm:^2.1.2": - version: 2.2.0 - resolution: "json5@npm:2.2.0" - dependencies: - minimist: ^1.2.5 +"json5@npm:^2.1.2, json5@npm:^2.2.2": + version: 2.2.3 + resolution: "json5@npm:2.2.3" bin: json5: lib/cli.js - checksum: e88fc5274bb58fc99547baa777886b069d2dd96d9cfc4490b305fd16d711dabd5979e35a4f90873cefbeb552e216b041a304fe56702bedba76e19bc7845f208d + checksum: 2a7436a93393830bce797d4626275152e37e877b265e94ca69c99e3d20c2b9dab021279146a39cdb700e71b2dd32a4cebd1514cd57cee102b1af906ce5040349 languageName: node linkType: hard @@ -7144,6 +7381,25 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:3.2.0": + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 + languageName: node + linkType: hard + +"jsonfile@npm:^4.0.0": + version: 4.0.0 + resolution: "jsonfile@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.6 + dependenciesMeta: + graceful-fs: + optional: true + checksum: 6447d6224f0d31623eef9b51185af03ac328a7553efcee30fa423d98a9e276ca08db87d71e17f2310b0263fd3ffa6c2a90a6308367f661dc21580f9469897c9e + languageName: node + linkType: hard + "jsonfile@npm:^6.0.1": version: 6.1.0 resolution: "jsonfile@npm:6.1.0" @@ -7177,23 +7433,23 @@ __metadata: linkType: hard "jszip@npm:^3.1.3": - version: 3.7.1 - resolution: "jszip@npm:3.7.1" + version: 3.10.1 + resolution: "jszip@npm:3.10.1" dependencies: lie: ~3.3.0 pako: ~1.0.2 readable-stream: ~2.3.6 - set-immediate-shim: ~1.0.1 - checksum: 67d737a82b294cc102e7451e32d5acbbab29860399be460cae598084327e6f2ea0c9bca2d3dad701da6a75ddf77f34c6a1dd7db0c3d5c0fec5998b7e56d6d59d + setimmediate: ^1.0.5 + checksum: abc77bfbe33e691d4d1ac9c74c8851b5761fba6a6986630864f98d876f3fcc2d36817dfc183779f32c00157b5d53a016796677298272a714ae096dfe6b1c8b60 languageName: node linkType: hard "karma-chrome-launcher@npm:3.1.x": - version: 3.1.0 - resolution: "karma-chrome-launcher@npm:3.1.0" + version: 3.1.1 + resolution: "karma-chrome-launcher@npm:3.1.1" dependencies: which: ^1.2.1 - checksum: 63431ddec9aa40e2a0439d9e2bcfa58a6822efd08e2666bdbc3f55dfbe8fcc0b401035b71b1f6f21340339dc56c172edaed8e8c0ddc6949873318ad1666b2dd9 + checksum: 8442219105e1f11a9284fd47f2e21e34720f7e725f25ea08f7525a7ec2088e2c1b65e2def4d7780139d296afc5c30bf4e1d4a839a097eb814031c2f6b379b39f languageName: node linkType: hard @@ -7222,13 +7478,13 @@ __metadata: linkType: hard "karma-jasmine@npm:4.0.x": - version: 4.0.1 - resolution: "karma-jasmine@npm:4.0.1" + version: 4.0.2 + resolution: "karma-jasmine@npm:4.0.2" dependencies: jasmine-core: ^3.6.0 peerDependencies: karma: "*" - checksum: 28337c82941ed6c68e0c47ef432c2c91511214e84a336e78d9068daebd61a3c1cee6209207ddc5fe3ad78124597f58054b93aa0f973ff6dcc8a8fcd1951c9851 + checksum: bf884704af1fd19816d9f4e96b25e286ff1a57adcabe1f15e3d2b3e9c1da873c1c843b9eab4274c27e63a99f1c3dea864f1f5eca1a10dc065e6e9d5796c207b4 languageName: node linkType: hard @@ -7242,13 +7498,13 @@ __metadata: linkType: hard "karma@npm:6.3.x": - version: 6.3.16 - resolution: "karma@npm:6.3.16" + version: 6.3.20 + resolution: "karma@npm:6.3.20" dependencies: + "@colors/colors": 1.5.0 body-parser: ^1.19.0 braces: ^3.0.2 chokidar: ^3.5.1 - colors: 1.4.0 connect: ^3.7.0 di: ^0.0.1 dom-serialize: ^2.2.1 @@ -7264,14 +7520,14 @@ __metadata: qjobs: ^1.2.0 range-parser: ^1.2.1 rimraf: ^3.0.2 - socket.io: ^4.2.0 + socket.io: ^4.4.1 source-map: ^0.6.1 tmp: ^0.2.1 ua-parser-js: ^0.7.30 yargs: ^16.1.1 bin: karma: bin/karma - checksum: eb1703d4907ac31a47019e2b6b5f69e1ecd7870dabee1ed8f284d9730f665e02ae9ef1a75733b5d4b6a27fe68069536d0845b9e41747c43507128b3ac645c87f + checksum: 7060afc367c49b2ce2e145f6555c428eadfd72bd68a5d4ae392ec51c8d1c0484706fdd52cfd06d69a811c25517fd5129689a69173be850814dc68c3b7eafa6c8 languageName: node linkType: hard @@ -7283,16 +7539,16 @@ __metadata: linkType: hard "klona@npm:^2.0.4, klona@npm:^2.0.5": - version: 2.0.5 - resolution: "klona@npm:2.0.5" - checksum: 8c976126ea252b766e648a4866e1bccff9d3b08432474ad80c559f6c7265cf7caede2498d463754d8c88c4759895edd8210c85c0d3155e6aae4968362889466f + version: 2.0.6 + resolution: "klona@npm:2.0.6" + checksum: ac9ee3732e42b96feb67faae4d27cf49494e8a3bf3fa7115ce242fe04786788e0aff4741a07a45a2462e2079aa983d73d38519c85d65b70ef11447bbc3c58ce7 languageName: node linkType: hard -"known-css-properties@npm:^0.25.0": - version: 0.25.0 - resolution: "known-css-properties@npm:0.25.0" - checksum: 1e6860b9cb8f671fc913f0a94a04c278769d9d8ac69f7975986440ef19825bdc26d8833e59ef7ef7ec3d4984e28e4f73e7bf99b9deb24803841d39135c26a1e6 +"known-css-properties@npm:^0.26.0": + version: 0.26.0 + resolution: "known-css-properties@npm:0.26.0" + checksum: e706f4af9d2683202df9f717e7d713f0f8c3330f155842c40d8f3b2a5837956c34aeb7ba08760977ccde1afce8b5377e29b40eb3e5c0b42bef28ddd108543cfb languageName: node linkType: hard @@ -7353,9 +7609,9 @@ __metadata: languageName: node linkType: hard -"license-webpack-plugin@npm:4.0.1": - version: 4.0.1 - resolution: "license-webpack-plugin@npm:4.0.1" +"license-webpack-plugin@npm:4.0.2": + version: 4.0.2 + resolution: "license-webpack-plugin@npm:4.0.2" dependencies: webpack-sources: ^3.0.0 peerDependenciesMeta: @@ -7363,7 +7619,7 @@ __metadata: optional: true webpack-sources: optional: true - checksum: ed3f8183bcb6cc6ec8aeeb17823d11a99d872609eedcf704ff4bb1a212df8d653c83eb42b7da04e0e25bb4924d784b6b603c5ba3a816dd17d5419b2273f70e5c + checksum: e88ebdb9c8bdfc0926dd7211d7fe2ee8697a44bb00a96bb5e6ca844b6acb7d24dd54eb17ec485e2e0140c3cc86709d1c2bd46e091ab52af076e1e421054c8322 languageName: node linkType: hard @@ -7376,10 +7632,10 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:2.0.4": - version: 2.0.4 - resolution: "lilconfig@npm:2.0.4" - checksum: 02ae530aa49218d782eb79e92c600ea5220828987f85aa3403fa512cadc7efe38c0ac7d0cd2edf600ad3fae1f6c1752f5b4bb78c0d9950435b044d53d507c9e1 +"lilconfig@npm:2.0.5": + version: 2.0.5 + resolution: "lilconfig@npm:2.0.5" + checksum: f7bb9e42656f06930ad04e583026f087508ae408d3526b8b54895e934eb2a966b7aafae569656f2c79a29fe6d779b3ec44ba577e80814734c8655d6f71cdf2d1 languageName: node linkType: hard @@ -7390,31 +7646,38 @@ __metadata: languageName: node linkType: hard +"lines-and-columns@npm:~2.0.3": + version: 2.0.3 + resolution: "lines-and-columns@npm:2.0.3" + checksum: 5955363dfd7d3d7c476d002eb47944dbe0310d57959e2112dce004c0dc76cecfd479cf8c098fd479ff344acdf04ee0e82b455462a26492231ac152f6c48d17a1 + languageName: node + linkType: hard + "lint-staged@npm:^12.3.7": - version: 12.4.1 - resolution: "lint-staged@npm:12.4.1" + version: 12.5.0 + resolution: "lint-staged@npm:12.5.0" dependencies: cli-truncate: ^3.1.0 colorette: ^2.0.16 - commander: ^8.3.0 - debug: ^4.3.3 + commander: ^9.3.0 + debug: ^4.3.4 execa: ^5.1.1 - lilconfig: 2.0.4 - listr2: ^4.0.1 - micromatch: ^4.0.4 + lilconfig: 2.0.5 + listr2: ^4.0.5 + micromatch: ^4.0.5 normalize-path: ^3.0.0 - object-inspect: ^1.12.0 + object-inspect: ^1.12.2 pidtree: ^0.5.0 string-argv: ^0.3.1 - supports-color: ^9.2.1 + supports-color: ^9.2.2 yaml: ^1.10.2 bin: lint-staged: bin/lint-staged.js - checksum: b57183b537064cda6caef6679918bf271903145f7c28d09567e918b8b13094048b579f8df808ea590dbd7ea2ec332327c5e372cf3d77e85b7b0254f6541ce4c3 + checksum: ac203917be098305bc0aebd5f1a969e88ea0854e8fb2199ebcbbb059d8bce324cf97db8f3d25f7954dd48c0666ae13987fb4db569d5b6fecda06f9fb742278e1 languageName: node linkType: hard -"listr2@npm:^4.0.1": +"listr2@npm:^4.0.5": version: 4.0.5 resolution: "listr2@npm:4.0.5" dependencies: @@ -7436,9 +7699,9 @@ __metadata: linkType: hard "loader-runner@npm:^4.2.0": - version: 4.2.0 - resolution: "loader-runner@npm:4.2.0" - checksum: e61aea8b6904b8af53d9de6f0484da86c462c0001f4511bedc837cec63deb9475cea813db62f702cd7930420ccb0e75c78112270ca5c8b61b374294f53c0cb3a + version: 4.3.0 + resolution: "loader-runner@npm:4.3.0" + checksum: a90e00dee9a16be118ea43fec3192d0b491fe03a32ed48a4132eb61d498f5536a03a1315531c19d284392a8726a4ecad71d82044c28d7f22ef62e029bf761569 languageName: node linkType: hard @@ -7450,24 +7713,24 @@ __metadata: linkType: hard "loader-utils@npm:^1.4.0": - version: 1.4.0 - resolution: "loader-utils@npm:1.4.0" + version: 1.4.2 + resolution: "loader-utils@npm:1.4.2" dependencies: big.js: ^5.2.2 emojis-list: ^3.0.0 json5: ^1.0.1 - checksum: d150b15e7a42ac47d935c8b484b79e44ff6ab4c75df7cc4cb9093350cf014ec0b17bdb60c5d6f91a37b8b218bd63b973e263c65944f58ca2573e402b9a27e717 + checksum: eb6fb622efc0ffd1abdf68a2022f9eac62bef8ec599cf8adb75e94d1d338381780be6278534170e99edc03380a6d29bc7eb1563c89ce17c5fed3a0b17f1ad804 languageName: node linkType: hard "loader-utils@npm:^2.0.0": - version: 2.0.2 - resolution: "loader-utils@npm:2.0.2" + version: 2.0.4 + resolution: "loader-utils@npm:2.0.4" dependencies: big.js: ^5.2.2 emojis-list: ^3.0.0 json5: ^2.1.2 - checksum: 9078d1ed47cadc57f4c6ddbdb2add324ee7da544cea41de3b7f1128e8108fcd41cd3443a85b7ee8d7d8ac439148aa221922774efe4cf87506d4fb054d5889303 + checksum: a5281f5fff1eaa310ad5e1164095689443630f3411e927f95031ab4fb83b4a98f388185bb1fe949e8ab8d4247004336a625e9255c22122b815bb9a4c5d8fc3b7 languageName: node linkType: hard @@ -7480,6 +7743,15 @@ __metadata: languageName: node linkType: hard +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: ^5.0.0 + checksum: 72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a + languageName: node + linkType: hard + "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -7531,15 +7803,15 @@ __metadata: linkType: hard "log4js@npm:^6.4.1": - version: 6.4.1 - resolution: "log4js@npm:6.4.1" + version: 6.8.0 + resolution: "log4js@npm:6.8.0" dependencies: - date-format: ^4.0.3 - debug: ^4.3.3 - flatted: ^3.2.4 + date-format: ^4.0.14 + debug: ^4.3.4 + flatted: ^3.2.7 rfdc: ^1.3.0 - streamroller: ^3.0.2 - checksum: 0614949662314573ec7dcd841769a4d23d8cb8268685458a40fcd94f2ae6ec628234cfb9a6bc17821fb6ea6ce3765e779b4966ba1cf918f393dc37155a3615cb + streamroller: ^3.1.5 + checksum: bab8e0434ff8d314338542c3305da2ab4e1b581fad8fef356776053dba8bd210839abc06fe83d0d4facdc27e8e890f05fd25f8ae7dc6dcab5635d75dd5d6c66f languageName: node linkType: hard @@ -7554,9 +7826,18 @@ __metadata: linkType: hard "loglevel@npm:^1.4.1": - version: 1.8.0 - resolution: "loglevel@npm:1.8.0" - checksum: 41aeea17de24aba8dba68084a31fe9189648bce4f39c1277e021bb276c3c53a75b0d337395919cf271068ad40ecefabad0e4fdeb4a8f11908beee532b898f4a7 + version: 1.8.1 + resolution: "loglevel@npm:1.8.1" + checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: ^3.0.2 + checksum: c154ae1cbb0c2206d1501a0e94df349653c92c8cbb25236d7e85190bcaf4567a03ac6eb43166fabfa36fd35623694da7233e88d9601fbf411a9a481d85dbd2cb languageName: node linkType: hard @@ -7569,21 +7850,14 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.3.1": - version: 7.3.1 - resolution: "lru-cache@npm:7.3.1" - checksum: 34bb50c015ffc29fd83545e912f28cea6e03fbf41c497fa220c4f131b990f9ddf95babac98745b416cbc6c0d835254d61668d09b8a4ecb476934546afc9e51bd - languageName: node - linkType: hard - "lru-cache@npm:^7.7.1": - version: 7.14.1 - resolution: "lru-cache@npm:7.14.1" - checksum: d72c6713c6a6d86836a7a6523b3f1ac6764768cca47ec99341c3e76db06aacd4764620e5e2cda719a36848785a52a70e531822dc2b33fb071fa709683746c104 + version: 7.17.0 + resolution: "lru-cache@npm:7.17.0" + checksum: 28c2a98ad313b8d61beac1f08257b6f0ca990e39d24a9bc831030b6e209447cfb11c6d9d1a774282189bfc9609d1dfd17ebe485228dd68f7b96b6b9b7740894e languageName: node linkType: hard -"magic-string@npm:0.25.7, magic-string@npm:^0.25.0": +"magic-string@npm:0.25.7": version: 0.25.7 resolution: "magic-string@npm:0.25.7" dependencies: @@ -7592,6 +7866,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.26.0": + version: 0.26.7 + resolution: "magic-string@npm:0.26.7" + dependencies: + sourcemap-codec: ^1.4.8 + checksum: 89b0d60cbb32bbf3d1e23c46ea93db082d18a8230b972027aecb10a40bba51be519ecce0674f995571e3affe917b76b09f59d8dbc9a1b2c9c4102a2b6e8a2b01 + languageName: node + linkType: hard + "make-dir@npm:^2.1.0": version: 2.1.0 resolution: "make-dir@npm:2.1.0" @@ -7618,31 +7901,7 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.1": - version: 10.0.2 - resolution: "make-fetch-happen@npm:10.0.2" - dependencies: - agentkeepalive: ^4.2.0 - cacache: ^15.3.0 - http-cache-semantics: ^4.1.0 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 - is-lambda: ^1.0.1 - lru-cache: ^7.3.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^1.4.1 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - negotiator: ^0.6.3 - promise-retry: ^2.0.1 - socks-proxy-agent: ^6.1.1 - ssri: ^8.0.1 - checksum: 5a620f84ab9e28673f70b0640b15133cb329ca4dbeec17e24b5029f07b134c52d96ebac150cab99979c48c5e6e99056978ed0321e4ce1e22a4d4d6fc7148c2fd - languageName: node - linkType: hard - -"make-fetch-happen@npm:^10.0.3": +"make-fetch-happen@npm:^10.0.1, make-fetch-happen@npm:^10.0.3": version: 10.2.1 resolution: "make-fetch-happen@npm:10.2.1" dependencies: @@ -7718,12 +7977,12 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.2.2": - version: 3.4.1 - resolution: "memfs@npm:3.4.1" +"memfs@npm:^3.2.2, memfs@npm:^3.4.3": + version: 3.4.13 + resolution: "memfs@npm:3.4.13" dependencies: - fs-monkey: 1.0.3 - checksum: 6d2f49d447d1be24ff9c747618933784eeb059189bc6a0d77b7a51c7daf06e2d3a74674a2e2ff1520e2c312bf91e719ed37144cf05087379b3ba0aef0b6aa062 + fs-monkey: ^1.0.3 + checksum: 3f9717d6f060919d53f211acb6096a0ea2f566a8cbcc4ef7e1f2561e31e33dc456053fdf951c90a49c8ec55402de7f01b006b81683ab7bd4bdbbd8c9b9cdae5f languageName: node linkType: hard @@ -7785,19 +8044,19 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.51.0, mime-db@npm:>= 1.43.0 < 2": - version: 1.51.0 - resolution: "mime-db@npm:1.51.0" - checksum: 613b1ac9d6e725cc24444600b124a7f1ce6c60b1baa654f39a3e260d0995a6dffc5693190217e271af7e2a5612dae19f2a73f3e316707d797a7391165f7ef423 +"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f languageName: node linkType: hard "mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": - version: 2.1.34 - resolution: "mime-types@npm:2.1.34" + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" dependencies: - mime-db: 1.51.0 - checksum: 67013de9e9d6799bde6d669d18785b7e18bcd212e710d3e04a4727f92f67a8ad4e74aee24be28b685adb794944814bde649119b58ee3282ffdbee58f9278d9f3 + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 languageName: node linkType: hard @@ -7860,7 +8119,16 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.2": +"minimatch@npm:3.0.5": + version: 3.0.5 + resolution: "minimatch@npm:3.0.5" + dependencies: + brace-expansion: ^1.1.7 + checksum: a3b84b426eafca947741b864502cee02860c4e7b145de11ad98775cfcf3066fef422583bc0ffce0952ddf4750c1ccf4220b1556430d4ce10139f66247d87d69e + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -7870,11 +8138,11 @@ __metadata: linkType: hard "minimatch@npm:^5.0.1": - version: 5.1.0 - resolution: "minimatch@npm:5.1.0" + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" dependencies: brace-expansion: ^2.0.1 - checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90 + checksum: 7564208ef81d7065a370f788d337cd80a689e981042cb9a1d0e6580b6c6a8c9279eba80010516e258835a988363f99f54a6f711a315089b8b42694f5da9d0d77 languageName: node linkType: hard @@ -7889,10 +8157,10 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": - version: 1.2.6 - resolution: "minimist@npm:1.2.6" - checksum: d15428cd1e11eb14e1233bcfb88ae07ed7a147de251441d61158619dfb32c4d7e9061d09cab4825fdee18ecd6fce323228c8c47b5ba7cd20af378ca4048fb3fb +"minimist@npm:^1.2.0, minimist@npm:^1.2.6": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 languageName: node linkType: hard @@ -7973,18 +8241,18 @@ __metadata: linkType: hard "minipass@npm:^3.0.0, minipass@npm:^3.1.0, minipass@npm:^3.1.1, minipass@npm:^3.1.3, minipass@npm:^3.1.6": - version: 3.1.6 - resolution: "minipass@npm:3.1.6" + version: 3.3.6 + resolution: "minipass@npm:3.3.6" dependencies: yallist: ^4.0.0 - checksum: 57a04041413a3531a65062452cb5175f93383ef245d6f4a2961d34386eb9aa8ac11ac7f16f791f5e8bbaf1dfb1ef01596870c88e8822215db57aa591a5bb0a77 + checksum: a30d083c8054cee83cdcdc97f97e4641a3f58ae743970457b1489ce38ee1167b3aaf7d815cd39ec7a99b9c40397fd4f686e83750e73e652b21cb516f6d845e48 languageName: node linkType: hard "minipass@npm:^4.0.0": - version: 4.0.3 - resolution: "minipass@npm:4.0.3" - checksum: a09f405e2f380ae7f6ee0cbb53b45c1fcc1b6c70fc3896f4d20649d92a10e61892c57bd9960a64cedf6c90b50022cb6c195905b515039c335b423202f99e6f18 + version: 4.2.0 + resolution: "minipass@npm:4.2.0" + checksum: 3c3ce269eacdcecb56b5dfe4bb4ab905d60fea1af4c967c7ed54baadfdd4af03e4d926567fb655957504fb4c9e2e7adbb2dc636927dfb56c829f2b25f1b2b3dd languageName: node linkType: hard @@ -7998,14 +8266,14 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.5": - version: 0.5.5 - resolution: "mkdirp@npm:0.5.5" +"mkdirp@npm:^0.5.5, mkdirp@npm:^0.5.6": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" dependencies: - minimist: ^1.2.5 + minimist: ^1.2.6 bin: mkdirp: bin/cmd.js - checksum: 3bce20ea525f9477befe458ab85284b0b66c8dc3812f94155af07c827175948cdd8114852ac6c6d82009b13c1048c37f6d98743eb019651ee25c39acc8aabe7d + checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 languageName: node linkType: hard @@ -8025,14 +8293,14 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.2, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f languageName: node linkType: hard -"ms@npm:2.1.3": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -8109,29 +8377,29 @@ __metadata: linkType: hard "ng-apexcharts@npm:1.7.x": - version: 1.7.0 - resolution: "ng-apexcharts@npm:1.7.0" + version: 1.7.4 + resolution: "ng-apexcharts@npm:1.7.4" dependencies: tslib: ^2.0.0 peerDependencies: "@angular/common": ">=13.0.0" "@angular/core": ">=13.0.0" - apexcharts: ^3.31.0 + apexcharts: ^3.36.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: a615eea8a3c6f6fc145f6a16a77732207bf37a2387bcb9148a2da29ef58fbca10dedb3efb5f4ddb86bbe72850385f5ecd330dcca32ff5e0c9ffbf9ae862db5f9 + checksum: 33bed66db3999847d6e9cbe8494237085042e240f9adfd45d2a7d6d4106e1e39209efc2394e2bab4287a2f1e71456eb3bbffbde05d9839e3146ecbead04a78f5 languageName: node linkType: hard "ngx-color-picker@npm:12.0.x": - version: 12.0.0 - resolution: "ngx-color-picker@npm:12.0.0" + version: 12.0.1 + resolution: "ngx-color-picker@npm:12.0.1" dependencies: tslib: ^2.3.0 peerDependencies: "@angular/common": ">=9.0.0" "@angular/core": ">=9.0.0" "@angular/forms": ">=9.0.0" - checksum: 6e8f9676ddfc390e4a4d0e7751e97cebd4b16b6cb4086254e9f5f5823ada12a5ee101a2a868f726a1b1dc412fa47aee885173461228d2a9904ed36041df38772 + checksum: f0c2361ec92c4cd76ddf5561e9beeb1b5da56d4aafe929189c648bda21166c0cddd6d3e44278fd28eb76e00e24a9254a73de6b9ffff9d3b9197c052ea373577e languageName: node linkType: hard @@ -8155,14 +8423,14 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^1.2.0": - version: 1.3.0 - resolution: "node-forge@npm:1.3.0" - checksum: 3d8124168dd82006fafbb079f40a529afa0de5bf4d77e6a5a471877e9d39bece31fdc8339e8aee30d5480dc79ffcd1059cfcb21983d350dd3f2a9f226db6ca85 +"node-forge@npm:^1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 languageName: node linkType: hard -"node-gyp-build@npm:^4.2.2": +"node-gyp-build@npm:^4.2.2, node-gyp-build@npm:^4.3.0": version: 4.6.0 resolution: "node-gyp-build@npm:4.6.0" bin: @@ -8173,17 +8441,6 @@ __metadata: languageName: node linkType: hard -"node-gyp-build@npm:^4.3.0": - version: 4.3.0 - resolution: "node-gyp-build@npm:4.3.0" - bin: - node-gyp-build: bin.js - node-gyp-build-optional: optional.js - node-gyp-build-test: build-test.js - checksum: 1ecab16d9f275174d516e223f60f65ebe07540347d5c04a6a7d6921060b7f2e3af4f19463d9d1dcedc452e275c2ae71354a99405e55ebd5b655bb2f38025c728 - languageName: node - linkType: hard - "node-gyp@npm:^8.2.0": version: 8.4.1 resolution: "node-gyp@npm:8.4.1" @@ -8224,10 +8481,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.1": - version: 2.0.2 - resolution: "node-releases@npm:2.0.2" - checksum: da858bf86b4d512842379749f5a5e4196ddab05ba18ffcf29f05bf460beceaca927f070f4430bb5046efec18941ddbc85e4c5fdbb83afc28a38dd6069a2f255e +"node-releases@npm:^2.0.8": + version: 2.0.10 + resolution: "node-releases@npm:2.0.10" + checksum: d784ecde25696a15d449c4433077f5cce620ed30a1656c4abf31282bfc691a70d9618bae6868d247a67914d1be5cc4fde22f65a05f4398cdfb92e0fc83cadfbc languageName: node linkType: hard @@ -8377,23 +8634,23 @@ __metadata: linkType: hard "npmlog@npm:^6.0.0": - version: 6.0.1 - resolution: "npmlog@npm:6.0.1" + version: 6.0.2 + resolution: "npmlog@npm:6.0.2" dependencies: are-we-there-yet: ^3.0.0 console-control-strings: ^1.1.0 - gauge: ^4.0.0 + gauge: ^4.0.3 set-blocking: ^2.0.0 - checksum: f1a4078a73ebc89896a832bbf869f491c32ecb12e0434b9a7499878ce8f29f22e72befe3c53cd8cdc9dbf4b4057297e783ab0b6746a8b067734de6205af4d538 + checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a languageName: node linkType: hard "nth-check@npm:^2.0.1": - version: 2.0.1 - resolution: "nth-check@npm:2.0.1" + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" dependencies: boolbase: ^1.0.0 - checksum: 5386d035c48438ff304fe687704d93886397349d1bed136de97aeae464caba10e8ffac55a04b215b86b3bc8897f33e0a5aa1045a9d8b2f251ae61b2a3ad3e450 + checksum: 5afc3dafcd1573b08877ca8e6148c52abd565f1d06b1eb08caf982e3fa289a82f2cae697ffb55b5021e146d60443f1590a5d6b944844e944714a5b549675bcd3 languageName: node linkType: hard @@ -8408,17 +8665,27 @@ __metadata: languageName: node linkType: hard -"nx@npm:14.1.7": - version: 14.1.7 - resolution: "nx@npm:14.1.7" +"nx@npm:15.7.2": + version: 15.7.2 + resolution: "nx@npm:15.7.2" dependencies: - "@nrwl/cli": 14.1.7 - "@nrwl/tao": 14.1.7 + "@nrwl/cli": 15.7.2 + "@nrwl/nx-darwin-arm64": 15.7.2 + "@nrwl/nx-darwin-x64": 15.7.2 + "@nrwl/nx-linux-arm-gnueabihf": 15.7.2 + "@nrwl/nx-linux-arm64-gnu": 15.7.2 + "@nrwl/nx-linux-arm64-musl": 15.7.2 + "@nrwl/nx-linux-x64-gnu": 15.7.2 + "@nrwl/nx-linux-x64-musl": 15.7.2 + "@nrwl/nx-win32-arm64-msvc": 15.7.2 + "@nrwl/nx-win32-x64-msvc": 15.7.2 + "@nrwl/tao": 15.7.2 "@parcel/watcher": 2.0.4 - "@swc-node/register": ^1.4.2 - "@swc/core": ^1.2.173 - chalk: 4.1.0 - chokidar: ^3.5.1 + "@yarnpkg/lockfile": ^1.1.0 + "@yarnpkg/parsers": ^3.0.0-rc.18 + "@zkochan/js-yaml": 0.0.6 + axios: ^1.0.0 + chalk: ^4.1.0 cli-cursor: 3.1.0 cli-spinners: 2.6.1 cliui: ^7.0.2 @@ -8427,27 +8694,55 @@ __metadata: fast-glob: 3.2.7 figures: 3.2.0 flat: ^5.0.2 - fs-extra: ^10.1.0 + fs-extra: ^11.1.0 glob: 7.1.4 ignore: ^5.0.4 - jsonc-parser: 3.0.0 - minimatch: 3.0.4 + js-yaml: 4.1.0 + jsonc-parser: 3.2.0 + lines-and-columns: ~2.0.3 + minimatch: 3.0.5 npm-run-path: ^4.0.1 open: ^8.4.0 - rxjs: ^6.5.4 - rxjs-for-await: 0.0.2 semver: 7.3.4 string-width: ^4.2.3 + strong-log-transformer: ^2.1.0 tar-stream: ~2.2.0 tmp: ~0.2.1 - tsconfig-paths: ^3.9.0 + tsconfig-paths: ^4.1.2 tslib: ^2.3.0 v8-compile-cache: 2.3.0 - yargs: ^17.4.0 - yargs-parser: 21.0.1 + yargs: ^17.6.2 + yargs-parser: 21.1.1 + peerDependencies: + "@swc-node/register": ^1.4.2 + "@swc/core": ^1.2.173 + dependenciesMeta: + "@nrwl/nx-darwin-arm64": + optional: true + "@nrwl/nx-darwin-x64": + optional: true + "@nrwl/nx-linux-arm-gnueabihf": + optional: true + "@nrwl/nx-linux-arm64-gnu": + optional: true + "@nrwl/nx-linux-arm64-musl": + optional: true + "@nrwl/nx-linux-x64-gnu": + optional: true + "@nrwl/nx-linux-x64-musl": + optional: true + "@nrwl/nx-win32-arm64-msvc": + optional: true + "@nrwl/nx-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc-node/register": + optional: true + "@swc/core": + optional: true bin: nx: bin/nx.js - checksum: b6b4047092c4ed4c0a563180674c6274953ca6613d90d0a481fa44fc2cdaff1980d549f53d9b137488f05953c118d05768ef4756504b1c7182e225c72f00fc50 + checksum: c5765575146bb94f54c1da3d6dec600e6414c0cc64c198821a3de10a72583933f1a752a61452b74f02492e3518f4ead3c287416173b1bb3bbc15ce82bd8d2bb4 languageName: node linkType: hard @@ -8465,10 +8760,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0": - version: 1.12.1 - resolution: "object-inspect@npm:1.12.1" - checksum: 5c7c3b641417606db7f545760cfdbc686870c4ac03c86d05f3e1194b19de39b48030f2145ef813e6e8228268d48408eceb9bdcfeb0a502d8d9e5a057982c31a0 +"object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": + version: 1.12.3 + resolution: "object-inspect@npm:1.12.3" + checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db languageName: node linkType: hard @@ -8482,25 +8777,13 @@ __metadata: languageName: node linkType: hard -"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": +"object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a languageName: node linkType: hard -"object.assign@npm:^4.1.0": - version: 4.1.2 - resolution: "object.assign@npm:4.1.2" - dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - has-symbols: ^1.0.1 - object-keys: ^1.1.1 - checksum: d621d832ed7b16ac74027adb87196804a500d80d9aca536fccb7ba48d33a7e9306a75f94c1d29cbfa324bc091bfc530bc24789568efdaee6a47fcfa298993814 - languageName: node - linkType: hard - "obuf@npm:^1.0.0, obuf@npm:^1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" @@ -8508,6 +8791,15 @@ __metadata: languageName: node linkType: hard +"on-finished@npm:2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: 1.1.1 + checksum: d20929a25e7f0bb62f937a425b5edeb4e4cde0540d77ba146ec9357f00b0d497cdb3b9b05b9c8e46222407d1548d08166bff69cc56dfa55ba0e4469228920ff0 + languageName: node + linkType: hard + "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -8542,7 +8834,7 @@ __metadata: languageName: node linkType: hard -"open@npm:8.4.0, open@npm:^8.0.9, open@npm:^8.4.0": +"open@npm:8.4.0": version: 8.4.0 resolution: "open@npm:8.4.0" dependencies: @@ -8553,6 +8845,17 @@ __metadata: languageName: node linkType: hard +"open@npm:^8.0.9, open@npm:^8.4.0": + version: 8.4.2 + resolution: "open@npm:8.4.2" + dependencies: + define-lazy-prop: ^2.0.0 + is-docker: ^2.1.1 + is-wsl: ^2.2.0 + checksum: 6388bfff21b40cb9bd8f913f9130d107f2ed4724ea81a8fd29798ee322b361ca31fa2cdfb491a5c31e43a3996cfe9566741238c7a741ada8d7af1cb78d85cf26 + languageName: node + linkType: hard + "optionator@npm:^0.9.1": version: 0.9.1 resolution: "optionator@npm:0.9.1" @@ -8600,6 +8903,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: ^0.1.0 + checksum: 7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 + languageName: node + linkType: hard + "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -8609,6 +8921,15 @@ __metadata: languageName: node linkType: hard +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: ^3.0.2 + checksum: 1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 + languageName: node + linkType: hard + "p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" @@ -8619,12 +8940,12 @@ __metadata: linkType: hard "p-retry@npm:^4.5.0": - version: 4.6.1 - resolution: "p-retry@npm:4.6.1" + version: 4.6.2 + resolution: "p-retry@npm:4.6.2" dependencies: - "@types/retry": ^0.12.0 + "@types/retry": 0.12.0 retry: ^0.13.1 - checksum: e6d540413bb3d0b96e0db44f74a7af1dce41f5005e6e84d617960110b148348c86a3987be07797749e3ddd55817dd3a8ffd6eae3428758bc2994d987e48c3a70 + checksum: 45c270bfddaffb4a895cea16cb760dcc72bdecb6cb45fef1971fa6ea2e91ddeafddefe01e444ac73e33b1b3d5d29fb0dd18a7effb294262437221ddc03ce0f2e languageName: node linkType: hard @@ -8857,13 +9178,6 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.5": - version: 4.0.5 - resolution: "pirates@npm:4.0.5" - checksum: c9994e61b85260bec6c4fc0307016340d9b0c4f4b6550a957afaaff0c9b1ad58fbbea5cfcf083860a25cb27a375442e2b0edf52e2e1e40e69934e08dcc52d227 - languageName: node - linkType: hard - "piscina@npm:3.2.0": version: 3.2.0 resolution: "piscina@npm:3.2.0" @@ -8896,121 +9210,124 @@ __metadata: linkType: hard "portfinder@npm:^1.0.28": - version: 1.0.28 - resolution: "portfinder@npm:1.0.28" + version: 1.0.32 + resolution: "portfinder@npm:1.0.32" dependencies: - async: ^2.6.2 - debug: ^3.1.1 - mkdirp: ^0.5.5 - checksum: 91fef602f13f8f4c64385d0ad2a36cc9dc6be0b8d10a2628ee2c3c7b9917ab4fefb458815b82cea2abf4b785cd11c9b4e2d917ac6fa06f14b6fa880ca8f8928c + async: ^2.6.4 + debug: ^3.2.7 + mkdirp: ^0.5.6 + checksum: 116b4aed1b9e16f6d5503823d966d9ffd41b1c2339e27f54c06cd2f3015a9d8ef53e2a53b57bc0a25af0885977b692007353aa28f9a0a98a44335cb50487240d languageName: node linkType: hard "postcss-attribute-case-insensitive@npm:^5.0.0": - version: 5.0.0 - resolution: "postcss-attribute-case-insensitive@npm:5.0.0" + version: 5.0.2 + resolution: "postcss-attribute-case-insensitive@npm:5.0.2" dependencies: - postcss-selector-parser: ^6.0.2 + postcss-selector-parser: ^6.0.10 peerDependencies: - postcss: ^8.0.2 - checksum: 6e0e872af10ba040af79fd0ee63b29cd6bc87a23a146fe71f9942d15769619c1f5b993b3238bdf30eb4f4c24887d2b85755692bc17e21e0ed3b24bd650cbf38b + postcss: ^8.2 + checksum: c0b8139f37e68dba372724cba03a53c30716224f0085f98485cada99489beb7c3da9d598ffc1d81519b59d9899291712c9041c250205e6ec0b034bb2c144dcf9 languageName: node linkType: hard "postcss-color-functional-notation@npm:^4.2.1": - version: 4.2.2 - resolution: "postcss-color-functional-notation@npm:4.2.2" + version: 4.2.4 + resolution: "postcss-color-functional-notation@npm:4.2.4" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: 77cc5d5526c3228737f2642472546498f0d963b8617c7cae453423331ecb868712ed1557007eab0cd5ff183d60bba24fa2e4bc83e550ddd45f1399e354704b81 + postcss: ^8.2 + checksum: b763e164fe3577a1de96f75e4bf451585c4f80b8ce60799763a51582cc9402d76faed57324a5d5e5556d90ca7ea0ebde565acb820c95e04bee6f36a91b019831 languageName: node linkType: hard "postcss-color-hex-alpha@npm:^8.0.2": - version: 8.0.3 - resolution: "postcss-color-hex-alpha@npm:8.0.3" + version: 8.0.4 + resolution: "postcss-color-hex-alpha@npm:8.0.4" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 3b5c1d12f86fc2b4b5b618e3842d03754eeae8c25cf252201a9bf67d2ef2845b50c23bd2854e631d8133418c13700be93a2a8689cccdfee446f25436adff9e46 + checksum: a2f3173a60176cf0aea3b7ebbc799b2cb08229127f0fff708fa31efa14e4ded47ca49aff549d8ed92e74ffe24adee32d5b9d557dbde0524fde5fe389bc520b4e languageName: node linkType: hard "postcss-color-rebeccapurple@npm:^7.0.2": - version: 7.0.2 - resolution: "postcss-color-rebeccapurple@npm:7.0.2" + version: 7.1.1 + resolution: "postcss-color-rebeccapurple@npm:7.1.1" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.3 - checksum: 7d734ac50769f2cf42ac1e58247e45dffa3cc5fb663e67fa5b8ca1a71e1e950603263aad2e98d1629db6058b173ade0c5b5de0390d51d240da8c8674c036c8c7 + postcss: ^8.2 + checksum: 03482f9b8170da0fa014c41a5d88bce7b987471fb73fc456d397222a2455c89ac7f974dd6ddf40fd31907e768aad158057164b7c5f62cee63a6ecf29d47d7467 languageName: node linkType: hard "postcss-custom-media@npm:^8.0.0": - version: 8.0.0 - resolution: "postcss-custom-media@npm:8.0.0" + version: 8.0.2 + resolution: "postcss-custom-media@npm:8.0.2" + dependencies: + postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.1.0 - checksum: 11c22e1b8cd5ec13093cb563a3a44817b38127e7f97bde954027f377a6848976092fb5482b96ef0f8b3f716038d9804a01a928eebe98c2d8a1fa9806ff4d3436 + postcss: ^8.3 + checksum: 887bbbacf6f8fab688123796e5dc1e8283b99f21e4c674235bd929dc8018c50df8634ea08932033ec93baaca32670ef2b87e6632863e0b4d84847375dbde9366 languageName: node linkType: hard "postcss-custom-properties@npm:^12.1.2": - version: 12.1.4 - resolution: "postcss-custom-properties@npm:12.1.4" + version: 12.1.11 + resolution: "postcss-custom-properties@npm:12.1.11" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: c5ef5c5ff126a65adcae9f1842b6c82df8adc337481497a405de5dfb2121c799d381e75aed3dbfe855f4258e9d90927e77801012166fb667ea5c8f7d79562ada + postcss: ^8.2 + checksum: 421f9d8d6b9c9066919f39251859232efc4dc5dd406c01e62e08734319a6ccda6d03dd6b46063ba0971053ac6ad3f7abade56d67650b3e370851b2291e8e45e6 languageName: node linkType: hard "postcss-custom-selectors@npm:^6.0.0": - version: 6.0.0 - resolution: "postcss-custom-selectors@npm:6.0.0" + version: 6.0.3 + resolution: "postcss-custom-selectors@npm:6.0.3" dependencies: postcss-selector-parser: ^6.0.4 peerDependencies: - postcss: ^8.1.2 - checksum: 64640f6beab468222fefc7194b5de1520b0962654d860b71996ab8582e22e9918775582488fe8567faf9d0fb6a032fbafe89a836cfe9008d0985fe4f1d2f033e + postcss: ^8.3 + checksum: 18080d60a8a77a76d8ddff185104d65418fffd02bbf9824499f807ced7941509ba63828ab8fe3ec1d6b0d6c72a482bb90a79d79cdef58e5f4b30113cca16e69b languageName: node linkType: hard "postcss-dir-pseudo-class@npm:^6.0.3": - version: 6.0.4 - resolution: "postcss-dir-pseudo-class@npm:6.0.4" + version: 6.0.5 + resolution: "postcss-dir-pseudo-class@npm:6.0.5" dependencies: - postcss-selector-parser: ^6.0.9 + postcss-selector-parser: ^6.0.10 peerDependencies: - postcss: ^8.4 - checksum: e493e6ed54c50b8b1bda1a0cde55fc2dec04d22983e5af178090ff592854a29866c1c255637cb047b2b40c18e6ef15c1aa45aa354735f79a7709e9add5ea2e3e + postcss: ^8.2 + checksum: 7810c439d8d1a9072c00f8ab39261a1492873ad170425745bd2819c59767db2f352f906588fc2a7d814e91117900563d7e569ecd640367c7332b26b9829927ef languageName: node linkType: hard "postcss-double-position-gradients@npm:^3.0.4": - version: 3.0.5 - resolution: "postcss-double-position-gradients@npm:3.0.5" + version: 3.1.2 + resolution: "postcss-double-position-gradients@npm:3.1.2" dependencies: + "@csstools/postcss-progressive-custom-properties": ^1.1.0 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: 43c676a085ba908e7ceabffb180e367a949d3a566d31271ca2194fb398260393de4e1aaa95733184d5c337a97ec47cd8c42e42573ed5d60736533f44a1bb5ebd + postcss: ^8.2 + checksum: ca09bf2aefddc180f1c1413f379eef30d492b8147543413f7251216f23f413c394b2ed10b7cd255e87b18e0c8efe36087ea8b9bfb26a09813f9607a0b8e538b6 languageName: node linkType: hard "postcss-env-function@npm:^4.0.4": - version: 4.0.5 - resolution: "postcss-env-function@npm:4.0.5" + version: 4.0.6 + resolution: "postcss-env-function@npm:4.0.6" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.4 - checksum: 0dca3569f136e8f6d11a98d61494054f8ca84de4a679ad80909891d60929f725a188c5e9dc53a3b1c419b00db240b3b64c43bf7bafac081fe718e10460fbb5be + checksum: 645b2363cfa21be9dcce7fe4a0f172f0af70c00d6a4c1eb3d7ff7e9cfe26d569e291ec2533114d77b12d610023cd168a92d62c38f2fc969fa333b5ae2bff5ffe languageName: node linkType: hard @@ -9046,22 +9363,22 @@ __metadata: linkType: hard "postcss-gap-properties@npm:^3.0.2": - version: 3.0.3 - resolution: "postcss-gap-properties@npm:3.0.3" + version: 3.0.5 + resolution: "postcss-gap-properties@npm:3.0.5" peerDependencies: - postcss: ^8.4 - checksum: 8b7bb4292093fa66fa874143b69297d25ab83e5b8aef643f0a39cff900d9754cae55f0fb267f9230dbccbf31d538f2e885c59274daabe57a7b56716292dd89d5 + postcss: ^8.2 + checksum: aed559d6d375203a08a006c9ae8cf5ae90d9edaec5cadd20fe65c1b8ce63c2bc8dfe752d4331880a6e24a300541cde61058be790b7bd9b5d04d470c250fbcd39 languageName: node linkType: hard "postcss-image-set-function@npm:^4.0.4": - version: 4.0.6 - resolution: "postcss-image-set-function@npm:4.0.6" + version: 4.0.7 + resolution: "postcss-image-set-function@npm:4.0.7" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: bdcd11d5ef9e5beb8ce14888125e8b526b7e01902dcb78b47ea4418297f64cf188343194670a5beb8ee5831cc902a591a8887e3512403a6b932cff921be85de3 + postcss: ^8.2 + checksum: 7e509330986de14250ead1a557e8da8baaf66ebe8a40354a5dff60ab40d99a483d92aa57d52713251ca1adbf0055ef476c5702b0d0ba5f85a4f407367cdabac0 languageName: node linkType: hard @@ -9088,14 +9405,14 @@ __metadata: linkType: hard "postcss-lab-function@npm:^4.0.3": - version: 4.1.0 - resolution: "postcss-lab-function@npm:4.1.0" + version: 4.2.1 + resolution: "postcss-lab-function@npm:4.2.1" dependencies: "@csstools/postcss-progressive-custom-properties": ^1.1.0 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: cf48efa4e23a94a2c7c639b929e57b3ef1ccbb21f636d64ae571a1a72bb0538878c8239c95371e9372448e06890bc2010303e75cc8a715bd2323cc189895872e + postcss: ^8.2 + checksum: 26ac74b430011271b5581beba69b2cd788f56375fcb64c90f6ec1577379af85f6022dc38c410ff471dac520c7ddc289160a6a16cca3c7ff76f5af7e90d31eaa3 languageName: node linkType: hard @@ -9183,22 +9500,25 @@ __metadata: linkType: hard "postcss-nesting@npm:^10.1.2": - version: 10.1.2 - resolution: "postcss-nesting@npm:10.1.2" + version: 10.2.0 + resolution: "postcss-nesting@npm:10.2.0" dependencies: - postcss-selector-parser: ^6.0.8 + "@csstools/selector-specificity": ^2.0.0 + postcss-selector-parser: ^6.0.10 peerDependencies: - postcss: ^8.3 - checksum: 571385eb40ba2874d9e87ed4f8e6e1743c2626e8e626d2d8dc857eeaff7299cc378aa5a662aea93de807955bd32282cf4eaa0434bdb4bd0fdaa2b74703ba98f7 + postcss: ^8.2 + checksum: 25e6e66186bd7f18bc4628cf0f43e02189268f28a449aa4a63b33b8f2c33745af99acfcd4ce2ac69319dc850e83b28dbaabcf517e3977dfe20e37fed0e032c7d languageName: node linkType: hard "postcss-overflow-shorthand@npm:^3.0.2": - version: 3.0.3 - resolution: "postcss-overflow-shorthand@npm:3.0.3" + version: 3.0.4 + resolution: "postcss-overflow-shorthand@npm:3.0.4" + dependencies: + postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: 52080efd1cefbc01a0f931f247c69470a565684cd8e3585c3f5bfa45e849abe12cd4997b031179ea66fc1339eaf72dc9e3d87a218822fd6b958ce71632da23cb + postcss: ^8.2 + checksum: 74009022491e3901263f8f5811630393480323e51f5d23ef17f3fdc7e03bf9c2502a632f3ba8fe6a468b57590f13b2fa3b17a68ef19653589e76277607696743 languageName: node linkType: hard @@ -9212,13 +9532,13 @@ __metadata: linkType: hard "postcss-place@npm:^7.0.3": - version: 7.0.4 - resolution: "postcss-place@npm:7.0.4" + version: 7.0.5 + resolution: "postcss-place@npm:7.0.5" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: dd1738ec9bf324889e4c51f390b4e2774c3b7a040ff277ce88c6e2f139374cf2a5d921d78b156347d57ee618e9029c1907790a50290f48918afb67c5e53bc36e + postcss: ^8.2 + checksum: 903fec0c313bb7ec20f2c8f0a125866fb7804aa3186b5b2c7c2d58cb9039ff301461677a060e9db643d1aaffaf80a0ff71e900a6da16705dad6b49c804cb3c73 languageName: node linkType: hard @@ -9266,13 +9586,13 @@ __metadata: linkType: hard "postcss-pseudo-class-any-link@npm:^7.0.2": - version: 7.1.1 - resolution: "postcss-pseudo-class-any-link@npm:7.1.1" + version: 7.1.6 + resolution: "postcss-pseudo-class-any-link@npm:7.1.6" dependencies: - postcss-selector-parser: ^6.0.9 + postcss-selector-parser: ^6.0.10 peerDependencies: - postcss: ^8.4 - checksum: d177b7ad6025c1b0dd348b0efa49892d670bc5f4e742f53084625a3110595f45b30ae8fb60d19a09f57112e441d1e1e31421a0fb212e2fde5f8375dc07644efb + postcss: ^8.2 + checksum: 43aa18ea1ef1b168f61310856dd92f46ceb3dc60b6cf820e079ca1a849df5cc0f12a1511bdc1811a23f03d60ddcc959200c80c3f9a7b57feebe32bab226afb39 languageName: node linkType: hard @@ -9302,11 +9622,11 @@ __metadata: linkType: hard "postcss-scss@npm:^4.0.2": - version: 4.0.4 - resolution: "postcss-scss@npm:4.0.4" + version: 4.0.6 + resolution: "postcss-scss@npm:4.0.6" peerDependencies: - postcss: ^8.3.3 - checksum: b4f240dd5eeb0c21738b673d9caf9a06b9a6db665a5b1c815ee4ca10c4c74a67c54f11cd5a4970dea98475cbb9e6d846e05dd3e48924189c2ecbf1f50cd44aa4 + postcss: ^8.4.19 + checksum: 133a1cba31e2e167f4e841e66ec6a798eaf44c7911f9182ade0b5b1e71a8198814aa390b8c9d5db6b01358115232e5b15b1a4f8c5198acfccfb1f3fdbd328cdf languageName: node linkType: hard @@ -9321,13 +9641,13 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6, postcss-selector-parser@npm:^6.0.8, postcss-selector-parser@npm:^6.0.9": - version: 6.0.10 - resolution: "postcss-selector-parser@npm:6.0.10" +"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6, postcss-selector-parser@npm:^6.0.9": + version: 6.0.11 + resolution: "postcss-selector-parser@npm:6.0.11" dependencies: cssesc: ^3.0.0 util-deprecate: ^1.0.2 - checksum: 46afaa60e3d1998bd7adf6caa374baf857cc58d3ff944e29459c9a9e4680a7fe41597bd5b755fc81d7c388357e9bf67c0251d047c640a09f148e13606b8a8608 + checksum: 0b01aa9c2d2c8dbeb51e9b204796b678284be9823abc8d6d40a8b16d4149514e922c264a8ed4deb4d6dbced564b9be390f5942c058582d8656351516d6c49cde languageName: node linkType: hard @@ -9338,7 +9658,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.5, postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7": +"postcss@npm:8.4.5": version: 8.4.5 resolution: "postcss@npm:8.4.5" dependencies: @@ -9349,14 +9669,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.13": - version: 8.4.14 - resolution: "postcss@npm:8.4.14" +"postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7, postcss@npm:^8.4.19": + version: 8.4.21 + resolution: "postcss@npm:8.4.21" dependencies: nanoid: ^3.3.4 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 + checksum: e39ac60ccd1542d4f9d93d894048aac0d686b3bb38e927d8386005718e6793dbbb46930f0a523fe382f1bbd843c6d980aaea791252bf5e176180e5a4336d9679 languageName: node linkType: hard @@ -9398,7 +9718,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:2.6.2, prettier@npm:^2.5.1": +"prettier@npm:2.6.2": version: 2.6.2 resolution: "prettier@npm:2.6.2" bin: @@ -9407,6 +9727,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^2.5.1": + version: 2.8.4 + resolution: "prettier@npm:2.8.4" + bin: + prettier: bin-prettier.js + checksum: c173064bf3df57b6d93d19aa98753b9b9dd7657212e33b41ada8e2e9f9884066bb9ca0b4005b89b3ab137efffdf8fbe0b462785aba20364798ff4303aadda57e + languageName: node + linkType: hard + "pretty-bytes@npm:^5.3.0": version: 5.6.0 resolution: "pretty-bytes@npm:5.6.0" @@ -9484,6 +9813,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 + languageName: node + linkType: hard + "prr@npm:~1.0.1": version: 1.0.1 resolution: "prr@npm:1.0.1" @@ -9492,26 +9828,33 @@ __metadata: linkType: hard "psl@npm:^1.1.28": - version: 1.8.0 - resolution: "psl@npm:1.8.0" - checksum: 6150048ed2da3f919478bee8a82f3828303bc0fc730fb015a48f83c9977682c7b28c60ab01425a72d82a2891a1681627aa530a991d50c086b48a3be27744bde7 + version: 1.9.0 + resolution: "psl@npm:1.9.0" + checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d languageName: node linkType: hard "punycode@npm:^2.1.0, punycode@npm:^2.1.1": - version: 2.1.1 - resolution: "punycode@npm:2.1.1" - checksum: 823bf443c6dd14f669984dea25757b37993f67e8d94698996064035edd43bed8a5a17a9f12e439c2b35df1078c6bec05a6c86e336209eb1061e8025c481168e8 + version: 2.3.0 + resolution: "punycode@npm:2.3.0" + checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 languageName: node linkType: hard -"q@npm:1.4.1, q@npm:^1.4.1": +"q@npm:1.4.1": version: 1.4.1 resolution: "q@npm:1.4.1" checksum: 22c8e1f24f416d0977e6da63f24712189c5dd789489999fc040467480e4e0ef4bd0e3126cce1b8ef72c709bbe1fcce10eba0f4991a03fc64ecb5a17e05ed8d35 languageName: node linkType: hard +"q@npm:^1.4.1": + version: 1.5.1 + resolution: "q@npm:1.5.1" + checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12 + languageName: node + linkType: hard + "qjobs@npm:^1.2.0": version: 1.2.0 resolution: "qjobs@npm:1.2.0" @@ -9519,10 +9862,12 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.9.6": - version: 6.9.6 - resolution: "qs@npm:6.9.6" - checksum: cb6df402bb8a3dbefa4bd46eba0dfca427079baca923e6b8d28a03e6bfb16a5c1dcdb96e69388f9c5813ac8ff17bb8bbca22f2ecd31fe1e344a55cb531b5fabf +"qs@npm:6.11.0": + version: 6.11.0 + resolution: "qs@npm:6.11.0" + dependencies: + side-channel: ^1.0.4 + checksum: 6e1f29dd5385f7488ec74ac7b6c92f4d09a90408882d0c208414a34dd33badc1a621019d4c799a3df15ab9b1d0292f97c1dd71dc7c045e69f81a8064e5af7297 languageName: node linkType: hard @@ -9563,15 +9908,27 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.4.2": - version: 2.4.2 - resolution: "raw-body@npm:2.4.2" +"raw-body@npm:2.5.1": + version: 2.5.1 + resolution: "raw-body@npm:2.5.1" + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e + languageName: node + linkType: hard + +"raw-body@npm:2.5.2": + version: 2.5.2 + resolution: "raw-body@npm:2.5.2" dependencies: - bytes: 3.1.1 - http-errors: 1.8.1 + bytes: 3.1.2 + http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - checksum: c6f8d6a75c65c0a047f888cb29efc97f60fb36e950ba2cb31fefce694f98186e844a03367920faa7dc5bffaf33df08aee0b9dd935280e366439fa6492a5b163e + checksum: ba1583c8d8a48e8fbb7a873fdbb2df66ea4ff83775421bfe21ee120140949ab048200668c47d9ae3880012f6e217052690628cf679ddfbd82c9fc9358d574676 languageName: node linkType: hard @@ -9669,12 +10026,12 @@ __metadata: languageName: node linkType: hard -"regenerate-unicode-properties@npm:^10.0.1": - version: 10.0.1 - resolution: "regenerate-unicode-properties@npm:10.0.1" +"regenerate-unicode-properties@npm:^10.1.0": + version: 10.1.0 + resolution: "regenerate-unicode-properties@npm:10.1.0" dependencies: regenerate: ^1.4.2 - checksum: 1b638b7087d8143e5be3e20e2cda197ea0440fa0bc2cc49646b2f50c5a2b1acdc54b21e4215805a5a2dd487c686b2291accd5ad00619534098d2667e76247754 + checksum: b1a8929588433ab8b9dc1a34cf3665b3b472f79f2af6ceae00d905fc496b332b9af09c6718fb28c730918f19a00dc1d7310adbaa9b72a2ec7ad2f435da8ace17 languageName: node linkType: hard @@ -9685,19 +10042,26 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:0.13.9, regenerator-runtime@npm:^0.13.4": +"regenerator-runtime@npm:0.13.9": version: 0.13.9 resolution: "regenerator-runtime@npm:0.13.9" checksum: 65ed455fe5afd799e2897baf691ca21c2772e1a969d19bb0c4695757c2d96249eb74ee3553ea34a91062b2a676beedf630b4c1551cc6299afb937be1426ec55e languageName: node linkType: hard -"regenerator-transform@npm:^0.14.2": - version: 0.14.5 - resolution: "regenerator-transform@npm:0.14.5" +"regenerator-runtime@npm:^0.13.11, regenerator-runtime@npm:^0.13.4": + version: 0.13.11 + resolution: "regenerator-runtime@npm:0.13.11" + checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 + languageName: node + linkType: hard + +"regenerator-transform@npm:^0.15.1": + version: 0.15.1 + resolution: "regenerator-transform@npm:0.15.1" dependencies: "@babel/runtime": ^7.8.4 - checksum: a467a3b652b4ec26ff964e9c5f1817523a73fc44cb928b8d21ff11aebeac5d10a84d297fe02cea9f282bcec81a0b0d562237da69ef0f40a0160b30a4fa98bc94 + checksum: 2d15bdeadbbfb1d12c93f5775493d85874dbe1d405bec323da5c61ec6e701bc9eea36167483e1a5e752de9b2df59ab9a2dfff6bf3784f2b28af2279a673d29a4 languageName: node linkType: hard @@ -9709,12 +10073,13 @@ __metadata: linkType: hard "regexp.prototype.flags@npm:^1.2.0": - version: 1.4.1 - resolution: "regexp.prototype.flags@npm:1.4.1" + version: 1.4.3 + resolution: "regexp.prototype.flags@npm:1.4.3" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.3 - checksum: 77944a3ea5ae84f391fa80bff9babfedc47eadc9dc38e282b5fd746368fb787deec89c68ce3114195bf6b5782b160280a278b62d41ccc6e125afab1a7f816de8 + functions-have-names: ^1.2.2 + checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 languageName: node linkType: hard @@ -9725,35 +10090,28 @@ __metadata: languageName: node linkType: hard -"regexpu-core@npm:^5.0.1": - version: 5.0.1 - resolution: "regexpu-core@npm:5.0.1" +"regexpu-core@npm:^5.3.1": + version: 5.3.1 + resolution: "regexpu-core@npm:5.3.1" dependencies: + "@babel/regjsgen": ^0.8.0 regenerate: ^1.4.2 - regenerate-unicode-properties: ^10.0.1 - regjsgen: ^0.6.0 - regjsparser: ^0.8.2 + regenerate-unicode-properties: ^10.1.0 + regjsparser: ^0.9.1 unicode-match-property-ecmascript: ^2.0.0 - unicode-match-property-value-ecmascript: ^2.0.0 - checksum: 6151a9700dad512fadb5564ad23246d54c880eb9417efa5e5c3658b910c1ff894d622dfd159af2ed527ffd44751bfe98682ae06c717155c254d8e2b4bab62785 - languageName: node - linkType: hard - -"regjsgen@npm:^0.6.0": - version: 0.6.0 - resolution: "regjsgen@npm:0.6.0" - checksum: c5158ebd735e75074e41292ade1ff05d85566d205426cc61501e360c450a63baced8512ee3ae238e5c0a0e42969563c7875b08fa69d6f0402daf36bcb3e4d348 + unicode-match-property-value-ecmascript: ^2.1.0 + checksum: 446fbbb79059afcd64d11ea573276e2df97ee7ad45aa452834d3b2aef7edf7bfe206c310f57f9345d8c95bfedbf9c16a9529f9219a05ae6a6b0d6f0dbe523b33 languageName: node linkType: hard -"regjsparser@npm:^0.8.2": - version: 0.8.4 - resolution: "regjsparser@npm:0.8.4" +"regjsparser@npm:^0.9.1": + version: 0.9.1 + resolution: "regjsparser@npm:0.9.1" dependencies: jsesc: ~0.5.0 bin: regjsparser: bin/parser - checksum: d069b932491761cda127ce11f6bd2729c3b1b394a35200ec33f1199e937423db28ceb86cf33f0a97c76ecd7c0f8db996476579eaf0d80a1f74c1934f4ca8b27a + checksum: 5e1b76afe8f1d03c3beaf9e0d935dd467589c3625f6d65fb8ffa14f224d783a0fed4bf49c2c1b8211043ef92b6117313419edf055a098ed8342e340586741afc languageName: node linkType: hard @@ -9847,7 +10205,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:1.22.0, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": +"resolve@npm:1.22.0": version: 1.22.0 resolution: "resolve@npm:1.22.0" dependencies: @@ -9860,7 +10218,20 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@1.22.0#~builtin, resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": +"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": + version: 1.22.1 + resolution: "resolve@npm:1.22.1" + dependencies: + is-core-module: ^2.9.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e + languageName: node + linkType: hard + +"resolve@patch:resolve@1.22.0#~builtin": version: 1.22.0 resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" dependencies: @@ -9873,6 +10244,19 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": + version: 1.22.1 + resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" + dependencies: + is-core-module: ^2.9.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b + languageName: node + linkType: hard + "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -9958,7 +10342,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.5.x, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": +"rxjs@npm:6.5.x": version: 6.5.5 resolution: "rxjs@npm:6.5.5" dependencies: @@ -9967,7 +10351,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.6.7": +"rxjs@npm:6.6.7, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -9976,32 +10360,23 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.2.0": - version: 7.5.4 - resolution: "rxjs@npm:7.5.4" - dependencies: - tslib: ^2.1.0 - checksum: 6f55f835f2543bc8214900f9e28b6320e6adc95875011fbca63e80a66eb18c9ff7cfdccb23b2180cbb6412762b98ed158c89fd51cb020799d127c66ea38c3c0e - languageName: node - linkType: hard - -"rxjs@npm:^7.5.5": - version: 7.5.5 - resolution: "rxjs@npm:7.5.5" +"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": + version: 7.8.0 + resolution: "rxjs@npm:7.8.0" dependencies: tslib: ^2.1.0 - checksum: e034f60805210cce756dd2f49664a8108780b117cf5d0e2281506e9e6387f7b4f1532d974a8c8b09314fa7a16dd2f6cff3462072a5789672b5dcb45c4173f3c6 + checksum: 61b4d4fd323c1043d8d6ceb91f24183b28bcf5def4f01ca111511d5c6b66755bc5578587fe714ef5d67cf4c9f2e26f4490d4e1d8cabf9bd5967687835e9866a2 languageName: node linkType: hard -"safe-buffer@npm:5.1.2, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": +"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -10120,11 +10495,11 @@ __metadata: linkType: hard "selfsigned@npm:^2.0.0": - version: 2.0.0 - resolution: "selfsigned@npm:2.0.0" + version: 2.1.1 + resolution: "selfsigned@npm:2.1.1" dependencies: - node-forge: ^1.2.0 - checksum: 43fca39a5aded2a8e97c1756af74c049a9dde12d47d302820f7d507d25c2ad7da4b04bc439a36620d63b4c0149bcf34ae7a729f978bf3b1bf48859c36ae34cee + node-forge: ^1 + checksum: aa9ce2150a54838978d5c0aee54d7ebe77649a32e4e690eb91775f71fdff773874a4fbafd0ac73d8ec3b702ff8a395c604df4f8e8868528f36fd6c15076fb43a languageName: node linkType: hard @@ -10137,15 +10512,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.0.0": - version: 7.0.0 - resolution: "semver@npm:7.0.0" - bin: - semver: bin/semver.js - checksum: 272c11bf8d083274ef79fe40a81c55c184dff84dd58e3c325299d0927ba48cece1f020793d138382b85f89bab5002a35a5ba59a3a68a7eebbb597eb733838778 - languageName: node - linkType: hard - "semver@npm:7.3.4": version: 7.3.4 resolution: "semver@npm:7.3.4" @@ -10157,7 +10523,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.3.5, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": +"semver@npm:7.3.5": version: 7.3.5 resolution: "semver@npm:7.3.5" dependencies: @@ -10177,33 +10543,44 @@ __metadata: languageName: node linkType: hard -"send@npm:0.17.2": - version: 0.17.2 - resolution: "send@npm:0.17.2" +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": + version: 7.3.8 + resolution: "semver@npm:7.3.8" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 + languageName: node + linkType: hard + +"send@npm:0.18.0": + version: 0.18.0 + resolution: "send@npm:0.18.0" dependencies: debug: 2.6.9 - depd: ~1.1.2 - destroy: ~1.0.4 + depd: 2.0.0 + destroy: 1.2.0 encodeurl: ~1.0.2 escape-html: ~1.0.3 etag: ~1.8.1 fresh: 0.5.2 - http-errors: 1.8.1 + http-errors: 2.0.0 mime: 1.6.0 ms: 2.1.3 - on-finished: ~2.3.0 + on-finished: 2.4.1 range-parser: ~1.2.1 - statuses: ~1.5.0 - checksum: c28f36deb4ccba9b8d6e6a1e472b8e7c40a1f51575bdf8f67303568cc9e71131faa3adc36fdb72611616ccad1584358bbe4c3ebf419e663ecc5de868ad3d3f03 + statuses: 2.0.1 + checksum: 74fc07ebb58566b87b078ec63e5a3e41ecd987e4272ba67b7467e86c6ad51bc6b0b0154133b6d8b08a2ddda360464f71382f7ef864700f34844a76c8027817a8 languageName: node linkType: hard "serialize-javascript@npm:^6.0.0": - version: 6.0.0 - resolution: "serialize-javascript@npm:6.0.0" + version: 6.0.1 + resolution: "serialize-javascript@npm:6.0.1" dependencies: randombytes: ^2.1.0 - checksum: 56f90b562a1bdc92e55afb3e657c6397c01a902c588c0fe3d4c490efdcc97dcd2a3074ba12df9e94630f33a5ce5b76a74784a7041294628a6f4306e0ec84bf93 + checksum: 3c4f4cb61d0893b988415bdb67243637333f3f574e9e9cc9a006a2ced0b390b0b3b44aef8d51c951272a9002ec50885eefdc0298891bc27eb2fe7510ea87dc4f languageName: node linkType: hard @@ -10222,15 +10599,15 @@ __metadata: languageName: node linkType: hard -"serve-static@npm:1.14.2": - version: 1.14.2 - resolution: "serve-static@npm:1.14.2" +"serve-static@npm:1.15.0": + version: 1.15.0 + resolution: "serve-static@npm:1.15.0" dependencies: encodeurl: ~1.0.2 escape-html: ~1.0.3 parseurl: ~1.3.3 - send: 0.17.2 - checksum: d97f3183b1dfcd8ce9c0e37e18e87fd31147ed6c8ee0b2c3a089d795e44ee851ca5061db01574f806d54f4e4b70bc694d9ca64578653514e04a28cbc97a1de05 + send: 0.18.0 + checksum: af57fc13be40d90a12562e98c0b7855cf6e8bd4c107fe9a45c212bf023058d54a1871b1c89511c3958f70626fff47faeb795f5d83f8cf88514dbaeb2b724464d languageName: node linkType: hard @@ -10241,10 +10618,10 @@ __metadata: languageName: node linkType: hard -"set-immediate-shim@npm:~1.0.1": - version: 1.0.1 - resolution: "set-immediate-shim@npm:1.0.1" - checksum: 5085c84039d1e5eee73d2bf48ce765fcec76159021d0cc7b40e23bcdf62cb6d450ffb781e3c62c1118425242c48eae96df712cba0a20a437e86b0d4a15d51a11 +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd languageName: node linkType: hard @@ -10287,7 +10664,18 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"side-channel@npm:^1.0.4": + version: 1.0.4 + resolution: "side-channel@npm:1.0.4" + dependencies: + call-bind: ^1.0.0 + get-intrinsic: ^1.0.2 + object-inspect: ^1.9.0 + checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -10347,35 +10735,36 @@ __metadata: languageName: node linkType: hard -"socket.io-adapter@npm:~2.3.3": - version: 2.3.3 - resolution: "socket.io-adapter@npm:2.3.3" - checksum: 73890e0a33e48a9e4be83e5fa2b8ea9728d2a35ae2fed373cad4d6744c6512c0e1c735e7820df9821e58c4738dc355bdaec5aae30bc56f4d6a41d999596d0c82 +"socket.io-adapter@npm:~2.5.2": + version: 2.5.2 + resolution: "socket.io-adapter@npm:2.5.2" + dependencies: + ws: ~8.11.0 + checksum: 481251c3547221e57eb5cb247d0b1a3cde4d152a4c1c9051cc887345a7770e59f3b47f1011cac4499e833f01fcfc301ed13c4ec6e72f7dbb48a476375a6344cd languageName: node linkType: hard -"socket.io-parser@npm:~4.0.4": - version: 4.0.4 - resolution: "socket.io-parser@npm:4.0.4" +"socket.io-parser@npm:~4.2.1": + version: 4.2.2 + resolution: "socket.io-parser@npm:4.2.2" dependencies: - "@types/component-emitter": ^1.2.10 - component-emitter: ~1.3.0 + "@socket.io/component-emitter": ~3.1.0 debug: ~4.3.1 - checksum: c173b4f3747c51e2af802eca35212f4dcfa8fe55d7fdc07b9a01da1ecc956791c1bf6591e307952548eab69e6500bcfe27cea8aff1386b860d9bb51f98e4fafb + checksum: ba929645cb252e23d9800f00c77092480d07cc5d6c97a5d11f515ef636870ea5b3ad6f62b7ba6147b4d703efc92588064f5638a0a0841c8530e4ac50c4b1197a languageName: node linkType: hard -"socket.io@npm:^4.2.0": - version: 4.4.1 - resolution: "socket.io@npm:4.4.1" +"socket.io@npm:^4.4.1": + version: 4.6.1 + resolution: "socket.io@npm:4.6.1" dependencies: accepts: ~1.3.4 base64id: ~2.0.0 debug: ~4.3.2 - engine.io: ~6.1.0 - socket.io-adapter: ~2.3.3 - socket.io-parser: ~4.0.4 - checksum: a559ae52359f1ca3ce5a347368cf985c72259e1ab1bf2bf769ca0add5db34e2a86f4e183a58f37f32676ec482c71fedb7b08d873dc31cf581f5ba0797a8382fe + engine.io: ~6.4.1 + socket.io-adapter: ~2.5.2 + socket.io-parser: ~4.2.1 + checksum: 447941727142669b3709c3ae59ed790a2c3ad312d935400e2e25fdf59a95cdc92ebcf6b000ab2042a2a77ae51bb87598b40845a8d3b1f6ea6a0dd1df9c8f8459 languageName: node linkType: hard @@ -10390,14 +10779,14 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^6.0.0, socks-proxy-agent@npm:^6.1.1": - version: 6.1.1 - resolution: "socks-proxy-agent@npm:6.1.1" +"socks-proxy-agent@npm:^6.0.0": + version: 6.2.1 + resolution: "socks-proxy-agent@npm:6.2.1" dependencies: agent-base: ^6.0.2 - debug: ^4.3.1 - socks: ^2.6.1 - checksum: 9a8a4f791bba0060315cf7291ca6f9db37d6fc280fd0860d73d8887d3efe4c22e823aa25a8d5375f6079279f8dc91b50c075345179bf832bfe3c7c26d3582e3c + debug: ^4.3.3 + socks: ^2.6.2 + checksum: 9ca089d489e5ee84af06741135c4b0d2022977dad27ac8d649478a114cdce87849e8d82b7c22b51501a4116e231241592946fc7fae0afc93b65030ee57084f58 languageName: node linkType: hard @@ -10412,16 +10801,6 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.6.1": - version: 2.6.2 - resolution: "socks@npm:2.6.2" - dependencies: - ip: ^1.1.5 - smart-buffer: ^4.2.0 - checksum: dd9194293059d737759d5c69273850ad4149f448426249325c4bea0e340d1cf3d266c3b022694b0dcf5d31f759de23657244c481fc1e8322add80b7985c36b5e - languageName: node - linkType: hard - "socks@npm:^2.6.2": version: 2.7.1 resolution: "socks@npm:2.7.1" @@ -10462,7 +10841,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:0.5.21, source-map-support@npm:^0.5.21, source-map-support@npm:^0.5.5, source-map-support@npm:^0.5.6, source-map-support@npm:~0.5.20": +"source-map-support@npm:0.5.21, source-map-support@npm:^0.5.5, source-map-support@npm:^0.5.6, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -10488,7 +10867,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:0.7.3, source-map@npm:^0.7.3, source-map@npm:~0.7.2": +"source-map@npm:0.7.3": version: 0.7.3 resolution: "source-map@npm:0.7.3" checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea @@ -10502,6 +10881,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.7.3, source-map@npm:~0.7.2": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 + languageName: node + linkType: hard + "sourcemap-codec@npm:1.4.8, sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" @@ -10537,9 +10923,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.11 - resolution: "spdx-license-ids@npm:3.0.11" - checksum: 1da1acb090257773e60b022094050e810ae9fec874dc1461f65dc0400cd42dd830ab2df6e64fb49c2db3dce386dd0362110780e1b154db7c0bb413488836aaeb + version: 3.0.12 + resolution: "spdx-license-ids@npm:3.0.12" + checksum: 92a4dddce62ce1db6fe54a7a839cf85e06abc308fc83b776a55b44e4f1906f02e7ebd506120847039e976bbbad359ea8bdfafb7925eae5cd7e73255f02e0b7d6 languageName: node linkType: hard @@ -10570,15 +10956,6 @@ __metadata: languageName: node linkType: hard -"specificity@npm:^0.4.1": - version: 0.4.1 - resolution: "specificity@npm:0.4.1" - bin: - specificity: ./bin/specificity - checksum: e558f1098f85aa54a8e90277309ac0d1913c84812c0bd349aa449076aa700964f71ab69f04f5fda9b7898bef9b7da3faa1cad9caedfd3f1a1ebfebedc18604ab - languageName: node - linkType: hard - "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -10625,21 +11002,28 @@ __metadata: languageName: node linkType: hard -"statuses@npm:>= 1.4.0 < 2, statuses@npm:>= 1.5.0 < 2, statuses@npm:~1.5.0": +"statuses@npm:2.0.1": + version: 2.0.1 + resolution: "statuses@npm:2.0.1" + checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb + languageName: node + linkType: hard + +"statuses@npm:>= 1.4.0 < 2, statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c languageName: node linkType: hard -"streamroller@npm:^3.0.2": - version: 3.0.2 - resolution: "streamroller@npm:3.0.2" +"streamroller@npm:^3.1.5": + version: 3.1.5 + resolution: "streamroller@npm:3.1.5" dependencies: - date-format: ^4.0.3 - debug: ^4.1.1 - fs-extra: ^10.0.0 - checksum: 1f323824f0e81cc085c24f33addfd8ef00d0c15aafee520a8cf207ca6e2dc674fd852528c7b4450cc87f4335d1269ed18b3f0188853d45d7f0912c9a205d1fc1 + date-format: ^4.0.14 + debug: ^4.3.4 + fs-extra: ^8.1.0 + checksum: c1df5612b785ffa4b6bbf16460590b62994c57265bc55a5166eebeeb0daf648e84bc52dc6d57e0cd4e5c7609bda93076753c63ff54589febd1e0b95590f0e443 languageName: node linkType: hard @@ -10747,6 +11131,19 @@ __metadata: languageName: node linkType: hard +"strong-log-transformer@npm:^2.1.0": + version: 2.1.0 + resolution: "strong-log-transformer@npm:2.1.0" + dependencies: + duplexer: ^0.1.1 + minimist: ^1.2.0 + through: ^2.3.4 + bin: + sl-log-transformer: bin/sl-log-transformer.js + checksum: abf9a4ac143118f26c3a0771b204b02f5cf4fa80384ae158f25e02bfbff761038accc44a7f65869ccd5a5995a7f2c16b1466b83149644ba6cecd3072a8927297 + languageName: node + linkType: hard + "style-search@npm:^0.1.0": version: 0.1.0 resolution: "style-search@npm:0.1.0" @@ -10755,12 +11152,12 @@ __metadata: linkType: hard "stylelint-config-html@npm:^1.0.0": - version: 1.0.0 - resolution: "stylelint-config-html@npm:1.0.0" + version: 1.1.0 + resolution: "stylelint-config-html@npm:1.1.0" peerDependencies: postcss-html: ^1.0.0 stylelint: ">=14.0.0" - checksum: 630a7b1dba4f05ba0eeb0b5fcf13ff2be7a0954367bc1bb990f0898a3e22ada4e80ca7bd8b5f796691e084e53b586362eb35bff747d408e81aacd166853fdb1f + checksum: d1b01043bcb07e33f826a59a0a22db762c07930d68e2c2c3a538aa8b0e02dc64b44abbb40e9a7583e053c04bbf18b167cbb1b714e343ef5bbe68b96860273480 languageName: node linkType: hard @@ -10779,14 +11176,14 @@ __metadata: linkType: hard "stylelint-config-prettier@npm:>=9.0.3, stylelint-config-prettier@npm:^9.0.3": - version: 9.0.3 - resolution: "stylelint-config-prettier@npm:9.0.3" + version: 9.0.5 + resolution: "stylelint-config-prettier@npm:9.0.5" peerDependencies: - stylelint: ">=11.0.0" + stylelint: ">= 11.x < 15" bin: stylelint-config-prettier: bin/check.js stylelint-config-prettier-check: bin/check.js - checksum: 9ff3f719daf3865878615ba52c31de6ef0a0d25d41cb58c41afe2f1c459a838997ff912cc4a5b4d401f92e2193667ff4d140b6d303cf8192e894b5cb454c41b9 + checksum: 3d04e463e0bb7e42a5ddec49eea6ef4ea07705d887e8a3ff1fcb82278a5e2bec1a36b8498ea7ed2d24878de29d7c94ac75b1d3ac4f8b19c3a84970595b29261f languageName: node linkType: hard @@ -10863,8 +11260,8 @@ __metadata: linkType: hard "stylelint-scss@npm:^4.0.0": - version: 4.2.0 - resolution: "stylelint-scss@npm:4.2.0" + version: 4.4.0 + resolution: "stylelint-scss@npm:4.4.0" dependencies: lodash: ^4.17.21 postcss-media-query-parser: ^0.2.3 @@ -10872,58 +11269,56 @@ __metadata: postcss-selector-parser: ^6.0.6 postcss-value-parser: ^4.1.0 peerDependencies: - stylelint: ^14.5.1 - checksum: 1fb9e850fc7301c3481f7df193d9dda520a0d54a56284ffda2de5766432aebd4205daa5c3002e784548ba26d5d1dee402c162bbb46beb6377a40d75abc409a3d + stylelint: ^14.5.1 || ^15.0.0 + checksum: f206bb9014d59af5ccc16f7c0d81bc9126c908f09f3aba296695a200f31c354386d6e2d6177821af761bcfc10fe4be41f3d00d6b2057561710b06d0c30407cc0 languageName: node linkType: hard "stylelint@npm:^14.8.2": - version: 14.8.3 - resolution: "stylelint@npm:14.8.3" + version: 14.16.1 + resolution: "stylelint@npm:14.16.1" dependencies: + "@csstools/selector-specificity": ^2.0.2 balanced-match: ^2.0.0 - colord: ^2.9.2 - cosmiconfig: ^7.0.1 - css-functions-list: ^3.0.1 + colord: ^2.9.3 + cosmiconfig: ^7.1.0 + css-functions-list: ^3.1.0 debug: ^4.3.4 - execall: ^2.0.0 - fast-glob: ^3.2.11 - fastest-levenshtein: ^1.0.12 + fast-glob: ^3.2.12 + fastest-levenshtein: ^1.0.16 file-entry-cache: ^6.0.1 - get-stdin: ^8.0.0 global-modules: ^2.0.0 globby: ^11.1.0 globjoin: ^0.1.4 html-tags: ^3.2.0 - ignore: ^5.2.0 + ignore: ^5.2.1 import-lazy: ^4.0.0 imurmurhash: ^0.1.4 is-plain-object: ^5.0.0 - known-css-properties: ^0.25.0 + known-css-properties: ^0.26.0 mathml-tag-names: ^2.1.3 meow: ^9.0.0 micromatch: ^4.0.5 normalize-path: ^3.0.0 picocolors: ^1.0.0 - postcss: ^8.4.13 + postcss: ^8.4.19 postcss-media-query-parser: ^0.2.3 postcss-resolve-nested-selector: ^0.1.1 postcss-safe-parser: ^6.0.0 - postcss-selector-parser: ^6.0.10 + postcss-selector-parser: ^6.0.11 postcss-value-parser: ^4.2.0 resolve-from: ^5.0.0 - specificity: ^0.4.1 string-width: ^4.2.3 strip-ansi: ^6.0.1 style-search: ^0.1.0 - supports-hyperlinks: ^2.2.0 + supports-hyperlinks: ^2.3.0 svg-tags: ^1.0.0 - table: ^6.8.0 + table: ^6.8.1 v8-compile-cache: ^2.3.0 - write-file-atomic: ^4.0.1 + write-file-atomic: ^4.0.2 bin: stylelint: bin/stylelint.js - checksum: b9446b298ddfea7d2c9ad03dd50204b7213b08e2e8eba96b8cf94bd4dddaa6c8fa4b1ecb6b421e35db65c51aed751820e80e4a2b3abeca7103da086d7fd89140 + checksum: bc24050415e3c357a76d8ca2799e74ce31f33c9158b4086462512b0191db5d6a161b81ef35b064039c6eacf98a5553e45fca4c5f21eb4d45e7f1d44b2d226e9b languageName: node linkType: hard @@ -10991,20 +11386,20 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^9.2.1": - version: 9.2.2 - resolution: "supports-color@npm:9.2.2" - checksum: 976d84877402fc38c1d43b1fde20b0a8dc0283273f21cfebe4ff7507d27543cdfbeec7db108a96b82d694465f06d64e8577562b05d0520b41710088e0a33cc50 +"supports-color@npm:^9.2.2": + version: 9.3.1 + resolution: "supports-color@npm:9.3.1" + checksum: 00c4d1082a7ba0ee21cba1d4e4a466642635412e40476777b530aa5110d035e99a420cd048e1fb6811f2254c0946095fbb87a1eccf1af1d1ca45ab0a4535db93 languageName: node linkType: hard -"supports-hyperlinks@npm:^2.2.0": - version: 2.2.0 - resolution: "supports-hyperlinks@npm:2.2.0" +"supports-hyperlinks@npm:^2.3.0": + version: 2.3.0 + resolution: "supports-hyperlinks@npm:2.3.0" dependencies: has-flag: ^4.0.0 supports-color: ^7.0.0 - checksum: aef04fb41f4a67f1bc128f7c3e88a81b6cf2794c800fccf137006efe5bafde281da3e42e72bf9206c2fcf42e6438f37e3a820a389214d0a88613ca1f2d36076a + checksum: 9ee0de3c8ce919d453511b2b1588a8205bd429d98af94a01df87411391010fe22ca463f268c84b2ce2abad019dfff8452aa02806eeb5c905a8d7ad5c4f4c52b8 languageName: node linkType: hard @@ -11100,16 +11495,16 @@ __metadata: languageName: node linkType: hard -"table@npm:^6.8.0": - version: 6.8.0 - resolution: "table@npm:6.8.0" +"table@npm:^6.8.1": + version: 6.8.1 + resolution: "table@npm:6.8.1" dependencies: ajv: ^8.0.1 lodash.truncate: ^4.4.2 slice-ansi: ^4.0.0 string-width: ^4.2.3 strip-ansi: ^6.0.1 - checksum: 5b07fe462ee03d2e1fac02cbb578efd2e0b55ac07e3d3db2e950aa9570ade5a4a2b8d3c15e9f25c89e4e50b646bc4269934601ee1eef4ca7968ad31960977690 + checksum: 08249c7046125d9d0a944a6e96cfe9ec66908d6b8a9db125531be6eb05fa0de047fd5542e9d43b4f987057f00a093b276b8d3e19af162a9c40db2681058fd306 languageName: node linkType: hard @@ -11133,21 +11528,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.2": - version: 6.1.11 - resolution: "tar@npm:6.1.11" - dependencies: - chownr: ^2.0.0 - fs-minipass: ^2.0.0 - minipass: ^3.0.0 - minizlib: ^2.1.1 - mkdirp: ^1.0.3 - yallist: ^4.0.0 - checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f - languageName: node - linkType: hard - -"tar@npm:^6.1.11": +"tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.2": version: 6.1.13 resolution: "tar@npm:6.1.13" dependencies: @@ -11162,14 +11543,14 @@ __metadata: linkType: hard "terser-webpack-plugin@npm:^5.1.3": - version: 5.3.1 - resolution: "terser-webpack-plugin@npm:5.3.1" + version: 5.3.6 + resolution: "terser-webpack-plugin@npm:5.3.6" dependencies: + "@jridgewell/trace-mapping": ^0.3.14 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.0 - source-map: ^0.6.1 - terser: ^5.7.2 + terser: ^5.14.1 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -11179,25 +11560,35 @@ __metadata: optional: true uglify-js: optional: true - checksum: 1b808fd4f58ce0b532baacc50b9a850fc69ce0077a0e9e5076d4156c52fab3d40b02d5d9148a3eba64630cf7f40057de54f6a5a87fac1849b1f11d6bfdb42072 + checksum: 8f3448d7fdb0434ce6a0c09d95c462bfd2f4a5a430233d854163337f734a7f5c07c74513d16081e06d4ca33d366d5b1a36f5444219bc41a7403afd6162107bad languageName: node linkType: hard -"terser@npm:5.10.0, terser@npm:^5.7.2": - version: 5.10.0 - resolution: "terser@npm:5.10.0" +"terser@npm:5.11.0": + version: 5.11.0 + resolution: "terser@npm:5.11.0" dependencies: + acorn: ^8.5.0 commander: ^2.20.0 source-map: ~0.7.2 source-map-support: ~0.5.20 - peerDependencies: + bin: + terser: bin/terser + checksum: cc72b7a0e87421b5a6ef3f8a3c86ef251f6e7f8d6327b83c63045b8991a041cc4a42ea64e07701128e1786489902c8c44b5904056b0f12ceedb52924d493db04 + languageName: node + linkType: hard + +"terser@npm:^5.14.1": + version: 5.16.4 + resolution: "terser@npm:5.16.4" + dependencies: + "@jridgewell/source-map": ^0.3.2 acorn: ^8.5.0 - peerDependenciesMeta: - acorn: - optional: true + commander: ^2.20.0 + source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: 1080faeb6d5cd155bb39d9cc41d20a590eafc9869560d5285f255f6858604dcd135311e344188a106f87fedb12d096ad3799cfc2e65acd470b85d468b1c7bd4c + checksum: 92c7b38b7322340993d6a3578d74818c3556f362b3014f18a9b3d079ac7fa5da57954fda9a0d40d53013bc3ba82f50758296881abc40761e1bafdbde9a2ab967 languageName: node linkType: hard @@ -11219,7 +11610,7 @@ __metadata: languageName: node linkType: hard -"through@npm:^2.3.6, through@npm:^2.3.8": +"through@npm:^2.3.4, through@npm:^2.3.6, through@npm:^2.3.8": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd @@ -11326,26 +11717,25 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.9.0": - version: 3.14.1 - resolution: "tsconfig-paths@npm:3.14.1" +"tsconfig-paths@npm:^4.1.2": + version: 4.1.2 + resolution: "tsconfig-paths@npm:4.1.2" dependencies: - "@types/json5": ^0.0.29 - json5: ^1.0.1 + json5: ^2.2.2 minimist: ^1.2.6 strip-bom: ^3.0.0 - checksum: 8afa01c673ebb4782ba53d3a12df97fa837ce524f8ad38ee4e2b2fd57f5ac79abc21c574e9e9eb014d93efe7fe8214001b96233b5c6ea75bd1ea82afe17a4c6d + checksum: 3d9151ecea139594e25618717de15769ab9f38f8e6d510ac16e592b23e7f7105ea13cec5694c3de7e132c98277b775e18edd1651964164ee6d75737c408494cc languageName: node linkType: hard -"tslib@npm:2.0.x, tslib@npm:^2.0.0": +"tslib@npm:2.0.x": version: 2.0.3 resolution: "tslib@npm:2.0.3" checksum: 00fcdd1f9995c9f8eb6a4a1ad03f55bc95946321b7f55434182dddac259d4e095fedf78a84f73b6e32dd3f881d9281f09cb583123d3159ed4bdac9ad7393ef8b languageName: node linkType: hard -"tslib@npm:2.3.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0": +"tslib@npm:2.3.1": version: 2.3.1 resolution: "tslib@npm:2.3.1" checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 @@ -11359,10 +11749,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.4.0": - version: 2.4.0 - resolution: "tslib@npm:2.4.0" - checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": + version: 2.5.0 + resolution: "tslib@npm:2.5.0" + checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 languageName: node linkType: hard @@ -11448,13 +11838,13 @@ __metadata: linkType: hard "typed-assert@npm:^1.0.8": - version: 1.0.8 - resolution: "typed-assert@npm:1.0.8" - checksum: bed460f76da5b142da561b75a10164c3a226ac99353fa503ad1874aa375b51823088c72406148c8cbfb277ca2a416fbbd250689be84b2734944be101e79f4117 + version: 1.0.9 + resolution: "typed-assert@npm:1.0.9" + checksum: 79351bd3ea184a552bf55a77bd3012f128741c841ed718d054c5abbbc8925362aa033ae2cdcc79e1f445a15112447c8a95a08ddf7ff8aeb04f805e92187f77c1 languageName: node linkType: hard -"typescript@npm:4.5.x, typescript@npm:^4.5.4": +"typescript@npm:4.5.x": version: 4.5.5 resolution: "typescript@npm:4.5.5" bin: @@ -11464,7 +11854,17 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@4.5.x#~builtin, typescript@patch:typescript@^4.5.4#~builtin": +"typescript@npm:^4.5.4": + version: 4.9.5 + resolution: "typescript@npm:4.9.5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db + languageName: node + linkType: hard + +"typescript@patch:typescript@4.5.x#~builtin": version: 4.5.5 resolution: "typescript@patch:typescript@npm%3A4.5.5#~builtin::version=4.5.5&hash=bda367" bin: @@ -11474,10 +11874,20 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@^4.5.4#~builtin": + version: 4.9.5 + resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=bda367" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 2eee5c37cad4390385db5db5a8e81470e42e8f1401b0358d7390095d6f681b410f2c4a0c496c6ff9ebd775423c7785cdace7bcdad76c7bee283df3d9718c0f20 + languageName: node + linkType: hard + "ua-parser-js@npm:^0.7.30": - version: 0.7.31 - resolution: "ua-parser-js@npm:0.7.31" - checksum: e2f8324a83d1715601576af85b2b6c03890699aaa7272950fc77ea925c70c5e4f75060ae147dc92124e49f7f0e3d6dd2b0a91e7f40d267e92df8894be967ba8b + version: 0.7.33 + resolution: "ua-parser-js@npm:0.7.33" + checksum: 1510e9ec26fcaf0d8c6ae8f1078a8230e8816f083e1b5f453ea19d06b8ef2b8a596601c92148fd41899e8b3e5f83fa69c42332bd5729b931a721040339831696 languageName: node linkType: hard @@ -11498,17 +11908,17 @@ __metadata: languageName: node linkType: hard -"unicode-match-property-value-ecmascript@npm:^2.0.0": - version: 2.0.0 - resolution: "unicode-match-property-value-ecmascript@npm:2.0.0" - checksum: 8fe6a09d9085a625cabcead5d95bdbc1a2d5d481712856092ce0347231e81a60b93a68f1b69e82b3076a07e415a72c708044efa2aa40ae23e2e7b5c99ed4a9ea +"unicode-match-property-value-ecmascript@npm:^2.1.0": + version: 2.1.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.1.0" + checksum: 8d6f5f586b9ce1ed0e84a37df6b42fdba1317a05b5df0c249962bd5da89528771e2d149837cad11aa26bcb84c35355cb9f58a10c3d41fa3b899181ece6c85220 languageName: node linkType: hard "unicode-property-aliases-ecmascript@npm:^2.0.0": - version: 2.0.0 - resolution: "unicode-property-aliases-ecmascript@npm:2.0.0" - checksum: dda4d39128cbbede2ac60fbb85493d979ec65913b8a486bf7cb7a375a2346fa48cbf9dc6f1ae23376e7e8e684c2b411434891e151e865a661b40a85407db51d0 + version: 2.1.0 + resolution: "unicode-property-aliases-ecmascript@npm:2.1.0" + checksum: 243524431893649b62cc674d877bd64ef292d6071dd2fd01ab4d5ad26efbc104ffcd064f93f8a06b7e4ec54c172bf03f6417921a0d8c3a9994161fe1f88f815b languageName: node linkType: hard @@ -11548,6 +11958,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^0.1.0": + version: 0.1.2 + resolution: "universalify@npm:0.1.2" + checksum: 40cdc60f6e61070fe658ca36016a8f4ec216b29bf04a55dce14e3710cc84c7448538ef4dad3728d0bfe29975ccd7bfb5f414c45e7b78883567fb31b246f02dff + languageName: node + linkType: hard + "universalify@npm:^2.0.0": version: 2.0.0 resolution: "universalify@npm:2.0.0" @@ -11562,6 +11979,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.0.10": + version: 1.0.10 + resolution: "update-browserslist-db@npm:1.0.10" + dependencies: + escalade: ^3.1.1 + picocolors: ^1.0.0 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + browserslist-lint: cli.js + checksum: 12db73b4f63029ac407b153732e7cd69a1ea8206c9100b482b7d12859cd3cd0bc59c602d7ae31e652706189f1acb90d42c53ab24a5ba563ed13aebdddc5561a0 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -11603,7 +12034,7 @@ __metadata: languageName: node linkType: hard -"v8-compile-cache@npm:2.3.0, v8-compile-cache@npm:^2.0.3, v8-compile-cache@npm:^2.3.0": +"v8-compile-cache@npm:2.3.0, v8-compile-cache@npm:^2.3.0": version: 2.3.0 resolution: "v8-compile-cache@npm:2.3.0" checksum: adb0a271eaa2297f2f4c536acbfee872d0dd26ec2d76f66921aa7fc437319132773483344207bdbeee169225f4739016d8d2dbf0553913a52bb34da6d0334f8e @@ -11672,12 +12103,12 @@ __metadata: linkType: hard "watchpack@npm:^2.3.1": - version: 2.3.1 - resolution: "watchpack@npm:2.3.1" + version: 2.4.0 + resolution: "watchpack@npm:2.4.0" dependencies: glob-to-regexp: ^0.4.1 graceful-fs: ^4.1.2 - checksum: 70a34f92842d94b5d842980f866d568d7a467de667c96ae5759c759f46587e49265863171f4650bdbafc5f3870a28f2b4453e9e847098ec4b718b38926d47d22 + checksum: 23d4bc58634dbe13b86093e01c6a68d8096028b664ab7139d58f0c37d962d549a940e98f2f201cecdabd6f9c340338dc73ef8bf094a2249ef582f35183d1a131 languageName: node linkType: hard @@ -11710,10 +12141,10 @@ __metadata: linkType: hard "webdriver-manager@npm:^12.1.7": - version: 12.1.8 - resolution: "webdriver-manager@npm:12.1.8" + version: 12.1.9 + resolution: "webdriver-manager@npm:12.1.9" dependencies: - adm-zip: ^0.4.9 + adm-zip: ^0.5.2 chalk: ^1.1.1 del: ^2.2.0 glob: ^7.0.3 @@ -11726,11 +12157,11 @@ __metadata: xml2js: ^0.4.17 bin: webdriver-manager: bin/webdriver-manager - checksum: cf6a06b86eb936795d984a0c9ce3fe7c91b3e8607f32de1b2075953dcb01cd8b645e53d07d75949b0f7b6fcf283ee61ab6e167f9dc1ec5446fc514839a678257 + checksum: 1f43bf3849ce605c0bd00fe66497f588a0973f42076cf582d7320118998e77332caaa5a4f13853921285085f648b742ef4527167a28ff0008584786c18813400 languageName: node linkType: hard -"webpack-dev-middleware@npm:5.3.0, webpack-dev-middleware@npm:^5.3.0": +"webpack-dev-middleware@npm:5.3.0": version: 5.3.0 resolution: "webpack-dev-middleware@npm:5.3.0" dependencies: @@ -11745,6 +12176,21 @@ __metadata: languageName: node linkType: hard +"webpack-dev-middleware@npm:^5.3.0": + version: 5.3.3 + resolution: "webpack-dev-middleware@npm:5.3.3" + dependencies: + colorette: ^2.0.10 + memfs: ^3.4.3 + mime-types: ^2.1.31 + range-parser: ^1.2.1 + schema-utils: ^4.0.0 + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + checksum: dd332cc6da61222c43d25e5a2155e23147b777ff32fdf1f1a0a8777020c072fbcef7756360ce2a13939c3f534c06b4992a4d659318c4a7fe2c0530b52a8a6621 + languageName: node + linkType: hard + "webpack-dev-server@npm:4.7.3": version: 4.7.3 resolution: "webpack-dev-server@npm:4.7.3" @@ -11905,7 +12351,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.2": +"wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: @@ -11957,19 +12403,34 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^4.0.1": - version: 4.0.1 - resolution: "write-file-atomic@npm:4.0.1" +"write-file-atomic@npm:^4.0.2": + version: 4.0.2 + resolution: "write-file-atomic@npm:4.0.2" dependencies: imurmurhash: ^0.1.4 signal-exit: ^3.0.7 - checksum: 8f780232533ca6223c63c9b9c01c4386ca8c625ebe5017a9ed17d037aec19462ae17109e0aa155bff5966ee4ae7a27b67a99f55caf3f32ffd84155e9da3929fc + checksum: 5da60bd4eeeb935eec97ead3df6e28e5917a6bd317478e4a85a5285e8480b8ed96032bbcc6ecd07b236142a24f3ca871c924ec4a6575e623ec1b11bf8c1c253c languageName: node linkType: hard -"ws@npm:^8.1.0, ws@npm:~8.2.3": - version: 8.2.3 - resolution: "ws@npm:8.2.3" +"ws@npm:^8.1.0": + version: 8.12.1 + resolution: "ws@npm:8.12.1" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 97301c1c4d838fc81bd413f370f75c12aabe44527b31323b761eab3043a9ecb7e32ffd668548382c9a6a5ad3a1c3a9249608e8338e6b939f2f9540f1e21970b5 + languageName: node + linkType: hard + +"ws@npm:~8.11.0": + version: 8.11.0 + resolution: "ws@npm:8.11.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -11978,7 +12439,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: c869296ccb45f218ac6d32f8f614cd85b50a21fd434caf11646008eef92173be53490810c5c23aea31bc527902261fbfd7b062197eea341b26128d4be56a85e4 + checksum: 316b33aba32f317cd217df66dbfc5b281a2f09ff36815de222bc859e3424d83766d9eb2bd4d667de658b6ab7be151f258318fb1da812416b30be13103e5b5c67 languageName: node linkType: hard @@ -12013,6 +12474,13 @@ __metadata: languageName: node linkType: hard +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 48f7bb00dc19fc635a13a39fe547f527b10c9290e7b3e836b9a8f1ca04d4d342e85714416b3c2ab74949c9c66f9cebb0473e6bc353b79035356103b47641285d + languageName: node + linkType: hard + "yallist@npm:^4.0.0": version: 4.0.0 resolution: "yallist@npm:4.0.0" @@ -12034,10 +12502,10 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:21.0.1, yargs-parser@npm:^21.0.0": - version: 21.0.1 - resolution: "yargs-parser@npm:21.0.1" - checksum: c3ea2ed12cad0377ce3096b3f138df8267edf7b1aa7d710cd502fe16af417bafe4443dd71b28158c22fcd1be5dfd0e86319597e47badf42ff83815485887323a +"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c languageName: node linkType: hard @@ -12092,18 +12560,18 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.2.1, yargs@npm:^17.3.1, yargs@npm:^17.4.0": - version: 17.5.1 - resolution: "yargs@npm:17.5.1" +"yargs@npm:^17.2.1, yargs@npm:^17.3.1, yargs@npm:^17.6.2": + version: 17.7.1 + resolution: "yargs@npm:17.7.1" dependencies: - cliui: ^7.0.2 + cliui: ^8.0.1 escalade: ^3.1.1 get-caller-file: ^2.0.5 require-directory: ^2.1.1 string-width: ^4.2.3 y18n: ^5.0.5 - yargs-parser: ^21.0.0 - checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde + yargs-parser: ^21.1.1 + checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 languageName: node linkType: hard @@ -12114,11 +12582,18 @@ __metadata: languageName: node linkType: hard +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 + languageName: node + linkType: hard + "zone.js@npm:~0.11.4": - version: 0.11.4 - resolution: "zone.js@npm:0.11.4" + version: 0.11.8 + resolution: "zone.js@npm:0.11.8" dependencies: - tslib: ^2.0.0 - checksum: 20c3e39898019de4747a434a29ed528e5d730a674570c3e72775a57f9d57dba812e70d67c3932ff54e95db9b778f06a9b18119c5184dfd9552d3622544a6729f + tslib: ^2.3.0 + checksum: ad4a2f4aafce10f5bc3f60213384bb6ee4727de09d87fb77cb8089eecdb44dd5838fd9fa43ac4c351afec142b0e5477a22093928260a486804f70b8cace6c7d3 languageName: node linkType: hard From 3061529ad53f6dee8c90d21fb657182480dbab3f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 14:57:04 +0100 Subject: [PATCH 161/498] Submissions are no longer cached in memory. --- .../dev/dres/data/model/run/AbstractTask.kt | 17 ++++++++++++----- .../run/InteractiveAsynchronousEvaluation.kt | 1 - .../run/InteractiveSynchronousEvaluation.kt | 6 +----- .../data/model/run/NonInteractiveEvaluation.kt | 5 +---- .../run/InteractiveSynchronousRunManager.kt | 9 ++++----- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 5dabe2fe7..284ea1050 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -3,12 +3,18 @@ package dev.dres.data.model.run import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.TemplateId import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.interfaces.SubmissionValidator +import kotlinx.dnq.query.FilteringContext.contains +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct +import kotlinx.dnq.query.mapDistinct import kotlinx.dnq.util.findById import java.util.concurrent.ConcurrentLinkedQueue @@ -46,9 +52,6 @@ abstract class AbstractTask(task: DbTask): TaskRun { */ final override val templateId: TemplateId = this.task.template.templateId - /** List of [DbSubmission]s* registered for this [AbstractTask]. */ - protected val submissions: ConcurrentLinkedQueue = ConcurrentLinkedQueue() - /** * Timestamp of when this [AbstractTask] was started. * @@ -140,8 +143,12 @@ abstract class AbstractTask(task: DbTask): TaskRun { this.status = TaskStatus.RUNNING } - /** Returns a [List] of all [DbSubmission]s held by this [AbstractTask]. */ - override fun getSubmissions() = this.submissions.asSequence() + /** Returns a [Sequence] of all [DbSubmission]s connected to this [AbstractTask]. */ + override fun getSubmissions() = DbAnswerSet.filter { + a -> a.task.id eq this@AbstractTask.id + }.mapDistinct { + it.submission + }.asSequence() /** * Adds a new [DbSubmission] to this [AbstractInteractiveTask]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index cc4832cea..6760abb1e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -215,7 +215,6 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe val dbSubmission: DbSubmission = submission.toDb() /* Process Submission. */ - this.submissions.add(dbSubmission) this.validator.validate(submission) DbAuditLogger.validateSubmission(submission, this.validator) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 637e12d5c..da64e6741 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -147,11 +147,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu this.filter.acceptOrThrow(submission) /* At this point, the submission is considered valid and is persisted */ - val dbSubmission: DbSubmission = submission.toDb() - - /* Process Submission. */ - this.submissions.add(dbSubmission) - this.validator.validate(submission) + this.validator.validate(submission.toDb()) DbAuditLogger.validateSubmission(submission, this.validator) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 65e4058e3..bca2aff95 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -63,10 +63,7 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev /* At this point, the submission is considered valid and is persisted */ val dbSubmission: DbSubmission = submission.toDb() - /* Process Submission. */ - this.submissions.add(dbSubmission) - - /* TODO: Validation? */ + /* TODO: Process and validate submission. */ } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 5e3246bed..e4571e182 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -145,13 +145,13 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Start the run. */ this.evaluation.start() - /* Update status. */ - this.status = RunManagerStatus.ACTIVE - /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) + + /* Log and update status. */ LOGGER.info("SynchronousRunManager ${this.id} started") + this.status = RunManagerStatus.ACTIVE } override fun end(context: RunActionContext) = this.stateLock.write { @@ -449,8 +449,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch assureTaskRunning() /* Register submission. */ - val task = this.currentTask(context) - ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") + val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") task.postSubmission(submission) /** Checks for the presence of the [DbTaskOption.PROLONG_ON_SUBMISSION] and applies it. */ From 881a944fb1fd8b39441612eafe88938a38039f3d Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 14:57:35 +0100 Subject: [PATCH 162/498] Fixed a bug that prevented display of submissions. --- .../src/app/viewer/teams-viewer.component.ts | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index 6f55492be..84cf989aa 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -14,10 +14,9 @@ import {AppConfig} from '../app.config'; import {AudioPlayerUtilities} from '../utilities/audio-player.utilities'; import {animate, keyframes, style, transition, trigger} from '@angular/animations'; import { - ApiAnswer, ApiAnswerType, + ApiAnswerType, ApiEvaluationInfo, ApiEvaluationState, ApiMediaItem, ApiScoreOverview, ApiSubmission, - ApiSubmissionInfo, ApiTaskTemplateInfo, ApiVerdictStatus, EvaluationScoresService, EvaluationService @@ -86,7 +85,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { @Input() taskEnded: Observable; /** Observable that tracks all the submissions. */ - submissions: Observable; + submissions: Observable; /** Observable that tracks all the submissions per team. */ submissionsPerTeam: Observable>; @@ -142,7 +141,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { map(([submissions, info]) => { const submissionsPerTeam = new Map(); info.teams.forEach((t) => { - submissionsPerTeam.set(t.id, submissions.flatMap((s) => s.submissions).filter((s) => s.teamId === t.id)); + submissionsPerTeam.set(t.id, submissions.filter((s) => s.teamId === t.id)); }); return submissionsPerTeam; }), @@ -220,12 +219,10 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { .pipe( withLatestFrom(this.submissions), map(([ended, submissions]) => { - for (const i of submissions) { - for (const s of i.submissions) { - for (const a of s.answers) { - if (a.status === 'CORRECT') { - return true; - } + for (const s of submissions) { + for (const a of s.answers) { + if (a.status === 'CORRECT') { + return true; } } } @@ -251,8 +248,8 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /** * Generates a URL for the preview image of a submission. */ - public previewOfItem(item: ApiMediaItem, start: number, end: number): Observable { - return of('') + public previewOfItem(item: ApiMediaItem, start: number, end: number): string { + return'' /* TODO: I believe this endpoint needs to be redesigned. */ /* return this.runId.pipe(map((evaluationId) => this.config.resolveApiUrl(`/preview/submission/${evaluationId}/${submission.submissionId}`))); */ } From d6b7faa78b1b55cbb930954207bf90934fc374b9 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 22 Feb 2023 16:11:06 +0100 Subject: [PATCH 163/498] Comparing media items by id in validators -changed validators to use media item id in order to avoid clash between Db* and Api* classes -renamed id to mediaItemId in MediaItem interface -some general cleanup in validators --- .../handler/collection/UpdateMediaItemHandler.kt | 4 ++-- .../handler/judgement/DequeueJudgementHandler.kt | 4 ++-- .../rest/handler/judgement/DequeueVoteHandler.kt | 4 ++-- .../handler/submission/LegacySubmissionHandler.kt | 2 +- .../api/rest/types/collection/ApiMediaItem.kt | 7 ++----- .../dres/data/model/media/DbMediaCollection.kt | 1 - .../dev/dres/data/model/media/DbMediaItem.kt | 2 ++ .../kotlin/dev/dres/data/model/media/MediaItem.kt | 4 +++- .../dres/data/model/media/MediaItemCollection.kt | 4 +++- .../validation/MediaItemsSubmissionValidator.kt | 7 +++++-- .../TemporalContainmentSubmissionValidator.kt | 12 ++++++------ .../TemporalOverlapSubmissionValidator.kt | 15 ++++++++------- .../dev/dres/run/validation/TextValidator.kt | 8 ++++---- .../run/validation/interfaces/VoteValidator.kt | 2 +- .../run/validation/judged/BasicVoteValidator.kt | 2 +- .../dev/dres/run/validation/judged/ItemRange.kt | 2 +- doc/oas-client.json | 4 ++-- doc/oas.json | 4 ++-- .../media-item-builder-dialog.component.ts | 6 +++--- .../collection-viewer.component.ts | 4 ++-- .../competition-builder-task-dialog.component.ts | 2 +- .../video-player-segment-builder.component.ts | 2 +- 22 files changed, 54 insertions(+), 48 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 4748a3ef8..85b7f893d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -46,8 +46,8 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa } return this.store.transactional { - val item = DbMediaItem.query(DbMediaItem::id eq mediaItem.id).firstOrNull() - ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.id} not found.", ctx) + val item = DbMediaItem.query(DbMediaItem::id eq mediaItem.mediaItemId).firstOrNull() + ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.mediaItemId} not found.", ctx) item.type = mediaItem.type.toDb() item.name = mediaItem.name diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 04bf1de08..d6a2d59f8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -58,14 +58,14 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa } AnswerType.ITEM -> { val item = next.second.answers().firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(next.first, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) + return@transactional ApiJudgementRequest(next.first, item.type().toApi(), validator.id, item.collection.id, item.mediaItemId, taskDescription, null, null) } AnswerType.TEMPORAL -> { val answer = next.second.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue val end = answer.end ?: continue - return@transactional ApiJudgementRequest(next.first, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) + return@transactional ApiJudgementRequest(next.first, item.type().toApi(), validator.id, item.collection.id, item.mediaItemId, taskDescription, start, end) } else -> continue } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 270548b77..e6c244448 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -56,14 +56,14 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( } AnswerType.ITEM -> { val item = next.answers().firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, null, null) + return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.collection.id, item.mediaItemId, taskDescription, null, null) } AnswerType.TEMPORAL -> { val answer = next.answers().firstOrNull() ?: continue val item = answer.item ?: continue val start = answer.start ?: continue val end = answer.end ?: continue - return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.collection.id, item.id!!, taskDescription, start, end) + return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.collection.id, item.mediaItemId, taskDescription, start, end) } else -> continue } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 417450c82..1d849ed5f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -365,7 +365,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v private fun generatePreview(answerSet: AnswerSet) { if (answerSet.answers().firstOrNull()?.type() != AnswerType.TEMPORAL) return if (answerSet.answers().firstOrNull()?.item == null) return - val item = DbMediaItem.query((DbMediaItem::id eq answerSet.answers().firstOrNull()?.item!!.id)).firstOrNull() ?: return + val item = DbMediaItem.query((DbMediaItem::id eq answerSet.answers().firstOrNull()?.item!!.mediaItemId)).firstOrNull() ?: return val destinationPath = Paths.get( this.config.cachePath, "previews", diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index f11cce6fb..7f482f3f9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -1,10 +1,7 @@ package dev.dres.api.rest.types.collection import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.MediaItemCollection -import dev.dres.data.model.media.MediaItemType +import dev.dres.data.model.media.* /** * The RESTful API equivalent for [DbMediaItem]. @@ -14,7 +11,7 @@ import dev.dres.data.model.media.MediaItemType * @version 1.1.0 */ data class ApiMediaItem( - override val id: String?, + override val mediaItemId: MediaItemId, override val name: String, val type: ApiMediaType, val collectionId: String, diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt index eb3eba4cb..d02ab62b0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt @@ -4,7 +4,6 @@ import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -typealias CollectionId = String /** * A named media collection consisting of media items. diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt index 61108e6e5..ac55ed6b1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt @@ -26,6 +26,8 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { /** The name of this [DbMediaItem]. */ override var name by xdRequiredStringProp(unique = false, trimmed = false) + override val mediaItemId: MediaItemId + get() = this.id /** The [DbMediaType] of this [DbMediaItem]. */ var type by xdLink1(DbMediaType) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt index af66abb6a..9488a69f5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItem.kt @@ -1,9 +1,11 @@ package dev.dres.data.model.media +typealias MediaItemId = String + interface MediaItem { val name: String - val id: String? + val mediaItemId: MediaItemId val collection: MediaItemCollection fun type(): MediaItemType diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt index 918921527..2836a9c8d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/MediaItemCollection.kt @@ -1,7 +1,9 @@ package dev.dres.data.model.media +typealias CollectionId = String + interface MediaItemCollection { - val id: String + val id: CollectionId } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt index dedea931e..c6a59816d 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt @@ -1,6 +1,7 @@ package dev.dres.run.validation import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission @@ -14,11 +15,13 @@ import kotlinx.dnq.query.asSequence * @author Luca Rossetto * @version 1.0.1 */ -class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { +class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { /** This type of [SubmissionValidator] can be executed directly.*/ override val deferring = false + private val itemIds = items.map { it.mediaItemId } + /** * Performs the validation. * @@ -26,7 +29,7 @@ class MediaItemsSubmissionValidator(private val items : Set) : Subm */ override fun validate(submission: Submission) { submission.answerSets().forEach { answerSet -> - if (answerSet.answers().any { it.item == null || it.item !in this.items} ) { /* TODO: This doesn't work cause we're comparing DbMediaItem with ApiMediaItem. */ + if (answerSet.answers().any { it.item == null || it.item!!.mediaItemId !in this.itemIds} ) { answerSet.status(VerdictStatus.WRONG) } else { answerSet.status(VerdictStatus.CORRECT) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt index 3dd7797a7..d11bb52ec 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt @@ -25,14 +25,14 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien * @param submission The [DbSubmission] to validate. */ override fun validate(submission: Submission) { - submission.answerSets().forEach { answerSet -> + submission.answerSets().forEach outer@{ answerSet -> - answerSet.answers().forEach { answer -> + answerSet.answers().forEach inner@{ answer -> /* Perform sanity checks. */ if (answer.type() != AnswerType.TEMPORAL) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } val start = answer.start @@ -40,14 +40,14 @@ class TemporalContainmentSubmissionValidator(private val targetSegment: Transien val item = answer.item if (item == null || start == null || end == null || start > end) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } /* Perform item validation. */ - if (answer.item != this.targetSegment.first) { + if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } /* Perform temporal validation. */ diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt index 0b3d5d821..f108c38ba 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt @@ -2,13 +2,14 @@ package dev.dres.run.validation import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.asSequence /** */ -typealias TransientMediaSegment = Pair +typealias TransientMediaSegment = Pair /** * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the @@ -29,14 +30,14 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed * @param submission The [DbSubmission] to validate. */ override fun validate(submission: Submission) { - submission.answerSets().forEach { answerSet -> + submission.answerSets().forEach outer@{ answerSet -> - answerSet.answers().forEach { answer -> + answerSet.answers().forEach inner@{ answer -> /* Perform sanity checks. */ if (answer.type() != AnswerType.TEMPORAL) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } val start = answer.start @@ -44,13 +45,13 @@ class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMed val item = answer.item if (item == null || start == null || end == null || start > end) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } /* Perform item validation. */ - if (answer.item != this.targetSegment.first) { + if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } /* Perform temporal validation. */ diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt index 1c56b6975..7e62a3a08 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt @@ -45,22 +45,22 @@ class TextValidator(targets: List) : SubmissionValidator { * @param submission The [DbSubmission] to validate. */ override fun validate(submission: Submission) { - submission.answerSets().forEach { answerSet -> + submission.answerSets().forEach outer@{ answerSet -> - answerSet.answers().forEach { + answerSet.answers().forEach inner@{ answer -> /* Perform sanity checks. */ if (answer.type() != AnswerType.TEXT) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } /* Perform text validation. */ val text = answer.text if (text == null) { answerSet.status(VerdictStatus.WRONG) - return@forEach + return@inner } if (regex.any { it matches text }) { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index a49ab1f98..fbe8833d4 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -21,7 +21,7 @@ interface VoteValidator : JudgementValidator { /** * Places a verdict for the currently active Submission */ - fun vote(verdict: VerdictStatus) + fun vote(status: VerdictStatus) /** * diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index ce80165e6..8d8fd977c 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -10,7 +10,7 @@ import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.write -class BasicVoteValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), val minimumVotes: Int = defaultMinimimVotes, val voteDifference: Int = defaultVoteDifference) : BasicJudgementValidator(knownCorrectRanges, knownWrongRanges), VoteValidator { +class BasicVoteValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), private val minimumVotes: Int = defaultMinimimVotes, private val voteDifference: Int = defaultVoteDifference) : BasicJudgementValidator(knownCorrectRanges, knownWrongRanges), VoteValidator { constructor(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), parameters: Map): this( knownCorrectRanges, knownWrongRanges, diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 6c4588171..57871eae8 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -17,7 +17,7 @@ data class ItemRange(val element: String, val start: Long, val end: Long){ constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) constructor(answer: Answer): this(when (answer.type()){ AnswerType.ITEM, - AnswerType.TEMPORAL -> answer.item!!.id!! + AnswerType.TEMPORAL -> answer.item!!.mediaItemId AnswerType.TEXT -> answer.text!! else -> throw IllegalStateException("Submission contains neither item nor text.") }, answer.start ?: 0, answer.end ?: 0) diff --git a/doc/oas-client.json b/doc/oas-client.json index 9dc05d610..1e869ebd0 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -3470,7 +3470,7 @@ "type" : "object", "additionalProperties" : false, "properties" : { - "id" : { + "mediaItemId" : { "type" : "string" }, "name" : { @@ -3497,7 +3497,7 @@ "$ref" : "#/components/schemas/MediaItemCollection" } }, - "required" : [ "name", "type", "collectionId", "location", "collection" ] + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "collection" ] }, "ApiMediaType" : { "type" : "string", diff --git a/doc/oas.json b/doc/oas.json index 8ed92257f..7dd8770e3 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4817,7 +4817,7 @@ "type" : "object", "additionalProperties" : false, "properties" : { - "id" : { + "mediaItemId" : { "type" : "string" }, "name" : { @@ -4844,7 +4844,7 @@ "$ref" : "#/components/schemas/MediaItemCollection" } }, - "required" : [ "name", "type", "collectionId", "location", "collection" ] + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "collection" ] }, "ApiMediaType" : { "type" : "string", diff --git a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts index caaa65f96..1d15c694f 100644 --- a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts @@ -23,7 +23,7 @@ export class MediaItemBuilderDialogComponent implements OnInit { @Inject(MAT_DIALOG_DATA) public data: MediaItemBuilderData ) { this.form = new FormGroup({ - id: new FormControl(data?.item?.id), + id: new FormControl(data?.item?.mediaItemId), name: new FormControl(data?.item?.name, [Validators.required, Validators.minLength(3)]), location: new FormControl(data?.item?.location, Validators.required), type: new FormControl({ value: data?.item?.type, disabled: this.isEditing() }, [Validators.required]), @@ -49,7 +49,7 @@ export class MediaItemBuilderDialogComponent implements OnInit { } isEditing(): boolean { - return this.data?.item?.id !== undefined; + return this.data?.item?.mediaItemId !== undefined; } ngOnInit(): void {} @@ -81,7 +81,7 @@ export class MediaItemBuilderDialogComponent implements OnInit { } as ApiMediaItem; /* Are we editing ? */ if (this.isEditing()) { - item.id = this.form.get('id').value; + item.mediaItemId = this.form.get('id').value; } /* only relevant for video */ if (item.type === ApiMediaType.VIDEO) { diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index adfe43d91..2c991fd43 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -127,7 +127,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { this.collectionId.subscribe((colId: string) => { const config = { width: '500px' } as MatDialogConfig>; if (id) { - config.data = { item: this.dataSource.data.find((it) => it.id === id), collectionId: colId } as MediaItemBuilderData; + config.data = { item: this.dataSource.data.find((it) => it.mediaItemId === id), collectionId: colId } as MediaItemBuilderData; } else { config.data = { collectionId: colId } as Partial; } @@ -157,7 +157,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { } resolveMediaItemById(_: number, item: ApiMediaItem) { - return item.id; + return item.mediaItemId; } /** diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index ee8b598fe..ad1f4a44a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -273,7 +273,7 @@ export class CompetitionBuilderTaskDialogComponent { getImageUrl(mi: ApiMediaItem) { if (mi && mi.type === 'IMAGE') { - return this.config.resolveApiUrl(`/media/${mi.id}`); + return this.config.resolveApiUrl(`/media/${mi.mediaItemId}`); } return ''; } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts index 8473d69f5..6cd0d1b96 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts @@ -51,7 +51,7 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest */ if (this.data.mediaItem) { this.videoUrl = of( - this.config.resolveApiUrl(`/media/${this.data?.mediaItem?.id}`) + this.config.resolveApiUrl(`/media/${this.data?.mediaItem?.mediaItemId}`) ); this.durationInSeconds = this.data.mediaItem.durationMs / 1000; this.setNewRange(0, this.durationInSeconds); From 6cfdff4afa135085a1e10da9c626a46c730da900 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 22 Feb 2023 16:48:38 +0100 Subject: [PATCH 164/498] Updated gradle download plugin and prevented repeated ffmpeg download --- backend/build.gradle | 13 +++++++------ build.gradle | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 2fb2ed309..4407bf944 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -152,9 +152,6 @@ distributions { task downloadFFmpeg(type: Download) { def f = new File("$buildDir/cache/ffmpeg.zip") - outputs.upToDateWhen { - return f.exists() - } def os = "" if (Os.isFamily(Os.FAMILY_WINDOWS)) { @@ -167,13 +164,13 @@ task downloadFFmpeg(type: Download) { src "https://github.com/vot/ffbinaries-prebuilt/releases/download/v4.2.1/ffmpeg-4.2.1-$os-64.zip" dest f + onlyIf { + !f.exists() + } } task downloadFFprobe(type: Download) { def f = new File("$buildDir/cache/ffprobe.zip") - outputs.upToDateWhen { - return f.exists() - } def os = "" if (Os.isFamily(Os.FAMILY_WINDOWS)) { @@ -186,6 +183,10 @@ task downloadFFprobe(type: Download) { src "https://github.com/vot/ffbinaries-prebuilt/releases/download/v4.2.1/ffprobe-4.2.1-$os-64.zip" dest f + onlyIf { + !f.exists() + } + } task copyFFmpeg(type: Copy) { diff --git a/build.gradle b/build.gradle index 6660cc6dd..830b028c4 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { plugins { /// Download task for FFMPEG binaries download - id 'de.undercouch.download' version '4.0.4' + id 'de.undercouch.download' version '5.3.1' /// OpenAPI Generator for Frontend internal api generation id 'org.openapi.generator' version '5.2.0' From 4e5092a41d9db29167e8a6417dc7309a25588491 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 22 Feb 2023 16:49:26 +0100 Subject: [PATCH 165/498] cleaned up some openapi warnings --- .../handler/audit/ListAuditLogsHandler.kt | 4 +- .../audit/ListAuditLogsInRangeHandler.kt | 4 +- .../collection/DeleteCollectionHandler.kt | 2 +- .../collection/DeleteMediaItemHandler.kt | 2 +- .../collection/ListExternalItemHandler.kt | 2 +- .../collection/ListMediaItemHandler.kt | 4 +- .../collection/RandomMediaItemHandler.kt | 2 +- .../ResolveMediaItemListByNameHandler.kt | 2 +- .../collection/ShowCollectionHandler.kt | 2 +- .../client/ClientTaskInfoHandler.kt | 2 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 2 +- .../judgement/DequeueJudgementHandler.kt | 2 +- .../handler/judgement/DequeueVoteHandler.kt | 2 +- .../judgement/JudgementStatusHandler.kt | 2 +- .../handler/judgement/PostJudgementHandler.kt | 2 +- .../rest/handler/judgement/PostVoteHandler.kt | 2 +- .../preview/SubmissionPreviewHandler.kt | 4 +- .../DeleteEvaluationTemplateHandler.kt | 2 +- .../rest/handler/template/ListTasksHandler.kt | 2 +- .../rest/handler/template/ListTeamHandler.kt | 2 +- .../template/ShowEvaluationTemplateHandler.kt | 2 +- .../rest/handler/users/DeleteUsersHandler.kt | 2 +- .../rest/handler/users/UpdateUsersHandler.kt | 2 +- .../rest/handler/users/UserDetailsHandler.kt | 2 +- doc/oas-client.json | 28 +++++----- doc/oas.json | 52 +++++++++---------- 26 files changed, 68 insertions(+), 68 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt index 3bbb74da8..0414b351e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt @@ -37,8 +37,8 @@ class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandle summary = "Lists all audit logs matching the query.", path = "/api/v2/audit/log/list/limit/{limit}/{page}", pathParams = [ - OpenApiParam(LIMIT_PARAM, Int::class, "The maximum number of results. Default: 500"), - OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.") + OpenApiParam(LIMIT_PARAM, Int::class, "The maximum number of results. Default: 500", required = true), + OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.", required = true) ], tags = ["Audit"], operationId = OpenApiOperation.AUTO_GENERATE, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt index 77b644a28..428465e8f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt @@ -25,8 +25,8 @@ class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLog summary = "Lists all audit logs matching the query", path = "/api/v2/audit/log/list/since/{since}/{upto}", pathParams = [ - OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include"), - OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.") + OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include", required = true), + OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.", required = true) ], operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Audit"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt index 4ca65ad07..5a01f6013 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt @@ -17,7 +17,7 @@ class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionH @OpenApi( summary = "Deletes a media collection identified by a collection id.", path = "/api/v2/collection/{collectionId}", - pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], + pathParams = [OpenApiParam("collectionId", String::class, "Collection ID", required = true)], tags = ["Collection"], operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.DELETE], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt index 500429abf..33bb10bd4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -27,7 +27,7 @@ class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.DELETE], pathParams = [ - OpenApiParam("mediaId", String::class, "Media item ID") + OpenApiParam("mediaId", String::class, "Media item ID", required = true) ], tags = ["Collection"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index 96712dd4b..14da15577 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -38,7 +38,7 @@ class ListExternalItemHandler(config: Config) : GetRestHandler> { operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ - OpenApiParam("startsWith", String::class, "Name starts with.", required = false) + OpenApiParam("startsWith", String::class, "Name starts with.", required = true) ], tags = ["Collection"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt index c824d1da9..3fd176b51 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -26,8 +26,8 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ - OpenApiParam("collectionId", String::class, "Collection ID"), - OpenApiParam("startsWith", String::class, "Name the item(s) should start with.", required = false) + OpenApiParam("collectionId", String::class, "Collection ID", required = true), + OpenApiParam("startsWith", String::class, "Name the item(s) should start with.", required = true) ], tags = ["Collection"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt index 7f44fa4a9..bceb2e763 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -30,7 +30,7 @@ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], pathParams = [ - OpenApiParam("collectionId", String::class, "Collection ID") + OpenApiParam("collectionId", String::class, "Collection ID", required = true) ], tags = ["Collection"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt index 5f5440468..195cc52e2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt @@ -25,7 +25,7 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], pathParams = [ - OpenApiParam("collectionId", String::class, "Collection ID") + OpenApiParam("collectionId", String::class, "Collection ID", required = true) ], requestBody = OpenApiRequestBody([OpenApiContent(Array::class)]), tags = ["Collection"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index 43818b20f..72cebe9d2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -25,7 +25,7 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan summary = "Shows the content of the specified media collection.", path = "/api/v2/collection/{collectionId}", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("collectionId", String::class, "Collection ID")], + pathParams = [OpenApiParam("collectionId", String::class, "Collection ID", required = true)], tags = ["Collection"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiPopulatedMediaCollection::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index f08073a01..a6069ac31 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -27,7 +27,7 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie path = "/api/v2/client/evaluation/currentTask/{evaluationId}", operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation Client"], - queryParams = [ + pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) ], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index 0067d4e0d..3b38420bc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -28,7 +28,7 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class,"The evaluation ID.", required = true), - OpenApiParam("timestamp", Long::class, "Timestamp that marks the lower bound for returned submissions.", required = false) + OpenApiParam("timestamp", Long::class, "Timestamp that marks the lower bound for returned submissions.", required = true) ], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index d6a2d59f8..a610a7f3d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -28,7 +28,7 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa summary = "Gets the next open submission that is waiting to be judged.", path = "/api/v2/evaluation/{evaluationId}/judge/next", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], tags = ["Judgement"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiJudgementRequest::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index e6c244448..1fb89690b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -28,7 +28,7 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( summary = "Gets the next open submission that is waiting to be voted on.", path = "/api/v2/evaluation/{evaluationId}/vote/next", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], tags = ["Judgement"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiJudgementRequest::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt index 509f0cb56..cf6e9c066 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -25,7 +25,7 @@ class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHand summary = "Retrieves the status of all judgement validators.", path = "/api/v2/evaluation/{evaluationId}/judge/status", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], tags = ["Judgement"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index b21f8a991..e25d82550 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -30,7 +30,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle summary = "Endpoint to post a judgement for a previously detached judgement request.", path = "/api/v2/evaluation/{evaluationId}/judge", methods = [HttpMethod.POST], operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], requestBody = OpenApiRequestBody([OpenApiContent(ApiJudgement::class)]), tags = ["Judgement"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index 94d26b3dd..cfb8181b7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -29,7 +29,7 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto summary = "Returns a Vote.", path = "/api/v2/evaluation/{evaluationId}/judge/vote", methods = [HttpMethod.POST], operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.")], + pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true)], requestBody = OpenApiRequestBody([OpenApiContent(ApiVote::class)]), tags = ["Judgement"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt index 6fd895b51..fca9627ff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/SubmissionPreviewHandler.kt @@ -27,8 +27,8 @@ class SubmissionPreviewHandler(store: TransientEntityStore, config: Config) : Ab path = "/api/v2/preview/submission/{evaluationId}/{submissionId}", operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID."), - OpenApiParam("submissionId", String::class, "The submission ID") + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("submissionId", String::class, "The submission ID", required = true) ], tags = ["Media"], responses = [OpenApiResponse( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index 583f5b1d4..7de88bfcc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -25,7 +25,7 @@ class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva path = "/api/v2/template/{templateId}", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.DELETE], - pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.", required = true)], tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 2e0d504d3..cc8ea433f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -25,7 +25,7 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplate summary = "Lists the task templates contained in a specific evaluation template.", path = "/api/v2/template/{templateId}/task/list", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.", required = true)], tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index edacae6f6..843920228 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -25,7 +25,7 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateH summary = "Lists all the teams of a specific competition.", path = "/api/v2/template/{templateId}/team/list", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.", required = true)], tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index 083dd9af8..6420a3e9a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -23,7 +23,7 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvalu summary = "Loads the detailed definition of a specific evaluation template.", path = "/api/v2/template/{templateId}", operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.")], + pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.", required = true)], tags = ["Template"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiEvaluationTemplate::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index a7da553a9..ebe9d2ee6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -28,7 +28,7 @@ class DeleteUsersHandler(private val store: TransientEntityStore) : AbstractUser summary = "Deletes the specified user. Requires ADMIN privileges", path = "/api/v2/user/{userId}", methods = [HttpMethod.DELETE], operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("userId", String::class, "User ID")], + pathParams = [OpenApiParam("userId", String::class, "User ID", required = true)], tags = ["User"], responses = [ OpenApiResponse("200", [OpenApiContent(ApiUser::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt index 41ab436f2..0f408469e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -32,7 +32,7 @@ class UpdateUsersHandler(private val store: TransientEntityStore) : AbstractUser summary = "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", path = "/api/v2/user/{userId}", methods = [HttpMethod.PATCH], operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("userId", String::class, "User ID")], + pathParams = [OpenApiParam("userId", String::class, "User ID", required = true)], requestBody = OpenApiRequestBody([OpenApiContent(UserRequest::class)]), tags = ["User"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt index 2d8c424f4..1f388dcbf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -27,7 +27,7 @@ class UserDetailsHandler(private val store: TransientEntityStore) : AbstractUser path = "/api/v2/user/{userId}", operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ - OpenApiParam("userId", String::class, "User's UID") + OpenApiParam("userId", String::class, "User's UID", required = true) ], tags = ["User"], responses = [ diff --git a/doc/oas-client.json b/doc/oas-client.json index 1e869ebd0..fe2d44ac5 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -161,7 +161,7 @@ "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", "parameters" : [ { "name" : "evaluationId", - "in" : "query", + "in" : "path", "description" : "The evaluation ID.", "required" : true, "deprecated" : false, @@ -1561,7 +1561,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1653,7 +1653,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1735,7 +1735,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1800,7 +1800,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -1999,7 +1999,7 @@ "name" : "timestamp", "in" : "path", "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2274,7 +2274,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2757,7 +2757,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2817,7 +2817,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2949,7 +2949,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3014,7 +3014,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3203,7 +3203,7 @@ "name" : "userId", "in" : "path", "description" : "User ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3253,7 +3253,7 @@ "name" : "userId", "in" : "path", "description" : "User ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3323,7 +3323,7 @@ "name" : "userId", "in" : "path", "description" : "User's UID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { diff --git a/doc/oas.json b/doc/oas.json index 7dd8770e3..f25bf98dd 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -195,7 +195,7 @@ "name" : "limit", "in" : "path", "description" : "The maximum number of results. Default: 500", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -206,7 +206,7 @@ "name" : "page", "in" : "path", "description" : "The page index offset, relative to the limit.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -252,7 +252,7 @@ "name" : "since", "in" : "path", "description" : "Timestamp of the earliest audit log to include", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -263,7 +263,7 @@ "name" : "upto", "in" : "path", "description" : "Timestamp of the latest audit log to include.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -307,7 +307,7 @@ "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", "parameters" : [ { "name" : "evaluationId", - "in" : "query", + "in" : "path", "description" : "The evaluation ID.", "required" : true, "deprecated" : false, @@ -533,7 +533,7 @@ "name" : "collectionId", "in" : "path", "description" : "Collection ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -583,7 +583,7 @@ "name" : "collectionId", "in" : "path", "description" : "Collection ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -635,7 +635,7 @@ "name" : "collectionId", "in" : "path", "description" : "Collection ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -697,7 +697,7 @@ "name" : "collectionId", "in" : "path", "description" : "Collection ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -775,7 +775,7 @@ "name" : "collectionId", "in" : "path", "description" : "Collection ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -785,7 +785,7 @@ "name" : "startsWith", "in" : "path", "description" : "Name the item(s) should start with.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2223,7 +2223,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2315,7 +2315,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2397,7 +2397,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2462,7 +2462,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -2726,7 +2726,7 @@ "name" : "timestamp", "in" : "path", "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3001,7 +3001,7 @@ "name" : "evaluationId", "in" : "path", "description" : "The evaluation ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3073,7 +3073,7 @@ "name" : "startsWith", "in" : "path", "description" : "Name starts with.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -3398,7 +3398,7 @@ "name" : "mediaId", "in" : "path", "description" : "Media item ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4044,7 +4044,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4104,7 +4104,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4236,7 +4236,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4301,7 +4301,7 @@ "name" : "templateId", "in" : "path", "description" : "The evaluation template ID.", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4550,7 +4550,7 @@ "name" : "userId", "in" : "path", "description" : "User ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4600,7 +4600,7 @@ "name" : "userId", "in" : "path", "description" : "User ID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { @@ -4670,7 +4670,7 @@ "name" : "userId", "in" : "path", "description" : "User's UID", - "required" : false, + "required" : true, "deprecated" : false, "allowEmptyValue" : false, "schema" : { From 4fcf22a38714f251ce7a3dd2724860768d39d927 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 16:50:22 +0100 Subject: [PATCH 166/498] Completed and simplified Submission, ApiSubmission and DbSubmission (and related classes). --- .../api/rest/types/evaluation/ApiAnswer.kt | 16 ++++++- .../api/rest/types/evaluation/ApiAnswerSet.kt | 9 +++- .../rest/types/evaluation/ApiSubmission.kt | 32 ++++++------- .../dev/dres/data/model/run/AbstractTask.kt | 7 --- .../run/InteractiveAsynchronousEvaluation.kt | 22 --------- .../run/InteractiveSynchronousEvaluation.kt | 20 -------- .../dev/dres/data/model/submissions/Answer.kt | 3 -- .../dres/data/model/submissions/AnswerSet.kt | 4 -- .../dres/data/model/submissions/DbAnswer.kt | 3 +- .../data/model/submissions/DbAnswerSet.kt | 2 - .../data/model/submissions/DbSubmission.kt | 3 -- .../dres/data/model/submissions/Submission.kt | 3 -- .../run/InteractiveAsynchronousRunManager.kt | 29 +++++++++--- .../run/InteractiveSynchronousRunManager.kt | 47 +++++++++++-------- .../dev/dres/run/NonInteractiveRunManager.kt | 9 ++-- .../main/kotlin/dev/dres/run/RunManager.kt | 18 ++++--- 16 files changed, 99 insertions(+), 128 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt index c31b46aec..7495f5bfa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt @@ -1,9 +1,13 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.MediaItem import dev.dres.data.model.submissions.Answer import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswer +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.firstOrNull data class ApiAnswer( val type: ApiAnswerType, @@ -12,9 +16,19 @@ data class ApiAnswer( override val start: Long?, override val end: Long? ) : Answer { - override fun toDb(): DbAnswer { + + /** + * Creates a new [DbAnswer] for this [ApiAnswer]. Requires an ongoing transaction. + * + * @return [DbAnswer] + */ + fun toNewDb(): DbAnswer { return DbAnswer.new { this.type = this@ApiAnswer.type.toDb() + this.item = DbMediaItem.filter { it.id eq this@ApiAnswer.item?.mediaItemId }.firstOrNull() + this.text = this@ApiAnswer.text + this.start = this@ApiAnswer.start + this.end = this@ApiAnswer.end } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index 29f7a4653..d68424328 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -38,13 +38,18 @@ data class ApiAnswerSet( this.status = status.toApi() } - override fun toDb(): DbAnswerSet { + /** + * Creates a new [DbAnswerSet] for this [ApiAnswerSet]. Requires an ongoing transaction. + * + * @return [DbAnswerSet] + */ + fun toNewDb(): DbAnswerSet { return DbAnswerSet.new { this.status = this@ApiAnswerSet.status.toDb() this.task = DbTask.filter { it.id eq this@ApiAnswerSet.taskId }.first() this.answers.addAll( this@ApiAnswerSet.answers.map { - it.toDb() + it.toNewDb() } ) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt index 695144c6b..33b3e963a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt @@ -1,15 +1,9 @@ package dev.dres.api.rest.types.evaluation -import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.admin.User import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.team.DbTeam -import dev.dres.data.model.template.team.Team import kotlinx.dnq.query.addAll import kotlinx.dnq.query.filter import kotlinx.dnq.query.first @@ -40,17 +34,19 @@ data class ApiSubmission( } override fun answerSets(): Sequence = this.answers.asSequence() - override fun toDb(): DbSubmission { - - return DbSubmission.new { - this.id = this@ApiSubmission.submissionId - this.timestamp = this@ApiSubmission.timestamp - this.team = DbTeam.filter { it.id eq this@ApiSubmission.teamId }.first() - this.user = DbUser.filter { it.id eq this@ApiSubmission.memberId }.first() - this.answerSets.addAll( - this@ApiSubmission.answers.map { it.toDb() } - ) - } + /** + * Creates a new [DbSubmission] for this [ApiSubmission]. Requires an ongoing transaction. + * + * @return [DbSubmission] + */ + fun toNewDb(): DbSubmission = DbSubmission.new { + this.id = this@ApiSubmission.submissionId + this.timestamp = this@ApiSubmission.timestamp + this.team = DbTeam.filter { it.id eq this@ApiSubmission.teamId }.first() + this.user = DbUser.filter { it.id eq this@ApiSubmission.memberId }.first() + this.answerSets.addAll( + this@ApiSubmission.answers.map { it.toNewDb() } + ) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 284ea1050..7881994c3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -149,11 +149,4 @@ abstract class AbstractTask(task: DbTask): TaskRun { }.mapDistinct { it.submission }.asSequence() - - /** - * Adds a new [DbSubmission] to this [AbstractInteractiveTask]. - * - * @param submission The [DbSubmission] to append. - */ - abstract fun postSubmission(submission: Submission) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 6760abb1e..9463b33ec 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -196,27 +196,5 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe list } } - - /** - * Adds a [DbSubmission] to this [InteractiveAsynchronousEvaluation.IATaskRun]. - * - * @param submission The [DbSubmission] to add. - * @throws IllegalArgumentException If [DbSubmission] could not be added for any reason. - */ - @Synchronized - override fun postSubmission(submission: Submission) { - check(this.isRunning) { "Task run '${this@InteractiveAsynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } - - /* Execute submission filters. */ - this.filter.acceptOrThrow(submission) - - /* At this point, the submission is considered valid and is persisted */ - val dbSubmission: DbSubmission = submission.toDb() - - /* Process Submission. */ - this.validator.validate(submission) - DbAuditLogger.validateSubmission(submission, this.validator) - } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index da64e6741..05b7b2bd3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -130,25 +130,5 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { "Cannot create a new task. Another task is currently running." } (this@InteractiveSynchronousEvaluation.tasks as MutableList).add(this) } - - /** - * Adds a [DbSubmission] to this [DbTask]. - * - * @param submission The [DbSubmission] to add. - */ - @Synchronized - override fun postSubmission(submission: Submission) { - check(this.isRunning) { "Task run '${this@InteractiveSynchronousEvaluation.name}.${this.position}' is currently not running. This is a programmer's error!" } - check(this@InteractiveSynchronousEvaluation.description.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { - "Team ${submission.teamId} does not exists for evaluation run ${this@InteractiveSynchronousEvaluation.name}. This is a programmer's error!" - } - - /* Execute submission filters. */ - this.filter.acceptOrThrow(submission) - - /* At this point, the submission is considered valid and is persisted */ - this.validator.validate(submission.toDb()) - DbAuditLogger.validateSubmission(submission, this.validator) - } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt index 3184e2874..48df1c6c6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt @@ -21,8 +21,5 @@ interface Answer { return TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) } - fun toDb(): DbAnswer - fun type(): AnswerType - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index 73784c81f..306356d23 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -4,8 +4,6 @@ import dev.dres.data.model.run.Task import dev.dres.data.model.run.TaskId interface AnswerSet { //TODO - - val taskId: TaskId val submission: Submission @@ -15,6 +13,4 @@ interface AnswerSet { //TODO fun status() : VerdictStatus fun status(status: VerdictStatus) - - fun toDb(): DbAnswerSet } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt index 238205bea..d2bb18b0f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt @@ -28,9 +28,8 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { /** The text submitted. Only for [DbAnswerType.TEXT] . */ override var text by xdStringProp { requireIf { this.type == DbAnswerType.TEXT } } - override fun toDb(): DbAnswer = this - override fun type(): AnswerType = AnswerType.fromDb(this.type) + override fun type(): AnswerType = AnswerType.fromDb(this.type) fun toApi() = ApiAnswer( type = this.type().toApi(), diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index c76f7543d..18a5cb39a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -41,8 +41,6 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { this.status = status.toDb() } - override fun toDb(): DbAnswerSet = this - /** * Converts this [DbVerdictStatus] to a RESTful API representation [ApiAnswerSet]. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 6ac7f78f5..9855ee8d6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -13,8 +13,6 @@ import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.first import kotlinx.dnq.simple.min - - /** * A [DbSubmission] as submitted by a competition participant and received by DRES. * @@ -52,7 +50,6 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { val answerSets by xdChildren1_N(DbAnswerSet::submission) override fun answerSets(): Sequence = answerSets.asSequence() - override fun toDb(): DbSubmission = this /** * Converts this [DbSubmission] to a RESTful API representation [ApiSubmission]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 21e6c847c..9cd7949fe 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -15,7 +15,4 @@ interface Submission { val evaluationId: EvaluationId fun answerSets(): Sequence - - fun toDb(): DbSubmission - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 977c869cf..01491d878 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -1,6 +1,7 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage @@ -436,12 +437,12 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.tasks(context).find { it.id == taskId } /** - * Returns a reference to the currently active [AbstractInteractiveTask]. + * Returns a reference to the currently active [InteractiveAsynchronousEvaluation.IATaskRun]. * * @param context The [RunActionContext] used for the invocation. - * @return [AbstractInteractiveTask] that is currently active or null, if no such task is active. + * @return [InteractiveAsynchronousEvaluation.IATaskRun] that is currently active or null, if no such task is active. */ - override fun currentTask(context: RunActionContext): AbstractInteractiveTask? = this.stateLock.read { + override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } return this.evaluation.currentTaskForTeam(context.teamId) } @@ -507,15 +508,26 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ - override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } require(submission.answerSets().count() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ /* Register submission. */ - val task = this.currentTask(context) - ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") - task.postSubmission(submission) + val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") + + /* Sanity check. */ + check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } + check(task.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } + + /* Check if ApiSubmission meets formal requirements. */ + task.filter.acceptOrThrow(submission as Submission) + + /* At this point, the submission is considered valid and is persisted */ + task.validator.validate(submission as Submission) + + /* Persist the submission. */ + submission.toNewDb() /* Enqueue submission for post-processing. */ this.scoresUpdatable.enqueue(Pair(task, submission)) @@ -701,7 +713,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /** + * Checks if the team for the given [TeamId] has an active and running task. * + * @param teamId The [TeamId] to check. + * @return True if task is running for team, false otherwise. */ private fun teamHasRunningTask(teamId: TeamId) = this.evaluation.currentTaskForTeam(teamId)?.isRunning == true diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index e4571e182..d8e6c56bb 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -1,6 +1,7 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage @@ -24,7 +25,6 @@ import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.ReadyLatch import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* -import kotlinx.dnq.transactional import org.slf4j.LoggerFactory import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -336,14 +336,17 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @throws IllegalStateException If [RunManager] was not in wrong [RunManagerStatus]. */ override fun adjustDuration(context: RunActionContext, s: Int): Long = this.stateLock.read { - assureTaskRunning() checkContext(context) - val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") - val newDuration = currentTaskRun.duration + s - check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } - currentTaskRun.duration = newDuration - return (currentTaskRun.duration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) + /* Obtain task and perform sanity check. */ + val task = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") + check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } + + /* Adjust duration. */ + val newDuration = task.duration + s + check((newDuration * 1000L - (System.currentTimeMillis() - task.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } + task.duration = newDuration + return (task.duration * 1000L - (System.currentTimeMillis() - task.started!!)) } /** @@ -435,26 +438,36 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Processes incoming [DbSubmission]s. If a [DbTask] is running then that [DbSubmission] will usually + * Processes incoming [ApiSubmission]s. If a [DbTask] is running then that [ApiSubmission] will usually * be associated with that [DbTask]. * - * This method will not throw an exception and instead return false if a [DbSubmission] was + * This method will not throw an exception and instead return false if a [ApiSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * @param context The [RunActionContext] used for the invocation - * @param submission [DbSubmission] that should be registered. + * @param submission [ApiSubmission] that should be registered. */ - override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus = this.stateLock.read { - assureTaskRunning() - + override fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus = this.stateLock.read { /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") - task.postSubmission(submission) + + /* Sanity check. */ + check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } + check(this.template.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { "Team ${submission.teamId} does not exists for evaluation run ${this.name}. This is a programmer's error!" } + + /* Check if ApiSubmission meets formal requirements. */ + task.filter.acceptOrThrow(submission as Submission) + + /* At this point, the submission is considered valid and is persisted */ + task.validator.validate(submission as Submission) + + /* Persist the submission. */ + submission.toNewDb() /** Checks for the presence of the [DbTaskOption.PROLONG_ON_SUBMISSION] and applies it. */ if (task.template.taskGroup.type.options.contains(DbTaskOption.PROLONG_ON_SUBMISSION)) { - this.prolongOnSubmit(context, submission) + this.prolongOnSubmit(context, submission as Submission) } /* Enqueue submission for post-processing. */ @@ -636,10 +649,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (this.status !in status) throw IllegalRunStateException(this.status) } - private fun assureTaskRunning() { - if (this.evaluation.currentTask?.status != TaskStatus.RUNNING) throw IllegalStateException("Task not running") - } - private fun assureTaskPreparingOrRunning() { val status = this.evaluation.currentTask?.status if (status != TaskStatus.RUNNING && status != TaskStatus.PREPARING) throw IllegalStateException("Task not preparing or running") diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 15e694504..6a03b6aa9 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -1,15 +1,13 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.data.model.run.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable @@ -167,7 +165,8 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation * */ override fun tasks(context: RunActionContext): List = this.evaluation.tasks - override fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus { + + override fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index e88f777a4..28eb52e32 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -1,16 +1,14 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator @@ -95,22 +93,22 @@ interface RunManager : Runnable { fun tasks(context: RunActionContext): List /** - * Invoked by an external caller to post a new [DbSubmission] for the [TaskRun] that is currently being - * executed by this [InteractiveRunManager]. [DbSubmission]s usually cause updates to the internal state and/or + * Invoked by an external caller to post a new [ApiSubmission] for the [TaskRun] that is currently being + * executed by this [InteractiveRunManager]. [ApiSubmission]s usually cause updates to the internal state and/or * the [Scoreboard] of this [InteractiveRunManager]. * - * This method will not throw an exception and instead returns false if a [DbSubmission] was + * This method will not throw an exception and instead returns false if a [ApiSubmission] was * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * * * @param context The [RunActionContext] used for the invocation - * @param submission The [DbSubmission] to be posted. + * @param submission The [ApiSubmission] to be posted. * - * @return [DbVerdictStatus] of the [DbSubmission] + * @return [DbVerdictStatus] of the [ApiSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(context: RunActionContext, submission: Submission): VerdictStatus + fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. From 8645b31e8cb5350503d436e6c0642e1b5f963979 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 22 Feb 2023 16:55:16 +0100 Subject: [PATCH 167/498] Added note about potential race condition. --- .../main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index d8e6c56bb..b191c4bba 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -586,6 +586,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ if (this.evaluation.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { + /* TODO (Potential race condition): It can occur, that this part of the code is entered without the corresponding task being committed (status change in memory manifest faster than persistent information). */ this.stateLock.write { this.store.transactional { this.evaluation.currentTask!!.start() From b6bbe607984b7fac84a657c1162d3c37ddfa7f85 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 22 Feb 2023 17:41:40 +0100 Subject: [PATCH 168/498] Introduced TaskId field in ServerMessage --- .../evaluation/websocket/ServerMessage.kt | 3 +- .../model/run/NonInteractiveEvaluation.kt | 28 +++++++++---------- .../run/InteractiveAsynchronousRunManager.kt | 18 ++++++------ .../run/InteractiveSynchronousRunManager.kt | 18 ++++++------ .../main/kotlin/dev/dres/run/RunExecutor.kt | 2 +- .../dres/run/updatables/ScoresUpdatable.kt | 11 ++++++-- 6 files changed, 43 insertions(+), 37 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt index f3f2b93fe..1bbc4ecc9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.types.evaluation.websocket import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.data.model.run.interfaces.TaskId /** * Message send by the DRES server via WebSocket to inform clients about the state of the run. @@ -8,5 +9,5 @@ import dev.dres.data.model.run.interfaces.EvaluationId * @author Ralph Gasser * @version 1.1.0 */ -data class ServerMessage(val evaluationId: EvaluationId, val type: ServerMessageType, val timestamp: Long = System.currentTimeMillis()) +data class ServerMessage(val evaluationId: EvaluationId, val taskId: TaskId? = null, val type: ServerMessageType, val timestamp: Long = System.currentTimeMillis()) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index bca2aff95..b080eacc4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -51,19 +51,19 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev override val filter: SubmissionFilter get() = TODO("Can there be submission filters for non-interactive tasks?") - @Synchronized - override fun postSubmission(submission: Submission) { - check(this@NonInteractiveEvaluation.description.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { - "Team ${submission.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" - } - - /* Execute submission filters. */ - this.filter.acceptOrThrow(submission) - - /* At this point, the submission is considered valid and is persisted */ - val dbSubmission: DbSubmission = submission.toDb() - - /* TODO: Process and validate submission. */ - } +// @Synchronized +// override fun postSubmission(submission: Submission) { +// check(this@NonInteractiveEvaluation.description.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { +// "Team ${submission.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" +// } +// +// /* Execute submission filters. */ +// this.filter.acceptOrThrow(submission) +// +// /* At this point, the submission is considered valid and is persisted */ +// val dbSubmission: DbSubmission = submission.toDb() +// +// /* TODO: Process and validate submission. */ +// } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 01491d878..0405e9331 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -165,7 +165,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.status = RunManagerStatus.ACTIVE /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null, ServerMessageType.COMPETITION_START)) LOGGER.info("Run manager ${this.id} started") } @@ -192,7 +192,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.status = RunManagerStatus.TERMINATED /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } @@ -287,7 +287,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue( - ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE), + ServerMessage(this.id, this.evaluation.currentTaskForTeam(context.teamId)?.id, ServerMessageType.COMPETITION_UPDATE), context.teamId ) @@ -327,7 +327,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue( - ServerMessage(this.id, ServerMessageType.TASK_PREPARE), + ServerMessage(this.id, currentTaskTemplate.id, ServerMessageType.TASK_PREPARE), context.teamId ) @@ -357,7 +357,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END), context.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, currentTask.id, ServerMessageType.TASK_END), context.teamId) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } @@ -533,7 +533,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoresUpdatable.enqueue(Pair(task, submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED), context.teamId) return submission.answerSets().first().status() } @@ -563,7 +563,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoresUpdatable.enqueue(Pair(task, answerSet.submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId!!) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED), context.teamId!!) return true } @@ -679,14 +679,14 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END), team.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_END), team.teamId) } } else if (teamHasPreparingTask(team.teamId)) { val task = this.evaluation.currentTaskForTeam(team.teamId) ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") task.start() DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_START), team.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_START), team.teamId) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index b191c4bba..6ea3a8426 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -146,7 +146,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.start() /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null,ServerMessageType.COMPETITION_START)) /* Log and update status. */ @@ -165,7 +165,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.status = RunManagerStatus.TERMINATED /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } @@ -222,7 +222,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.COMPETITION_UPDATE)) LOGGER.info("SynchronousRunManager ${this.id} set to task $index") } else { throw IndexOutOfBoundsException("Index $index is out of bounds for the number of available tasks.") @@ -252,7 +252,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.readyLatch.reset(VIEWER_TIME_OUT) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_PREPARE)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.TASK_PREPARE)) LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") } @@ -269,7 +269,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.currentTask(context)?.id, ServerMessageType.TASK_END)) LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") } @@ -474,7 +474,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoresUpdatable.enqueue(Pair(task, submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED)) return submission.answerSets().first().status() } @@ -503,7 +503,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoresUpdatable.enqueue(Pair(task, answerSet.submission)) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_UPDATED), context.teamId!!) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED), context.teamId!!) return true } @@ -595,7 +595,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.TASK_START)) } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ @@ -612,7 +612,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, ServerMessageType.TASK_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.TASK_END)) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index a72f3d575..32063973e 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -200,7 +200,7 @@ object RunExecutor : Consumer { ClientMessageType.ACK -> {} ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.add(WebSocketConnection(it)) } ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.remove(WebSocketConnection(it)) } - ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, ServerMessageType.PING)) + ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, null, ServerMessageType.PING)) } this.runManagers[message.evaluationId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index a2fda82e8..3773c8158 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.Submission @@ -34,22 +35,26 @@ class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreb fun enqueue(submission: Pair) = this.list.add(submission) override fun update(status: RunManagerStatus) { + val removed = mutableListOf() if (!this.list.isEmpty()) { - val removed = this.list.removeIf { + this.list.removeIf { val task = it.first val scores = it.first.scorer.computeScores( task.getSubmissions(), TaskContext(task.id, task.competition.description.teams.asSequence().map { t -> t.id }.toList(), task.started, task.template.duration, task.ended) ) task.updateTeamAggregation(scores) + removed.add(task.id) true } /* If elements were removed, then update scoreboards and tasks. */ - if (removed) { + if (removed.isNotEmpty()) { this.scoreboardsUpdatable.dirty = true - this.messageQueueUpdatable.enqueue(ServerMessage(this.evaluationId, ServerMessageType.TASK_UPDATED)) + removed.forEach { + this.messageQueueUpdatable.enqueue(ServerMessage(this.evaluationId, it, ServerMessageType.TASK_UPDATED)) + } } } } From 078fb264a8bc2f5fa432ee29d3f7d0e6b6bf4722 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 22 Feb 2023 17:45:26 +0100 Subject: [PATCH 169/498] Added taskId also on client side --- frontend/src/app/model/ws/ws-server-message.interface.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/model/ws/ws-server-message.interface.ts b/frontend/src/app/model/ws/ws-server-message.interface.ts index 4a1795b99..addcd8cbc 100644 --- a/frontend/src/app/model/ws/ws-server-message.interface.ts +++ b/frontend/src/app/model/ws/ws-server-message.interface.ts @@ -7,6 +7,7 @@ import { IWsMessage } from './ws-message.interface'; */ export interface IWsServerMessage extends IWsMessage { evaluationId: string; + taskId?: string; type: ServerMessageTypeEnum; timestamp: number; } From 29fb60a098c93687cf6911a5c7735230aea22317 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 24 Feb 2023 14:29:21 +0100 Subject: [PATCH 170/498] Refactored scoring functionality. --- .../handler/download/ScoreDownloadHandler.kt | 4 +- .../admin/ListSubmissionsHandler.kt | 2 +- .../evaluation/admin/StartTaskHandler.kt | 2 +- .../scores/CurrentTaskScoreHandler.kt | 2 +- .../scores/HistoryTaskScoreHandler.kt | 2 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 5 +- .../submission/LegacySubmissionHandler.kt | 2 +- .../rest/types/evaluation/ApiTaskOverview.kt | 2 +- .../data/model/run/AbstractInteractiveTask.kt | 2 +- .../dev/dres/data/model/run/AbstractTask.kt | 8 +- .../run/InteractiveAsynchronousEvaluation.kt | 65 ++- .../run/InteractiveSynchronousEvaluation.kt | 66 ++- .../model/run/NonInteractiveEvaluation.kt | 51 ++- .../model/run/interfaces/EvaluationRun.kt | 14 +- .../dres/data/model/run/interfaces/TaskRun.kt | 13 +- .../model/template/DbEvaluationTemplate.kt | 16 - .../interfaces/CachingTaskScorerFactory.kt | 20 - .../model/template/task/DbTaskTemplate.kt | 28 +- .../template/task/DbTaskTemplateTarget.kt | 12 +- .../data/model/template/task/DbTaskType.kt | 34 -- .../data/model/template/task/TaskTemplate.kt | 1 - .../template/task/options/DbScoreOption.kt | 17 - .../run/InteractiveAsynchronousRunManager.kt | 34 +- .../dev/dres/run/InteractiveRunManager.kt | 1 + .../run/InteractiveSynchronousRunManager.kt | 45 +- .../dev/dres/run/NonInteractiveRunManager.kt | 9 +- .../main/kotlin/dev/dres/run/RunManager.kt | 4 + .../kotlin/dev/dres/run/score/Scoreable.kt | 28 ++ .../kotlin/dev/dres/run/score/TaskContext.kt | 9 - .../scoreboard/MaxNormalizingScoreBoard.kt | 26 +- .../dres/run/score/scoreboard/Scoreboard.kt | 16 +- .../scoreboard/SumAggregateScoreBoard.kt | 12 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 30 +- .../run/score/scorer/CachingTaskScorer.kt | 59 ++- .../scorer/InferredAveragePrecisionScorer.kt | 8 +- .../dres/run/score/scorer/KisTaskScorer.kt | 37 +- .../dres/run/score/scorer/NewAvsTaskScorer.kt | 68 +-- .../dev/dres/run/score/scorer/TaskScorer.kt | 29 +- .../dres/run/updatables/EndTaskUpdatable.kt | 40 +- .../run/updatables/ScoreboardsUpdatable.kt | 10 +- .../dres/run/updatables/ScoresUpdatable.kt | 55 ++- .../run/score/scorer/AvsTaskScorerTest.kt | 80 ++-- .../run/score/scorer/KisTaskScorerTest.kt | 46 +- .../score/scorer/LegacyAvsTaskScorerTest.kt | 43 +- .../src/app/viewer/task-viewer.component.ts | 2 +- frontend/yarn.lock | 417 +++--------------- 46 files changed, 600 insertions(+), 876 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/score/Scoreable.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index 9543237dd..b3adf5496 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -43,7 +43,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle ctx.contentType("text/csv") ctx.header("Content-Disposition", "attachment; filename=\"scores-${manager.id}.csv\"") - return this.store.transactional(false) { + return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, manager) "startTime,task,group,team,score\n" + manager.tasks(rac).filter { it.started != null @@ -51,7 +51,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle it.started } .flatMap { task -> - task.scorer.scores().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.filter { t -> t.id eq it.first }.firstOrNull()?.name ?: "???"}\",${it.third}" } + task.scorer.scoreListFromCache().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.filter { t -> t.id eq it.first }.firstOrNull()?.name ?: "???"}\",${it.third}" } }.joinToString(separator = "\n") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index 3f09cc713..774a61eee 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -41,7 +41,7 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional(true) { evaluationManager.tasks(RunActionContext.runActionContext(ctx, evaluationManager)).filter { it.template.templateId == templateId }.map { - ApiSubmissionInfo(evaluationId, it.id, it.getSubmissions().map { sub -> sub.toApi() }.toList()) + ApiSubmissionInfo(evaluationId, it.taskId, it.getSubmissions().map { sub -> sub.toApi() }.toList()) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 6566b0145..0acc11657 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -55,7 +55,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.startTask(rac) - DbAuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.id, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) + DbAuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.taskId, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 80d61f461..afbb6d612 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -62,7 +62,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle "No active task run in evaluation ${ctx.evaluationId()}.", ctx ) - val scores = scorer.teamScoreMap() + val scores = scorer.scoreMapFromCache() return@transactional ApiScoreOverview( "task", manager.currentTaskTemplate(rac).taskGroup.name, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index bc4ee6b47..f6dc42ed9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -55,7 +55,7 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, manager) val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run ${manager.id}.", ctx) - val scores = scorer.teamScoreMap() + val scores = scorer.scoreMapFromCache() ApiScoreOverview("task", manager.currentTaskTemplate(rac).taskGroup.name, manager.template.teams.asSequence().map { ApiScore(it.teamId, scores[it.teamId] ?: 0.0) }.toList() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 654f63774..839318330 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -30,8 +30,8 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), - OpenApiParam("taskId", String::class, "The task ID.", required = true) + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("taskId", String::class, "The task ID.", required = true, allowEmptyValue = false) ], responses = [ OpenApiResponse("200", [OpenApiContent(ApiTargetContent::class)]), @@ -54,7 +54,6 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf var task = manager.currentTask(rac) if (task == null) { task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) - } if (task.status != TaskStatus.ENDED) { throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 1d849ed5f..84be5ff6b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -238,7 +238,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v /* If text is supplied, it supersedes other parameters */ val textParam = map[PARAMETER_NAME_TEXT]?.first() val itemParam = map[PARAMETER_NAME_ITEM]?.first() - val currentTaskId = runManager.currentTask(rac)?.id + val currentTaskId = runManager.currentTask(rac)?.taskId val task = DbTask.query(DbTask::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException( 404, "No active task for ID '$currentTaskId' could be found.", diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt index ec292ba8c..0d542f166 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt @@ -19,7 +19,7 @@ data class ApiTaskOverview( task.template.taskGroup.name, task.template.taskGroup.type.name, task.template.duration, - task.id, + task.taskId, task.status, task.started, task.ended) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index ed537b670..426f64cae 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -26,7 +26,7 @@ abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ - abstract var duration: Long + override abstract var duration: Long /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ val teamGroupAggregators: Map by lazy { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 7881994c3..701b60c15 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -5,18 +5,14 @@ import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.TemplateId import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.FilteringContext.contains import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter -import kotlinx.dnq.query.flatMapDistinct import kotlinx.dnq.query.mapDistinct import kotlinx.dnq.util.findById -import java.util.concurrent.ConcurrentLinkedQueue /** * An abstract [Run] implementation that can be used by different subtypes. @@ -43,7 +39,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - final override val id: TaskId = this.task.id + final override val taskId: TaskId = this.task.id /** * The [TemplateId] of this [AbstractTask]. @@ -145,7 +141,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { /** Returns a [Sequence] of all [DbSubmission]s connected to this [AbstractTask]. */ override fun getSubmissions() = DbAnswerSet.filter { - a -> a.task.id eq this@AbstractTask.id + a -> a.task.id eq this@AbstractTask.taskId }.mapDistinct { it.submission }.asSequence() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 9463b33ec..de67504b8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -5,11 +5,20 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission -import dev.dres.run.audit.DbAuditLogger +import dev.dres.data.model.template.task.options.DbConfiguredOption +import dev.dres.data.model.template.task.options.DbScoreOption import dev.dres.run.exceptions.IllegalTeamIdException +import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.filter.SubmissionFilterAggregator +import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard +import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.score.scoreboard.SumAggregateScoreBoard +import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.KisTaskScorer +import dev.dres.run.score.scorer.TaskScorer +import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -106,8 +115,20 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe /** Tracks the current [DbTaskTemplate] per [TeamId]. */ private val navigationMap: MutableMap = HashMap() + /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ + override val scoreboards: List + init { - /* TODO: Reconstruct TaskRuns from stored data. */ + /* Prepare the evaluation scoreboards. */ + val teams = this.description.teams.asSequence().map { it.teamId }.toList() + val groupBoards = this.description.taskGroups.asSequence().map { group -> + MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) + }.toList() + val aggregateScoreBoard = SumAggregateScoreBoard("sum", this, groupBoards) + this.scoreboards = groupBoards.plus(aggregateScoreBoard) + + /* Load all ongoing tasks. */ + /* this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } */ } fun goTo(teamId: TeamId, index: Int) { @@ -179,15 +200,17 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.teamId]?.indexOf(this) ?: -1 /** The [SubmissionFilter] instance used by this [IATaskRun]. */ - override val filter: SubmissionFilter = this.template.newFilter() + override val filter: SubmissionFilter - /** The [TeamTaskScorer] instance used by this [InteractiveAsynchronousEvaluation].*/ - override val scorer = this.template.newScorer() - ?: throw IllegalArgumentException("specified scorer is not of type TeamTaskScorer") + /** The [CachingTaskScorer] instance used by this [InteractiveAsynchronousEvaluation].*/ + override val scorer: CachingTaskScorer /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration + /** The [List] of [TeamId]s working on this [IATaskRun]. */ + override val teams: List = listOf(this.teamId) + init { this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.teamId) { _, v -> val list = v ?: LinkedList() @@ -195,6 +218,32 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe list.add(this) list } + + /* Initialize submission filter. */ + if (this.template.taskGroup.type.submission.isEmpty) { + this.filter = AllSubmissionFilter + } else { + this.filter = SubmissionFilterAggregator( + this.template.taskGroup.type.submission.asSequence().map { option -> + val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() + ) + } + + + /* Initialize task scorer. */ + this.scorer = CachingTaskScorer( + when(val scoreOption = this.template.taskGroup.type.score) { + DbScoreOption.KIS -> KisTaskScorer( + this, + this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description).asSequence().map { it.key to it.value }.toMap() + ) + DbScoreOption.AVS -> AvsTaskScorer(this) + else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") + } + ) } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 05b7b2bd3..e8c572a8e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -5,10 +5,20 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.TemplateId -import dev.dres.run.audit.DbAuditLogger +import dev.dres.data.model.template.task.options.DbConfiguredOption +import dev.dres.data.model.template.task.options.DbScoreOption +import dev.dres.data.model.template.team.TeamId +import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.filter.SubmissionFilterAggregator +import dev.dres.run.score.Scoreable +import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard +import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.score.scoreboard.SumAggregateScoreBoard +import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.KisTaskScorer import kotlinx.dnq.query.* import java.lang.IndexOutOfBoundsException import java.util.LinkedList @@ -49,6 +59,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ private val templates = this.description.tasks.asSequence().map { it.templateId }.toList() + /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ var templateIndex: Int = 0 private set @@ -57,9 +68,20 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu val currentTask: AbstractInteractiveTask? get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex] } + /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ + override val scoreboards: List + init { + /* Prepare the evaluation scoreboards. */ + val teams = this.description.teams.asSequence().map { it.teamId }.toList() + val groupBoards = this.description.taskGroups.asSequence().map { group -> + MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) + }.toList() + val aggregateScoreBoard = SumAggregateScoreBoard("sum", this, groupBoards) + this.scoreboards = groupBoards.plus(aggregateScoreBoard) + /* Load all ongoing tasks. */ - this.evaluation.tasks.asSequence().map { ISTaskRun(it) }.toMutableList() + this.evaluation.tasks.asSequence().forEach { ISTaskRun(it) } } /** @@ -118,17 +140,47 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu get() = this@InteractiveSynchronousEvaluation.tasks.indexOf(this) /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ - override val filter: SubmissionFilter = this.template.newFilter() + override val filter: SubmissionFilter - /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ - override val scorer = this.template.newScorer() + /** The [CachingTaskScorer] instance used by this [ISTaskRun]. */ + override val scorer: CachingTaskScorer /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration + /** */ + override val teams: List = this@InteractiveSynchronousEvaluation.description.teams.asSequence().map { it.teamId }.toList() + init { - check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { "Cannot create a new task. Another task is currently running." } + check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { + "Cannot create a new task. Another task is currently running." + } (this@InteractiveSynchronousEvaluation.tasks as MutableList).add(this) + + /* Initialize submission filter. */ + if (this.template.taskGroup.type.submission.isEmpty) { + this.filter = AllSubmissionFilter + } else { + this.filter = SubmissionFilterAggregator( + this.template.taskGroup.type.submission.asSequence().map { option -> + val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() + ) + } + + /* Initialize task scorer. */ + this.scorer = CachingTaskScorer( + when(val scoreOption = this.template.taskGroup.type.score) { + DbScoreOption.KIS -> KisTaskScorer( + this, + this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description).asSequence().map { it.key to it.value }.toMap() + ) + DbScoreOption.AVS -> AvsTaskScorer(this) + else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") + } + ) } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index b080eacc4..68330ebba 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -1,13 +1,16 @@ package dev.dres.data.model.run -import dev.dres.data.model.run.InteractiveSynchronousEvaluation.ISTaskRun import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard +import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.score.scoreboard.SumAggregateScoreBoard +import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.TaskScorer import kotlinx.dnq.query.* @@ -32,11 +35,22 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev NITaskRun(it) }.toList() + /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ + override val scoreboards: List + + init { + val teams = this.description.teams.asSequence().map { it.teamId }.toList() + val groupBoards = this.description.taskGroups.asSequence().map { group -> + MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) + }.toList() + val aggregateScoreBoard = SumAggregateScoreBoard("sum", this, groupBoards) + this.scoreboards = groupBoards.plus(aggregateScoreBoard) + } + /** * The [TaskRun] used by a [NonInteractiveEvaluation]. */ inner class NITaskRun(task: DbTask): AbstractNonInteractiveTask(task) { - /** Reference to the [EvaluationRun] hosting this [NITaskRun]. */ override val competition: EvaluationRun get() = this@NonInteractiveEvaluation @@ -45,25 +59,16 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev override val position: Int get() = this@NonInteractiveEvaluation.tasks.indexOf(this) - /** The [TeamTaskScorer] instance used by this [ISTaskRun]. */ - override val scorer = this.template.newScorer() - /** */ - override val filter: SubmissionFilter - get() = TODO("Can there be submission filters for non-interactive tasks?") + /** The [CachingTaskScorer] instance used by this [NITaskRun]. */ + override val scorer: CachingTaskScorer = TODO("Will we have the same scorers for non-interactive tasks.") + + /** The [SubmissionFilter] instance used by this [NITaskRun]. */ + override val filter: SubmissionFilter = TODO("Can there be submission filters for non-interactive tasks?") -// @Synchronized -// override fun postSubmission(submission: Submission) { -// check(this@NonInteractiveEvaluation.description.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { -// "Team ${submission.teamId} does not exists for evaluation run ${this@NonInteractiveEvaluation.name}. This is a programmer's error!" -// } -// -// /* Execute submission filters. */ -// this.filter.acceptOrThrow(submission) -// -// /* At this point, the submission is considered valid and is persisted */ -// val dbSubmission: DbSubmission = submission.toDb() -// -// /* TODO: Process and validate submission. */ -// } + /** List of [TeamId]s that work on this [NITaskRun]. */ + override val teams: List = this@NonInteractiveEvaluation.description.teams.asSequence().map { it.teamId }.toList() + + /** */ + override val duration: Long = 0 } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index ba4dd6fb4..32642cf84 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -2,11 +2,17 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.DbEvaluation +import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard +import dev.dres.run.score.scoreboard.Scoreboard +import dev.dres.run.score.scoreboard.SumAggregateScoreBoard +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.toList + /** * Represents a [DbEvaluation] that a DRES user or client takes place in and that groups several [TaskRun]s * * @author Ralph Gasser - * @version 1.0.0 + * @version 2.0.0 */ interface EvaluationRun: Run { /** The unique [EvaluationId] that identifies this [EvaluationRun]. */ @@ -29,4 +35,8 @@ interface EvaluationRun: Run { /** A fixed limit on submission previews. */ var limitSubmissionPreviews: Int -} \ No newline at end of file + + /** List of [Scoreboard] implementations for this [EvaluationRun] */ + val scoreboards: List +} + diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 792bd92fc..bbe959a99 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -3,11 +3,13 @@ package dev.dres.data.model.run.interfaces import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.team.TeamId import dev.dres.run.TaskStatus +import dev.dres.run.score.Scoreable import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.TaskScorer + typealias TaskId = String /** @@ -16,9 +18,12 @@ typealias TaskId = String * @author Ralph Gasser * @version 1.0.0 */ -interface TaskRun: Run { +interface TaskRun: Run, Scoreable { /** The unique [TaskId] that identifies this [TaskRun]. */ - val id: TaskId + override val taskId: TaskId + + /** List of [TeamId]s that worked on this [TaskRun] */ + override val teams: List /** The unique [TemplateId] that identifies the task template underpinning [TaskRun]. */ val templateId: TemplateId @@ -32,7 +37,7 @@ interface TaskRun: Run { /** Reference to the [DbTaskTemplate] describing this [IATaskRun]. */ val template: DbTaskTemplate - /** The [TaskScorer] used to update score for this [IATaskRun]. */ + /** The [CachingTaskScorer] used to update score for this [IATaskRun]. */ val scorer: CachingTaskScorer /** The current status of this [TaskRun]. This is typically a transient property. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 8e37dce6e..76c15ed63 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -84,22 +84,6 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio judges = this.judges.asSequence().map { it.id }.toList() ) - /** - * Generates and returns the default [Scoreboard] implementations for this [DbEvaluationTemplate]. - * - * This is a convenience method and requires an active transaction context. - * - * @return List of [Scoreboard] implementations. - */ - fun generateDefaultScoreboards(): List { - val teams = this.teams.toList() - val groupBoards = this.taskGroups.asSequence().map { group -> - MaxNormalizingScoreBoard(group.name, teams, {task -> task.taskGroup.name == group.name}, group.name) - }.toList() - val aggregateScoreBoard = SumAggregateScoreBoard("sum", groupBoards) - return groupBoards.plus(aggregateScoreBoard) - } - /** * Generates and returns a list of all [DbMediaItem] for this [DbEvaluationTemplate]. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt deleted file mode 100644 index 3dc8a3aa7..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/CachingTaskScorerFactory.kt +++ /dev/null @@ -1,20 +0,0 @@ -package dev.dres.data.model.template.interfaces - -import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.TaskScorer - -/** - * A factory for [TaskScorer]s. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -interface CachingTaskScorerFactory { - /** - * Generates a new [TaskScorer]. Depending on the implementation, the returned instance - * is a new instance or being re-use. - * - * @return [TaskScorer]. - */ - fun newScorer(): CachingTaskScorer -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index e5310a8c8..616faae4a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -9,13 +9,11 @@ import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.interfaces.SubmissionFilterFactory -import dev.dres.data.model.template.interfaces.CachingTaskScorerFactory +import dev.dres.data.model.template.interfaces.TaskScorerFactory import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -33,7 +31,7 @@ import java.lang.Long.max * @author Luca Rossetto * @author Ralph Gasser */ -class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), CachingTaskScorerFactory, SubmissionFilterFactory, TaskTemplate { +class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { companion object: XdNaturalEntityType() /** The [TemplateId] of this [DbTaskTemplate]. */ @@ -65,24 +63,6 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), CachingTaskScor /** The [DbHint]s that act as clues to find the target media. */ val hints by xdChildren0_N(DbHint::task) - /** - * Generates a new [TaskScorer] for this [DbTaskTemplate]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * @return [TaskScorer]. - */ - override fun newScorer(): CachingTaskScorer = this.taskGroup.type.newScorer() - - - /** - * Generates and returns a [SubmissionValidator] instance for this [DbTaskTemplate]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * @return [SubmissionFilter] - */ - override fun newFilter(): SubmissionFilter = this.taskGroup.type.newFilter() - - /** * Generates and returns a [ApiHintContent] object to be used by the RESTful interface. * @@ -95,7 +75,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), CachingTaskScor * @throws IOException */ fun toTaskHint(config: Config): ApiHintContent { - val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> + val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> var index = 0 group.value.sortedBy { it.start ?: 0 }.flatMap { val ret = mutableListOf(it.toQueryContentElement(config)) @@ -127,7 +107,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), CachingTaskScor fun toTaskTarget(config: Config): ApiTargetContent { var cummulativeOffset = 0L val sequence = this.targets.asSequence().flatMap { - cummulativeOffset += Math.floorDiv(it.item!!.durationMs!!, 1000L) + 1L + cummulativeOffset += Math.floorDiv(it.item?.durationMs ?: 10000L, 1000L) + 1L listOf( it.toQueryContentElement(config), ApiContentElement(ApiContentType.EMPTY, null, cummulativeOffset) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt index 7f8473d51..5b75af39f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt @@ -99,12 +99,12 @@ class DbTaskTemplateTarget(entity: Entity) : XdEntity(entity) { DbMediaType.IMAGE -> ApiContentType.IMAGE else -> throw IllegalStateException("Invalid target description; type indicates presence of media item but item seems unsupported or unspecified.") } - val path = Paths.get(config.cachePath, this.item?.cachedItemName(this.start, this.end)) - ?: throw IllegalArgumentException("A target of type ${this.type.description} must have a valid media item.") - val data = Files.newInputStream(path, StandardOpenOption.READ).use { stream -> - stream.readAllBytes() - } - Base64.getEncoder().encodeToString(data) to type + val filePath = this.item?.pathToOriginal() + if (filePath != null && Files.exists(filePath)) { + Base64.getEncoder().encodeToString(Files.readAllBytes(filePath)) + } else { + null + } to type } DbTargetType.TEXT -> this.text to ApiContentType.TEXT else -> throw IllegalStateException("The content type ${this.type.description} is not supported.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index b367f720d..7f2ec4f90 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -7,8 +7,6 @@ import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter import dev.dres.run.filter.SubmissionFilterAggregator -import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.TaskScorer import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -71,36 +69,4 @@ class DbTaskType(entity: Entity) : XdEntity(entity) { scoreOption = this.score.toApi(), configuration = this.configurations.asSequence().map { it.key to it.value }.toMap() ) - - /** - * Generates a new [TaskScorer] for this [DbTaskTemplate]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * Calling this method requires an ongoing transaction! - * - * @return [TaskScorer]. - */ - fun newScorer(): CachingTaskScorer { - val parameters = this.configurations.query(DbConfiguredOption::key eq this.score.description) - .asSequence().map { it.key to it.value }.toMap() - return this.score.scorer(parameters) - } - - /** - * Generates and returns a [SubmissionValidator] instance for this [DbTaskTemplate]. Depending - * on the implementation, the returned instance is a new instance or being re-use. - * - * Calling this method requires an ongoing transaction! - * - * @return [SubmissionFilter] - */ - fun newFilter(): SubmissionFilter { - if (this.submission.isEmpty) return AllSubmissionFilter - val filters = this.submission.asSequence().map { option -> - val parameters = this.configurations.query(DbConfiguredOption::key eq this.score.description) - .asSequence().map { it.key to it.value }.toMap() - option.newFilter(parameters) - }.toList() - return SubmissionFilterAggregator(filters) - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt index 25b280db9..91447d4f1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt @@ -6,5 +6,4 @@ interface TaskTemplate { fun textualDescription(): String val templateId: TemplateId - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt index 60055e359..04d460efb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt @@ -1,10 +1,6 @@ package dev.dres.data.model.template.task.options import dev.dres.api.rest.types.competition.tasks.options.ApiScoreOption -import dev.dres.run.score.scorer.TaskScorer -import dev.dres.run.score.scorer.AvsTaskScorer -import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.KisTaskScorer import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -26,19 +22,6 @@ class DbScoreOption(entity: Entity) : XdEnumEntity(entity) { var description by xdRequiredStringProp(unique = true) private set - /** - * Returns the [TaskScorer] for this [ScoringOption]. - * - * @param parameters The parameter [Map] used to configure the [TaskScorer] - */ - fun scorer(parameters: Map): CachingTaskScorer = CachingTaskScorer( - when (this) { - KIS -> KisTaskScorer(parameters) - AVS -> AvsTaskScorer - else -> throw IllegalStateException("The task score option ${this.description} is currently not supported.") - } - ) - /** * Converts this [DbHintOption] to a RESTful API representation [ApiScoreOption]. * diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 0405e9331..bd4821ec4 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -42,7 +42,7 @@ import kotlin.math.max * @version 1.0.0 * @author Ralph Gasser */ -class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsynchronousEvaluation, private val store: TransientEntityStore): InteractiveRunManager { +class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsynchronousEvaluation, override val store: TransientEntityStore): InteractiveRunManager { companion object { private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) @@ -64,13 +64,13 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn private val stateLock = ReentrantReadWriteLock() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.evaluation) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable) + private val scoresUpdatable = ScoresUpdatable(this) /** List of [Updatable] held by this [InteractiveAsynchronousRunManager]. */ private val updatables = mutableListOf() @@ -99,14 +99,14 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override val judgementValidators: List get() = this.evaluation.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } + /** The list of [Scoreboard]s maintained by this [InteractiveAsynchronousEvaluation]. */ override val scoreboards: List - get() = this.scoreboardsUpdatable.scoreboards + get() = this.evaluation.scoreboards + /** The score history for this [InteractiveAsynchronousEvaluation]. */ override val scoreHistory: List get() = this.scoreboardsUpdatable.timeSeries - - init { /* Register relevant Updatables. */ this.updatables.add(this.scoresUpdatable) @@ -134,7 +134,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> - this.scoresUpdatable.enqueue(Pair(task, sub)) + this.scoresUpdatable.enqueue(task) if (sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } @@ -287,7 +287,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ this.messageQueueUpdatable.enqueue( - ServerMessage(this.id, this.evaluation.currentTaskForTeam(context.teamId)?.id, ServerMessageType.COMPETITION_UPDATE), + ServerMessage(this.id, this.evaluation.currentTaskForTeam(context.teamId)?.taskId, ServerMessageType.COMPETITION_UPDATE), context.teamId ) @@ -357,7 +357,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, currentTask.id, ServerMessageType.TASK_END), context.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, currentTask.taskId, ServerMessageType.TASK_END), context.teamId) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } @@ -434,7 +434,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * @param taskId The [SubmissionId] of the [AbstractInteractiveTask]. */ override fun taskForId(context: RunActionContext, taskId: SubmissionId): AbstractInteractiveTask? = - this.tasks(context).find { it.id == taskId } + this.tasks(context).find { it.taskId == taskId } /** * Returns a reference to the currently active [InteractiveAsynchronousEvaluation.IATaskRun]. @@ -530,10 +530,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn submission.toNewDb() /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, submission)) + this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED), context.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED), context.teamId) return submission.answerSets().first().status() } @@ -560,10 +560,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn answerSet.status = submissionStatus /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, answerSet.submission)) + this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED), context.teamId!!) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED), context.teamId!!) return true } @@ -675,18 +675,18 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn if (timeLeft <= 0) { this.stateLock.write { task.end() - DbAuditLogger.taskEnd(this.id, task.id, DbAuditLogSource.INTERNAL, null) + DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_END), team.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_END), team.teamId) } } else if (teamHasPreparingTask(team.teamId)) { val task = this.evaluation.currentTaskForTeam(team.teamId) ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") task.start() DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_START), team.teamId) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_START), team.teamId) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index b59198007..fe983a10d 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -9,6 +9,7 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard +import jetbrains.exodus.database.TransientEntityStore interface InteractiveRunManager : RunManager { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 6ea3a8426..def6767f3 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -38,7 +38,7 @@ import kotlin.math.max * @version 3.0.0 * @author Ralph Gasser */ -class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynchronousEvaluation, private val store: TransientEntityStore) : InteractiveRunManager { +class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynchronousEvaluation, override val store: TransientEntityStore) : InteractiveRunManager { private val VIEWER_TIME_OUT = 30L //TODO make configurable @@ -80,7 +80,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** List of [Scoreboard]s for this [InteractiveSynchronousRunManager]. */ override val scoreboards: List - get() = this.scoreboardsUpdatable.scoreboards + get() = this.evaluation.scoreboards /** List of [ScoreTimePoint]s tracking the states of the different [Scoreboard]s over time. */ override val scoreHistory: List @@ -90,13 +90,13 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private val readyLatch = ReadyLatch() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.evaluation) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoresUpdatable = ScoresUpdatable(this.id, this.scoreboardsUpdatable, this.messageQueueUpdatable) + private val scoresUpdatable = ScoresUpdatable(this) /** The internal [EndTaskUpdatable] used to end a task once no more submissions are possible */ private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.INTERNAL) @@ -129,7 +129,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> - this.scoresUpdatable.enqueue(Pair(task, sub)) + this.scoresUpdatable.enqueue(task) if (sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { task.validator.validate(sub) } @@ -222,7 +222,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.COMPETITION_UPDATE)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.COMPETITION_UPDATE)) LOGGER.info("SynchronousRunManager ${this.id} set to task $index") } else { throw IndexOutOfBoundsException("Index $index is out of bounds for the number of available tasks.") @@ -252,7 +252,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.readyLatch.reset(VIEWER_TIME_OUT) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.TASK_PREPARE)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.TASK_PREPARE)) LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") } @@ -269,7 +269,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.currentTask(context)?.id, ServerMessageType.TASK_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.currentTask(context)?.taskId, ServerMessageType.TASK_END)) LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") } @@ -297,7 +297,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param taskId The [EvaluationId] of the [TaskRun]. */ override fun taskForId(context: RunActionContext, taskId: EvaluationId) = - this.evaluation.tasks.find { it.id == taskId } + this.evaluation.tasks.find { it.taskId == taskId } /** * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. @@ -471,10 +471,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, submission)) + this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED)) return submission.answerSets().first().status() } @@ -500,10 +500,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch answerSet.status = submissionStatus /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(Pair(task, answerSet.submission)) + this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.id, ServerMessageType.TASK_UPDATED), context.teamId!!) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED), context.teamId!!) return true } @@ -567,14 +567,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private fun invokeUpdatables() { this.updatables.forEach { if (it.shouldBeUpdated(this.status)) { - try { - it.update(this.status) - } catch (e: Throwable) { - LOGGER.error( - "Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", - e - ) - } + it.update(this.status) } } } @@ -590,12 +583,12 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.write { this.store.transactional { this.evaluation.currentTask!!.start() - DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.id, this.evaluation.getCurrentTemplate(), DbAuditLogSource.INTERNAL, null) + DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate(), DbAuditLogSource.INTERNAL, null) } } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.TASK_START)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.TASK_START)) } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ @@ -606,13 +599,13 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (timeLeft <= 0) { this.store.transactional { task.end() - DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.id, DbAuditLogSource.INTERNAL, null) - EventStreamProcessor.event(TaskEndEvent(this.id, task.id)) + DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.taskId, DbAuditLogSource.INTERNAL, null) + EventStreamProcessor.event(TaskEndEvent(this.id, task.taskId)) } } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.id, ServerMessageType.TASK_END)) + this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.TASK_END)) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 6a03b6aa9..d4e759584 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read -class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation, private val store: TransientEntityStore) : RunManager { +class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation, override val store: TransientEntityStore) : RunManager { private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable @@ -45,10 +45,11 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation get() = this.evaluation.description /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this.template.generateDefaultScoreboards(), SCOREBOARD_UPDATE_INTERVAL_MS, this.evaluation) //TODO requires some changes + private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) //TODO requires some changes + /** The [List] of [Scoreboard]s maintained by this [NonInteractiveRunManager]. */ override val scoreboards: List - get() = this.scoreboardsUpdatable.scoreboards + get() = this.evaluation.scoreboards @Volatile override var status: RunManagerStatus = if (this.evaluation.hasStarted) { @@ -124,7 +125,7 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation break } - val task = this.evaluation.tasks.find { it.id == idNamePair.first } + val task = this.evaluation.tasks.find { it.taskId == idNamePair.first } if (task == null) { LOGGER.error("Unable to retrieve task with changed id ${idNamePair.first}") diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 28eb52e32..cd775678a 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -11,6 +11,7 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator +import jetbrains.exodus.database.TransientEntityStore /** * A managing class for concrete executions of [DbEvaluationTemplate], i.e. [InteractiveSynchronousEvaluation]s. @@ -45,6 +46,9 @@ interface RunManager : Runnable { /** [JudgementValidator]s for all tasks that use them */ val runProperties: RunProperties + /** The [TransientEntityStore] that backs this [InteractiveRunManager]. */ + val store: TransientEntityStore + /** * Starts this [RunManager] moving [RunManager.status] from [RunManagerStatus.CREATED] to * [RunManagerStatus.ACTIVE]. A [RunManager] can refuse to start. diff --git a/backend/src/main/kotlin/dev/dres/run/score/Scoreable.kt b/backend/src/main/kotlin/dev/dres/run/score/Scoreable.kt new file mode 100644 index 000000000..515a2ee8a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/Scoreable.kt @@ -0,0 +1,28 @@ +package dev.dres.run.score + +import dev.dres.data.model.run.Task +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.team.TeamId + +/** + * A [Scoreable] is subject of a [TaskScorer]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +interface Scoreable { + /** The [TaskId] of the [Task] masked by this [Scoreable]. */ + val taskId: TaskId + + /** The [TeamId]s of teams that work on the task identified by this [Scoreable]. */ + val teams: List + + /** Timestamp of when the [Task] identified by this [Scoreable] was started. */ + val duration: Long + + /** Timestamp of when the [Task] identified by this [Scoreable] was started. */ + val started: Long? + + /** Timestamp of when the [Task] identified by this [Scoreable] ended. */ + val ended: Long? +} diff --git a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt b/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt deleted file mode 100644 index 63b47504b..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/TaskContext.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.dres.run.score - -import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.template.team.TeamId - -/** - * - */ -data class TaskContext(val taskId: EvaluationId, val teamIds: Collection, val taskStartTime: Long?, val taskDuration: Long?, val taskEndTime: Long? = null) diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 518e205dd..1ba4690f9 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,12 +1,9 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.TemplateId -import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.TaskScorer import java.util.concurrent.ConcurrentHashMap import kotlin.math.max @@ -16,13 +13,11 @@ import kotlin.math.max * @author Luca Rossett * @version 1.1.0 */ -class MaxNormalizingScoreBoard(override val name: String, teams: List, private val taskFilter: (DbTaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 1000.0) : Scoreboard { +class MaxNormalizingScoreBoard(override val name: String, override val run: EvaluationRun, val teamIds: List, private val taskFilter: (DbTaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 1000.0) : Scoreboard { /** Tracks the score per [TemplateId] (references a [DbTaskTemplate]). */ private val scorePerTaskMap = ConcurrentHashMap>() - private val teamIds = teams.map { it.teamId } - private fun overallScoreMap(): Map { val scoreSums = scorePerTaskMap.values .flatMap {it.entries} //all team to score pairs independent of task @@ -42,20 +37,15 @@ class MaxNormalizingScoreBoard(override val name: String, teams: List, p override fun score(teamId: TeamId) = overallScoreMap()[teamId] ?: 0.0 - - override fun update(scorers: Map) { + override fun update() { this.scorePerTaskMap.clear() - this.scorePerTaskMap.putAll(scorers.map { - it.key to it.value.scores().groupBy { it.first }.mapValues { - it.value.maxByOrNull { it.third }?.third ?: 0.0 + val scorers = this.run.tasks.filter { this.taskFilter(it.template) && (it.started != null) }.map { it.scorer } + this.scorePerTaskMap.putAll(scorers.associate { scorer -> + scorer.scoreable.taskId to scorer.scoreListFromCache().groupBy { s -> s.first }.mapValues { + it.value.maxByOrNull { s -> s.third }?.third ?: 0.0 } - }.toMap() - ) + }) } - override fun update(runs: List) = update( - runs.filter { taskFilter(it.template) && (it.started != null) }.associate { it.id to it.scorer } - ) - override fun overview() = ScoreOverview(name, taskGroupName, scores()) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt index 03182123f..33e3ba8e2 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/Scoreboard.kt @@ -2,19 +2,22 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.TaskScorer /** * A [Scoreboard] tracks the [Score]s for different [DbTeam]s * * @author Ralph Gasser - * @version 1.0.1 + * @version 1.1.0 */ interface Scoreboard { + /** The [EvaluationRun] this [Scoreboard] instance belongs to. */ + val run: EvaluationRun + /** * Returns the name of the [Scoreboard] */ @@ -34,14 +37,9 @@ interface Scoreboard { fun score(teamId: TeamId): Double /** - * Updates the [Scoreboard]. - */ - fun update(runs: List) - - /** - * Updates using a map of the [TaskId] ids to the corresponding [TaskScorer]s + * Updates the scores held by this [Scoreboard]. */ - fun update(scorers: Map) + fun update() /** * Returns a summary of all current scores in a [ScoreOverview] diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt index 777e38b24..5b0a4645c 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/SumAggregateScoreBoard.kt @@ -2,17 +2,17 @@ package dev.dres.run.score.scoreboard import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.TemplateId -import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.TaskScorer /** * A [Scoreboard] that keeps tracks the total score per team and task group. * * @author Luca Rossett - * @version 1.1.0 + * @version 1.2.0 */ -class SumAggregateScoreBoard(override val name: String, private val boards: List, private val taskGroupName: String? = null) : Scoreboard { +class SumAggregateScoreBoard(override val name: String, override val run: EvaluationRun, private val boards: List, private val taskGroupName: String? = null) : Scoreboard { override fun scores(): List = this.boards.map { it.scores() } .flatten().groupBy { it.teamId }.values @@ -20,11 +20,7 @@ class SumAggregateScoreBoard(override val name: String, private val boards: List override fun score(teamId: TeamId) = boards.sumOf { it.score(teamId) } - override fun update(runs: List) { - //since calls are delegated, nothing needs to be done here - } - - override fun update(scorers: Map) { + override fun update() { //since calls are delegated, nothing needs to be done here } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 74316d47c..1e4159f45 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -2,8 +2,9 @@ package dev.dres.run.score.scorer import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.media.MediaItemType +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* -import dev.dres.run.score.TaskContext +import dev.dres.run.score.Scoreable import dev.dres.utilities.TimeUtil /** @@ -12,27 +13,26 @@ import dev.dres.utilities.TimeUtil * @author Luca Rossetto * @version 1.1.0 */ -object AvsTaskScorer : TaskScorer { - +class AvsTaskScorer(override val scoreable: Scoreable) : TaskScorer { /** - * TODO: Check for correctness especially if a submission has more than one verdict. Maybe add sanity checks. + * Computes and returns the scores for this [KisTaskScorer]. Requires an ongoing database transaction. + * + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [Map] of [TeamId] to calculated task score. */ - override fun computeScores(submissions: Sequence, context: TaskContext): Map { - val correctSubmissions = - submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.CORRECT } } - val wrongSubmissions = - submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.WRONG } } + override fun scoreMap(submissions: Sequence): Map { + val correctSubmissions = submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.CORRECT } } + val wrongSubmissions = submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.WRONG } } val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.teamId } val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.teamId } val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() - return context.teamIds.map { teamid -> - val correctSubs = correctSubmissionsPerTeam[teamid] ?: return@map teamid to 0.0 + return this.scoreable.teams.map { teamId -> + val correctSubs = correctSubmissionsPerTeam[teamId] ?: return@map teamId to 0.0 val correct = correctSubs.size - val wrong = wrongSubmissionsPerTeam[teamid]?.size ?: 0 - teamid to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs.asSequence()).toDouble() / totalCorrectQuantized) + val wrong = wrongSubmissionsPerTeam[teamId]?.size ?: 0 + teamId to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs.asSequence()).toDouble() / totalCorrectQuantized) }.toMap() - } private fun countQuantized(submissions: Sequence): Int = submissions @@ -48,6 +48,4 @@ object AvsTaskScorer : TaskScorer { else -> throw IllegalStateException("Unsupported media type ${it.key!!.type()} for AVS task scorer.") } }.sum() - - } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt index c1e97fc75..848ce9372 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt @@ -1,22 +1,59 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.TaskContext +import dev.dres.run.score.Scoreable -class CachingTaskScorer(private val innerScorer: TaskScorer) : TaskScorer { +/** + * A [TaskScorer] that caches intermediate results for performance gains. + * + * @author Luca Rossetto + * @version 1.0.0 + */ +class CachingTaskScorer(private val wrapped: TaskScorer): TaskScorer { + /** The [Scoreable] held by the wrapped [TaskScorer]. */ + override val scoreable: Scoreable + get() = this.wrapped.scoreable + + /** Map of the latest scores that has been cached by this [TaskScorer]. */ private var latest: Map = emptyMap() - override fun computeScores(submissions: Sequence, context: TaskContext): Map { - latest = innerScorer.computeScores(submissions, context) - return latest + /** + * Returns the cached value of this [TaskScorer]d. + * + * @return A [Map] of [TeamId] to calculated task score. + */ + fun scoreMapFromCache(): Map = this.latest + + /** + * Returns the cached value of this [TaskScorer]. + * + * @return A [List] of [ScoreEntry]. + */ + fun scoreListFromCache(): List = this.latest.map { ScoreEntry(it.key, null, it.value) } + + /** + * Returns a map of [TeamId] to score as generated by this [TaskScorer]. Updates the local cache as as side-effect. + * + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [Map] of [TeamId] to score value + */ + override fun scoreMap(submissions: Sequence): Map { + val map = this.wrapped.scoreMap(submissions) + this.latest = map + return map } - fun scores(): List = latest.map { ScoreEntry(it.key, null, it.value) } - - fun teamScoreMap() : Map = latest - - + /** + * Returns a list of [ScoreEntry] generated by this [TaskScorer]. Updates the local cache as as side-effect. + * + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [Map] of [TeamId] to score value + */ + override fun scores(submissions: Sequence): List { + val map = this.wrapped.scoreMap(submissions) + this.latest = map + return map.map { ScoreEntry(it.key, null, it.value) } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index 356e8c9d5..a05629a09 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,20 +1,20 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.TaskContext +import dev.dres.run.score.Scoreable +import dev.dres.run.score.scorer.TaskScorer /** * @author Luca Rossetto * @version 1.0.0 */ -object InferredAveragePrecisionScorer : TaskScorer { +class InferredAveragePrecisionScorer(override val scoreable: Scoreable) : TaskScorer { private val epsilon = 0.01 //TODO check what TRECVID uses - override fun computeScores(submissions: Sequence, context: TaskContext): Map { + override fun scoreMap(submissions: Sequence): Map { TODO("Not yet implemented") } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index dbcdb309f..69c369951 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -1,22 +1,21 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.run.score.TaskContext -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter +import dev.dres.run.score.Scoreable import kotlin.math.max class KisTaskScorer( - private val maxPointsPerTask: Double = defaultmaxPointsPerTask, - private val maxPointsAtTaskEnd: Double = defaultmaxPointsAtTaskEnd, - private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission + override val scoreable: Scoreable, + private val maxPointsPerTask: Double = defaultmaxPointsPerTask, + private val maxPointsAtTaskEnd: Double = defaultmaxPointsAtTaskEnd, + private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission ) : TaskScorer { - constructor(parameters: Map) : this( + constructor(run: TaskRun, parameters: Map) : this( + run, parameters.getOrDefault("maxPointsPerTask", "$defaultmaxPointsPerTask").toDoubleOrNull() ?: defaultmaxPointsPerTask, parameters.getOrDefault("maxPointsAtTaskEnd", "$defaultmaxPointsAtTaskEnd").toDoubleOrNull() ?: defaultmaxPointsAtTaskEnd, parameters.getOrDefault("penaltyPerWrongSubmission", "$defaultpenaltyPerWrongSubmission").toDoubleOrNull() ?: defaultpenaltyPerWrongSubmission @@ -28,12 +27,17 @@ class KisTaskScorer( private const val defaultpenaltyPerWrongSubmission: Double = 100.0 } - - override fun computeScores(submissions: Sequence, context: TaskContext): Map { - val taskStartTime = context.taskStartTime ?: throw IllegalArgumentException("No task start time specified.") - val taskDuration = context.taskDuration ?: throw IllegalArgumentException("No task duration specified.") - val tDur = max(taskDuration * 1000L, (context.taskEndTime ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition - return context.teamIds.associateWith { teamId -> + /** + * Computes and returns the scores for this [KisTaskScorer]. Requires an ongoing database transaction. + * + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [Map] of [TeamId] to calculated task score. + */ + override fun scoreMap(submissions: Sequence): Map { + val taskDuration = this.scoreable.duration + val taskStartTime = this.scoreable.started ?: throw IllegalArgumentException("No task start time specified.") + val tDur = max(taskDuration * 1000L, (scoreable.ended ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition + return this.scoreable.teams.associateWith { teamId -> val verdicts = submissions.filter { it.teamId == teamId }.sortedBy { it.timestamp }.flatMap { sub -> sub.answerSets().filter { (it.status() == VerdictStatus.CORRECT) or (it.status() == VerdictStatus.WRONG) }.asSequence() }.toList() @@ -52,5 +56,4 @@ class KisTaskScorer( score } } - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt index 9ef7bdc0f..d4d247b48 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt @@ -1,14 +1,10 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.TaskContext -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.firstOrNull +import dev.dres.run.score.Scoreable import java.lang.Double.max import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -18,21 +14,19 @@ import kotlin.math.abs /** * The new AVS Scorer. */ -class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPointsPerTask: Double) : TaskScorer { +class NewAvsTaskScorer(override val scoreable: Scoreable, private val penaltyConstant: Double, private val maxPointsPerTask: Double) : TaskScorer { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - constructor(parameters: Map) : this( - abs( - parameters.getOrDefault("penalty", "$defaultPenalty").toDoubleOrNull() ?: defaultPenalty - ), - parameters.getOrDefault("maxPointsPerTask", "$defaultMaxPointsPerTask").toDoubleOrNull() - ?: defaultMaxPointsPerTask + constructor(context: Scoreable, parameters: Map) : this( + context, + abs(parameters.getOrDefault("penalty", "$defaultPenalty").toDoubleOrNull() ?: defaultPenalty), + parameters.getOrDefault("maxPointsPerTask", "$defaultMaxPointsPerTask").toDoubleOrNull() ?: defaultMaxPointsPerTask ) - constructor() : this(defaultPenalty, defaultMaxPointsPerTask) + constructor(context: Scoreable) : this(context, defaultPenalty, defaultMaxPointsPerTask) companion object { const val defaultPenalty: Double = 0.2 @@ -54,21 +48,22 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } - override fun computeScores( - submissions: Sequence, - context: TaskContext - ): Map { - + /** + * Computes and returns the scores for this [NewAvsTaskScorer]. Requires an ongoing database transaction. + * + * @return A [Map] of [TeamId] to calculated task score. + */ + override fun scoreMap(submissions: Sequence): Map { + /* Make necessary calculations. */ val distinctCorrectVideos = submissions.flatMap { submission -> submission.answerSets().filter { it.status() == VerdictStatus.CORRECT && it.answers().firstOrNull()?.item != null } }.mapNotNullTo(mutableSetOf()) { it.answers().firstOrNull()?.item } .size - //no correct submissions yet if (distinctCorrectVideos == 0) { lastScores = this.lastScoresLock.write { - teamScoreMapSanitised(mapOf(), context.teamIds) + teamScoreMapSanitised(mapOf(), this.scoreable.teams) } this.lastScoresLock.read { return lastScores @@ -91,37 +86,6 @@ class NewAvsTaskScorer(private val penaltyConstant: Double, private val maxPoint } }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize ) - }.toMap(), context.teamIds) - - -// lastScores = this.lastScoresLock.write { -// teamScoreMapSanitised( -// submissions.filter { -// it.verdicts.asSequence().any { it.type == VerdictType.TEMPORAL && (it.status == VerdictStatus.CORRECT || it.status == VerdictStatus.WRONG) } -// } -// -// .groupBy { it.teamId } -// .map { submissionsPerTeam -> -// submissionsPerTeam.key to -// max(0.0, //prevent negative total scores -// submissionsPerTeam.value.groupBy { submission -> -// submission as ItemAspect -// submission.item.id -// }.map { -// val firstCorrectIdx = it.value.sortedBy { s -> s.timestamp } -// .indexOfFirst { s -> s.status == SubmissionStatus.CORRECT } -// if (firstCorrectIdx < 0) { //no correct submissions, only penalty -// it.value.size * -penaltyConstant -// } else { //apply penalty for everything before correct submission -// 1.0 - firstCorrectIdx * penaltyConstant -// } -// }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize -// ) -// }.toMap(), context.teamIds -// ) -// } - + }.toMap(), this.scoreable.teams) } - - } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt index 407c1f917..970cb4d29 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/TaskScorer.kt @@ -1,28 +1,37 @@ package dev.dres.run.score.scorer -import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.TaskContext +import dev.dres.run.score.Scoreable /** Type alias for a */ typealias ScoreEntry = Triple /** - * A [TaskScorer] that re-computes the current scores of all teams for a given [TaskRun] based on the - * entire [Submission] history. + * A [TaskScorer] that re-computes the current scores of all teams for a given task based on the entire submission history. * * @author Luca Rossetto * @author Ralph Gasser - * @version 1.1.0 + * @version 2.0.0 */ interface TaskScorer { + + /** The [Scoreable] this [TaskScorer] is scoring. */ + val scoreable: Scoreable + + /** + * Returns List of [ScoreEntry]s as generated by this [TaskScorer] + * + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [List] of [ScoreEntry] + */ + fun scores(submissions: Sequence): List = this.scoreMap(submissions).map { ScoreEntry(it.key, null, it.value) } + /** - * Re-computes this [TaskScorer]'s score based on the given [Submission] history. + * Returns a map of [TeamId] to score as generated by this [TaskScorer] * - * @param submissions The [Submission]s used to update this [TaskScorer] with. - * @param context The [TaskContext] in which scoring takes place. + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [Map] of [TeamId] to score value */ - fun computeScores(submissions: Sequence, context: TaskContext): Map + fun scoreMap(submissions: Sequence): Map } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt index 4367f488b..0811a6b8a 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt @@ -15,7 +15,7 @@ import java.util.concurrent.atomic.AtomicInteger * @author Luca Rossetto * @version 1.1.0 */ -class EndTaskUpdatable(private val run: InteractiveRunManager, private val context: RunActionContext) : Updatable { +class EndTaskUpdatable(private val manager: InteractiveRunManager, private val context: RunActionContext) : Updatable { /** The [EndTaskUpdatable] always belongs to the [Phase.MAIN]. */ override val phase: Phase = Phase.MAIN @@ -24,29 +24,31 @@ class EndTaskUpdatable(private val run: InteractiveRunManager, private val conte private var submissions = AtomicInteger(0) /** Internal flag indicating whether the provided [InteractiveRunManager] is asynchronous. */ - private val isAsync = this.run is InteractiveAsynchronousRunManager + private val isAsync = this.manager is InteractiveAsynchronousRunManager override fun update(status: RunManagerStatus) { - val taskRun = this.run.currentTask(this.context) + val taskRun = this.manager.currentTask(this.context) if (taskRun != null) { - if (taskRun.template.taskGroup.type.submission.contains(DbSubmissionOption.LIMIT_CORRECT_PER_TEAM)) { - val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 - if (this.run.timeLeft(this.context) > 0) { - val submissionCount = taskRun.getSubmissions().count() - if (this.submissions.getAndSet(submissionCount) < submissionCount) { - val allDone = if (this.isAsync) { - val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == context.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } - numberOfSubmissions >= limit - } else { - /* Determine of all teams have submitted . */ - this.run.template.teams.asSequence().all { team -> - val numberOfSubmissions = this.run.currentSubmissions(this.context).count { it.team.id == team.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } + this.manager.store.transactional(true) { + if (taskRun.template.taskGroup.type.submission.contains(DbSubmissionOption.LIMIT_CORRECT_PER_TEAM)) { + val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 + if (this.manager.timeLeft(this.context) > 0) { + val submissionCount = taskRun.getSubmissions().count() + if (this.submissions.getAndSet(submissionCount) < submissionCount) { + val allDone = if (this.isAsync) { + val numberOfSubmissions = this.manager.currentSubmissions(this.context).count { it.team.id == context.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } numberOfSubmissions >= limit + } else { + /* Determine of all teams have submitted . */ + this.manager.template.teams.asSequence().all { team -> + val numberOfSubmissions = this.manager.currentSubmissions(this.context).count { it.team.id == team.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } + numberOfSubmissions >= limit + } + } + if (allDone) { + this.manager.abortTask(this.context) + this.submissions.set(0) } - } - if (allDone) { - this.run.abortTask(this.context) - this.submissions.set(0) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index 51726a197..1d125b755 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -15,7 +15,7 @@ import java.util.* * @author Ralph Gasser & Luca Rossetto * @version 1.1.0 */ -class ScoreboardsUpdatable(val scoreboards: List, private val updateIntervalMs: Long, private val competition: EvaluationRun): StatefulUpdatable { +class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs: Long): StatefulUpdatable { companion object { private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) @@ -40,9 +40,11 @@ class ScoreboardsUpdatable(val scoreboards: List, private val update if (this.dirty && (now - lastUpdate) > this.updateIntervalMs) { this.dirty = false this.lastUpdate = now - this.scoreboards.forEach { - it.update(this.competition.tasks.filterIsInstance()) - it.scores().map{ score -> this._timeSeries.add(ScoreTimePoint(it.name, score)) } + this.manager.store.transactional(true) { + this.manager.scoreboards.forEach { + it.update() + it.scores().map{ score -> this._timeSeries.add(ScoreTimePoint(it.name, score)) } + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index 3773c8158..e8af6c8e5 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -1,59 +1,54 @@ package dev.dres.run.updatables -import dev.dres.api.rest.types.evaluation.websocket.ServerMessage -import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.Submission +import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus -import dev.dres.run.score.TaskContext +import dev.dres.run.score.Scoreable import kotlinx.dnq.query.asSequence -import java.util.* +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.mapDistinct +import kotlin.collections.LinkedHashSet /** - * This is a [Updatable] that runs necessary post-processing after a [DbSubmission] has been validated and updates the scores for the respective [TaskContext]. + * This is a [Updatable] that runs necessary post-processing after a [DbSubmission] has been validated and updates the scores for the respective [Scoreable]. * * @author Ralph Gasser - * @version 1.2.0 + * @version 1.3.0 */ -class ScoresUpdatable(private val evaluationId: EvaluationId, private val scoreboardsUpdatable: ScoreboardsUpdatable, private val messageQueueUpdatable: MessageQueueUpdatable): Updatable { +class ScoresUpdatable(private val manager: RunManager): Updatable { companion object { private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) } /** Internal list of [DbAnswerSet] that pend processing. */ - private val list = LinkedList>() + private val set = LinkedHashSet() /** The [Phase] this [ScoresUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN - /** Enqueues a new [DbAnswerSet] for post-processing. */ - fun enqueue(submission: Pair) = this.list.add(submission) + /** + * Enqueues a new [AbstractInteractiveTask] that requires score re-calculation. + * + * @param task The [AbstractInteractiveTask] tha requires score re-calculation. + */ + @Synchronized + fun enqueue(task: AbstractInteractiveTask) = this.set.add(task) + @Synchronized override fun update(status: RunManagerStatus) { val removed = mutableListOf() - if (!this.list.isEmpty()) { - this.list.removeIf { - val task = it.first - val scores = it.first.scorer.computeScores( - task.getSubmissions(), - TaskContext(task.id, task.competition.description.teams.asSequence().map { t -> t.id }.toList(), task.started, task.template.duration, task.ended) - ) - task.updateTeamAggregation(scores) - removed.add(task.id) - - true - } - - /* If elements were removed, then update scoreboards and tasks. */ - if (removed.isNotEmpty()) { - this.scoreboardsUpdatable.dirty = true - removed.forEach { - this.messageQueueUpdatable.enqueue(ServerMessage(this.evaluationId, it, ServerMessageType.TASK_UPDATED)) + if (this.set.isNotEmpty()) { + this.manager.store.transactional(true) { + this.set.removeIf { task -> + val submissions = DbAnswerSet.filter { a -> a.task.id eq task.taskId }.mapDistinct { it.submission }.asSequence() + val scores = task.scorer.scoreMap(submissions) + task.updateTeamAggregation(scores) + removed.add(task.taskId) + true } } } diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index e16eae1ce..57c067aa6 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -4,7 +4,9 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.evaluation.* -import dev.dres.run.score.TaskContext +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.team.TeamId +import dev.dres.run.score.Scoreable import dev.dres.run.score.scorer.NewAvsTaskScorer import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach @@ -16,10 +18,17 @@ class AvsTaskScorerTest { private lateinit var scorer: NewAvsTaskScorer private val teams = listOf("team-1", "team-2", "team-3") private val defaultTaskDuration = 3 * 60L // 3min + private val taskStartTime = 100_000L private val dummyVideoItems: List private val maxPointsPerTask = 1000.0 private val penalty = 0.2 - + private val scoreable = object: Scoreable { + override val taskId: TaskId = "task1" + override val teams: List = this@AvsTaskScorerTest.teams + override val duration: Long = this@AvsTaskScorerTest.defaultTaskDuration + override val started: Long = this@AvsTaskScorerTest.taskStartTime + override val ended: Long? = null + } init { val collectionId = "testCollection" val list = mutableListOf() @@ -47,14 +56,13 @@ class AvsTaskScorerTest { @BeforeEach fun setup(){ - this.scorer = NewAvsTaskScorer(penaltyConstant = penalty, maxPointsPerTask) + this.scorer = NewAvsTaskScorer(this.scoreable, penaltyConstant = penalty, maxPointsPerTask) } @Test @DisplayName("Three teams all without a submission. Expected score: 0.0") fun noSubmissions(){ - val scores = this.scorer.computeScores(emptySequence(), TaskContext("task1", teams, 100_000, defaultTaskDuration)) - + val scores = this.scorer.scoreMap(emptySequence()) Assertions.assertEquals(0.0, scores[teams[0]]) Assertions.assertEquals(0.0, scores[teams[1]]) Assertions.assertEquals(0.0, scores[teams[2]]) @@ -63,11 +71,10 @@ class AvsTaskScorerTest { @Test @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") fun onlyTeamOneWithAllEqualsOneCorrect(){ - val taskStart = 100_000L val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 1000, "task2" ), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 1000, "task2" ), ) - val scores = this.scorer.computeScores(subs, TaskContext("task2", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(subs) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) Assertions.assertEquals(0.0, scores[teams[1]]) Assertions.assertEquals(0.0, scores[teams[2]]) @@ -76,13 +83,12 @@ class AvsTaskScorerTest { @Test @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") fun allTeamsWithAllEqualsOneCorrect(){ - val taskStart = 100_000L val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 1000, "task2"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 2000, "task2"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStart + 3000, "task2") + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 1000, "task2"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 2000, "task2"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 3000, "task2") ) - val scores = this.scorer.computeScores(subs, TaskContext("task2", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(subs) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) Assertions.assertEquals(maxPointsPerTask, scores[teams[1]]) Assertions.assertEquals(maxPointsPerTask, scores[teams[2]]) @@ -91,13 +97,12 @@ class AvsTaskScorerTest { @Test @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ - val taskStart = 100_000L val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStart + 1000, "task3"), - ApiSubmission(teams[0], teams[0], "user1", "team2", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000, "task3"), taskStart + 2000, "task3"), - ApiSubmission(teams[1], teams[1], "user2", "team3", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStart + 3000, "task3"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStartTime + 1000, "task3"), + ApiSubmission(teams[0], teams[0], "user1", "team2", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000, "task3"), taskStartTime + 2000, "task3"), + ApiSubmission(teams[1], teams[1], "user2", "team3", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStartTime + 3000, "task3"), ) - val scores = this.scorer.computeScores(subs, TaskContext("task3", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(subs) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) Assertions.assertEquals(maxPointsPerTask/2.0, scores[teams[1]]) Assertions.assertEquals(0.0, scores[teams[2]]) @@ -106,39 +111,38 @@ class AvsTaskScorerTest { @Test @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ - val taskStart = 100_000L val subs = sequenceOf( /* Team One: All correct */ - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStart + 1000, "task4"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000, "task4"), taskStart + 2000, "task4"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStart + 3000, "task4"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStartTime + 1000, "task4"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000, "task4"), taskStartTime + 2000, "task4"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStartTime + 3000, "task4"), /* Team Two: One correct, One correct with one wrong */ - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStart + 1000, "task4"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStart + 2000, "task4"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStart + 3000, "task4"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStartTime + 1000, "task4"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStartTime + 2000, "task4"), + ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStartTime + 3000, "task4"), /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ /* v1 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStart + 1000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStartTime + 1000, "task4"), /* v2 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStart + 2000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStart + 3000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStartTime + 2000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStartTime + 3000, "task4"), /* v3 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000, "task4"), taskStart + 4000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000, "task4"), taskStart + 5000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStart + 6000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000, "task4"), taskStartTime + 4000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000, "task4"), taskStartTime + 5000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStartTime + 6000, "task4"), /* v4 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000, "task4"), taskStart + 7000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000, "task4"), taskStart + 8000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000, "task4"), taskStart + 9000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000, "task4"), taskStartTime + 7000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000, "task4"), taskStartTime + 8000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000, "task4"), taskStartTime + 9000, "task4"), /* v5 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000, "task4"), taskStart + 10000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000, "task4"), taskStart + 11000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000, "task4"), taskStart + 12000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000, "task4"), taskStartTime + 10000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000, "task4"), taskStartTime + 11000, "task4"), + ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000, "task4"), taskStartTime + 12000, "task4"), ) - val scores = this.scorer.computeScores(subs, TaskContext("task4", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(subs) /* Team One: No penalty => 1000 = 1000 * [1/3 * 1+1+1] => 1000 * 3/3 */ diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 26a8d81f1..1a77bb36b 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -3,9 +3,9 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.evaluation.* -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.run.score.TaskContext +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.team.TeamId +import dev.dres.run.score.Scoreable import dev.dres.run.score.scorer.KisTaskScorer import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -14,12 +14,20 @@ import org.junit.jupiter.api.Test class KisTaskScorerTest { private lateinit var scorer: KisTaskScorer - private val teams = listOf("team-1", "team-2", "team-3") + private val teams: List = listOf("team-1", "team-2", "team-3") private val dummyImageItem = ApiMediaItem("image1", "image 1", ApiMediaType.IMAGE, "testcollection", "images/1") private val defaultTaskDuration = 5 * 60L + private val taskStartTime = System.currentTimeMillis() - 100_000 private val maxPointsPerTask = 100.0 private val maxPointsAtTaskEnd = 50.0 private val penaltyPerWrongSubmission = 10.0 + private val scoreable = object: Scoreable { + override val taskId: TaskId = "task1" + override val teams: List = this@KisTaskScorerTest.teams + override val duration: Long = this@KisTaskScorerTest.defaultTaskDuration + override val started: Long = this@KisTaskScorerTest.taskStartTime + override val ended: Long? = null + } private val wrongAnswer = listOf( ApiAnswerSet( @@ -51,13 +59,12 @@ class KisTaskScorerTest { @BeforeEach fun setup() { - this.scorer = KisTaskScorer(maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) + this.scorer = KisTaskScorer(this.scoreable, maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) } @Test fun noSubmissions() { - val scores = - this.scorer.computeScores(emptySequence(), TaskContext("task1", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(emptySequence()) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -65,15 +72,12 @@ class KisTaskScorerTest { @Test fun allWrong() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", wrongAnswer, taskStartTime + 1000, "task2"), ApiSubmission(teams[1], teams[1], "user2", "team1", "user2", wrongAnswer, taskStartTime + 2000, "task2"), ApiSubmission(teams[2], teams[2], "user3", "team1", "user3", wrongAnswer, taskStartTime + 3000, "task2") ) - val scores = - this.scorer.computeScores(submissions, TaskContext("task2", teams, taskStartTime, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -81,13 +85,10 @@ class KisTaskScorerTest { @Test fun immediatelyRight() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime, "task3"), ) - val scores = - this.scorer.computeScores(submissions, TaskContext("task3", teams, taskStartTime, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(maxPointsPerTask, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -95,33 +96,24 @@ class KisTaskScorerTest { @Test fun rightAtTheEnd() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000), "task4"), ) - - val scores = - this.scorer.computeScores(submissions, TaskContext("task4", teams, taskStartTime, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) } @Test fun rightInTheMiddle() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task5"), ) - - val scores = - this.scorer.computeScores(submissions, TaskContext("task5", teams, taskStartTime, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) } @Test fun wrongSubmissionPenalty() { - val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( //incorrect submissions @@ -140,7 +132,7 @@ class KisTaskScorerTest { ApiSubmission(teams[2], teams[2], "user3", "team3","user3", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), ) - val scores = this.scorer.computeScores(submissions, TaskContext("task6", teams, taskStartTime, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals( maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2 - penaltyPerWrongSubmission, diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index 1ed177caf..cf4d16ba6 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -3,7 +3,9 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.evaluation.* -import dev.dres.run.score.TaskContext +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.team.TeamId +import dev.dres.run.score.Scoreable import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -15,8 +17,16 @@ class LegacyAvsTaskScorerTest { private lateinit var scorer: AvsTaskScorer private val teams = listOf("team-1", "team-2", "team-3") private val defaultTaskDuration = 5 * 60L + private val taskStartTime = System.currentTimeMillis() - 100_000 private val dummyImageItems: List private val dummyVideoItems: List + private val scoreable = object: Scoreable { + override val taskId: TaskId = "task1" + override val teams: List = this@LegacyAvsTaskScorerTest.teams + override val duration: Long = this@LegacyAvsTaskScorerTest.defaultTaskDuration + override val started: Long = this@LegacyAvsTaskScorerTest.taskStartTime + override val ended: Long? = null + } init { val collectionId = "testCollection" @@ -73,12 +83,12 @@ class LegacyAvsTaskScorerTest { @BeforeEach fun setup() { - this.scorer = AvsTaskScorer + this.scorer = AvsTaskScorer(this.scoreable) } @Test fun noSubmissions() { - val scores = this.scorer.computeScores(emptySequence(), TaskContext("task1", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(emptySequence()) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -86,14 +96,12 @@ class LegacyAvsTaskScorerTest { @Test fun allWrongSameImage() { - val taskStartTime = System.currentTimeMillis() - 100_000 - val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000, "task2"), ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 2000, "task2"), ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 3000, "task2") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task2", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -101,13 +109,12 @@ class LegacyAvsTaskScorerTest { @Test fun allWrongDifferentImages() { - val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000, "task3"), ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[1]), taskStartTime + 2000, "task3"), ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[2]), taskStartTime + 3000, "task3") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task3", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(0.0, scores[teams[0]]) assertEquals(0.0, scores[teams[1]]) assertEquals(0.0, scores[teams[2]]) @@ -115,13 +122,12 @@ class LegacyAvsTaskScorerTest { @Test fun allSameImageAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task4"), ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 2000, "task4"), ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 3000, "task4") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task4", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(100.0, scores[teams[0]]) assertEquals(100.0, scores[teams[1]]) assertEquals(100.0, scores[teams[2]]) @@ -129,13 +135,12 @@ class LegacyAvsTaskScorerTest { @Test fun allDifferentImageAllCorrect() { - val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task5"), ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000, "task5"), ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000, "task5") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task5", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) assertEquals(100.0 / 3.0, scores[teams[1]]!!, 0.001) assertEquals(100.0 / 3.0, scores[teams[2]]!!, 0.001) @@ -168,7 +173,7 @@ class LegacyAvsTaskScorerTest { ) - val scores = this.scorer.computeScores(submissions, TaskContext("task6", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) /* c = q(c) = 3, i = 1, q(p) = 4 @@ -210,7 +215,7 @@ class LegacyAvsTaskScorerTest { ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 3000, "task7") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task7", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(100.0, scores[teams[0]]) assertEquals(100.0, scores[teams[1]]) assertEquals(100.0, scores[teams[2]]) @@ -227,7 +232,7 @@ class LegacyAvsTaskScorerTest { ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 50_000, 60_000), taskStartTime + 3000, "task8") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task8", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) @@ -244,7 +249,7 @@ class LegacyAvsTaskScorerTest { ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[3], 50_000, 60_000), taskStartTime + 3000, "task9") ) - val scores = this.scorer.computeScores(submissions, TaskContext("task9", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(33.33333333333333, scores[teams[0]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[1]]!!, 0.0001) assertEquals(33.33333333333333, scores[teams[2]]!!, 0.0001) @@ -269,7 +274,7 @@ class LegacyAvsTaskScorerTest { ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000, "task10"), ) - val scores = this.scorer.computeScores(submissions, TaskContext("task10", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(100.0, scores[teams[0]]) assertEquals(100.0, scores[teams[1]]) assertEquals(100.0, scores[teams[2]]) @@ -297,7 +302,7 @@ class LegacyAvsTaskScorerTest { ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 3000, "task11"), ) - val scores = this.scorer.computeScores(submissions, TaskContext("task11", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) assertEquals(85.71428571428571, scores[teams[1]]!!, 0.001) @@ -356,7 +361,7 @@ class LegacyAvsTaskScorerTest { //c = 4, q(c) = 2, i = 2 ) - val scores = this.scorer.computeScores(submissions, TaskContext("task12", teams, 100_000, defaultTaskDuration)) + val scores = this.scorer.scoreMap(submissions) /* c = 5, q(c) = 3, i = 1, q(p) = 3 diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index ff90ddaf2..cb2f2518e 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -103,7 +103,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskTarget = this.state.pipe( filter(s => s.taskStatus == ApiTaskStatus.ENDED), switchMap((s) => - this.runService.getApiV2EvaluationByEvaluationIdHintByTaskId(s.evaluationId, s.currentTemplate?.templateId).pipe( + this.runService.getApiV2EvaluationByEvaluationIdTargetByTaskId(s.evaluationId, s.currentTemplate?.templateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index e90a65d36..544607d55 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -516,7 +516,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:7.16.12": +"@babel/core@npm:7.16.12, @babel/core@npm:^7.12.3": version: 7.16.12 resolution: "@babel/core@npm:7.16.12" dependencies: @@ -539,7 +539,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.12.3, @babel/core@npm:^7.17.2": +"@babel/core@npm:^7.17.2": version: 7.21.0 resolution: "@babel/core@npm:7.21.0" dependencies: @@ -562,7 +562,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:7.16.8": +"@babel/generator@npm:7.16.8, @babel/generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/generator@npm:7.16.8" dependencies: @@ -573,7 +573,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.16.8, @babel/generator@npm:^7.21.0": +"@babel/generator@npm:^7.21.0": version: 7.21.1 resolution: "@babel/generator@npm:7.21.1" dependencies: @@ -759,7 +759,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.16.8, @babel/helper-remap-async-to-generator@npm:^7.18.9": +"@babel/helper-remap-async-to-generator@npm:^7.16.8": version: 7.18.9 resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" dependencies: @@ -902,7 +902,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:7.16.8": +"@babel/plugin-proposal-async-generator-functions@npm:7.16.8, @babel/plugin-proposal-async-generator-functions@npm:^7.16.8": version: 7.16.8 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.8" dependencies: @@ -915,20 +915,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:^7.16.8": - version: 7.20.7 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - "@babel/plugin-syntax-async-generators": ^7.8.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 - languageName: node - linkType: hard - "@babel/plugin-proposal-class-properties@npm:^7.16.7": version: 7.18.6 resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" @@ -1269,7 +1255,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.16.8": +"@babel/plugin-transform-async-to-generator@npm:7.16.8, @babel/plugin-transform-async-to-generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.8" dependencies: @@ -1282,19 +1268,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.16.8": - version: 7.20.7 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" - dependencies: - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 - languageName: node - linkType: hard - "@babel/plugin-transform-block-scoped-functions@npm:^7.16.7": version: 7.18.6 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" @@ -1782,7 +1755,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.16.7": +"@babel/runtime@npm:7.16.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": version: 7.16.7 resolution: "@babel/runtime@npm:7.16.7" dependencies: @@ -1791,16 +1764,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": - version: 7.21.0 - resolution: "@babel/runtime@npm:7.21.0" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab - languageName: node - linkType: hard - -"@babel/template@npm:7.16.7": +"@babel/template@npm:7.16.7, @babel/template@npm:^7.16.7": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" dependencies: @@ -1811,7 +1775,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.16.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7": +"@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7": version: 7.20.7 resolution: "@babel/template@npm:7.20.7" dependencies: @@ -2446,14 +2410,7 @@ __metadata: languageName: node linkType: hard -"@types/jasmine@npm:*": - version: 4.3.1 - resolution: "@types/jasmine@npm:4.3.1" - checksum: d31fc78f7e70ed4aed20fa59400be3de9f204c473a7755aba29df90eb7d9d19d4b04b821a07e30732e67ef6021c8191eb259211264e287c053832c1940ae6e71 - languageName: node - linkType: hard - -"@types/jasmine@npm:3.6.x": +"@types/jasmine@npm:*, @types/jasmine@npm:3.6.x": version: 3.6.11 resolution: "@types/jasmine@npm:3.6.11" checksum: ccb4b749dc43b9ccb4365f36b14bdba8aac5ad7fdd00cc693695064acfbddb6b32fd2d59accd7e70b8f3a1eba69b49e8afa263e96f2b29aed565d0b911efe6c4 @@ -2490,14 +2447,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=10.0.0": - version: 18.14.0 - resolution: "@types/node@npm:18.14.0" - checksum: d83fcf5e4ed544755dd9028f5cbb6b9d46235043159111bb2ad62223729aee581c0144a9f6df8ba73d74011db9ed4ebd7af2fd5e0996714e3beb508a5da8ac5c - languageName: node - linkType: hard - -"@types/node@npm:12.11.x": +"@types/node@npm:*, @types/node@npm:12.11.x, @types/node@npm:>=10.0.0": version: 12.11.7 resolution: "@types/node@npm:12.11.7" checksum: 7e60bb298fe80f85e3c4b26592d74983452f2eedc7a4bd65157c2059160685ff8ef26f670768ca4d20e41cd7195be2892bea9b483215faf8da1a737fd180e635 @@ -2631,7 +2581,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.17.0": +"@typescript-eslint/parser@npm:5.17.0, @typescript-eslint/parser@npm:^5.10.0": version: 5.17.0 resolution: "@typescript-eslint/parser@npm:5.17.0" dependencies: @@ -2648,23 +2598,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.10.0": - version: 5.53.0 - resolution: "@typescript-eslint/parser@npm:5.53.0" - dependencies: - "@typescript-eslint/scope-manager": 5.53.0 - "@typescript-eslint/types": 5.53.0 - "@typescript-eslint/typescript-estree": 5.53.0 - debug: ^4.3.4 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 979e5d63793a9e64998b1f956ba0f00f8a2674db3a664fafce7b2433323f5248bd776af8305e2419d73a9d94c55176fee099abc5c153b4cc52e5765c725c1edd - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/scope-manager@npm:5.17.0" @@ -2675,16 +2608,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.53.0": - version: 5.53.0 - resolution: "@typescript-eslint/scope-manager@npm:5.53.0" - dependencies: - "@typescript-eslint/types": 5.53.0 - "@typescript-eslint/visitor-keys": 5.53.0 - checksum: 51f31dc01e95908611f402441f58404da80a338c0237b2b82f4a7b0b2e8868c4bfe8f7cf44b2567dd56533de609156a5d4ac54bb1f9f09c7014b99428aef2543 - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/type-utils@npm:5.17.0" @@ -2708,13 +2631,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.53.0": - version: 5.53.0 - resolution: "@typescript-eslint/types@npm:5.53.0" - checksum: b0eaf23de4ab13697d4d2095838c959a3f410c30f0d19091e5ca08e62320c3cc3c72bcb631823fb6a4fbb31db0a059e386a0801244930d0a88a6a698e5f46548 - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/typescript-estree@npm:5.17.0" @@ -2733,24 +2649,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.53.0": - version: 5.53.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.53.0" - dependencies: - "@typescript-eslint/types": 5.53.0 - "@typescript-eslint/visitor-keys": 5.53.0 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 6e119c8e4167c8495d728c5556a834545a9c064918dd5e7b79b0d836726f4f8e2a0297b0ac82bf2b71f1e5427552217d0b59d8fb1406fd79bd3bf91b75dca873 - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:5.17.0": version: 5.17.0 resolution: "@typescript-eslint/utils@npm:5.17.0" @@ -2777,16 +2675,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.53.0": - version: 5.53.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.53.0" - dependencies: - "@typescript-eslint/types": 5.53.0 - eslint-visitor-keys: ^3.3.0 - checksum: 090695883c15364c6f401e97f56b13db0f31c1114f3bd22562bd41734864d27f6a3c80de33957e9dedab2d5f94b0f4480ba3fde1d4574e74dca4593917b7b54a - languageName: node - linkType: hard - "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -3121,7 +3009,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.9.0": +"ajv@npm:8.9.0, ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": version: 8.9.0 resolution: "ajv@npm:8.9.0" dependencies: @@ -3145,18 +3033,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": - version: 8.12.0 - resolution: "ajv@npm:8.12.0" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 - languageName: node - linkType: hard - "angularx-qrcode@npm:13.0.x": version: 13.0.15 resolution: "angularx-qrcode@npm:13.0.15" @@ -3169,20 +3045,13 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.1": +"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 languageName: node linkType: hard -"ansi-colors@npm:^4.1.1": - version: 4.1.3 - resolution: "ansi-colors@npm:4.1.3" - checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -4349,20 +4218,13 @@ __metadata: languageName: node linkType: hard -"core-util-is@npm:1.0.2": +"core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab languageName: node linkType: hard -"core-util-is@npm:~1.0.0": - version: 1.0.3 - resolution: "core-util-is@npm:1.0.3" - checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 - languageName: node - linkType: hard - "cors@npm:~2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" @@ -4552,19 +4414,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - -"debug@npm:4.3.3": +"debug@npm:4, debug@npm:4.3.3, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.3 resolution: "debug@npm:4.3.3" dependencies: @@ -4585,6 +4435,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -5698,20 +5560,13 @@ __metadata: languageName: node linkType: hard -"extsprintf@npm:1.3.0": +"extsprintf@npm:1.3.0, extsprintf@npm:^1.2.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 languageName: node linkType: hard -"extsprintf@npm:^1.2.0": - version: 1.4.1 - resolution: "extsprintf@npm:1.4.1" - checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 - languageName: node - linkType: hard - "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -6181,7 +6036,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0": +"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -6195,20 +6050,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.1.1 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 - languageName: node - linkType: hard - "glob@npm:^8.0.1": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -6591,7 +6432,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.0": +"https-proxy-agent@npm:5.0.0, https-proxy-agent@npm:^5.0.0": version: 5.0.0 resolution: "https-proxy-agent@npm:5.0.0" dependencies: @@ -6611,16 +6452,6 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -6689,14 +6520,14 @@ __metadata: languageName: node linkType: hard -"ignore@npm:5.2.0": +"ignore@npm:5.2.0, ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.1.9, ignore@npm:^5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 languageName: node linkType: hard -"ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.1.9, ignore@npm:^5.2.0, ignore@npm:^5.2.1": +"ignore@npm:^5.2.1": version: 5.2.4 resolution: "ignore@npm:5.2.4" checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef @@ -6878,7 +6709,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": +"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1": version: 2.11.0 resolution: "is-core-module@npm:2.11.0" dependencies: @@ -7182,20 +7013,13 @@ __metadata: languageName: node linkType: hard -"jasmine-core@npm:3.8.x": +"jasmine-core@npm:3.8.x, jasmine-core@npm:^3.6.0": version: 3.8.0 resolution: "jasmine-core@npm:3.8.0" checksum: 6490373bd96d4d2a3c74214d307f8c37394370b1e892b9f203d4c5eda56941d53f84aabee0e65538b6921a190710618f1e73af44bd1d93e6c6e6c3f5c4bbfab6 languageName: node linkType: hard -"jasmine-core@npm:^3.6.0": - version: 3.99.1 - resolution: "jasmine-core@npm:3.99.1" - checksum: 4e4a89739d99e471b86c7ccc4c5c244a77cc6d1e17b2b0d87d81266b8415697354d8873f7e764790a10661744f73a753a6e9bcd9b3e48c66a0c9b8a092b071b7 - languageName: node - linkType: hard - "jasmine-core@npm:~2.8.0": version: 2.8.0 resolution: "jasmine-core@npm:2.8.0" @@ -7977,7 +7801,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.2.2, memfs@npm:^3.4.3": +"memfs@npm:^3.2.2": version: 3.4.13 resolution: "memfs@npm:3.4.13" dependencies: @@ -8110,7 +7934,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.0.4": +"minimatch@npm:3.0.4, minimatch@npm:^3.0.4": version: 3.0.4 resolution: "minimatch@npm:3.0.4" dependencies: @@ -8128,7 +7952,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -8293,14 +8117,14 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.2": +"ms@npm:2.1.2, ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -8834,7 +8658,7 @@ __metadata: languageName: node linkType: hard -"open@npm:8.4.0": +"open@npm:8.4.0, open@npm:^8.0.9, open@npm:^8.4.0": version: 8.4.0 resolution: "open@npm:8.4.0" dependencies: @@ -8845,17 +8669,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^8.0.9, open@npm:^8.4.0": - version: 8.4.2 - resolution: "open@npm:8.4.2" - dependencies: - define-lazy-prop: ^2.0.0 - is-docker: ^2.1.1 - is-wsl: ^2.2.0 - checksum: 6388bfff21b40cb9bd8f913f9130d107f2ed4724ea81a8fd29798ee322b361ca31fa2cdfb491a5c31e43a3996cfe9566741238c7a741ada8d7af1cb78d85cf26 - languageName: node - linkType: hard - "optionator@npm:^0.9.1": version: 0.9.1 resolution: "optionator@npm:0.9.1" @@ -9658,7 +9471,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.5": +"postcss@npm:8.4.5, postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7": version: 8.4.5 resolution: "postcss@npm:8.4.5" dependencies: @@ -9669,7 +9482,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7, postcss@npm:^8.4.19": +"postcss@npm:^8.4.19": version: 8.4.21 resolution: "postcss@npm:8.4.21" dependencies: @@ -9718,7 +9531,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:2.6.2": +"prettier@npm:2.6.2, prettier@npm:^2.5.1": version: 2.6.2 resolution: "prettier@npm:2.6.2" bin: @@ -9727,15 +9540,6 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.5.1": - version: 2.8.4 - resolution: "prettier@npm:2.8.4" - bin: - prettier: bin-prettier.js - checksum: c173064bf3df57b6d93d19aa98753b9b9dd7657212e33b41ada8e2e9f9884066bb9ca0b4005b89b3ab137efffdf8fbe0b462785aba20364798ff4303aadda57e - languageName: node - linkType: hard - "pretty-bytes@npm:^5.3.0": version: 5.6.0 resolution: "pretty-bytes@npm:5.6.0" @@ -9841,20 +9645,13 @@ __metadata: languageName: node linkType: hard -"q@npm:1.4.1": +"q@npm:1.4.1, q@npm:^1.4.1": version: 1.4.1 resolution: "q@npm:1.4.1" checksum: 22c8e1f24f416d0977e6da63f24712189c5dd789489999fc040467480e4e0ef4bd0e3126cce1b8ef72c709bbe1fcce10eba0f4991a03fc64ecb5a17e05ed8d35 languageName: node linkType: hard -"q@npm:^1.4.1": - version: 1.5.1 - resolution: "q@npm:1.5.1" - checksum: 147baa93c805bc1200ed698bdf9c72e9e42c05f96d007e33a558b5fdfd63e5ea130e99313f28efc1783e90e6bdb4e48b67a36fcc026b7b09202437ae88a1fb12 - languageName: node - linkType: hard - "qjobs@npm:^1.2.0": version: 1.2.0 resolution: "qjobs@npm:1.2.0" @@ -10042,14 +9839,14 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:0.13.9": +"regenerator-runtime@npm:0.13.9, regenerator-runtime@npm:^0.13.4": version: 0.13.9 resolution: "regenerator-runtime@npm:0.13.9" checksum: 65ed455fe5afd799e2897baf691ca21c2772e1a969d19bb0c4695757c2d96249eb74ee3553ea34a91062b2a676beedf630b4c1551cc6299afb937be1426ec55e languageName: node linkType: hard -"regenerator-runtime@npm:^0.13.11, regenerator-runtime@npm:^0.13.4": +"regenerator-runtime@npm:^0.13.11": version: 0.13.11 resolution: "regenerator-runtime@npm:0.13.11" checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 @@ -10205,7 +10002,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:1.22.0": +"resolve@npm:1.22.0, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": version: 1.22.0 resolution: "resolve@npm:1.22.0" dependencies: @@ -10218,20 +10015,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": - version: 1.22.1 - resolution: "resolve@npm:1.22.1" - dependencies: - is-core-module: ^2.9.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e - languageName: node - linkType: hard - -"resolve@patch:resolve@1.22.0#~builtin": +"resolve@patch:resolve@1.22.0#~builtin, resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": version: 1.22.0 resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" dependencies: @@ -10244,19 +10028,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": - version: 1.22.1 - resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" - dependencies: - is-core-module: ^2.9.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b - languageName: node - linkType: hard - "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -10342,7 +10113,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.5.x": +"rxjs@npm:6.5.x, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": version: 6.5.5 resolution: "rxjs@npm:6.5.5" dependencies: @@ -10351,7 +10122,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.6.7, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": +"rxjs@npm:6.6.7": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -10523,7 +10294,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.3.5": +"semver@npm:7.3.5, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": version: 7.3.5 resolution: "semver@npm:7.3.5" dependencies: @@ -10543,17 +10314,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": - version: 7.3.8 - resolution: "semver@npm:7.3.8" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 - languageName: node - linkType: hard - "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -10867,7 +10627,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:0.7.3": +"source-map@npm:0.7.3, source-map@npm:^0.7.3, source-map@npm:~0.7.2": version: 0.7.3 resolution: "source-map@npm:0.7.3" checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea @@ -10881,13 +10641,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.7.3, source-map@npm:~0.7.2": - version: 0.7.4 - resolution: "source-map@npm:0.7.4" - checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 - languageName: node - linkType: hard - "sourcemap-codec@npm:1.4.8, sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" @@ -11728,7 +11481,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.0.x": +"tslib@npm:2.0.x, tslib@npm:^2.0.0": version: 2.0.3 resolution: "tslib@npm:2.0.3" checksum: 00fcdd1f9995c9f8eb6a4a1ad03f55bc95946321b7f55434182dddac259d4e095fedf78a84f73b6e32dd3f881d9281f09cb583123d3159ed4bdac9ad7393ef8b @@ -11749,7 +11502,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": +"tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.5.0 resolution: "tslib@npm:2.5.0" checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 @@ -11844,7 +11597,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:4.5.x": +"typescript@npm:4.5.x, typescript@npm:^4.5.4": version: 4.5.5 resolution: "typescript@npm:4.5.5" bin: @@ -11854,17 +11607,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.5.4": - version: 4.9.5 - resolution: "typescript@npm:4.9.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db - languageName: node - linkType: hard - -"typescript@patch:typescript@4.5.x#~builtin": +"typescript@patch:typescript@4.5.x#~builtin, typescript@patch:typescript@^4.5.4#~builtin": version: 4.5.5 resolution: "typescript@patch:typescript@npm%3A4.5.5#~builtin::version=4.5.5&hash=bda367" bin: @@ -11874,16 +11617,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^4.5.4#~builtin": - version: 4.9.5 - resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=bda367" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 2eee5c37cad4390385db5db5a8e81470e42e8f1401b0358d7390095d6f681b410f2c4a0c496c6ff9ebd775423c7785cdace7bcdad76c7bee283df3d9718c0f20 - languageName: node - linkType: hard - "ua-parser-js@npm:^0.7.30": version: 0.7.33 resolution: "ua-parser-js@npm:0.7.33" @@ -12161,7 +11894,7 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:5.3.0": +"webpack-dev-middleware@npm:5.3.0, webpack-dev-middleware@npm:^5.3.0": version: 5.3.0 resolution: "webpack-dev-middleware@npm:5.3.0" dependencies: @@ -12176,21 +11909,6 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:^5.3.0": - version: 5.3.3 - resolution: "webpack-dev-middleware@npm:5.3.3" - dependencies: - colorette: ^2.0.10 - memfs: ^3.4.3 - mime-types: ^2.1.31 - range-parser: ^1.2.1 - schema-utils: ^4.0.0 - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - checksum: dd332cc6da61222c43d25e5a2155e23147b777ff32fdf1f1a0a8777020c072fbcef7756360ce2a13939c3f534c06b4992a4d659318c4a7fe2c0530b52a8a6621 - languageName: node - linkType: hard - "webpack-dev-server@npm:4.7.3": version: 4.7.3 resolution: "webpack-dev-server@npm:4.7.3" @@ -12413,22 +12131,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.1.0": - version: 8.12.1 - resolution: "ws@npm:8.12.1" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 97301c1c4d838fc81bd413f370f75c12aabe44527b31323b761eab3043a9ecb7e32ffd668548382c9a6a5ad3a1c3a9249608e8338e6b939f2f9540f1e21970b5 - languageName: node - linkType: hard - -"ws@npm:~8.11.0": +"ws@npm:^8.1.0, ws@npm:~8.11.0": version: 8.11.0 resolution: "ws@npm:8.11.0" peerDependencies: From 6b5e93a54e1b37bc9b1e256d4f4d36df216e6e3d Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 24 Feb 2023 15:49:10 +0100 Subject: [PATCH 171/498] Fixed minor issue in task-viewer.component.ts that broke the countdown-latch mechanism. --- .../competition-scoreboard-viewer.component.ts | 2 +- frontend/src/app/viewer/task-viewer.component.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts index 2275be4a8..bb8710d44 100644 --- a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts +++ b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts @@ -37,7 +37,7 @@ export class CompetitionScoreboardViewerComponent implements OnInit { @Input() state: Observable; /** - * Whether or not to show competition overview scores. + * Whether show competition overview scores. * Otherwise, the current task group total is shown. */ @Input() competitionOverview = true; diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index cb2f2518e..397f9526a 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -152,7 +152,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { case 'PREPARING': this.viewerState.next(ViewerState.VIEWER_SYNC); if (h != null) { - this.webSocketSubject.next({ evaluationId: s.id, type: 'ACK' } as IWsClientMessage); + this.webSocketSubject.next({ evaluationId: s.evaluationId, type: 'ACK' } as IWsClientMessage); } /* Send ACK. */ break; case 'RUNNING': @@ -190,7 +190,6 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /* Map task hint to representation used by viewer. */ this.currentTaskHint = currentTaskHint.pipe( flatMap((hint) => { - console.log(`Current Task Hint fired`); return this.timeElapsed.pipe( take(1), flatMap((timeElapsed) => { From 770310cda08ba5a29bf604fb9376544ac578b9e6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 24 Feb 2023 15:49:38 +0100 Subject: [PATCH 172/498] The ScoreboardsUpdatable is now dirty by default, to trigger initial calculation. --- .../kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index 1d125b755..b3b2e8888 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -1,7 +1,5 @@ package dev.dres.run.updatables -import dev.dres.data.model.run.AbstractInteractiveTask -import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus import dev.dres.run.score.ScoreTimePoint @@ -25,13 +23,14 @@ class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs override val phase: Phase = Phase.MAIN @Volatile - override var dirty: Boolean = false + override var dirty: Boolean = true /** Timestamp of the last update. */ private var lastUpdate: Long = System.currentTimeMillis() /** List of all [ScoreTimePoint]s tracked by this [ScoreboardsUpdatable]. */ private val _timeSeries: MutableList = LinkedList() + val timeSeries: List get() = this._timeSeries From b888ba0401fc0f4d41d03d854fa8a27c3683c8ab Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 24 Feb 2023 15:50:18 +0100 Subject: [PATCH 173/498] ScoresUpdatable now proactively informs clients about updates via WebSocket. --- .../kotlin/dev/dres/run/updatables/ScoresUpdatable.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index e8af6c8e5..c948b5429 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -1,15 +1,21 @@ package dev.dres.run.updatables +import dev.dres.api.rest.types.evaluation.websocket.ServerMessage +import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.run.RunExecutor import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus +import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.Scoreable +import dev.dres.run.score.scoreboard.Scoreboard import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.mapDistinct +import java.util.* import kotlin.collections.LinkedHashSet /** @@ -43,6 +49,7 @@ class ScoresUpdatable(private val manager: RunManager): Updatable { val removed = mutableListOf() if (this.set.isNotEmpty()) { this.manager.store.transactional(true) { + /* Update scores. */ this.set.removeIf { task -> val submissions = DbAnswerSet.filter { a -> a.task.id eq task.taskId }.mapDistinct { it.submission }.asSequence() val scores = task.scorer.scoreMap(submissions) @@ -51,6 +58,9 @@ class ScoresUpdatable(private val manager: RunManager): Updatable { true } } + + /* Inform clients about changes. */ + removed.forEach { RunExecutor.broadcastWsMessage(ServerMessage(this.manager.evaluation.id, ServerMessageType.TASK_UPDATED, it)) } } } From a633234165ea3fb0dffbcb2fd03dc6397f00cb56 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 24 Feb 2023 15:51:03 +0100 Subject: [PATCH 174/498] Changed structure of ServerMessage and removed MessageQueueUpdatable; WebSocket messages are now sent directly by whatever piece of logic triggers an update. --- .../evaluation/websocket/ServerMessage.kt | 5 +- .../run/InteractiveAsynchronousEvaluation.kt | 6 +- .../run/InteractiveSynchronousEvaluation.kt | 6 +- .../run/InteractiveAsynchronousRunManager.kt | 71 +++++++++---------- .../run/InteractiveSynchronousRunManager.kt | 32 ++++----- .../main/kotlin/dev/dres/run/RunExecutor.kt | 25 ++----- .../run/updatables/MessageQueueUpdatable.kt | 47 ------------ 7 files changed, 60 insertions(+), 132 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt index 1bbc4ecc9..904ae0219 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/websocket/ServerMessage.kt @@ -2,12 +2,13 @@ package dev.dres.api.rest.types.evaluation.websocket import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.team.TeamId /** * Message send by the DRES server via WebSocket to inform clients about the state of the run. * * @author Ralph Gasser - * @version 1.1.0 + * @version 1.2.0 */ -data class ServerMessage(val evaluationId: EvaluationId, val taskId: TaskId? = null, val type: ServerMessageType, val timestamp: Long = System.currentTimeMillis()) +data class ServerMessage(val evaluationId: EvaluationId, val type: ServerMessageType, val taskId: TaskId? = null, val timestamp: Long = System.currentTimeMillis()) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index de67504b8..cbfa3f39a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -119,6 +119,9 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe override val scoreboards: List init { + /* Load all ongoing tasks. */ + /* this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } */ + /* Prepare the evaluation scoreboards. */ val teams = this.description.teams.asSequence().map { it.teamId }.toList() val groupBoards = this.description.taskGroups.asSequence().map { group -> @@ -126,9 +129,6 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe }.toList() val aggregateScoreBoard = SumAggregateScoreBoard("sum", this, groupBoards) this.scoreboards = groupBoards.plus(aggregateScoreBoard) - - /* Load all ongoing tasks. */ - /* this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } */ } fun goTo(teamId: TeamId, index: Int) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index e8c572a8e..d303bc71c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -72,6 +72,9 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu override val scoreboards: List init { + /* Load all ongoing tasks. */ + this.evaluation.tasks.asSequence().forEach { ISTaskRun(it) } + /* Prepare the evaluation scoreboards. */ val teams = this.description.teams.asSequence().map { it.teamId }.toList() val groupBoards = this.description.taskGroups.asSequence().map { group -> @@ -79,9 +82,6 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu }.toList() val aggregateScoreBoard = SumAggregateScoreBoard("sum", this, groupBoards) this.scoreboards = groupBoards.plus(aggregateScoreBoard) - - /* Load all ongoing tasks. */ - this.evaluation.tasks.asSequence().forEach { ISTaskRun(it) } } /** diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index bd4821ec4..ea8ae24e2 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -66,9 +66,6 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) - /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ - private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) - /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this) @@ -111,7 +108,6 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Register relevant Updatables. */ this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) - this.updatables.add(this.messageQueueUpdatable) this.store.transactional(true) { this.template.teams.asSequence().forEach { @@ -140,7 +136,6 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn } } } - this.scoresUpdatable.update(this.status) } } @@ -165,7 +160,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.status = RunManagerStatus.ACTIVE /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null, ServerMessageType.COMPETITION_START)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) LOGGER.info("Run manager ${this.id} started") } @@ -192,12 +187,15 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.status = RunManagerStatus.TERMINATED /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null, ServerMessageType.COMPETITION_END)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } } + /** + * + */ override fun updateProperties(properties: RunProperties) { TODO("Not yet implemented") } @@ -286,10 +284,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue( - ServerMessage(this.id, this.evaluation.currentTaskForTeam(context.teamId)?.taskId, ServerMessageType.COMPETITION_UPDATE), - context.teamId - ) + RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE, this.evaluation.currentTaskForTeam(context.teamId)?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} set to task $idx") @@ -326,10 +321,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue( - ServerMessage(this.id, currentTaskTemplate.id, ServerMessageType.TASK_PREPARE), - context.teamId - ) + RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId)) LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") } @@ -357,7 +349,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, currentTask.taskId, ServerMessageType.TASK_END), context.teamId) + RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId)) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } @@ -533,7 +525,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED), context.teamId) + RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) return submission.answerSets().first().status() } @@ -563,8 +555,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED), context.teamId!!) - + RunExecutor.broadcastWsMessage(context.teamId!!, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) return true } @@ -606,7 +597,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn var errorCounter = 0 while (this.status != RunManagerStatus.TERMINATED) { try { - this.store.transactional { + this.stateLock.read { /* 1) Invoke all relevant [Updatable]s. */ this.invokeUpdatables() @@ -616,7 +607,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* 3) Reset error counter and yield to other threads. */ errorCounter = 0 } - Thread.sleep(10) + Thread.sleep(250) } catch (ie: InterruptedException) { LOGGER.info("Interrupted run manager thread; exiting...") return @@ -666,27 +657,29 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn private fun internalStateUpdate() = this.stateLock.read { for (team in this.evaluation.description.teams.asSequence()) { if (teamHasRunningTask(team.teamId)) { - val task = this.evaluation.currentTaskForTeam(team.teamId) - ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") - val timeLeft = max( - 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION - ) - if (timeLeft <= 0) { - this.stateLock.write { - task.end() - DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) + this.stateLock.write { + this.store.transactional { + val task = this.evaluation.currentTaskForTeam(team.teamId) ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") + val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) + if (timeLeft <= 0) { + task.end() + DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) + + /* Enqueue WS message for sending */ + RunExecutor.broadcastWsMessage(team.teamId, ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId)) + } } - - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_END), team.teamId) } } else if (teamHasPreparingTask(team.teamId)) { - val task = this.evaluation.currentTaskForTeam(team.teamId) - ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") - task.start() - DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_START), team.teamId) + this.stateLock.write { + this.store.transactional { + val task = this.evaluation.currentTaskForTeam(team.teamId) + ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") + task.start() + DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + RunExecutor.broadcastWsMessage(team.teamId, ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId)) + } + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index def6767f3..edcba6ca6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -92,9 +92,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) - /** The internal [MessageQueueUpdatable] instance used by this [InteractiveSynchronousRunManager]. */ - private val messageQueueUpdatable = MessageQueueUpdatable(RunExecutor) - /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this) @@ -117,7 +114,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Register relevant Updatables. */ this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) - this.updatables.add(this.messageQueueUpdatable) this.updatables.add(this.endTaskUpdatable) @@ -135,7 +131,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } } } - this.scoresUpdatable.update(this.status) } override fun start(context: RunActionContext) = this.stateLock.write { @@ -146,8 +141,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.start() /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null,ServerMessageType.COMPETITION_START)) - + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) /* Log and update status. */ LOGGER.info("SynchronousRunManager ${this.id} started") @@ -165,7 +159,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.status = RunManagerStatus.TERMINATED /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, null, ServerMessageType.COMPETITION_END)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } @@ -222,7 +216,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.COMPETITION_UPDATE)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} set to task $index") } else { throw IndexOutOfBoundsException("Index $index is out of bounds for the number of available tasks.") @@ -252,7 +246,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.readyLatch.reset(VIEWER_TIME_OUT) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.TASK_PREPARE)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") } @@ -269,7 +263,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.currentTask(context)?.taskId, ServerMessageType.TASK_END)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.currentTask(context)?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") } @@ -474,7 +468,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) return submission.answerSets().first().status() } @@ -503,7 +497,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, task.taskId, ServerMessageType.TASK_UPDATED), context.teamId!!) + RunExecutor.broadcastWsMessage(context.teamId!!, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) return true } @@ -533,7 +527,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /* 3) Yield to other threads. */ - Thread.sleep(50) + Thread.sleep(250) /* Reset error counter. */ errorCounter = 0 @@ -545,7 +539,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch LOGGER.error("This is the ${++errorCounter}. in a row, will terminate loop after $maxErrorCount errors") // oh shit, something went horribly, horribly wrong - if (errorCounter >= maxErrorCount) { + if (errorCounter >= this.maxErrorCount) { LOGGER.error("Reached maximum consecutive error count, terminating loop") RunExecutor.dump(this.evaluation) break //terminate loop @@ -588,7 +582,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.TASK_START)) + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_START, this.evaluation.currentTask?.taskId)) } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ @@ -602,10 +596,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.taskId, DbAuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.taskId)) } - } - /* Enqueue WS message for sending */ - this.messageQueueUpdatable.enqueue(ServerMessage(this.id, this.evaluation.currentTask?.taskId, ServerMessageType.TASK_END)) + /* Enqueue WS message for sending */ + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.evaluation.currentTask?.taskId)) + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 32063973e..d601508e0 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -200,7 +200,7 @@ object RunExecutor : Consumer { ClientMessageType.ACK -> {} ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.add(WebSocketConnection(it)) } ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.remove(WebSocketConnection(it)) } - ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, null, ServerMessageType.PING)) + ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, ServerMessageType.PING)) } this.runManagers[message.evaluationId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ } @@ -227,27 +227,15 @@ object RunExecutor : Consumer { return this.runManagers[evaluationId] } - /** - * Broadcasts a [ServerMessage] to all clients currently connected. - * - * @param message The [ServerMessage] that should be broadcast. - */ - fun broadcastWsMessage(message: ServerMessage) = this.clientLock.read { - this.connectedClients.values.forEach { - it.send(message) - } - } - /** * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager]. * - * @param evaluationId The [EvaluationId] identifying the [RunManager] for which clients should receive the message. * @param message The [ServerMessage] that should be broadcast. */ - fun broadcastWsMessage(evaluationId: EvaluationId, message: ServerMessage) = this.clientLock.read { + fun broadcastWsMessage(message: ServerMessage) = this.clientLock.read { this.runManagerLock.read { this.connectedClients.values.filter { - this.observingClients[evaluationId]?.contains(it) ?: false + this.observingClients[message.evaluationId]?.contains(it) ?: false }.forEach { it.send(message) } @@ -257,17 +245,16 @@ object RunExecutor : Consumer { /** * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager] and are member of the specified team. * - * @param evaluationId The run ID identifying the [RunManager] for which clients should receive the message. * @param teamId The [TeamId] of the relevant team * @param message The [ServerMessage] that should be broadcast. */ - fun broadcastWsMessage(evaluationId: EvaluationId, teamId: TeamId, message: ServerMessage) = this.clientLock.read { - val manager = managerForId(evaluationId) + fun broadcastWsMessage(teamId: TeamId, message: ServerMessage) = this.clientLock.read { + val manager = managerForId(message.evaluationId) if (manager != null) { val teamMembers = manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() this.runManagerLock.read { this.connectedClients.values.filter { - this.observingClients[evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.sessionId) in teamMembers + this.observingClients[message.evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.sessionId) in teamMembers }.forEach { it.send(message) } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt deleted file mode 100644 index a6bb07777..000000000 --- a/backend/src/main/kotlin/dev/dres/run/updatables/MessageQueueUpdatable.kt +++ /dev/null @@ -1,47 +0,0 @@ -package dev.dres.run.updatables - -import dev.dres.api.rest.types.evaluation.websocket.ServerMessage -import dev.dres.data.model.template.team.TeamId -import dev.dres.run.RunExecutor -import dev.dres.run.RunManager -import dev.dres.run.RunManagerStatus -import java.util.concurrent.ConcurrentLinkedQueue - -/** - * An internal queue of [ServerMessage]s that are due for sending by the [RunManager]. - * - * @author Ralph Gasser - * @version 1.1.0 - */ -class MessageQueueUpdatable(private val executor: RunExecutor) : Updatable { - - /** The [MessageQueueUpdatable] always belongs to the [Phase.FINALIZE]. */ - override val phase: Phase = Phase.FINALIZE - - /** Internal queue of all [ServerMessage] that are due for sending. */ - private val messageQueue = ConcurrentLinkedQueue>() - - /** Sends all [ServerMessage]s that are due for sending. */ - override fun update(status: RunManagerStatus) { - var message: Pair? = this.messageQueue.poll() - while (message != null) { - if (message.first == null) { - this.executor.broadcastWsMessage(message.second.evaluationId, message.second) - } else { - this.executor.broadcastWsMessage(message.second.evaluationId, message.first!!, message.second) - } - message = this.messageQueue.poll() - } - } - - /** - * Enqueues a [ServerMessage] for later sending. - * - * @param message The [ServerMessage] to enqueue. - */ - fun enqueue(message: ServerMessage) = this.messageQueue.offer(null to message) - - fun enqueue(message: ServerMessage, recipient: TeamId) = this.messageQueue.offer(recipient to message) - - override fun shouldBeUpdated(status: RunManagerStatus): Boolean = true -} \ No newline at end of file From bd86b986d45034f071c4208ebece05b24cefa3ac Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 24 Feb 2023 18:31:02 +0100 Subject: [PATCH 175/498] Introduced SubmissionTransformers --- .../IdentitySubmissionTransformer.kt | 7 +++ .../transformer/MapToSegmentTransformer.kt | 50 +++++++++++++++++++ .../transformer/SubmissionTaskMatchFilter.kt | 16 ++++++ .../run/transformer/SubmissionTransformer.kt | 10 ++++ .../SubmissionTransformerAggregator.kt | 16 ++++++ 5 files changed, 99 insertions(+) create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt new file mode 100644 index 000000000..37ab89326 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt @@ -0,0 +1,7 @@ +package dev.dres.run.transformer + +import dev.dres.api.rest.types.evaluation.ApiSubmission + +object IdentitySubmissionTransformer : SubmissionTransformer { + override fun transform(submission: ApiSubmission): ApiSubmission = submission +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt new file mode 100644 index 000000000..6025fe8fd --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt @@ -0,0 +1,50 @@ +package dev.dres.run.transformer + +import dev.dres.api.rest.types.evaluation.ApiAnswer +import dev.dres.api.rest.types.evaluation.ApiAnswerType +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.data.model.media.DbMediaItem +import dev.dres.utilities.TimeUtil +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query +import kotlinx.dnq.query.toList + +class MapToSegmentTransformer : SubmissionTransformer { + override fun transform(submission: ApiSubmission): ApiSubmission = submission.copy( + answers = submission.answers.map { apiAnswerSet -> + apiAnswerSet.copy( + answers = apiAnswerSet.answers.map { mapAnswer(it) } + ) + } + ) + + private fun mapAnswer(answer: ApiAnswer) : ApiAnswer { + + if (answer.type != ApiAnswerType.TEMPORAL) { + return answer + } + + val item = DbMediaItem.query(DbMediaItem::id eq answer.item?.mediaItemId).firstOrNull() ?: throw IllegalStateException("MediaItem with id ${answer.item?.mediaItemId} not found") + val segments = item.segments.toList() + + val startSegment = answer.start?.let { TimeUtil.timeToSegment(it, segments) } + val endSegment = answer.end?.let { TimeUtil.timeToSegment(it, segments) } + + val bounds = when{ + startSegment != null && endSegment == null -> startSegment + startSegment == null && endSegment != null -> endSegment + startSegment == null && endSegment == null -> throw IllegalStateException("Cannot map answer time to segment, no matching segment found") + startSegment == endSegment -> startSegment!! + else -> throw IllegalStateException("Cannot map answer time to segment, range does not fall within one segment") + } + + return answer.copy( + start = bounds.first, + end = bounds.second + ) + + + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt new file mode 100644 index 000000000..094ffa1d0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt @@ -0,0 +1,16 @@ +package dev.dres.run.transformer + +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.data.model.run.TaskId + +/** + * Removes all [AnswerSet]s from a [Submission] that do not match a specified [Task] + */ + +class SubmissionTaskMatchFilter(private val taskId: TaskId) : SubmissionTransformer { + override fun transform(submission: ApiSubmission): ApiSubmission = + submission.copy( + answers = submission.answers.filter { it.taskId == taskId } + ) + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt new file mode 100644 index 000000000..d25d1bac8 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt @@ -0,0 +1,10 @@ +package dev.dres.run.transformer + +import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.data.model.submissions.Submission + +interface SubmissionTransformer { + + fun transform(submission: ApiSubmission): ApiSubmission + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt new file mode 100644 index 000000000..1a613b10b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt @@ -0,0 +1,16 @@ +package dev.dres.run.transformer + +import dev.dres.api.rest.types.evaluation.ApiSubmission + +class SubmissionTransformerAggregator(private val transformers: List) : SubmissionTransformer { + + override fun transform(submission: ApiSubmission): ApiSubmission { + + var transformed = submission + for (transformer in transformers) { + transformed = transformer.transform(transformed) + } + return transformed + + } +} \ No newline at end of file From a63eb66acda8a8d816f9d12c032a82e20b5944ab Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 26 Feb 2023 12:58:01 +0100 Subject: [PATCH 176/498] Added handlers to manage teams independently of templates -regenerated openapi spec -minor cleanup --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 6 + .../admin/CreateEvaluationHandler.kt | 2 +- .../evaluation/team/CreateTeamHandler.kt | 73 +++++++ .../evaluation/team/ListAllTeamsHandler.kt | 42 ++++ .../evaluation/team/UpdateTeamHandler.kt | 79 ++++++++ .../AbstractEvaluationTemplateHandler.kt | 2 +- .../rest/handler/template/ListTeamHandler.kt | 2 +- .../UpdateEvaluationTemplateHandler.kt | 33 +-- .../rest/types/competition/team/ApiTeam.kt | 45 ++++- .../data/model/media/time/TemporalPoint.kt | 6 +- .../model/template/task/DbTaskTemplate.kt | 1 - .../dres/run/validation/judged/ItemRange.kt | 1 - doc/oas.json | 191 +++++++++++++++++- 13 files changed, 442 insertions(+), 41 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/CreateTeamHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/ListAllTeamsHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/UpdateTeamHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 41fa627f9..4c40a5fea 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -13,6 +13,9 @@ import dev.dres.api.rest.handler.evaluation.admin.* import dev.dres.api.rest.handler.evaluation.client.ClientListEvaluationsHandler import dev.dres.api.rest.handler.evaluation.client.ClientTaskInfoHandler import dev.dres.api.rest.handler.evaluation.scores.* +import dev.dres.api.rest.handler.evaluation.team.CreateTeamHandler +import dev.dres.api.rest.handler.evaluation.team.ListAllTeamsHandler +import dev.dres.api.rest.handler.evaluation.team.UpdateTeamHandler import dev.dres.api.rest.handler.evaluation.viewer.* import dev.dres.api.rest.handler.judgement.* import dev.dres.api.rest.handler.log.QueryLogHandler @@ -163,6 +166,9 @@ object RestApi { ListSubmissionsHandler(store), ListPastTaskHandler(store), EvaluationOverviewHandler(store), + ListAllTeamsHandler(store), + CreateTeamHandler(store), + UpdateTeamHandler(store), // Judgement DequeueJudgementHandler(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index aa549230d..ab3be49bb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -66,7 +66,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs } /* Prepare run manager. */ - val evaluationId = this.store.transactional { tx -> + val evaluationId = this.store.transactional { val template = DbEvaluationTemplate.query(DbEvaluationTemplate::id eq message.templateId).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID ${message.templateId} not found.'", ctx) /* ensure that only one synchronous run of a competition is happening at any given time */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/CreateTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/CreateTeamHandler.kt new file mode 100644 index 000000000..42076c3f5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/CreateTeamHandler.kt @@ -0,0 +1,73 @@ +package dev.dres.api.rest.handler.evaluation.team + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.competition.team.ApiTeam +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.team.DbTeam +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.addAll +import kotlinx.dnq.query.containsIn +import kotlinx.dnq.query.query + +class CreateTeamHandler(private val store: TransientEntityStore) : PostRestHandler, AccessManagedRestHandler { + + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + override val apiVersion = "v2" + + override val route = "template/team" + + @OpenApi( + summary = "Creates a new team.", + path = "/api/v2/template/team", + operationId = OpenApiOperation.AUTO_GENERATE, + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(ApiTeam::class)]), + tags = ["Template", "Team"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): SuccessStatus { + + val createRequest = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + return this.store.transactional(readonly = false) { + + val team = DbTeam.new {//id is set automatically on create + this.name = createRequest.name ?: throw ErrorStatusException(404, "Team name must be specified.", ctx) + this.color = createRequest.color ?: throw ErrorStatusException(404, "Team color must be specified.", ctx) + } + + /* Process logo data. */ + val logoData = createRequest.logoStream() + if (logoData != null) { + team.logo = logoData + } + + team.users.addAll(DbUser.query(DbUser::id.containsIn(*createRequest.users.map { it.id }.toTypedArray()))) + + return@transactional SuccessStatus("Team created") + + } + + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/ListAllTeamsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/ListAllTeamsHandler.kt new file mode 100644 index 000000000..6c0702aaa --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/ListAllTeamsHandler.kt @@ -0,0 +1,42 @@ +package dev.dres.api.rest.handler.evaluation.team + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.types.competition.team.ApiTeam +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.template.team.DbTeam +import io.javalin.http.Context +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.asSequence + +class ListAllTeamsHandler(private val store: TransientEntityStore) : GetRestHandler>, AccessManagedRestHandler { + + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + override val apiVersion = "v2" + + override val route = "template/team/list" + + @OpenApi( + summary = "Lists all the teams across all evaluations.", + path = "/api/v2/template/team/list", + operationId = OpenApiOperation.AUTO_GENERATE, + tags = ["Template", "Team"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List = this.store.transactional(true) { + DbTeam.all().asSequence().map { it.toApi() }.toList() + } + + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/UpdateTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/UpdateTeamHandler.kt new file mode 100644 index 000000000..f6f974f31 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/team/UpdateTeamHandler.kt @@ -0,0 +1,79 @@ +package dev.dres.api.rest.handler.evaluation.team + +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.PatchRestHandler +import dev.dres.api.rest.types.competition.team.ApiTeam +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.template.team.DbTeam +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import io.javalin.http.bodyAsClass +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import kotlinx.dnq.query.FilteringContext.eq + +class UpdateTeamHandler(private val store: TransientEntityStore) : PatchRestHandler, AccessManagedRestHandler { + + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + override val apiVersion = "v2" + + override val route = "template/team/{teamId}" + + @OpenApi( + summary = "Creates a new team.", + path = "/api/v2/template/team/{teamId}", + operationId = OpenApiOperation.AUTO_GENERATE, + methods = [HttpMethod.POST], + requestBody = OpenApiRequestBody([OpenApiContent(ApiTeam::class)]), + pathParams = [OpenApiParam("teamId", String::class, "The team ID.", required = true, allowEmptyValue = false)], + tags = ["Template", "Team"], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPatch(ctx: Context): SuccessStatus { + + val teamId = ctx.pathParam("teamId") + + val apiTeam = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) + } + + return this.store.transactional(readonly = false) { + val team = DbTeam.filter { it.id eq teamId }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team $teamId.", ctx) + + apiTeam.name?.let { + team.name = it + } + + apiTeam.color?.let { + team.color = it + } + + apiTeam.logoStream()?.let { + team.logo = it + } + + if(apiTeam.users.isNotEmpty()) { + team.users.clear() + team.users.addAll(DbUser.query(DbUser::id.containsIn(*apiTeam.users.map { it.id }.toTypedArray()))) + } + + SuccessStatus("Team $teamId updated") + + } + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt index db5c80b66..185593860 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt @@ -27,7 +27,7 @@ abstract class AbstractEvaluationTemplateHandler(protected val store: TransientE /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN) - /** All [AbstractCollectionHandler]s are part of the v1 API. */ + /** All [AbstractCollectionHandler]s are part of the v2 API. */ override val apiVersion = "v2" /** Convenience method to extract [DbEvaluationTemplate]'s ID from [Context]. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index 843920228..b089dc3e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -22,7 +22,7 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateH override val route: String = "template/{templateId}/team/list" @OpenApi( - summary = "Lists all the teams of a specific competition.", + summary = "Lists all the teams of a specific evaluation template.", path = "/api/v2/template/{templateId}/team/list", operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [OpenApiParam("templateId", String::class, "The evaluation template ID.", required = true)], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index d4a516dd4..4bac0fc06 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.handler.template import com.github.kittinunf.fuel.util.decodeBase64 import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.competition.ApiEvaluationTemplate +import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -176,7 +177,7 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C t.color = team.color ?: throw ErrorStatusException(404, "Team colour must be specified.", ctx) /* Process logo data. */ - val logoData = team.logoData?.let { submitted -> ByteArrayInputStream(this.normalizeLogo(submitted)) } + val logoData = team.logoStream() if (logoData != null) { t.logo = logoData } @@ -216,36 +217,6 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C return SuccessStatus("Competition with ID ${apiValue.id} was updated successfully.") } - /** - * Tries to normalize the logo data to a PNG image of a given size. - * - * @param submittedData The submitted data as base 64 encoded string. - * @return [ByteArray] representation of the normalized log. - */ - private fun normalizeLogo(submittedData: String): ByteArray? { - /* Try to read image. */ - val image: BufferedImage = submittedData.drop(submittedData.indexOf(',') + 1).decodeBase64().let { - try { - ImageIO.read(ByteArrayInputStream(it)) - } catch (e: Exception) { - null - } - } ?: return null - - /* Scale image to a maximum of 500x500 pixels. */ - val scaled: Image = if (image.width > image.height) { - image.getScaledInstance(200, -1, Image.SCALE_DEFAULT) - } else { - image.getScaledInstance(-1, 200, Image.SCALE_DEFAULT) - } - val outputImage = BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_ARGB) - outputImage.graphics.drawImage(scaled, 0, 0, null); - - /* Write image as PNG. */ - val out = ByteArrayOutputStream() - ImageIO.write(outputImage, "png", out) - return out.toByteArray() - } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt index 03e92cf45..5a384119b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeam.kt @@ -2,10 +2,16 @@ package dev.dres.api.rest.types.competition.team import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonProperty +import com.github.kittinunf.fuel.util.decodeBase64 import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.Team import dev.dres.data.model.template.team.TeamId +import java.awt.Image +import java.awt.image.BufferedImage +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import javax.imageio.ImageIO /** * A RESTful API representation of a [DbTeam] @@ -22,6 +28,43 @@ data class ApiTeam( ) : Team { override val teamId: TeamId @JsonIgnore(true) - get() =this.id ?: "" + get() = this.id ?: "" + + + fun logoStream() : ByteArrayInputStream? = this.logoData?.let { submitted -> ByteArrayInputStream(normalizeLogo(submitted)) } + companion object { + + /** + * Tries to normalize the logo data to a PNG image of a given size. + * + * @param submittedData The submitted data as base 64 encoded string. + * @return [ByteArray] representation of the normalized log. + */ + fun normalizeLogo(submittedData: String): ByteArray? { + /* Try to read image. */ + val image: BufferedImage = submittedData.drop(submittedData.indexOf(',') + 1).decodeBase64().let { + try { + ImageIO.read(ByteArrayInputStream(it)) + } catch (e: Exception) { + null + } + } ?: return null + + /* Scale image to a maximum of 500x500 pixels. */ + val scaled: Image = if (image.width > image.height) { + image.getScaledInstance(500, -1, Image.SCALE_DEFAULT) + } else { + image.getScaledInstance(-1, 500, Image.SCALE_DEFAULT) + } + val outputImage = BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_ARGB) + outputImage.graphics.drawImage(scaled, 0, 0, null); + + /* Write image as PNG. */ + val out = ByteArrayOutputStream() + ImageIO.write(outputImage, "png", out) + return out.toByteArray() + } + + } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt index e12fc1e09..3f9a6def8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/time/TemporalPoint.kt @@ -4,7 +4,7 @@ import dev.dres.data.model.media.DbMediaItem import java.lang.IllegalArgumentException /** - * Notion of a [TemporalPoint] within a [DbMediaItem] that exhibits temporal development (e.g. [VideoItem]). + * Notion of a [TemporalPoint] within a [DbMediaItem] that exhibits temporal development. * * @version 2.2.0 * @author Luca Rossetto & Ralph Gasser @@ -52,7 +52,7 @@ sealed class TemporalPoint { * Transforms a time code of the form HH:MM:SS:FF or HH:MM:SS.mmm to milliseconds * @return time in milliseconds or null if the input is not a valid time code */ - fun timeCodeToMilliseconds(timecode: String, fps: Float = 24.0f): Long? = parseFrameTimecode(timecode, fps) ?: parseMsTimecode(timecode, fps) + fun timeCodeToMilliseconds(timecode: String, fps: Float = 24.0f): Long? = parseFrameTimecode(timecode, fps) ?: parseMsTimecode(timecode) private fun parseFrameTimecode(timecode: String, fps: Float = 24.0f): Long? { @@ -67,7 +67,7 @@ sealed class TemporalPoint { } - private fun parseMsTimecode(timecode: String, fps: Float = 24.0f): Long? { + private fun parseMsTimecode(timecode: String): Long? { val matches = timecodeMsRegex.matchEntire(timecode) ?: return null diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 616faae4a..f372fb464 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -9,7 +9,6 @@ import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.interfaces.SubmissionFilterFactory -import dev.dres.data.model.template.interfaces.TaskScorerFactory import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 57871eae8..4d31c2b20 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -19,7 +19,6 @@ data class ItemRange(val element: String, val start: Long, val end: Long){ AnswerType.ITEM, AnswerType.TEMPORAL -> answer.item!!.mediaItemId AnswerType.TEXT -> answer.text!! - else -> throw IllegalStateException("Submission contains neither item nor text.") }, answer.start ?: 0, answer.end ?: 0) override fun equals(other: Any?): Boolean { diff --git a/doc/oas.json b/doc/oas.json index f25bf98dd..81ff92da8 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4035,6 +4035,195 @@ "security" : [ ] } }, + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", + "parameters" : [ { + "name" : "teamId", + "in" : "path", + "description" : "The team ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/template/{templateId}" : { "delete" : { "tags" : [ "Template" ], @@ -4295,7 +4484,7 @@ "/api/v2/template/{templateId}/team/list" : { "get" : { "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific competition.", + "summary" : "Lists all the teams of a specific evaluation template.", "operationId" : "getApiV2TemplateByTemplateIdTeamList", "parameters" : [ { "name" : "templateId", From 2001e2e1cd9e361f2b9059ff9152148d75d95d91 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Feb 2023 15:09:22 +0100 Subject: [PATCH 177/498] Removed unnecessary interfaces --- .../interfaces/SubmissionFilterFactory.kt | 19 ------------------- .../model/template/task/DbTaskTemplate.kt | 4 ---- 2 files changed, 23 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt b/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt deleted file mode 100644 index 6a70c88e0..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/template/interfaces/SubmissionFilterFactory.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.dres.data.model.template.interfaces - -import dev.dres.run.filter.SubmissionFilter - -/** - * A factory for [SubmissionFilter]s. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -interface SubmissionFilterFactory { - /** - * Generates and returns a [SubmissionFilter]. Depending on the implementation, the returned instance - * is a new instance or can re-used. - * - * @return [SubmissionFilter] - */ - fun newFilter(): SubmissionFilter -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 616faae4a..9f76753b7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -8,13 +8,9 @@ import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection -import dev.dres.data.model.template.interfaces.SubmissionFilterFactory -import dev.dres.data.model.template.interfaces.TaskScorerFactory import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence From 546960949a5e2df2a1817ae6c2e80f318a0f494a Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Feb 2023 15:09:56 +0100 Subject: [PATCH 178/498] Removed unnecessary interfaces #2. --- .../kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index f372fb464..9f76753b7 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -8,12 +8,9 @@ import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection -import dev.dres.data.model.template.interfaces.SubmissionFilterFactory import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence From b310daea56d338dae7e54432e4d717dbab54b9a3 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 27 Feb 2023 08:39:53 +0100 Subject: [PATCH 179/498] Re-instated automatic prolongation of tasks after submission. --- .../model/template/task/options/Defaults.kt | 11 ++++++ .../model/template/task/options/Parameters.kt | 18 +++++++++ .../run/InteractiveSynchronousRunManager.kt | 38 +++++++++++-------- 3 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/template/task/options/Parameters.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt new file mode 100644 index 000000000..b7e20157f --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt @@ -0,0 +1,11 @@ +package dev.dres.data.model.template.task.options + +object Defaults { + /** Default value for [Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM]. Defaults to the rule being applied during the final 5 seconds. */ + const val PROLONG_ON_SUBMISSION_LIMIT_DEFAULT = 5 + + /** Default value for [Parameters.PROLONG_ON_SUBMISSION_BY_PARAM]. Defaults to a prolongation of 5 seconds. */ + const val PROLONG_ON_SUBMISSION_BY_DEFAULT = 5 + + const val PROLONG_ON_SUBMISSION_CORRECT_DEFAULT = false +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Parameters.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Parameters.kt new file mode 100644 index 000000000..6d4dfda27 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Parameters.kt @@ -0,0 +1,18 @@ +package dev.dres.data.model.template.task.options + +object Parameters { + /** + * Key for [DbTaskOption.PROLONG_ON_SUBMISSION]; [Int] value expected. + * + * The parameter determines how many milliseconds before the end of a task the option should be applied. + * If a task has a total duration of 500s, then a value of 5s means that submissions arriving between 495s and 500s + * may trigger prolongation of the task. + */ + const val PROLONG_ON_SUBMISSION_LIMIT_PARAM = "PROLONG_ON_SUBMISSION_LIMIT" + + /** Key for [DbTaskOption.PROLONG_ON_SUBMISSION]; [Int] value expected. Determines by how many seconds a task should be prolonged. */ + const val PROLONG_ON_SUBMISSION_BY_PARAM = "PROLONG_ON_SUBMISSION_BY" + + /** Key for [DbTaskOption.PROLONG_ON_SUBMISSION]; [Boolean] value expected. If true, task will only be prolonged for correct submissions. */ + const val PROLONG_ON_SUBMISSION_CORRECT_PARAM = "PROLONG_ON_SUBMISSION_CORRECT" +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index edcba6ca6..c20f8b657 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -14,6 +14,8 @@ import dev.dres.data.model.submissions.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.options.DbTaskOption +import dev.dres.data.model.template.task.options.Defaults +import dev.dres.data.model.template.task.options.Parameters import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent @@ -605,27 +607,31 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Applies the [SimpleOption.PROLONG_ON_SUBMISSION] [Option]. + * Applies the [DbTaskOption.PROLONG_ON_SUBMISSION]. * * @param context [RunActionContext] used for invocation. - * @param sub The [DbSubmission] to apply the [Option] for. + * @param sub The [Submission] to apply the [DbTaskOption] for. */ private fun prolongOnSubmit(context: RunActionContext, sub: Submission) { - /* require(option.option == SimpleOption.PROLONG_ON_SUBMISSION) { "Cannot process ${option.option} in prolongOnSubmit()." } - val limit = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM) - ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT - val prolongBy = option.getAsInt(SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_PARAM) - ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_BY_DEFAULT - val correctOnly = option.getAsBool(SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM) - ?: SimpleOptionParameters.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT - if (correctOnly && sub.status != VerdictStatus.CORRECT) { - return + val option = this.evaluation.currentTask?.template?.taskGroup?.type?.options?.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }?.any() + if (option == true) { + val limit = this.evaluation.currentTask?.template?.taskGroup?.type?.configurations?.filter { + it.key eq Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM + }?.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT + val prolongBy = this.evaluation.currentTask?.template?.taskGroup?.type?.configurations?.filter { + it.key eq Parameters.PROLONG_ON_SUBMISSION_BY_PARAM + }?.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_BY_DEFAULT + val correctOnly = this.evaluation.currentTask?.template?.taskGroup?.type?.configurations?.filter { + it.key eq Parameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM + }?.firstOrNull()?.value?.toBooleanStrictOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT + if (correctOnly && sub.answerSets().all { it.status() != VerdictStatus.CORRECT }) { + return + } + val timeLeft = Math.floorDiv(this.timeLeft(context), 1000) + if (timeLeft in 0 until limit) { + this.adjustDuration(context, prolongBy) + } } - val timeLeft = Math.floorDiv(this.timeLeft(context), 1000) - if (timeLeft in 0 until limit) { - this.adjustDuration(context, prolongBy) - } */ - TODO("Fetch information from database and prolong.") } /** From 7c5d0ebbd3646aa8e55f9e8a619760856fea533a Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 27 Feb 2023 08:49:13 +0100 Subject: [PATCH 180/498] Fixed linking of task to team for interactive, asynchronous evaluations. --- .../kotlin/dev/dres/data/model/run/DbTask.kt | 2 +- .../run/InteractiveAsynchronousEvaluation.kt | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index 5486cc642..2e0cc0c83 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -10,7 +10,7 @@ import dev.dres.data.model.template.team.DbTeam import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.sortedBy + /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index cbfa3f39a..f8979d5ab 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -120,7 +120,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe init { /* Load all ongoing tasks. */ - /* this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } */ + this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } /* Prepare the evaluation scoreboards. */ val teams = this.description.teams.asSequence().map { it.teamId }.toList() @@ -176,7 +176,12 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe /** * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousEvaluation]. */ - inner class IATaskRun internal constructor(task: DbTask, val teamId: TeamId) : AbstractInteractiveTask(task) { + inner class IATaskRun internal constructor(task: DbTask) : AbstractInteractiveTask(task) { + + init { + /* Sanity check. */ + require(task.team != null) { "The task of an interactive asynchronous evaluation must be assigned to a single team." } + } /** * Constructor used to generate an [IATaskRun] from a [DbTaskTemplate]. @@ -189,7 +194,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe this.template = template this.team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId }.singleOrNull() ?: throw IllegalArgumentException("Cannot start a new task run for team with ID ${teamId}. Team is not registered for competition.") - }, teamId) + }) /** The [InteractiveAsynchronousEvaluation] this [IATaskRun] belongs to.*/ override val competition: InteractiveAsynchronousEvaluation @@ -197,7 +202,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe /** The position of this [IATaskRun] within the [InteractiveAsynchronousEvaluation]. */ override val position: Int - get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.teamId]?.indexOf(this) ?: -1 + get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.task.team!!.id]?.indexOf(this) ?: -1 /** The [SubmissionFilter] instance used by this [IATaskRun]. */ override val filter: SubmissionFilter @@ -209,10 +214,10 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe override var duration: Long = this.template.duration /** The [List] of [TeamId]s working on this [IATaskRun]. */ - override val teams: List = listOf(this.teamId) + override val teams: List = listOf(this.task.team!!.id) init { - this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.teamId) { _, v -> + this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.task.team!!.id) { _, v -> val list = v ?: LinkedList() check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } list.add(this) From f68109e96fbe62f0dea37a80590466ee291c8ef1 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 3 Mar 2023 14:13:31 +0100 Subject: [PATCH 181/498] Validation mechanism now works on the level of AnswerSet rather than Submission --- .../data/model/run/AbstractInteractiveTask.kt | 18 ++--- .../model/run/AbstractNonInteractiveTask.kt | 18 ++--- .../dev/dres/data/model/run/AbstractTask.kt | 6 +- .../run/InteractiveAsynchronousEvaluation.kt | 8 ++- .../data/model/template/task/DbTaskType.kt | 4 -- .../run/InteractiveAsynchronousRunManager.kt | 13 ++-- .../run/InteractiveSynchronousRunManager.kt | 13 ++-- .../dev/dres/run/audit/DbAuditLogger.kt | 8 +-- .../validation/ChainedAnswerSetValidator.kt | 45 ++++++++++++ .../validation/ChainedSubmissionValidator.kt | 47 ------------- .../MediaItemsAnswerSetValidator.kt | 31 +++++++++ .../MediaItemsSubmissionValidator.kt | 39 ----------- .../TemporalContainmentAnswerSetValidator.kt | 55 +++++++++++++++ .../TemporalContainmentSubmissionValidator.kt | 66 ------------------ .../TemporalOverlapAnswerSetValidator.kt | 65 +++++++++++++++++ .../TemporalOverlapSubmissionValidator.kt | 69 ------------------- ...Validator.kt => TextAnswerSetValidator.kt} | 56 +++++++-------- .../interfaces/AnswerSetValidator.kt | 25 +++++++ .../interfaces/JudgementValidator.kt | 4 +- .../interfaces/SubmissionValidator.kt | 26 ------- .../judged/BasicJudgementValidator.kt | 51 +++++++------- 21 files changed, 321 insertions(+), 346 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt rename backend/src/main/kotlin/dev/dres/run/validation/{TextValidator.kt => TextAnswerSetValidator.kt} (50%) create mode 100644 backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index 426f64cae..da89e5526 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -6,11 +6,11 @@ import dev.dres.data.model.template.team.TeamAggregatorImpl import dev.dres.data.model.template.team.TeamGroupId import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.DbSubmission -import dev.dres.run.validation.MediaItemsSubmissionValidator -import dev.dres.run.validation.TemporalOverlapSubmissionValidator -import dev.dres.run.validation.TextValidator +import dev.dres.run.validation.MediaItemsAnswerSetValidator +import dev.dres.run.validation.TemporalOverlapAnswerSetValidator +import dev.dres.run.validation.TextAnswerSetValidator import dev.dres.run.validation.TransientMediaSegment -import dev.dres.run.validation.interfaces.SubmissionValidator +import dev.dres.run.validation.interfaces.AnswerSetValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.BasicVoteValidator import dev.dres.run.validation.judged.ItemRange @@ -33,17 +33,17 @@ abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { this.competition.description.teamGroups.asSequence().associate { it.id to it.newAggregator() } } - /** The [SubmissionValidator] used to validate [DbSubmission]s. */ - final override val validator: SubmissionValidator + /** The [AnswerSetValidator] used to validate [DbSubmission]s. */ + final override val validator: AnswerSetValidator init { this.validator = when (val targetOption = this.template.taskGroup.type.target) { - DbTargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.filter { it.item ne null }.mapDistinct { it.item }.toSet()) + DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(this.template.targets.filter { it.item ne null }.mapDistinct { it.item }.toSet()) DbTargetOption.MEDIA_SEGMENT -> { val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() - TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) + TemporalOverlapAnswerSetValidator(TransientMediaSegment(target.item!!, target.range!!)) } - DbTargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + DbTargetOption.TEXT -> TextAnswerSetValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) DbTargetOption.JUDGEMENT -> { val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index dd47d0158..b278c8227 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,11 +1,11 @@ package dev.dres.data.model.run import dev.dres.data.model.template.task.options.DbTargetOption -import dev.dres.run.validation.MediaItemsSubmissionValidator -import dev.dres.run.validation.TemporalOverlapSubmissionValidator -import dev.dres.run.validation.TextValidator +import dev.dres.run.validation.MediaItemsAnswerSetValidator +import dev.dres.run.validation.TemporalOverlapAnswerSetValidator +import dev.dres.run.validation.TextAnswerSetValidator import dev.dres.run.validation.TransientMediaSegment -import dev.dres.run.validation.interfaces.SubmissionValidator +import dev.dres.run.validation.interfaces.AnswerSetValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.ItemRange import kotlinx.dnq.query.* @@ -18,17 +18,17 @@ import kotlinx.dnq.query.* * @version 2.0.0 */ abstract class AbstractNonInteractiveTask(task: DbTask): AbstractTask(task) { - /** The [SubmissionValidator] used by this [AbstractNonInteractiveTask]. */ - final override val validator: SubmissionValidator + /** The [AnswerSetValidator] used by this [AbstractNonInteractiveTask]. */ + final override val validator: AnswerSetValidator init { this.validator = when (val targetOption = this.template.taskGroup.type.target) { - DbTargetOption.MEDIA_ITEM -> MediaItemsSubmissionValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) + DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) DbTargetOption.MEDIA_SEGMENT -> { val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() - TemporalOverlapSubmissionValidator(TransientMediaSegment(target.item!!, target.range!!)) + TemporalOverlapAnswerSetValidator(TransientMediaSegment(target.item!!, target.range!!)) } - DbTargetOption.TEXT -> TextValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) + DbTargetOption.TEXT -> TextAnswerSetValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) DbTargetOption.JUDGEMENT -> { val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 701b60c15..0cc608d0d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -8,7 +8,7 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.validation.interfaces.SubmissionValidator +import dev.dres.run.validation.interfaces.AnswerSetValidator import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.mapDistinct @@ -90,8 +90,8 @@ abstract class AbstractTask(task: DbTask): TaskRun { /** The [SubmissionFilter] used to filter [DbSubmission]s. */ abstract val filter: SubmissionFilter - /** The [SubmissionValidator] used to validate [DbSubmission]s. */ - abstract val validator: SubmissionValidator + /** The [AnswerSetValidator] used to validate [DbSubmission]s. */ + abstract val validator: AnswerSetValidator /** * Prepares this [TaskRun] for later starting. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index f8979d5ab..3bc240cc1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -17,8 +17,6 @@ import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer -import dev.dres.run.score.scorer.TaskScorer -import dev.dres.run.validation.interfaces.SubmissionValidator import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -213,8 +211,12 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration + val teamId = this.task.team!!.id + /** The [List] of [TeamId]s working on this [IATaskRun]. */ - override val teams: List = listOf(this.task.team!!.id) + override val teams: List = listOf(teamId) + + init { this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.task.team!!.id) { _, v -> diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index 7f2ec4f90..79ccf95d6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -4,10 +4,6 @@ import dev.dres.api.rest.types.competition.tasks.ApiTaskType import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.options.* import dev.dres.data.model.template.task.options.DbConfiguredOption -import dev.dres.run.filter.AllSubmissionFilter -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.filter.SubmissionFilterAggregator -import dev.dres.run.validation.interfaces.SubmissionValidator import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.* diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index ea8ae24e2..f467def7f 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -131,8 +131,8 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(task) - if (sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.any()) { - task.validator.validate(sub) + sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.asSequence().forEach { + task.validator.validate(it) } } } @@ -513,10 +513,15 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn check(task.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } /* Check if ApiSubmission meets formal requirements. */ - task.filter.acceptOrThrow(submission as Submission) + task.filter.acceptOrThrow(submission) + + //TODO apply submission transformer /* At this point, the submission is considered valid and is persisted */ - task.validator.validate(submission as Submission) + /* Validator is applied to each answer set */ + submission.answerSets().forEach { + task.validator.validate(it) + } /* Persist the submission. */ submission.toNewDb() diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index c20f8b657..e48baf335 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -128,8 +128,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(task) - if (sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.any()) { - task.validator.validate(sub) + sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.asSequence().forEach { + task.validator.validate(it) } } } @@ -453,10 +453,15 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch check(this.template.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { "Team ${submission.teamId} does not exists for evaluation run ${this.name}. This is a programmer's error!" } /* Check if ApiSubmission meets formal requirements. */ - task.filter.acceptOrThrow(submission as Submission) + task.filter.acceptOrThrow(submission) + + //TODO apply submission transformer /* At this point, the submission is considered valid and is persisted */ - task.validator.validate(submission as Submission) + /* Validator is applied to each answer set */ + submission.answerSets().forEach { + task.validator.validate(it) + } /* Persist the submission. */ submission.toNewDb() diff --git a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt index 85b03595d..d5d8c9ed0 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt @@ -11,10 +11,8 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator -import dev.dres.run.validation.interfaces.SubmissionValidator -import jetbrains.exodus.database.TransientEntityStore +import dev.dres.run.validation.interfaces.AnswerSetValidator import kotlinx.dnq.query.first -import org.joda.time.DateTime /** * Audit logging instance of DRES. Requires one-time initialisation @@ -144,9 +142,9 @@ object DbAuditLogger { * Logs the validation of a [DbSubmission] to DRES. * * @param submission The [DbSubmission] the submission that was validated - * @param validator The [SubmissionValidator] instance. + * @param validator The [AnswerSetValidator] instance. */ - fun validateSubmission(submission: Submission, validator: SubmissionValidator) { + fun validateSubmission(submission: Submission, validator: AnswerSetValidator) { DbAuditLogEntry.new { this.type = DbAuditLogType.SUBMISSION_VALIDATION this.source = DbAuditLogSource.INTERNAL diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt new file mode 100644 index 000000000..07bb181db --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt @@ -0,0 +1,45 @@ +package dev.dres.run.validation + +import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.run.validation.interfaces.AnswerSetValidator + +/** + * A [AnswerSetValidator] class that allows for the combination of two [AnswerSetValidator]s. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 + */ +class ChainedAnswerSetValidator(private val firstValidator: AnswerSetValidator, private val continueStates: Set, private val secondValidator: AnswerSetValidator) : AnswerSetValidator { + + companion object{ + fun of(continueStates: Set, vararg validator: AnswerSetValidator) : ChainedAnswerSetValidator { + return when { + validator.size < 2 -> throw IllegalArgumentException("Chain needs at least two validators") + validator.size == 2 -> ChainedAnswerSetValidator(validator[0], continueStates, validator[1]) + else -> ChainedAnswerSetValidator(validator[0], continueStates, of(continueStates, *validator.sliceArray(1..(validator.size)))) + } + } + } + + override val deferring: Boolean + get() = this.secondValidator.deferring + + init { + require(!this.firstValidator.deferring) {"First validator cannot be a deferring validation."} + } + + /** + * Validates a [DbSubmission] based on two [AnswerSetValidator]s. + * + * @param submission The [DbSubmission] to validate. + */ + override fun validate(answerSet: AnswerSet) { + this.firstValidator.validate(answerSet) + if (this.continueStates.contains(answerSet.status())) { + this.secondValidator.validate(answerSet) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt deleted file mode 100644 index 80fda550f..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedSubmissionValidator.kt +++ /dev/null @@ -1,47 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.asSequence - -/** - * A [SubmissionValidator] class that allows for the combination of two [SubmissionValidator]s. - * - * @author Luca Rossetto - * @author Ralph Gasser - * @version 1.1.0 - */ -class ChainedSubmissionValidator(private val firstValidator: SubmissionValidator, private val continueStates: Set, private val secondValidator: SubmissionValidator) : SubmissionValidator { - - companion object{ - fun of(continueStates: Set, vararg validator: SubmissionValidator) : ChainedSubmissionValidator { - return when { - validator.size < 2 -> throw IllegalArgumentException("Chain needs at least two validators") - validator.size == 2 -> ChainedSubmissionValidator(validator[0], continueStates, validator[1]) - else -> ChainedSubmissionValidator(validator[0], continueStates, of(continueStates, *validator.sliceArray(1..(validator.size)))) - } - } - } - - override val deferring: Boolean - get() = this.secondValidator.deferring - - init { - require(!this.firstValidator.deferring) {"First validator cannot be a deferring validation."} - } - - /** - * Validates a [DbSubmission] based on two [SubmissionValidator]s. - * - * @param submission The [DbSubmission] to validate. - */ - override fun validate(submission: Submission) { - this.firstValidator.validate(submission) - if (submission.answerSets().any { this.continueStates.contains(it.status()) }) { - this.secondValidator.validate(submission) - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt new file mode 100644 index 000000000..805bb0909 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt @@ -0,0 +1,31 @@ +package dev.dres.run.validation + +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.run.validation.interfaces.AnswerSetValidator + +/** + * A [AnswerSetValidator] that checks if the items specified by a [Submission] match the items in the provided set. + * + * @author Luca Rossetto + * @version 1.0.1 + */ +class MediaItemsAnswerSetValidator(items: Set) : AnswerSetValidator { + + /** This type of [AnswerSetValidator] can be executed directly.*/ + override val deferring = false + + private val itemIds = items.map { it.mediaItemId } + + override fun validate(answerSet: AnswerSet) { + + if (answerSet.answers().any { it.item == null || it.item!!.mediaItemId !in this.itemIds }) { + answerSet.status(VerdictStatus.WRONG) + } else { + answerSet.status(VerdictStatus.CORRECT) + } + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt deleted file mode 100644 index c6a59816d..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsSubmissionValidator.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.asSequence - -/** - * A [SubmissionValidator] that checks if the items specified by a [DbSubmission] match the items in the provided set. - * - * @author Luca Rossetto - * @version 1.0.1 - */ -class MediaItemsSubmissionValidator(private val items : Set) : SubmissionValidator { - - /** This type of [SubmissionValidator] can be executed directly.*/ - override val deferring = false - - private val itemIds = items.map { it.mediaItemId } - - /** - * Performs the validation. - * - * @param submission The [DbSubmission] to validate. - */ - override fun validate(submission: Submission) { - submission.answerSets().forEach { answerSet -> - if (answerSet.answers().any { it.item == null || it.item!!.mediaItemId !in this.itemIds} ) { - answerSet.status(VerdictStatus.WRONG) - } else { - answerSet.status(VerdictStatus.CORRECT) - } - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt new file mode 100644 index 000000000..492e82dc7 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt @@ -0,0 +1,55 @@ +package dev.dres.run.validation + +import dev.dres.data.model.submissions.* +import dev.dres.run.validation.interfaces.AnswerSetValidator + +/** + * A [AnswerSetValidator] class that checks, if a submission is correct based on the target segment and the + * complete containment of the [DbSubmission] within the provided [MediaSegmentTaskDescription]. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 + */ +class TemporalContainmentAnswerSetValidator(private val targetSegment: TransientMediaSegment) : AnswerSetValidator { + + override val deferring: Boolean + get() = false + + override fun validate(answerSet: AnswerSet) { + + answerSet.answers().forEach { answer -> + + /* Perform sanity checks. */ + if (answer.type() != AnswerType.TEMPORAL) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } + + val start = answer.start + val end = answer.end + val item = answer.item + if (item == null || start == null || end == null || start > end) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + + } + + /* Perform item validation. */ + if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } + + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if (outer.first <= start && outer.second >= end) { + answerSet.status(VerdictStatus.CORRECT) + } else { + answerSet.status(VerdictStatus.WRONG) + } + + } + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt deleted file mode 100644 index d11bb52ec..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentSubmissionValidator.kt +++ /dev/null @@ -1,66 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.submissions.* -import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.asSequence - -/** - * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the - * complete containment of the [DbSubmission] within the provided [MediaSegmentTaskDescription]. - * - * @author Luca Rossetto - * @author Ralph Gasser - * @version 1.1.0 - */ -class TemporalContainmentSubmissionValidator(private val targetSegment: TransientMediaSegment) : SubmissionValidator { - - override val deferring: Boolean - get() = false - - /** - * Validates a [DbSubmission] based on the target segment and the temporal overlap of the - * [DbSubmission] with the [DbTaskTemplate]. - * - * @param submission The [DbSubmission] to validate. - */ - override fun validate(submission: Submission) { - submission.answerSets().forEach outer@{ answerSet -> - - answerSet.answers().forEach inner@{ answer -> - - /* Perform sanity checks. */ - if (answer.type() != AnswerType.TEMPORAL) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - val start = answer.start - val end = answer.end - val item = answer.item - if (item == null || start == null || end == null || start > end) { - answerSet.status(VerdictStatus.WRONG) - return@inner - - } - - /* Perform item validation. */ - if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if (outer.first <= start && outer.second >= end) { - answerSet.status(VerdictStatus.CORRECT) - } else { - answerSet.status(VerdictStatus.WRONG) - } - - } - - - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt new file mode 100644 index 000000000..557e48f55 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt @@ -0,0 +1,65 @@ +package dev.dres.run.validation + +import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.submissions.* +import dev.dres.run.validation.interfaces.AnswerSetValidator + +/** */ +typealias TransientMediaSegment = Pair + +/** + * A [AnswerSetValidator] class that checks, if a submission is correct based on the target segment and the + * temporal overlap of the [DbSubmission] with the provided [TransientMediaSegment]. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.1.0 + */ +class TemporalOverlapAnswerSetValidator(private val targetSegment: TransientMediaSegment) : AnswerSetValidator { + + override val deferring: Boolean = false + + /** + * Validates a [DbSubmission] based on the target segment and the temporal overlap of the + * [DbSubmission] with the [DbTaskTemplate]. + * + * @param submission The [DbSubmission] to validate. + */ + override fun validate(answerSet: AnswerSet) { + + answerSet.answers().forEach { answer -> + + /* Perform sanity checks. */ + if (answer.type() != AnswerType.TEMPORAL) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } + + val start = answer.start + val end = answer.end + val item = answer.item + if (item == null || start == null || end == null || start > end) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } + + /* Perform item validation. */ + if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } + + /* Perform temporal validation. */ + val outer = this.targetSegment.second.toMilliseconds() + if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { + answerSet.status(VerdictStatus.CORRECT) + } else { + answerSet.status(VerdictStatus.WRONG) + } + } + + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt deleted file mode 100644 index f108c38ba..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapSubmissionValidator.kt +++ /dev/null @@ -1,69 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.* -import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.asSequence - -/** */ -typealias TransientMediaSegment = Pair - -/** - * A [SubmissionValidator] class that checks, if a submission is correct based on the target segment and the - * temporal overlap of the [DbSubmission] with the provided [TransientMediaSegment]. - * - * @author Luca Rossetto - * @author Ralph Gasser - * @version 1.1.0 - */ -class TemporalOverlapSubmissionValidator(private val targetSegment: TransientMediaSegment) : SubmissionValidator { - - override val deferring: Boolean = false - - /** - * Validates a [DbSubmission] based on the target segment and the temporal overlap of the - * [DbSubmission] with the [DbTaskTemplate]. - * - * @param submission The [DbSubmission] to validate. - */ - override fun validate(submission: Submission) { - submission.answerSets().forEach outer@{ answerSet -> - - answerSet.answers().forEach inner@{ answer -> - - /* Perform sanity checks. */ - if (answer.type() != AnswerType.TEMPORAL) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - val start = answer.start - val end = answer.end - val item = answer.item - if (item == null || start == null || end == null || start > end) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - /* Perform item validation. */ - if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { - answerSet.status(VerdictStatus.CORRECT) - } else { - answerSet.status(VerdictStatus.WRONG) - } - } - - - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt similarity index 50% rename from backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt rename to backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt index 7e62a3a08..e235b8187 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt @@ -1,17 +1,16 @@ package dev.dres.run.validation import dev.dres.data.model.submissions.* -import dev.dres.run.validation.interfaces.SubmissionValidator -import kotlinx.dnq.query.asSequence +import dev.dres.run.validation.interfaces.AnswerSetValidator /** - * A [SubmissionValidator] class that valiadates textual submissions based on [Regex]. + * A [AnswerSetValidator] class that valiadates textual submissions based on [Regex]. * * @author Luca Rossetto * @author Ralph Gasser * @version 1.1.0 */ -class TextValidator(targets: List) : SubmissionValidator { +class TextAnswerSetValidator(targets: List) : AnswerSetValidator { override val deferring = false @@ -30,48 +29,41 @@ class TextValidator(targets: List) : SubmissionValidator { it.startsWith("\\") && it.endsWith("\\") -> { Regex(it.substring(1, it.length - 1), RegexOption.CANON_EQ) } + it.startsWith("\\") && it.endsWith("\\i") -> { Regex(it.substring(1, it.length - 2), setOf(RegexOption.CANON_EQ, RegexOption.IGNORE_CASE)) } + else -> { Regex(it, setOf(RegexOption.CANON_EQ, RegexOption.LITERAL)) } } } - /** - * Validates a textual [DbSubmission] based on the provided [Regex]. - * - * @param submission The [DbSubmission] to validate. - */ - override fun validate(submission: Submission) { - submission.answerSets().forEach outer@{ answerSet -> - - answerSet.answers().forEach inner@{ - answer -> + override fun validate(answerSet: AnswerSet) { - /* Perform sanity checks. */ - if (answer.type() != AnswerType.TEXT) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - /* Perform text validation. */ - val text = answer.text - if (text == null) { - answerSet.status(VerdictStatus.WRONG) - return@inner - } - - if (regex.any { it matches text }) { - answerSet.status(VerdictStatus.CORRECT) - } else { - answerSet.status(VerdictStatus.WRONG) - } + answerSet.answers().forEach { answer -> + /* Perform sanity checks. */ + if (answer.type() != AnswerType.TEXT) { + answerSet.status(VerdictStatus.WRONG) + return@forEach } + /* Perform text validation. */ + val text = answer.text + if (text == null) { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } + if (regex.any { it matches text }) { + answerSet.status(VerdictStatus.CORRECT) + } else { + answerSet.status(VerdictStatus.WRONG) + return@forEach + } } + } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt new file mode 100644 index 000000000..2ff0c5f13 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt @@ -0,0 +1,25 @@ +package dev.dres.run.validation.interfaces + +import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.submissions.DbSubmission + +/** + * A validator class that checks, if a [DbSubmission] is correct. + * + * @author Luca Rossetto & Ralph Gasser + * @version 1.1 + */ +interface AnswerSetValidator { + /** + * Validates the [AnswerSet] and updates its [VerdictStatus]. + * + * @param answerSet The [AnswerSet] to validate. + */ + fun validate(answerSet: AnswerSet) + + /** + * Indicates whether this [AnswerSetValidator] needs to defer the validation to some later point in time + * or changes the status of a submission immediately + */ + val deferring: Boolean +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index 9e8428b62..7aa0533e6 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -3,9 +3,9 @@ package dev.dres.run.validation.interfaces import dev.dres.data.model.submissions.* /** - * A [SubmissionValidator] that bases validation on human (manual) verdicts. + * A [AnswerSetValidator] that bases validation on human (manual) verdicts. * - * This kind of [SubmissionValidator] is inherently asynchronous. + * This kind of [AnswerSetValidator] is inherently asynchronous. * * @author Luca Rossetto & Ralph Gasser * @version 1.1.0 diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt deleted file mode 100644 index 618de774a..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/SubmissionValidator.kt +++ /dev/null @@ -1,26 +0,0 @@ -package dev.dres.run.validation.interfaces - -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission - -/** - * A validator class that checks, if a [DbSubmission] is correct. - * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1 - */ -interface SubmissionValidator { - /** - * Validates the [DbSubmission] and updates its [DbVerdictStatus]. - * - * @param submission The [DbSubmission] to validate. - */ - fun validate(submission: Submission) - - /** - * Indicates whether this [SubmissionValidator] needs to defer the validation to some later point in time - * or changes the status of a submission immediately - */ - val deferring: Boolean -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 947d68e77..7396f27f3 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -4,7 +4,7 @@ import dev.dres.data.model.submissions.* import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator -import dev.dres.run.validation.interfaces.SubmissionValidator +import dev.dres.run.validation.interfaces.AnswerSetValidator import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -20,7 +20,10 @@ import kotlin.concurrent.write * @author Ralph Gasser * @version 1.1.0 */ -open class BasicJudgementValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList()): SubmissionValidator, JudgementValidator { +open class BasicJudgementValidator( + knownCorrectRanges: Collection = emptyList(), + knownWrongRanges: Collection = emptyList() +) : AnswerSetValidator, JudgementValidator { companion object { private val counter = AtomicInteger() @@ -90,30 +93,30 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * * @param submission The [DbSubmission] to validate. */ - override fun validate(submission: Submission) = this.updateLock.read { - for (verdict in submission.answerSets()) { - //only validate submissions which are not already validated - if (verdict.status() != VerdictStatus.INDETERMINATE){ - continue - } + override fun validate(answerSet: AnswerSet) = this.updateLock.read { + + //only validate submissions which are not already validated + if (answerSet.status() != VerdictStatus.INDETERMINATE) { + return@read + } - //check cache first - val itemRange = ItemRange(submission.answerSets().first().answers().first()) //TODO reason about semantics - val cachedStatus = this.cache[itemRange] - if (cachedStatus != null) { - verdict.status(cachedStatus) - } else if (itemRange !in queuedItemRanges.keys) { - updateLock.write { - this.queue.offer(verdict) - verdict.status(VerdictStatus.INDETERMINATE) - this.queuedItemRanges[itemRange] = mutableListOf(verdict) - } - } else { - this.updateLock.write { - this.queuedItemRanges[itemRange]!!.add(verdict) - } + //check cache first + val itemRange = ItemRange(answerSet.answers().first()) //TODO reason about semantics + val cachedStatus = this.cache[itemRange] + if (cachedStatus != null) { + answerSet.status(cachedStatus) + } else if (itemRange !in queuedItemRanges.keys) { + updateLock.write { + this.queue.offer(answerSet) + answerSet.status(VerdictStatus.INDETERMINATE) + this.queuedItemRanges[itemRange] = mutableListOf(answerSet) + } + } else { + this.updateLock.write { + this.queuedItemRanges[itemRange]!!.add(answerSet) } } + } /** @@ -150,7 +153,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e /** * */ - fun processSubmission(token: String, status: VerdictStatus) : AnswerSet = this.updateLock.write { + fun processSubmission(token: String, status: VerdictStatus): AnswerSet = this.updateLock.write { val verdict = this.waiting[token] ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged val itemRange = ItemRange(verdict.answers().first()) //TODO reason about semantics From 8cd42d85beed5b7aaa333bd7a432cb5589a507ff Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 3 Mar 2023 14:46:27 +0100 Subject: [PATCH 182/498] Started using SubmissionTransformers --- .../dev/dres/data/model/run/AbstractTask.kt | 8 +++++-- .../run/InteractiveAsynchronousEvaluation.kt | 17 ++++++++++++++ .../run/InteractiveSynchronousEvaluation.kt | 22 +++++++++++++++++-- .../model/run/NonInteractiveEvaluation.kt | 16 ++++++++++++++ .../run/InteractiveAsynchronousRunManager.kt | 9 ++++---- .../run/InteractiveSynchronousRunManager.kt | 9 ++++---- 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 0cc608d0d..9023e54f5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -8,6 +8,7 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.transformer.SubmissionTransformer import dev.dres.run.validation.interfaces.AnswerSetValidator import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter @@ -87,12 +88,15 @@ abstract class AbstractTask(task: DbTask): TaskRun { } protected set - /** The [SubmissionFilter] used to filter [DbSubmission]s. */ + /** The [SubmissionFilter] used to filter [Submission]s. */ abstract val filter: SubmissionFilter - /** The [AnswerSetValidator] used to validate [DbSubmission]s. */ + abstract val transformer: SubmissionTransformer + + /** The [AnswerSetValidator] used to validate [Submission]s. */ abstract val validator: AnswerSetValidator + /** * Prepares this [TaskRun] for later starting. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 3bc240cc1..e4282c852 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -7,6 +7,7 @@ import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter @@ -17,6 +18,10 @@ import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer +import dev.dres.run.transformer.MapToSegmentTransformer +import dev.dres.run.transformer.SubmissionTaskMatchFilter +import dev.dres.run.transformer.SubmissionTransformer +import dev.dres.run.transformer.SubmissionTransformerAggregator import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -205,6 +210,8 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe /** The [SubmissionFilter] instance used by this [IATaskRun]. */ override val filter: SubmissionFilter + override val transformer: SubmissionTransformer + /** The [CachingTaskScorer] instance used by this [InteractiveAsynchronousEvaluation].*/ override val scorer: CachingTaskScorer @@ -239,6 +246,16 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe ) } + this.transformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { + SubmissionTransformerAggregator( + listOf( + SubmissionTaskMatchFilter(this.taskId), + MapToSegmentTransformer() + ) + ) + } else { + SubmissionTaskMatchFilter(this.taskId) + } /* Initialize task scorer. */ this.scorer = CachingTaskScorer( diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index d303bc71c..05dca1429 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -8,6 +8,7 @@ import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter @@ -19,6 +20,10 @@ import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer +import dev.dres.run.transformer.MapToSegmentTransformer +import dev.dres.run.transformer.SubmissionTaskMatchFilter +import dev.dres.run.transformer.SubmissionTransformer +import dev.dres.run.transformer.SubmissionTransformerAggregator import kotlinx.dnq.query.* import java.lang.IndexOutOfBoundsException import java.util.LinkedList @@ -26,7 +31,7 @@ import java.util.LinkedList /** * Represents a concrete, interactive and synchronous [Run] of a [DbEvaluationTemplate]. * - * [InteractiveSynchronousEvaluation]s can be started, ended and they can be used to create new [TaskRun]s and access the current [TaskRun]. + * [InteractiveSynchronousEvaluation]s can be started, ended, and they can be used to create new [TaskRun]s and access the current [TaskRun]. * * @author Ralph Gasser * @param 2.0.0 @@ -142,6 +147,8 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ override val filter: SubmissionFilter + override val transformer: SubmissionTransformer + /** The [CachingTaskScorer] instance used by this [ISTaskRun]. */ override val scorer: CachingTaskScorer @@ -155,7 +162,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { "Cannot create a new task. Another task is currently running." } - (this@InteractiveSynchronousEvaluation.tasks as MutableList).add(this) + (this@InteractiveSynchronousEvaluation.tasks).add(this) /* Initialize submission filter. */ if (this.template.taskGroup.type.submission.isEmpty) { @@ -170,6 +177,17 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu ) } + this.transformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { + SubmissionTransformerAggregator( + listOf( + SubmissionTaskMatchFilter(this.taskId), + MapToSegmentTransformer() + ) + ) + } else { + SubmissionTaskMatchFilter(this.taskId) + } + /* Initialize task scorer. */ this.scorer = CachingTaskScorer( when(val scoreOption = this.template.taskGroup.type.score) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 68330ebba..3ad39ce6d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.SubmissionFilter import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard @@ -11,6 +12,10 @@ import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.TaskScorer +import dev.dres.run.transformer.MapToSegmentTransformer +import dev.dres.run.transformer.SubmissionTaskMatchFilter +import dev.dres.run.transformer.SubmissionTransformer +import dev.dres.run.transformer.SubmissionTransformerAggregator import kotlinx.dnq.query.* @@ -62,6 +67,17 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev /** The [CachingTaskScorer] instance used by this [NITaskRun]. */ override val scorer: CachingTaskScorer = TODO("Will we have the same scorers for non-interactive tasks.") + override val transformer: SubmissionTransformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { + SubmissionTransformerAggregator( + listOf( + SubmissionTaskMatchFilter(this.taskId), + MapToSegmentTransformer() + ) + ) + } else { + SubmissionTaskMatchFilter(this.taskId) + } + /** The [SubmissionFilter] instance used by this [NITaskRun]. */ override val filter: SubmissionFilter = TODO("Can there be submission filters for non-interactive tasks?") diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index f467def7f..2c500a8da 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -515,23 +515,24 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Check if ApiSubmission meets formal requirements. */ task.filter.acceptOrThrow(submission) - //TODO apply submission transformer + /* Apply transformations to submissions */ + val transformedSubmission = task.transformer.transform(submission) /* At this point, the submission is considered valid and is persisted */ /* Validator is applied to each answer set */ - submission.answerSets().forEach { + transformedSubmission.answerSets().forEach { task.validator.validate(it) } /* Persist the submission. */ - submission.toNewDb() + transformedSubmission.toNewDb() /* Enqueue submission for post-processing. */ this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) - return submission.answerSets().first().status() + return transformedSubmission.answerSets().first().status() } /** diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index e48baf335..732bee832 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -455,20 +455,21 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Check if ApiSubmission meets formal requirements. */ task.filter.acceptOrThrow(submission) - //TODO apply submission transformer + /* Apply transformations to submissions */ + val transformedSubmission = task.transformer.transform(submission) /* At this point, the submission is considered valid and is persisted */ /* Validator is applied to each answer set */ - submission.answerSets().forEach { + transformedSubmission.answerSets().forEach { task.validator.validate(it) } /* Persist the submission. */ - submission.toNewDb() + transformedSubmission.toNewDb() /** Checks for the presence of the [DbTaskOption.PROLONG_ON_SUBMISSION] and applies it. */ if (task.template.taskGroup.type.options.contains(DbTaskOption.PROLONG_ON_SUBMISSION)) { - this.prolongOnSubmit(context, submission as Submission) + this.prolongOnSubmit(context, transformedSubmission) } /* Enqueue submission for post-processing. */ From 21a43711536f9d7911e5611b766c1e47ac225dfd Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 8 Mar 2023 11:52:41 +0100 Subject: [PATCH 183/498] Bugfix. --- .../dres/data/model/run/InteractiveSynchronousEvaluation.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 05dca1429..dbea62177 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -176,8 +176,8 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu }.toList() ) } - - this.transformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { + val mapToSegment = this.template.taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) + this.transformer = if (mapToSegment) { SubmissionTransformerAggregator( listOf( SubmissionTaskMatchFilter(this.taskId), From 21a4efd1fd097564a12f26d34c89bb78e3f15ae6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 8 Mar 2023 11:59:59 +0100 Subject: [PATCH 184/498] It should now once again be possible to update run properties of the InteractiveSynchronousRunManager. --- .../handler/evaluation/admin/AdjustPropertiesHandler.kt | 2 +- .../kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index 0f9cb4cd0..c6fddb64e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -51,7 +51,7 @@ class AdjustPropertiesHandler(store: TransientEntityStore): AbstractEvaluationAd val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional { evaluationManager.updateProperties(properties) - SuccessStatus("Properties updated") + SuccessStatus("Properties updated successfully!") } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 732bee832..318675acc 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -167,10 +167,14 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** + * Updates the [RunProperties] for the [InteractiveSynchronousEvaluation] backing this [InteractiveSynchronousRunManager]. * + * @param properties The set of new [RunProperties] */ override fun updateProperties(properties: RunProperties) { - TODO("Not yet implemented") + this.evaluation.allowRepeatedTasks = properties.allowRepeatedTasks + this.evaluation.limitSubmissionPreviews = properties.limitSubmissionPreviews + this.evaluation.participantCanView = properties.participantCanView } /** From b898ff18e787845cfd18e5ad28cd316f106657e2 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 8 Mar 2023 12:00:24 +0100 Subject: [PATCH 185/498] Updated dependencies. --- frontend/package-lock.json | 552 ++++++++++++++++++++----------------- frontend/package.json | 2 +- frontend/yarn.lock | 512 +++++++++++++++++----------------- 3 files changed, 555 insertions(+), 511 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9ef0094fd..99ec5f01c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -30,7 +30,7 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "13.2.x", + "@angular-devkit/build-angular": "13.3.x", "@angular-eslint/builder": "13.2.0", "@angular-eslint/eslint-plugin": "13.2.0", "@angular-eslint/eslint-plugin-template": "13.2.0", @@ -71,13 +71,13 @@ } }, "node_modules/@ampproject/remapping": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.1.1.tgz", - "integrity": "sha512-YVAcA4DKLOj296CF5SrQ8cYiMRiUGc2sqFpLxsDGWE34suHqhGP/5yMsDHKsrh8hs8I5TiRVXNwKPWQpX3iGjw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -117,15 +117,15 @@ "dev": true }, "node_modules/@angular-devkit/build-angular": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.2.6.tgz", - "integrity": "sha512-Y2ojy6xbZ0kwScppcutLHBP8eW0qNOjburTISSBU/L5l/9FOeZ1E7yAreKuVu/qibZiLbSJfAhk+SLwhRHFSSQ==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.10.tgz", + "integrity": "sha512-eKMjwr7XHlh/lYqYvdIrHZfVPM8fCxP4isKzCDiOjsJ+4fl+Ycq8RvjtOLntBN6A1U8h93rZNE+VOTEGCJcGig==", "dev": true, "dependencies": { - "@ampproject/remapping": "1.1.1", - "@angular-devkit/architect": "0.1302.6", - "@angular-devkit/build-webpack": "0.1302.6", - "@angular-devkit/core": "13.2.6", + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1303.10", + "@angular-devkit/build-webpack": "0.1303.10", + "@angular-devkit/core": "13.3.10", "@babel/core": "7.16.12", "@babel/generator": "7.16.8", "@babel/helper-annotate-as-pure": "7.16.7", @@ -136,9 +136,9 @@ "@babel/runtime": "7.16.7", "@babel/template": "7.16.7", "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.2.6", + "@ngtools/webpack": "13.3.10", "ansi-colors": "4.1.1", - "babel-loader": "8.2.3", + "babel-loader": "8.2.5", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.9.1", "cacache": "15.3.0", @@ -156,9 +156,9 @@ "less": "4.1.2", "less-loader": "10.2.0", "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.0", + "loader-utils": "3.2.1", "mini-css-extract-plugin": "2.5.3", - "minimatch": "3.0.4", + "minimatch": "3.0.5", "open": "8.4.0", "ora": "5.4.1", "parse5-html-rewriting-stream": "6.0.1", @@ -170,18 +170,18 @@ "regenerator-runtime": "0.13.9", "resolve-url-loader": "5.0.0", "rxjs": "6.6.7", - "sass": "1.49.0", + "sass": "1.49.9", "sass-loader": "12.4.0", "semver": "7.3.5", "source-map-loader": "3.0.1", "source-map-support": "0.5.21", "stylus": "0.56.0", "stylus-loader": "6.2.0", - "terser": "5.11.0", + "terser": "5.14.2", "text-table": "0.2.0", "tree-kill": "1.2.2", "tslib": "2.3.1", - "webpack": "5.67.0", + "webpack": "5.70.0", "webpack-dev-middleware": "5.3.0", "webpack-dev-server": "4.7.3", "webpack-merge": "5.8.0", @@ -196,14 +196,14 @@ "esbuild": "0.14.22" }, "peerDependencies": { - "@angular/compiler-cli": "^13.0.0", - "@angular/localize": "^13.0.0", - "@angular/service-worker": "^13.0.0", + "@angular/compiler-cli": "^13.0.0 || ^13.3.0-rc.0", + "@angular/localize": "^13.0.0 || ^13.3.0-rc.0", + "@angular/service-worker": "^13.0.0 || ^13.3.0-rc.0", "karma": "^6.3.0", "ng-packagr": "^13.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.4.3 <4.6" + "typescript": ">=4.4.3 <4.7" }, "peerDependenciesMeta": { "@angular/localize": { @@ -226,6 +226,48 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { + "version": "0.1303.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.10.tgz", + "integrity": "sha512-A8blp98GY9Lg5RdgZ4M/nT0DhWsFv+YikC6+ebJsUTn/L06GcQNhyZKGCwB69S4Xe/kcYJgKpI2nAYnOLDpJlQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.3.10", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.10.tgz", + "integrity": "sha512-NSjyrccES+RkVL/wt1t1jNmJOV9z5H4/DtVjJQbAt/tDE5Mo0ygnhELd/QiUmjVfzfSkhr75LqQD8NtURoGBwQ==", + "dev": true, + "dependencies": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -251,12 +293,12 @@ "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1302.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1302.6.tgz", - "integrity": "sha512-TYEh2n9tPe932rEIgdiSpojOqtDppW2jzb/empVqCkLF7WUZsXKvTanttZC34L6R2VD6SAGWhb6JDg75ghUVYA==", + "version": "0.1303.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.10.tgz", + "integrity": "sha512-nthTy6r4YQQTrvOpOS3dqjoJog/SL9Hn5YLytqnEp2r2he5evYsKV2Jtqi49/VgW1ohrGzw+bI0c3dUGKweyfw==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1302.6", + "@angular-devkit/architect": "0.1303.10", "rxjs": "6.6.7" }, "engines": { @@ -269,6 +311,48 @@ "webpack-dev-server": "^4.0.0" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { + "version": "0.1303.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.10.tgz", + "integrity": "sha512-A8blp98GY9Lg5RdgZ4M/nT0DhWsFv+YikC6+ebJsUTn/L06GcQNhyZKGCwB69S4Xe/kcYJgKpI2nAYnOLDpJlQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.3.10", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.10.tgz", + "integrity": "sha512-NSjyrccES+RkVL/wt1t1jNmJOV9z5H4/DtVjJQbAt/tDE5Mo0ygnhELd/QiUmjVfzfSkhr75LqQD8NtURoGBwQ==", + "dev": true, + "dependencies": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -618,19 +702,6 @@ "typescript": ">=4.4.2 <4.6" } }, - "node_modules/@angular/compiler-cli/node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@angular/compiler-cli/node_modules/@babel/core": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", @@ -1224,9 +1295,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.0.tgz", - "integrity": "sha512-eD/JQ21IG2i1FraJnTMbUarAUkA7G988ofehG5MDCRXaUU91rEBJuCeSoou2Sk1y4RbLYXzqEg1QLwEmRU4qcQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -1235,8 +1306,8 @@ "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -1473,9 +1544,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz", - "integrity": "sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -2221,12 +2292,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", - "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-module-transforms": "^7.21.2", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-simple-access": "^7.20.2" }, @@ -2680,19 +2751,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.0.tgz", - "integrity": "sha512-Xdt2P1H4LKTO8ApPfnO1KmzYMFpp7D/EinoXzLYN/cHcBNrVCAkAtGUcXnHXrl/VGktureU6fkQrHSBE2URfoA==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.0", - "@babel/types": "^7.21.0", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2730,9 +2801,9 @@ } }, "node_modules/@babel/types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.0.tgz", - "integrity": "sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -2809,9 +2880,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", + "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2910,6 +2981,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", + "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -3058,9 +3138,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.6.tgz", - "integrity": "sha512-N8SvRV91+/57TcAfbghc0k0tKCukw/7KqbDaLPAQTGFekJ4xMGT3elMzOyBXTH3Hvp5HL8/hiBt2tG04qiMf+w==", + "version": "13.3.10", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.10.tgz", + "integrity": "sha512-QQ8ELLqW5PtvrEAMt99D0s982NW303k8UpZrQoQ9ODgnSVDMdbbzFPNTXq/20dg+nbp8nlOakUrkjB47TBwTNA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.15.0 || >=16.10.0", @@ -3069,7 +3149,7 @@ }, "peerDependencies": { "@angular/compiler-cli": "^13.0.0", - "typescript": ">=4.4.3 <4.6", + "typescript": ">=4.4.3 <4.7", "webpack": "^5.30.0" } }, @@ -3210,21 +3290,21 @@ } }, "node_modules/@nrwl/cli": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-15.7.2.tgz", - "integrity": "sha512-A/72FAW1e0ku8YB/PaCqN9BpVvciO83MS5F5bvX5PA8xCNqe1+iXp/5T2ASnN2lB9zR3fQJmvR7mHKTKQlqQQQ==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-15.8.5.tgz", + "integrity": "sha512-voy16nUO1MxRMRqCpLlhDB9U4KyPfGHZABXtfMEIQk+W3alncatFMMSVvMQZmi8HXwubM8LxWSOnPtTtOCKBrQ==", "dev": true, "dependencies": { - "nx": "15.7.2" + "nx": "15.8.5" } }, "node_modules/@nrwl/cli/node_modules/@nrwl/tao": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-15.7.2.tgz", - "integrity": "sha512-srx9heMIt/QIyuqfewiVYbRpFcD/2pHkTkrEEUKspPd25kzAL2adcAITQKVCHI7/VS2sPdDR67pVsGQPZFBMRQ==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-15.8.5.tgz", + "integrity": "sha512-pb/hUprOOv2vgvbevGz9hiu8LLOtK7KKuBe4JLSXrFxfHEQjMFsK/2aymnts0ZQrA83QlIG2Mr0tuSKj6/iWvg==", "dev": true, "dependencies": { - "nx": "15.7.2" + "nx": "15.8.5" }, "bin": { "tao": "index.js" @@ -3403,27 +3483,15 @@ "node": ">=10" } }, - "node_modules/@nrwl/cli/node_modules/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@nrwl/cli/node_modules/nx": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/nx/-/nx-15.7.2.tgz", - "integrity": "sha512-VRb+CZCji3G4ikdMAGoh6TeU9Q6n5atRwqRSFhUX63er8zhlMvWHLskPMZC4q/81edo/E7RhbmEVUD5MB0JoeA==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/nx/-/nx-15.8.5.tgz", + "integrity": "sha512-1c6Y3rPSzzlqQVJPo33Ej0HY/3t9ykeaPs074HpYxXH0+GU1BSIv/9EfXKQGvmBzjs5yAx6asGIv+H3QDrFt3A==", "dev": true, "hasInstallScript": true, "dependencies": { - "@nrwl/cli": "15.7.2", - "@nrwl/tao": "15.7.2", + "@nrwl/cli": "15.8.5", + "@nrwl/tao": "15.8.5", "@parcel/watcher": "2.0.4", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "^3.0.0-rc.18", @@ -3462,15 +3530,15 @@ "nx": "bin/nx.js" }, "optionalDependencies": { - "@nrwl/nx-darwin-arm64": "15.7.2", - "@nrwl/nx-darwin-x64": "15.7.2", - "@nrwl/nx-linux-arm-gnueabihf": "15.7.2", - "@nrwl/nx-linux-arm64-gnu": "15.7.2", - "@nrwl/nx-linux-arm64-musl": "15.7.2", - "@nrwl/nx-linux-x64-gnu": "15.7.2", - "@nrwl/nx-linux-x64-musl": "15.7.2", - "@nrwl/nx-win32-arm64-msvc": "15.7.2", - "@nrwl/nx-win32-x64-msvc": "15.7.2" + "@nrwl/nx-darwin-arm64": "15.8.5", + "@nrwl/nx-darwin-x64": "15.8.5", + "@nrwl/nx-linux-arm-gnueabihf": "15.8.5", + "@nrwl/nx-linux-arm64-gnu": "15.8.5", + "@nrwl/nx-linux-arm64-musl": "15.8.5", + "@nrwl/nx-linux-x64-gnu": "15.8.5", + "@nrwl/nx-linux-x64-musl": "15.8.5", + "@nrwl/nx-win32-arm64-msvc": "15.8.5", + "@nrwl/nx-win32-x64-msvc": "15.8.5" }, "peerDependencies": { "@swc-node/register": "^1.4.2", @@ -3581,9 +3649,9 @@ "dev": true }, "node_modules/@nrwl/nx-darwin-arm64": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.7.2.tgz", - "integrity": "sha512-F82exjuqkAkElSTxEcTFeLMhHpbGiccfTQh2VjXMS+ONldxM+Kd7atJjtUG8wKNXfg0lxxjjAdnzLy3iBuN/HQ==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.5.tgz", + "integrity": "sha512-/8yXbh1J3k85MAW/A6cDiPeEnbt66SE9BPnM2IPlGoZrXakQvAXEn+gsjQlvnP3q2EaEyv7e5+GA+8d+p6mT5A==", "cpu": [ "arm64" ], @@ -3597,9 +3665,9 @@ } }, "node_modules/@nrwl/nx-darwin-x64": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.7.2.tgz", - "integrity": "sha512-MNT7Bxz6yhoVLCgGpR0NtVkj20SER1CbrCaY7tmsKVNY9iA/EOZhz9qa3LeA1KZ4lw8Gpi2vD42mOngn7Mwr7w==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.5.tgz", + "integrity": "sha512-zEVoi0d+YChLrQMypoGFwu73t3YiD8UkXSozMtUEa2mg/se4e7jh+15tB6Te+Oq5aL0JKwQpr027GE4YtAmpLw==", "cpu": [ "x64" ], @@ -3613,9 +3681,9 @@ } }, "node_modules/@nrwl/nx-linux-arm-gnueabihf": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.7.2.tgz", - "integrity": "sha512-QGyPkYnZ9LnUnuCzrP50bwsMJ9n6r8K2bNC1sQQwioijY+4MHNL+bMTOGWc8+lYBP7Ju3gpTqozGV3FQVkaM2w==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.5.tgz", + "integrity": "sha512-4C5wN0C7gQD6/lC9+UKUsB6mbHvowKhlaO529GIgtzrCLmfEh/LJ/CybnnKGpFEB/8Y5GpCa2uTWyA1XcPDzUw==", "cpu": [ "arm" ], @@ -3629,9 +3697,9 @@ } }, "node_modules/@nrwl/nx-linux-arm64-gnu": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.7.2.tgz", - "integrity": "sha512-HqufFVIvuunfChEFGkIhsLhhQjWLTFcCH2aQBSNesHpm6AhFVRGyokNu+PT6NNobr+BTrqJMocBqNQR1uvSyRQ==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.5.tgz", + "integrity": "sha512-SMQ+oIsyK75JiKeSMprmb8VXce6MLdfcS5GWWOihpoDWfUC9FoQHAu4X1OtxHbVTmJfoEOInJKAhPxXAi5obdw==", "cpu": [ "arm64" ], @@ -3645,9 +3713,9 @@ } }, "node_modules/@nrwl/nx-linux-arm64-musl": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.7.2.tgz", - "integrity": "sha512-9B8q6I/OVyQuYe+Yg2wNyxza/CsbvejIUsrK3QGGWUwHlkklqOSmUOHyTrcyMHUSped6CWPyKdIywngYOQzltQ==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.5.tgz", + "integrity": "sha512-GVENjltZ17aJ6KOCibdBtLXQcGY5lpBqKolB9+rIYJvTWuV1k/uHOkYJDG7Vl70Rj46rC8K0Jp6BCpJHCv1ksQ==", "cpu": [ "arm64" ], @@ -3661,9 +3729,9 @@ } }, "node_modules/@nrwl/nx-linux-x64-gnu": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.7.2.tgz", - "integrity": "sha512-8/6WtQn4derYKUWu5SxWWM+1dGihSZXMhDW9l/sXOr/qbMZu3XBmM2XZSguw/+p9gEVHcMmN0+D+Cai+q6/vDQ==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.5.tgz", + "integrity": "sha512-AW8YjhZv3c+LRUoLvHLx4BZaDakQbPCPx70+c/uQyDkQP/ckYJc0gRjoZukolcI6+AvNcBhkI559RL9W4qb9iw==", "cpu": [ "x64" ], @@ -3677,9 +3745,9 @@ } }, "node_modules/@nrwl/nx-linux-x64-musl": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.7.2.tgz", - "integrity": "sha512-c5SbqYZZBeBHhH5E30xwb4cHzCMVa/GQMCyTpZgsS/AHAPHbdkv+pO6bxxALvLPTyimcub7V+xbLCL7rgALzyw==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.5.tgz", + "integrity": "sha512-m4Iy/pbzH0LTsADq/X+74nfVzm2Tt0zorOXXy/uQN4ozL/JNGVpwvxdOFxZ7e3RBXDX4u6awUzSE52Z2d2f0uA==", "cpu": [ "x64" ], @@ -3693,9 +3761,9 @@ } }, "node_modules/@nrwl/nx-win32-arm64-msvc": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.7.2.tgz", - "integrity": "sha512-gWD/+gSO3XBma8PHX1Dp86fM6EcntHFfa7n/BISwDFkZ19MfV/gK6HbO847fkD6I34/IcDM/z1PsFwoIpTeoow==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.5.tgz", + "integrity": "sha512-4AT1PHo5At8AXvgE5XlQbimE0THeSji6J3XZ1UTqq7n3L26QicNdnZcaHGyL1ukMtXRIwT/yed+xu1PFkXF4QA==", "cpu": [ "arm64" ], @@ -3709,9 +3777,9 @@ } }, "node_modules/@nrwl/nx-win32-x64-msvc": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.7.2.tgz", - "integrity": "sha512-ARE4qGPgk+e+pSm0uPhHan5UCRtwNYc5ddVNS88NFrVoDTPm5MxYLGdvLnshWWio/Bx526FcwUMSCBWSW8HIFw==", + "version": "15.8.5", + "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.5.tgz", + "integrity": "sha512-53vzsQErvN4OeF/qBgfPg6OZ3smX4V8Lza59bwql9aAjjlMe1Ff9Su0BgAqlhVfSiYGxAirfHljgA6aWFqpCHQ==", "cpu": [ "x64" ], @@ -4001,9 +4069,9 @@ } }, "node_modules/@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", + "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", "dev": true, "dependencies": { "@types/node": "*" @@ -4106,9 +4174,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", "dev": true, "dependencies": { "@types/mime": "*", @@ -4501,9 +4569,9 @@ "dev": true }, "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.39", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.39.tgz", - "integrity": "sha512-BsD4zq3EVmaHqlynXTceNuEFAtrfToV4fI9GA54moKlWZL4Eb2eXrhgf1jV2nMYx18SZxYO4Jc5Kf1sCDNRjOg==", + "version": "3.0.0-rc.40", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz", + "integrity": "sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q==", "dev": true, "dependencies": { "js-yaml": "^3.10.0", @@ -4641,28 +4709,19 @@ } }, "node_modules/agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", "dev": true, "dependencies": { "debug": "^4.1.0", - "depd": "^1.1.2", + "depd": "^2.0.0", "humanize-ms": "^1.2.1" }, "engines": { "node": ">= 8.0.0" } }, - "node_modules/agentkeepalive/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -4809,9 +4868,9 @@ } }, "node_modules/apexcharts": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.37.0.tgz", - "integrity": "sha512-0mg1gDKUo3JG00Q//LK0jEXBS6OLjpuglqZ8ec9cqfA5oP8owopD9n5EhfARbWROb5o8GSPzFuohTJiCm2ecWw==", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.37.1.tgz", + "integrity": "sha512-fmQ5Updeb/LASl+S1+mIxXUFxzY0Fa7gexfCs4o+OPP9f2NEBNjvybOtPrah44N4roK7U5o5Jis906QeEQu0cA==", "dependencies": { "svg.draggable.js": "^2.2.2", "svg.easing.js": "^2.0.0", @@ -5010,9 +5069,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz", - "integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -5041,13 +5100,13 @@ "dev": true }, "node_modules/babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, "dependencies": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, @@ -5059,30 +5118,18 @@ "webpack": ">=2" } }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "json5": "^2.1.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=8.9.0" } }, "node_modules/babel-plugin-istanbul": { @@ -5542,9 +5589,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001462", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz", + "integrity": "sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw==", "dev": true, "funding": [ { @@ -6154,9 +6201,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.28.0.tgz", - "integrity": "sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg==", + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.0.tgz", + "integrity": "sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ==", "dev": true, "dependencies": { "browserslist": "^4.21.5" @@ -6167,9 +6214,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.28.0.tgz", - "integrity": "sha512-DSOVleA9/v3LNj/vFxAPfUHttKTzrB2RXhAPvR5TPXn4vrra3Z2ssytvRyt8eruJwAfwAiFADEbrjcRdcvPLQQ==", + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.0.tgz", + "integrity": "sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ==", "dev": true, "hasInstallScript": true, "funding": { @@ -6949,9 +6996,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.305", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.305.tgz", - "integrity": "sha512-WETy6tG0CT5gm1O+xCbyapWNsCcmIvrn4NHViIGYo2AT8FV2qUCXdaB+WqYxSv/vS5mFqhBYnfZAAkVArjBmUg==", + "version": "1.4.324", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.324.tgz", + "integrity": "sha512-m+eBs/kh3TXnCuqDF6aHLLRwLK2U471JAbZ1KYigf0TM96fZglxv0/ZFBvyIxnLKsIWUoDiVnHTA2mhYz1fqdA==", "dev": true }, "node_modules/emoji-regex": { @@ -7517,12 +7564,13 @@ } }, "node_modules/eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", + "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.1", + "@eslint/eslintrc": "^2.0.0", + "@eslint/js": "8.35.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7536,7 +7584,7 @@ "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", "espree": "^9.4.0", - "esquery": "^1.4.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -7573,9 +7621,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", - "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", + "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -7941,9 +7989,9 @@ } }, "node_modules/esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -9053,9 +9101,9 @@ } }, "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -10426,9 +10474,9 @@ } }, "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -10995,9 +11043,9 @@ } }, "node_modules/loader-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", - "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", "dev": true, "engines": { "node": ">= 12.13.0" @@ -11217,9 +11265,9 @@ } }, "node_modules/log4js": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.8.0.tgz", - "integrity": "sha512-g+V8gZyurIexrOvWQ+AcZsIvuK/lBnx2argejZxL4gVZ4Hq02kUYH6WZOnqxgBml+zzQZYdaEoTN84B6Hzm8Fg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.0.tgz", + "integrity": "sha512-sAGxJKqxXFlK+05OlMH6SIDAdvgQHj95EfSDOfkHW6BQUlkz+fZw8PWhydfRHq+0UuWYWR55mVnR+KTnWE2JDA==", "dev": true, "dependencies": { "date-format": "^4.0.14", @@ -11649,9 +11697,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -12246,9 +12294,9 @@ } }, "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.17.0.tgz", - "integrity": "sha512-zSxlVVwOabhVyTi6E8gYv2cr6bXK+8ifYz5/uyJb9feXX6NACVDwY4p5Ut3WC3Ivo/QhpARHU3iujx2xGAYHbQ==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { "node": ">=12" @@ -14313,9 +14361,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -14751,9 +14799,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.0.tgz", - "integrity": "sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw==", + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -14764,7 +14812,7 @@ "sass": "sass.js" }, "engines": { - "node": ">=8.9.0" + "node": ">=12.0.0" } }, "node_modules/sass-loader": { @@ -15436,9 +15484,9 @@ "dev": true }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -16361,9 +16409,9 @@ } }, "node_modules/tar/node_modules/minipass": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.0.tgz", - "integrity": "sha512-ExlilAIS7zJ2EWUMaVXi14H+FnZ18kr17kFkGemMqBx6jW0m8P6XfqwYVPEG53ENlgsED+alVP9ZxC3JzkK23Q==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", + "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==", "dev": true, "engines": { "node": ">=8" @@ -16376,14 +16424,14 @@ "dev": true }, "node_modules/terser": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz", - "integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -16483,9 +16531,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.4.tgz", - "integrity": "sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==", + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", + "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.2", @@ -16748,9 +16796,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.34.tgz", + "integrity": "sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ==", "dev": true, "funding": [ { @@ -17156,13 +17204,13 @@ } }, "node_modules/webpack": { - "version": "5.67.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", - "integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==", + "version": "5.70.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", + "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", @@ -17170,7 +17218,7 @@ "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", + "enhanced-resolve": "^5.9.2", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -17416,9 +17464,9 @@ } }, "node_modules/webpack/node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", "dev": true }, "node_modules/webpack/node_modules/ajv": { diff --git a/frontend/package.json b/frontend/package.json index e488e3c92..124eba2ea 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "13.2.x", + "@angular-devkit/build-angular": "13.3.x", "@angular-eslint/builder": "13.2.0", "@angular-eslint/eslint-plugin": "13.2.0", "@angular-eslint/eslint-plugin-template": "13.2.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 544607d55..b31fe15aa 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5,17 +5,7 @@ __metadata: version: 6 cacheKey: 8 -"@ampproject/remapping@npm:1.1.1": - version: 1.1.1 - resolution: "@ampproject/remapping@npm:1.1.1" - dependencies: - "@jridgewell/resolve-uri": ^3.0.3 - sourcemap-codec: 1.4.8 - checksum: fd8b9defa56e3dcb35ba12461210092b5f7f21ab126affa81e9a0e6871fbf9bb0525b6fee9b81aff76a30b2562d54e8c78244c18b9cc7338811ecb5c0b20fe16 - languageName: node - linkType: hard - -"@ampproject/remapping@npm:^2.2.0": +"@ampproject/remapping@npm:2.2.0, @ampproject/remapping@npm:^2.2.0": version: 2.2.0 resolution: "@ampproject/remapping@npm:2.2.0" dependencies: @@ -35,14 +25,24 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/build-angular@npm:13.2.x": - version: 13.2.6 - resolution: "@angular-devkit/build-angular@npm:13.2.6" +"@angular-devkit/architect@npm:0.1303.10": + version: 0.1303.10 + resolution: "@angular-devkit/architect@npm:0.1303.10" dependencies: - "@ampproject/remapping": 1.1.1 - "@angular-devkit/architect": 0.1302.6 - "@angular-devkit/build-webpack": 0.1302.6 - "@angular-devkit/core": 13.2.6 + "@angular-devkit/core": 13.3.10 + rxjs: 6.6.7 + checksum: fa07d923c8a9d37511bb6fb6c88bd0df6044958c57ba7995c0cc610c2a8c6d2f84a3abb1aa4318015fd068a1e49414bfea4743882068b22d15cf8dab2a53c661 + languageName: node + linkType: hard + +"@angular-devkit/build-angular@npm:13.3.x": + version: 13.3.10 + resolution: "@angular-devkit/build-angular@npm:13.3.10" + dependencies: + "@ampproject/remapping": 2.2.0 + "@angular-devkit/architect": 0.1303.10 + "@angular-devkit/build-webpack": 0.1303.10 + "@angular-devkit/core": 13.3.10 "@babel/core": 7.16.12 "@babel/generator": 7.16.8 "@babel/helper-annotate-as-pure": 7.16.7 @@ -53,9 +53,9 @@ __metadata: "@babel/runtime": 7.16.7 "@babel/template": 7.16.7 "@discoveryjs/json-ext": 0.5.6 - "@ngtools/webpack": 13.2.6 + "@ngtools/webpack": 13.3.10 ansi-colors: 4.1.1 - babel-loader: 8.2.3 + babel-loader: 8.2.5 babel-plugin-istanbul: 6.1.1 browserslist: ^4.9.1 cacache: 15.3.0 @@ -74,9 +74,9 @@ __metadata: less: 4.1.2 less-loader: 10.2.0 license-webpack-plugin: 4.0.2 - loader-utils: 3.2.0 + loader-utils: 3.2.1 mini-css-extract-plugin: 2.5.3 - minimatch: 3.0.4 + minimatch: 3.0.5 open: 8.4.0 ora: 5.4.1 parse5-html-rewriting-stream: 6.0.1 @@ -88,31 +88,31 @@ __metadata: regenerator-runtime: 0.13.9 resolve-url-loader: 5.0.0 rxjs: 6.6.7 - sass: 1.49.0 + sass: 1.49.9 sass-loader: 12.4.0 semver: 7.3.5 source-map-loader: 3.0.1 source-map-support: 0.5.21 stylus: 0.56.0 stylus-loader: 6.2.0 - terser: 5.11.0 + terser: 5.14.2 text-table: 0.2.0 tree-kill: 1.2.2 tslib: 2.3.1 - webpack: 5.67.0 + webpack: 5.70.0 webpack-dev-middleware: 5.3.0 webpack-dev-server: 4.7.3 webpack-merge: 5.8.0 webpack-subresource-integrity: 5.1.0 peerDependencies: - "@angular/compiler-cli": ^13.0.0 - "@angular/localize": ^13.0.0 - "@angular/service-worker": ^13.0.0 + "@angular/compiler-cli": ^13.0.0 || ^13.3.0-rc.0 + "@angular/localize": ^13.0.0 || ^13.3.0-rc.0 + "@angular/service-worker": ^13.0.0 || ^13.3.0-rc.0 karma: ^6.3.0 ng-packagr: ^13.0.0 protractor: ^7.0.0 tailwindcss: ^2.0.0 || ^3.0.0 - typescript: ">=4.4.3 <4.6" + typescript: ">=4.4.3 <4.7" dependenciesMeta: esbuild: optional: true @@ -129,20 +129,20 @@ __metadata: optional: true tailwindcss: optional: true - checksum: f5b041a64661bcb845d9f91d19397fa9f14760c4904266974c6d4a7c9db3360ae7f76e2dd1332a85e68fc3d1d0051964a40fc2fdfbfef5ff912a29b67ffca978 + checksum: 242f0b9789e22da42d21e368da299ab3c14ab06cace81dbb5b1894730d632ee59e55e6030eb105ad636ba76b8f3e494f59c84c061680abd1e53f74d723f44a0c languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1302.6": - version: 0.1302.6 - resolution: "@angular-devkit/build-webpack@npm:0.1302.6" +"@angular-devkit/build-webpack@npm:0.1303.10": + version: 0.1303.10 + resolution: "@angular-devkit/build-webpack@npm:0.1303.10" dependencies: - "@angular-devkit/architect": 0.1302.6 + "@angular-devkit/architect": 0.1303.10 rxjs: 6.6.7 peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^4.0.0 - checksum: 9c8f7737211eeb5240a2abd0caf6a61a19065829d2b75f47faa9dd2ed48c117ad0572641701cddabc264201ec3c4c2037c139a4efb8514c991704f4ac030e0df + checksum: ede18e93d996509dc44b94d0fc7bafa0c96b3c0486e69631008992f6f1efdded251635e655eebd168ffd1ffc9ff25f6c6491fb6c72910dca8b07c948fb9fbe66 languageName: node linkType: hard @@ -165,6 +165,25 @@ __metadata: languageName: node linkType: hard +"@angular-devkit/core@npm:13.3.10": + version: 13.3.10 + resolution: "@angular-devkit/core@npm:13.3.10" + dependencies: + ajv: 8.9.0 + ajv-formats: 2.1.1 + fast-json-stable-stringify: 2.1.0 + magic-string: 0.25.7 + rxjs: 6.6.7 + source-map: 0.7.3 + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + checksum: 25140170e2adefa05bce8a7db30c4b1d26d4739d8be0cc5d09fd29ad4cb424ee1831993806cfbba4302749be2d117faf3f9bbfcb9814eda38a1a0a31f80211f2 + languageName: node + linkType: hard + "@angular-devkit/schematics@npm:13.2.6": version: 13.2.6 resolution: "@angular-devkit/schematics@npm:13.2.6" @@ -573,7 +592,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.21.0": +"@babel/generator@npm:^7.21.0, @babel/generator@npm:^7.21.1": version: 7.21.1 resolution: "@babel/generator@npm:7.21.1" dependencies: @@ -727,9 +746,9 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-module-transforms@npm:7.21.0" +"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0, @babel/helper-module-transforms@npm:^7.21.2": + version: 7.21.2 + resolution: "@babel/helper-module-transforms@npm:7.21.2" dependencies: "@babel/helper-environment-visitor": ^7.18.9 "@babel/helper-module-imports": ^7.18.6 @@ -737,9 +756,9 @@ __metadata: "@babel/helper-split-export-declaration": ^7.18.6 "@babel/helper-validator-identifier": ^7.19.1 "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.0 - "@babel/types": ^7.21.0 - checksum: bd92d0b73c12dc2f37be906954c58cc3fbec74ba243731e1aa223063b422eef6b961ca7fe19737a073be18db298e1385d370df2e5781646b8c09ecebd7c847de + "@babel/traverse": ^7.21.2 + "@babel/types": ^7.21.2 + checksum: 8a1c129a4f90bdf97d8b6e7861732c9580f48f877aaaafbc376ce2482febebcb8daaa1de8bc91676d12886487603f8c62a44f9e90ee76d6cac7f9225b26a49e1 languageName: node linkType: hard @@ -869,12 +888,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.0": - version: 7.21.1 - resolution: "@babel/parser@npm:7.21.1" +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.0, @babel/parser@npm:^7.21.2": + version: 7.21.2 + resolution: "@babel/parser@npm:7.21.2" bin: parser: ./bin/babel-parser.js - checksum: a564cff6dec4201a611d1f2ae5af8d7436ce80550e75a77ee72dca6b094df6188b5a4ccfae6a98c85991b56a51e6c48159e466cc5a374c7a37af706fcb5a6bc2 + checksum: e2b89de2c63d4cdd2cafeaea34f389bba729727eec7a8728f736bc472a59396059e3e9fe322c9bed8fd126d201fb609712949dc8783f4cae4806acd9a73da6ff languageName: node linkType: hard @@ -1426,15 +1445,15 @@ __metadata: linkType: hard "@babel/plugin-transform-modules-commonjs@npm:^7.16.8": - version: 7.20.11 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.20.11" + version: 7.21.2 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" dependencies: - "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-module-transforms": ^7.21.2 "@babel/helper-plugin-utils": ^7.20.2 "@babel/helper-simple-access": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ddd0623e2ad4b5c0faaa0ae30d3407a3fa484d911c968ed33cfb1b339ac3691321c959db60b66dc136dbd67770fff586f7e48a7ce0d7d357f92d6ef6fb7ed1a7 + checksum: 65aa06e3e3792f39b99eb5f807034693ff0ecf80438580f7ae504f4c4448ef04147b1889ea5e6f60f3ad4a12ebbb57c6f1f979a249dadbd8d11fe22f4441918b languageName: node linkType: hard @@ -1786,32 +1805,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/traverse@npm:7.21.0" +"@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2": + version: 7.21.2 + resolution: "@babel/traverse@npm:7.21.2" dependencies: "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.21.0 + "@babel/generator": ^7.21.1 "@babel/helper-environment-visitor": ^7.18.9 "@babel/helper-function-name": ^7.21.0 "@babel/helper-hoist-variables": ^7.18.6 "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.21.0 - "@babel/types": ^7.21.0 + "@babel/parser": ^7.21.2 + "@babel/types": ^7.21.2 debug: ^4.1.0 globals: ^11.1.0 - checksum: 99241b22db509d2f01a9af51bfab1d68e73cd3b66bbc2560f0f65e49880f68a05ead913e72a4e464152430a027f0c7822f126d6f1bcc3bc3e01ef8b8558a6dc6 + checksum: d851e3f5cfbdc2fac037a014eae7b0707709de50f7d2fbb82ffbf932d3eeba90a77431529371d6e544f8faaf8c6540eeb18fdd8d1c6fa2b61acea0fb47e18d4b languageName: node linkType: hard -"@babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.21.0 - resolution: "@babel/types@npm:7.21.0" +"@babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.21.2 + resolution: "@babel/types@npm:7.21.2" dependencies: "@babel/helper-string-parser": ^7.19.4 "@babel/helper-validator-identifier": ^7.19.1 to-fast-properties: ^2.0.0 - checksum: dbcdda202b3a2bfd59e4de880ce38652f1f8957893a9751be069ac86e47ad751222070fe6cd92220214d77973f1474e4e1111c16dc48199dfca1489c0ee8c0c5 + checksum: a45a52acde139e575502c6de42c994bdbe262bafcb92ae9381fb54cdf1a3672149086843fda655c7683ce9806e998fd002bbe878fa44984498d0fdc7935ce7ff languageName: node linkType: hard @@ -1864,9 +1883,9 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.4.1": - version: 1.4.1 - resolution: "@eslint/eslintrc@npm:1.4.1" +"@eslint/eslintrc@npm:^2.0.0": + version: 2.0.0 + resolution: "@eslint/eslintrc@npm:2.0.0" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -1877,7 +1896,14 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: cd3e5a8683db604739938b1c1c8b77927dc04fce3e28e0c88e7f2cd4900b89466baf83dfbad76b2b9e4d2746abdd00dd3f9da544d3e311633d8693f327d04cd7 + checksum: 31119c8ca06723d80384f18f5c78e0530d8e6306ad36379868650131a8b10dd7cffd7aff79a5deb3a2e9933660823052623d268532bae9538ded53d5b19a69a6 + languageName: node + linkType: hard + +"@eslint/js@npm:8.35.0": + version: 8.35.0 + resolution: "@eslint/js@npm:8.35.0" + checksum: 6687ceff659a6d617e37823f809dc9c4b096535961a81acead27d26b1a51a4cf608a5e59d831ddd57f24f6f8bb99340a4a0e19f9c99b390fbb4b275f51ed5f5e languageName: node linkType: hard @@ -1954,7 +1980,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": +"@jridgewell/resolve-uri@npm:3.1.0": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 @@ -1995,14 +2021,14 @@ __metadata: languageName: node linkType: hard -"@ngtools/webpack@npm:13.2.6": - version: 13.2.6 - resolution: "@ngtools/webpack@npm:13.2.6" +"@ngtools/webpack@npm:13.3.10": + version: 13.3.10 + resolution: "@ngtools/webpack@npm:13.3.10" peerDependencies: "@angular/compiler-cli": ^13.0.0 - typescript: ">=4.4.3 <4.6" + typescript: ">=4.4.3 <4.7" webpack: ^5.30.0 - checksum: 9dfc5d89393bb90b56fd78fb08da536a88ab23f43d772493cf78c8b9aad94f4b859cfd770aa520c3776a261444a999550467803bc074b69b66a7c3c7dc4170d6 + checksum: 4bb5bcf2aeff5e291f0b39eb0ce5b3338569610c240d492270c00406b228dbebb1e4266ca4b41ad0e6a443b62afcfcc504c75bbe0a7fa450eaac84d57efa8b39 languageName: node linkType: hard @@ -2129,12 +2155,12 @@ __metadata: languageName: node linkType: hard -"@nrwl/cli@npm:*, @nrwl/cli@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/cli@npm:15.7.2" +"@nrwl/cli@npm:*, @nrwl/cli@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/cli@npm:15.8.5" dependencies: - nx: 15.7.2 - checksum: c59130679458e1572181d8b49ff3a755bba4e07f8fe9a4c0b20330a16105548381467c557a4f3dd09051cf6ae8bcea15c3f2ccd2dba9ad6d0dfec79de0b61b49 + nx: 15.8.5 + checksum: a7c588d0d7185de0ee98bdfa22fdfc6faf33cfb2cc3277a296ffb00b96c5892e3543ae2a6181df48b95fef3bd2a5ca3a84788dbf0425f885d478763fb33c45f0 languageName: node linkType: hard @@ -2152,65 +2178,65 @@ __metadata: languageName: node linkType: hard -"@nrwl/nx-darwin-arm64@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-darwin-arm64@npm:15.7.2" +"@nrwl/nx-darwin-arm64@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-darwin-arm64@npm:15.8.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@nrwl/nx-darwin-x64@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-darwin-x64@npm:15.7.2" +"@nrwl/nx-darwin-x64@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-darwin-x64@npm:15.8.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@nrwl/nx-linux-arm-gnueabihf@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-linux-arm-gnueabihf@npm:15.7.2" +"@nrwl/nx-linux-arm-gnueabihf@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-linux-arm-gnueabihf@npm:15.8.5" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@nrwl/nx-linux-arm64-gnu@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-linux-arm64-gnu@npm:15.7.2" +"@nrwl/nx-linux-arm64-gnu@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-linux-arm64-gnu@npm:15.8.5" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@nrwl/nx-linux-arm64-musl@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-linux-arm64-musl@npm:15.7.2" +"@nrwl/nx-linux-arm64-musl@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-linux-arm64-musl@npm:15.8.5" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@nrwl/nx-linux-x64-gnu@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-linux-x64-gnu@npm:15.7.2" +"@nrwl/nx-linux-x64-gnu@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-linux-x64-gnu@npm:15.8.5" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@nrwl/nx-linux-x64-musl@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-linux-x64-musl@npm:15.7.2" +"@nrwl/nx-linux-x64-musl@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-linux-x64-musl@npm:15.8.5" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@nrwl/nx-win32-arm64-msvc@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-win32-arm64-msvc@npm:15.7.2" +"@nrwl/nx-win32-arm64-msvc@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-win32-arm64-msvc@npm:15.8.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@nrwl/nx-win32-x64-msvc@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/nx-win32-x64-msvc@npm:15.7.2" +"@nrwl/nx-win32-x64-msvc@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/nx-win32-x64-msvc@npm:15.8.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2236,14 +2262,14 @@ __metadata: languageName: node linkType: hard -"@nrwl/tao@npm:15.7.2": - version: 15.7.2 - resolution: "@nrwl/tao@npm:15.7.2" +"@nrwl/tao@npm:15.8.5": + version: 15.8.5 + resolution: "@nrwl/tao@npm:15.8.5" dependencies: - nx: 15.7.2 + nx: 15.8.5 bin: tao: index.js - checksum: cbf76f385bffb1bf1bde52d4ad950a8abd1ba9865f47afb67a58bb5dc5d7be0d21c5021a3332d2ab665d99b36ec1f08ee260c1289bb7e69783e69712145d2709 + checksum: 92d487dd227e90d3a46a23f12b5e24f62174b1e8551d1891034e53a24c9f2b857b58a1b7fafa7fa88634808d4d07e1b94434fc2431bab936b5bb8aeeb5a313c1 languageName: node linkType: hard @@ -2344,7 +2370,7 @@ __metadata: languageName: node linkType: hard -"@types/eslint-scope@npm:^3.7.0": +"@types/eslint-scope@npm:^3.7.3": version: 3.7.4 resolution: "@types/eslint-scope@npm:3.7.4" dependencies: @@ -2371,10 +2397,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:^0.0.50": - version: 0.0.50 - resolution: "@types/estree@npm:0.0.50" - checksum: 9a2b6a4a8c117f34d08fbda5e8f69b1dfb109f7d149b60b00fd7a9fb6ac545c078bc590aa4ec2f0a256d680cf72c88b3b28b60c326ee38a7bc8ee1ee95624922 +"@types/estree@npm:^0.0.51": + version: 0.0.51 + resolution: "@types/estree@npm:0.0.51" + checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 languageName: node linkType: hard @@ -2402,11 +2428,11 @@ __metadata: linkType: hard "@types/http-proxy@npm:^1.17.8": - version: 1.17.9 - resolution: "@types/http-proxy@npm:1.17.9" + version: 1.17.10 + resolution: "@types/http-proxy@npm:1.17.10" dependencies: "@types/node": "*" - checksum: 7a6746d00729b2a9fe9f9dd3453430b099931df879ec8f7a7b5f07b1795f6d99b0512640c45a67390b1e4bacb9401e36824952aeeaf089feba8627a063cf8e00 + checksum: 8fabee5d01715e338f426715325121d6c4b7a9694dee716ab61c874e0aaccee9a0fff7ccc3c9d7e37a8feeaab7c783c17aaa9943efbc8849c5e79ecd7eaf02ab languageName: node linkType: hard @@ -2520,12 +2546,12 @@ __metadata: linkType: hard "@types/serve-static@npm:*": - version: 1.15.0 - resolution: "@types/serve-static@npm:1.15.0" + version: 1.15.1 + resolution: "@types/serve-static@npm:1.15.1" dependencies: "@types/mime": "*" "@types/node": "*" - checksum: b6ac93d471fb0f53ddcac1f9b67572a09cd62806f7db5855244b28f6f421139626f24799392566e97d1ffc61b12f9de7f30380c39fcae3c8a161fe161d44edf2 + checksum: 2e078bdc1e458c7dfe69e9faa83cc69194b8896cce57cb745016580543c7ab5af07fdaa8ac1765eb79524208c81017546f66056f44d1204f812d72810613de36 languageName: node linkType: hard @@ -2848,12 +2874,12 @@ __metadata: linkType: hard "@yarnpkg/parsers@npm:^3.0.0-rc.18": - version: 3.0.0-rc.39 - resolution: "@yarnpkg/parsers@npm:3.0.0-rc.39" + version: 3.0.0-rc.40 + resolution: "@yarnpkg/parsers@npm:3.0.0-rc.40" dependencies: js-yaml: ^3.10.0 tslib: ^2.4.0 - checksum: b54fb3694bd09e09142d5a8d607240a8389ce3ccf44a70808ac770c178bb527948c3805a230b6fa55753f95574c93773a853a37ece978e323c9f2d229fd82252 + checksum: 64df0d8dad4cd43af5fcff758b0cfc47e7dc56e00eed87e3bd6ce8a9ea265c41fe758c1e01607e630bae46c9f8324ca853bced58be3d1c93492e125757ab7f30 languageName: node linkType: hard @@ -2955,13 +2981,13 @@ __metadata: linkType: hard "agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": - version: 4.2.1 - resolution: "agentkeepalive@npm:4.2.1" + version: 4.3.0 + resolution: "agentkeepalive@npm:4.3.0" dependencies: debug: ^4.1.0 - depd: ^1.1.2 + depd: ^2.0.0 humanize-ms: ^1.2.1 - checksum: 39cb49ed8cf217fd6da058a92828a0a84e0b74c35550f82ee0a10e1ee403c4b78ade7948be2279b188b7a7303f5d396ea2738b134731e464bf28de00a4f72a18 + checksum: 982453aa44c11a06826c836025e5162c846e1200adb56f2d075400da7d32d87021b3b0a58768d949d824811f5654223d5a8a3dad120921a2439625eb847c6260 languageName: node linkType: hard @@ -3141,8 +3167,8 @@ __metadata: linkType: hard "apexcharts@npm:3.37.x": - version: 3.37.0 - resolution: "apexcharts@npm:3.37.0" + version: 3.37.1 + resolution: "apexcharts@npm:3.37.1" dependencies: svg.draggable.js: ^2.2.2 svg.easing.js: ^2.0.0 @@ -3150,7 +3176,7 @@ __metadata: svg.pathmorphing.js: ^0.1.3 svg.resize.js: ^1.4.3 svg.select.js: ^3.0.1 - checksum: 888df7fc7fc49292788466215b86893ed0bd7b4fa52d4c692e74816d6ecdc73d47921babe9576e6fe5afb636fc98cbc13b1790aa5aa61fed7c103f3fc74867a8 + checksum: 83afb447f4cc2598bb986a2c6839f70811bad5f945e984554ec81cf489f587efff5c85bb3d38040e3db5cb998ca095a60b72328804519e909229e144048633cf languageName: node linkType: hard @@ -3350,13 +3376,13 @@ __metadata: linkType: hard "axios@npm:^1.0.0": - version: 1.3.3 - resolution: "axios@npm:1.3.3" + version: 1.3.4 + resolution: "axios@npm:1.3.4" dependencies: follow-redirects: ^1.15.0 form-data: ^4.0.0 proxy-from-env: ^1.1.0 - checksum: b734a4bc348e2fa27150a7d4289d783fa405feb3f79f8daf28fd05813a12c8525ae9d3854aafe7ba041b005a4a751a0ba3b923331ceed41296ae14c7e54e2f26 + checksum: 7440edefcf8498bc3cdf39de00443e8101f249972c83b739c6e880d9d669fea9486372dbe8739e88b3bf8bb1ad15f6106693f206f078f4516fe8fd47b1c3093c languageName: node linkType: hard @@ -3367,18 +3393,18 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:8.2.3": - version: 8.2.3 - resolution: "babel-loader@npm:8.2.3" +"babel-loader@npm:8.2.5": + version: 8.2.5 + resolution: "babel-loader@npm:8.2.5" dependencies: find-cache-dir: ^3.3.1 - loader-utils: ^1.4.0 + loader-utils: ^2.0.0 make-dir: ^3.1.0 schema-utils: ^2.6.5 peerDependencies: "@babel/core": ^7.0.0 webpack: ">=2" - checksum: 78e1e1a91954d644b6ce66366834d4d245febbc0fde33e4e2831725e83d6e760d12b3a78e9534ce92af69067bef1d9d9674df36d8c1f20ee127bc2354b2203ba + checksum: a6605557885eabbc3250412405f2c63ca87287a95a439c643fdb47d5ea3d5326f72e43ab97be070316998cb685d5dfbc70927ce1abe8be7a6a4f5919287773fb languageName: node linkType: hard @@ -3756,9 +3782,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001299, caniuse-lite@npm:^1.0.30001426, caniuse-lite@npm:^1.0.30001449": - version: 1.0.30001457 - resolution: "caniuse-lite@npm:1.0.30001457" - checksum: f311a7c5098681962402a86a0a367014ee91c3135395ee68bbfaf45caf0e36d581e42d7c5b1526ce99484a228e6cf5cf0e400678292c65f5a21512a3fc7a5fb6 + version: 1.0.30001462 + resolution: "caniuse-lite@npm:1.0.30001462" + checksum: e4a57d7851eec65e7c9b6c11c4bbcecdc49d87b1b01bff3c15ea27efb05f959891b4c70ac169842067c134d6fa126d9ad5a91d0f85c7387c5bd912eaf41ea647 languageName: node linkType: hard @@ -4196,18 +4222,18 @@ __metadata: linkType: hard "core-js-compat@npm:^3.20.2, core-js-compat@npm:^3.21.0": - version: 3.28.0 - resolution: "core-js-compat@npm:3.28.0" + version: 3.29.0 + resolution: "core-js-compat@npm:3.29.0" dependencies: browserslist: ^4.21.5 - checksum: 41d1d58c99ce7ee7abd8cf070f4c07a8f2655dbed1777d90a26246dddd7fac68315d53d2192584c8621a5328e6fe1a10da39b6bf2666e90fd5c2ff3b8f24e874 + checksum: ca5d370296c15ebd5f961dae6b6a24a153a84937bff58543099b7f1c407e8d5bbafafa7ca27e65baad522ece762d6356e1d6ea9efa99815f6fefd150fac7e8a5 languageName: node linkType: hard "core-js-pure@npm:^3.25.1": - version: 3.28.0 - resolution: "core-js-pure@npm:3.28.0" - checksum: 8bef96a435783ea7e62b2bd4d6cc3d427a7bfeb053954aadabb33b5dba14a85c6297f7638bba9676a144f9cd7a5a0185a576d41d67baaae15227a4c9982a8cef + version: 3.29.0 + resolution: "core-js-pure@npm:3.29.0" + checksum: 281805cda717a471a15fd44a526ce873e19598ce4f2a5ac00daf4324583becc4956b1a15a266d5488668326bba420cc84fc957abe42f198796e5cf0acc62dfc8 languageName: node linkType: hard @@ -4572,14 +4598,14 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0": +"depd@npm:2.0.0, depd@npm:^2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a languageName: node linkType: hard -"depd@npm:^1.1.2, depd@npm:~1.1.2": +"depd@npm:~1.1.2": version: 1.1.2 resolution: "depd@npm:1.1.2" checksum: 6b406620d269619852885ce15965272b829df6f409724415e0002c8632ab6a8c0a08ec1f0bd2add05dc7bd7507606f7e2cc034fa24224ab829580040b835ecd9 @@ -4756,7 +4782,7 @@ __metadata: version: 0.0.0-use.local resolution: "dres-frontend@workspace:." dependencies: - "@angular-devkit/build-angular": 13.2.x + "@angular-devkit/build-angular": 13.3.x "@angular-eslint/builder": 13.2.0 "@angular-eslint/eslint-plugin": 13.2.0 "@angular-eslint/eslint-plugin-template": 13.2.0 @@ -4859,9 +4885,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.284": - version: 1.4.305 - resolution: "electron-to-chromium@npm:1.4.305" - checksum: 574599461f7a5283026fc55727f9396b92f4d67952d3d333fc8dbb89e3b7f1ff646588c027fe8822b13227018d870405aeda2648a7324169871bab77108c4f40 + version: 1.4.324 + resolution: "electron-to-chromium@npm:1.4.324" + checksum: 06d0d8d27cbf8320fad6ba100bfffae2f48cd176eb30765cc179d199406a2fda395af00f86c9ca182add1f1b9fe0b8b6965ab4a6e5306ef3a163198b775d8fdf languageName: node linkType: hard @@ -4943,7 +4969,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.8.3": +"enhanced-resolve@npm:^5.9.2": version: 5.12.0 resolution: "enhanced-resolve@npm:5.12.0" dependencies: @@ -5272,13 +5298,13 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.5.0": - version: 8.6.0 - resolution: "eslint-config-prettier@npm:8.6.0" + version: 8.7.0 + resolution: "eslint-config-prettier@npm:8.7.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: ff0d0dfc839a556355422293428637e8d35693de58dabf8638bf0b6529131a109d0b2ade77521aa6e54573bb842d7d9d322e465dd73dd61c7590fa3834c3fa81 + checksum: b05bc7f2296ce3e0925c14147849706544870e0382d38af2352d709a6cf8521bdaff2bd8e5021f1780e570775a8ffa1d2bac28b8065d90d43a3f1f98fd26ce52 languageName: node linkType: hard @@ -5343,10 +5369,11 @@ __metadata: linkType: hard "eslint@npm:^8.12.0, eslint@npm:^8.7.0": - version: 8.34.0 - resolution: "eslint@npm:8.34.0" + version: 8.35.0 + resolution: "eslint@npm:8.35.0" dependencies: - "@eslint/eslintrc": ^1.4.1 + "@eslint/eslintrc": ^2.0.0 + "@eslint/js": 8.35.0 "@humanwhocodes/config-array": ^0.11.8 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 @@ -5360,7 +5387,7 @@ __metadata: eslint-utils: ^3.0.0 eslint-visitor-keys: ^3.3.0 espree: ^9.4.0 - esquery: ^1.4.0 + esquery: ^1.4.2 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 @@ -5387,7 +5414,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 4e13e9eb05ac2248efbb6acae0b2325091235d5c47ba91a4775c7d6760778cbcd358a773ebd42f4629d2ad89e27c02f5d66eb1f737d75d9f5fc411454f83b2e5 + checksum: 6212173691d90b1bc94dd3d640e1f210374b30c3905fc0a15e501cf71c6ca52aa3d80ea7a9a245adaaed26d6019169e01fb6881b3f2885b188d37069c749308c languageName: node linkType: hard @@ -5412,12 +5439,12 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.4.0": - version: 1.4.2 - resolution: "esquery@npm:1.4.2" +"esquery@npm:^1.4.0, esquery@npm:^1.4.2": + version: 1.5.0 + resolution: "esquery@npm:1.5.0" dependencies: estraverse: ^5.1.0 - checksum: 2f4ad89c5aafaca61cc2c15e256190f0d6deb4791cae6552d3cb4b1eb8867958cdf27a56aaa3272ff17435e3eaa19ee0d4129fac336ca6373d7354d7b5da7966 + checksum: aefb0d2596c230118656cd4ec7532d447333a410a48834d80ea648b1e7b5c9bc9ed8b5e33a89cb04e487b60d622f44cf5713bf4abed7c97343edefdc84a35900 languageName: node linkType: hard @@ -7178,17 +7205,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.1": - version: 1.0.2 - resolution: "json5@npm:1.0.2" - dependencies: - minimist: ^1.2.0 - bin: - json5: lib/cli.js - checksum: 866458a8c58a95a49bef3adba929c625e82532bcff1fe93f01d29cb02cac7c3fe1f4b79951b7792c2da9de0b32871a8401a6e3c5b36778ad852bf5b8a61165d7 - languageName: node - linkType: hard - "json5@npm:^2.1.2, json5@npm:^2.2.2": version: 2.2.3 resolution: "json5@npm:2.2.3" @@ -7529,21 +7545,10 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:3.2.0": - version: 3.2.0 - resolution: "loader-utils@npm:3.2.0" - checksum: c7b9a8dc4b3bc19e9ef563c48e3a18ea9f8bb2da1ad38a12e4b88358cfba5f148a7baf12d78fe78ffcb718ce1e062ab31fcf5c148459f1247a672a4213471e80 - languageName: node - linkType: hard - -"loader-utils@npm:^1.4.0": - version: 1.4.2 - resolution: "loader-utils@npm:1.4.2" - dependencies: - big.js: ^5.2.2 - emojis-list: ^3.0.0 - json5: ^1.0.1 - checksum: eb6fb622efc0ffd1abdf68a2022f9eac62bef8ec599cf8adb75e94d1d338381780be6278534170e99edc03380a6d29bc7eb1563c89ce17c5fed3a0b17f1ad804 +"loader-utils@npm:3.2.1": + version: 3.2.1 + resolution: "loader-utils@npm:3.2.1" + checksum: 4e3ea054cdc8be1ab1f1238f49f42fdf0483039eff920fb1d442039f3f0ad4ebd11fb8e584ccdf2cb7e3c56b3d40c1832416e6408a55651b843da288960cc792 languageName: node linkType: hard @@ -7627,15 +7632,15 @@ __metadata: linkType: hard "log4js@npm:^6.4.1": - version: 6.8.0 - resolution: "log4js@npm:6.8.0" + version: 6.9.0 + resolution: "log4js@npm:6.9.0" dependencies: date-format: ^4.0.14 debug: ^4.3.4 flatted: ^3.2.7 rfdc: ^1.3.0 streamroller: ^3.1.5 - checksum: bab8e0434ff8d314338542c3305da2ab4e1b581fad8fef356776053dba8bd210839abc06fe83d0d4facdc27e8e890f05fd25f8ae7dc6dcab5635d75dd5d6c66f + checksum: 2e8cd31c1df9f247d551ee89cd5346b0b6f3c680a0b3fadbbf5f17f4baf6b657b203c7e832d608fa433fa86f645e30ddefb9fcc447945178fa09b5418c52bbd5 languageName: node linkType: hard @@ -7675,9 +7680,9 @@ __metadata: linkType: hard "lru-cache@npm:^7.7.1": - version: 7.17.0 - resolution: "lru-cache@npm:7.17.0" - checksum: 28c2a98ad313b8d61beac1f08257b6f0ca990e39d24a9bc831030b6e209447cfb11c6d9d1a774282189bfc9609d1dfd17ebe485228dd68f7b96b6b9b7740894e + version: 7.18.3 + resolution: "lru-cache@npm:7.18.3" + checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 languageName: node linkType: hard @@ -7934,16 +7939,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.0.4, minimatch@npm:^3.0.4": - version: 3.0.4 - resolution: "minimatch@npm:3.0.4" - dependencies: - brace-expansion: ^1.1.7 - checksum: 66ac295f8a7b59788000ea3749938b0970344c841750abd96694f80269b926ebcafad3deeb3f1da2522978b119e6ae3a5869b63b13a7859a456b3408bd18a078 - languageName: node - linkType: hard - -"minimatch@npm:3.0.5": +"minimatch@npm:3.0.5, minimatch@npm:^3.0.4": version: 3.0.5 resolution: "minimatch@npm:3.0.5" dependencies: @@ -8074,9 +8070,9 @@ __metadata: linkType: hard "minipass@npm:^4.0.0": - version: 4.2.0 - resolution: "minipass@npm:4.2.0" - checksum: 3c3ce269eacdcecb56b5dfe4bb4ab905d60fea1af4c967c7ed54baadfdd4af03e4d926567fb655957504fb4c9e2e7adbb2dc636927dfb56c829f2b25f1b2b3dd + version: 4.2.4 + resolution: "minipass@npm:4.2.4" + checksum: c664f2ae4401408d1e7a6e4f50aca45f87b1b0634bc9261136df5c378e313e77355765f73f59c4a5abcadcdf43d83fcd3eb14e4a7cdcce8e36508e2290345753 languageName: node linkType: hard @@ -8489,21 +8485,21 @@ __metadata: languageName: node linkType: hard -"nx@npm:15.7.2": - version: 15.7.2 - resolution: "nx@npm:15.7.2" +"nx@npm:15.8.5": + version: 15.8.5 + resolution: "nx@npm:15.8.5" dependencies: - "@nrwl/cli": 15.7.2 - "@nrwl/nx-darwin-arm64": 15.7.2 - "@nrwl/nx-darwin-x64": 15.7.2 - "@nrwl/nx-linux-arm-gnueabihf": 15.7.2 - "@nrwl/nx-linux-arm64-gnu": 15.7.2 - "@nrwl/nx-linux-arm64-musl": 15.7.2 - "@nrwl/nx-linux-x64-gnu": 15.7.2 - "@nrwl/nx-linux-x64-musl": 15.7.2 - "@nrwl/nx-win32-arm64-msvc": 15.7.2 - "@nrwl/nx-win32-x64-msvc": 15.7.2 - "@nrwl/tao": 15.7.2 + "@nrwl/cli": 15.8.5 + "@nrwl/nx-darwin-arm64": 15.8.5 + "@nrwl/nx-darwin-x64": 15.8.5 + "@nrwl/nx-linux-arm-gnueabihf": 15.8.5 + "@nrwl/nx-linux-arm64-gnu": 15.8.5 + "@nrwl/nx-linux-arm64-musl": 15.8.5 + "@nrwl/nx-linux-x64-gnu": 15.8.5 + "@nrwl/nx-linux-x64-musl": 15.8.5 + "@nrwl/nx-win32-arm64-msvc": 15.8.5 + "@nrwl/nx-win32-x64-msvc": 15.8.5 + "@nrwl/tao": 15.8.5 "@parcel/watcher": 2.0.4 "@yarnpkg/lockfile": ^1.1.0 "@yarnpkg/parsers": ^3.0.0-rc.18 @@ -8566,7 +8562,7 @@ __metadata: optional: true bin: nx: bin/nx.js - checksum: c5765575146bb94f54c1da3d6dec600e6414c0cc64c198821a3de10a72583933f1a752a61452b74f02492e3518f4ead3c287416173b1bb3bbc15ce82bd8d2bb4 + checksum: 20f1c5231a8f75af4d3280534d206fda94669336eb04d8934ee3bc888841e06bf296840140a15a7aa8dbbebfe9a9a364f3abe78c0fa56eb830c3b162e399313e languageName: node linkType: hard @@ -9772,8 +9768,8 @@ __metadata: linkType: hard "readable-stream@npm:^2.0.1, readable-stream@npm:~2.3.6": - version: 2.3.7 - resolution: "readable-stream@npm:2.3.7" + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" dependencies: core-util-is: ~1.0.0 inherits: ~2.0.3 @@ -9782,18 +9778,18 @@ __metadata: safe-buffer: ~5.1.1 string_decoder: ~1.1.1 util-deprecate: ~1.0.1 - checksum: e4920cf7549a60f8aaf694d483a0e61b2a878b969d224f89b3bc788b8d920075132c4b55a7494ee944c7b6a9a0eada28a7f6220d80b0312ece70bbf08eeca755 + checksum: 65645467038704f0c8aaf026a72fbb588a9e2ef7a75cd57a01702ee9db1c4a1e4b03aaad36861a6a0926546a74d174149c8c207527963e0c2d3eee2f37678a42 languageName: node linkType: hard "readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": - version: 3.6.0 - resolution: "readable-stream@npm:3.6.0" + version: 3.6.1 + resolution: "readable-stream@npm:3.6.1" dependencies: inherits: ^2.0.3 string_decoder: ^1.1.1 util-deprecate: ^1.0.1 - checksum: d4ea81502d3799439bb955a3a5d1d808592cf3133350ed352aeaa499647858b27b1c4013984900238b0873ec8d0d8defce72469fb7a83e61d53f5ad61cb80dc8 + checksum: b7ab0508dba3c37277b9e43c0a970ea27635375698859a687f558c3c9393154b6c4f39c3aa5689641de183fffa26771bc1a45878ddde0236ad18fc8fdfde50ea languageName: node linkType: hard @@ -10183,16 +10179,16 @@ __metadata: languageName: node linkType: hard -"sass@npm:1.49.0": - version: 1.49.0 - resolution: "sass@npm:1.49.0" +"sass@npm:1.49.9": + version: 1.49.9 + resolution: "sass@npm:1.49.9" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 7cd60868594fb0b6fed87bf4870ad5a7da7e3746cb5127f3ede2565380f3e30ba2de72d64e9083f2d55d0de81f756894fe6d9314c97acb704cf9fc97fbbafd0c + checksum: e5653e3499274c5127dcb5c9e7c5f6930378fc61764d999a5d8965782e027181ed09714f94836dec74ef55e3a858107fe6c571954c0cab0ad0be5ab8e586829c languageName: node linkType: hard @@ -10627,7 +10623,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:0.7.3, source-map@npm:^0.7.3, source-map@npm:~0.7.2": +"source-map@npm:0.7.3, source-map@npm:^0.7.3": version: 0.7.3 resolution: "source-map@npm:0.7.3" checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea @@ -10641,7 +10637,7 @@ __metadata: languageName: node linkType: hard -"sourcemap-codec@npm:1.4.8, sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": +"sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" checksum: b57981c05611afef31605732b598ccf65124a9fcb03b833532659ac4d29ac0f7bfacbc0d6c5a28a03e84c7510e7e556d758d0bb57786e214660016fb94279316 @@ -10649,12 +10645,12 @@ __metadata: linkType: hard "spdx-correct@npm:^3.0.0": - version: 3.1.1 - resolution: "spdx-correct@npm:3.1.1" + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" dependencies: spdx-expression-parse: ^3.0.0 spdx-license-ids: ^3.0.0 - checksum: 77ce438344a34f9930feffa61be0eddcda5b55fc592906ef75621d4b52c07400a97084d8701557b13f7d2aae0cb64f808431f469e566ef3fe0a3a131dcb775a6 + checksum: e9ae98d22f69c88e7aff5b8778dc01c361ef635580e82d29e5c60a6533cc8f4d820803e67d7432581af0cc4fb49973125076ee3b90df191d153e223c004193b2 languageName: node linkType: hard @@ -11317,23 +11313,23 @@ __metadata: languageName: node linkType: hard -"terser@npm:5.11.0": - version: 5.11.0 - resolution: "terser@npm:5.11.0" +"terser@npm:5.14.2": + version: 5.14.2 + resolution: "terser@npm:5.14.2" dependencies: + "@jridgewell/source-map": ^0.3.2 acorn: ^8.5.0 commander: ^2.20.0 - source-map: ~0.7.2 source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: cc72b7a0e87421b5a6ef3f8a3c86ef251f6e7f8d6327b83c63045b8991a041cc4a42ea64e07701128e1786489902c8c44b5904056b0f12ceedb52924d493db04 + checksum: cabb50a640d6c2cfb351e4f43dc7bf7436f649755bb83eb78b2cacda426d5e0979bd44e6f92d713f3ca0f0866e322739b9ced888ebbce6508ad872d08de74fcc languageName: node linkType: hard "terser@npm:^5.14.1": - version: 5.16.4 - resolution: "terser@npm:5.16.4" + version: 5.16.5 + resolution: "terser@npm:5.16.5" dependencies: "@jridgewell/source-map": ^0.3.2 acorn: ^8.5.0 @@ -11341,7 +11337,7 @@ __metadata: source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: 92c7b38b7322340993d6a3578d74818c3556f362b3014f18a9b3d079ac7fa5da57954fda9a0d40d53013bc3ba82f50758296881abc40761e1bafdbde9a2ab967 + checksum: f2c1a087fac7f4ff04b1b4e79bffc52e2fc0b068b98912bfcc0b341184c284c30c19ed73f76ac92b225b71668f7f8fc586d99a7e50a29cdc1c916cb1265522ec languageName: node linkType: hard @@ -11618,9 +11614,9 @@ __metadata: linkType: hard "ua-parser-js@npm:^0.7.30": - version: 0.7.33 - resolution: "ua-parser-js@npm:0.7.33" - checksum: 1510e9ec26fcaf0d8c6ae8f1078a8230e8816f083e1b5f453ea19d06b8ef2b8a596601c92148fd41899e8b3e5f83fa69c42332bd5729b931a721040339831696 + version: 0.7.34 + resolution: "ua-parser-js@npm:0.7.34" + checksum: ddb7b8b590af49f37eaac37579ac98274f59fff3990610f45e33893aa62ce5b8fc265459cb78b9c9fa3fbbbaad5b2d7939303f18cbd7e8c25c96e57009443c42 languageName: node linkType: hard @@ -11985,12 +11981,12 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.67.0": - version: 5.67.0 - resolution: "webpack@npm:5.67.0" +"webpack@npm:5.70.0": + version: 5.70.0 + resolution: "webpack@npm:5.70.0" dependencies: - "@types/eslint-scope": ^3.7.0 - "@types/estree": ^0.0.50 + "@types/eslint-scope": ^3.7.3 + "@types/estree": ^0.0.51 "@webassemblyjs/ast": 1.11.1 "@webassemblyjs/wasm-edit": 1.11.1 "@webassemblyjs/wasm-parser": 1.11.1 @@ -11998,7 +11994,7 @@ __metadata: acorn-import-assertions: ^1.7.6 browserslist: ^4.14.5 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.8.3 + enhanced-resolve: ^5.9.2 es-module-lexer: ^0.9.0 eslint-scope: 5.1.1 events: ^3.2.0 @@ -12018,7 +12014,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: a7f810a5e1d4d78b533ca0caf42fa889839326073cedd3ac8e59e5c4890ca864ab0265fa5b2608715746ff3e34cbfaf4f15d56a92bc3f717a2f5c13202d58b6c + checksum: 00439884a9cdd5305aed3ce93735635785a15c5464a6d2cfce87e17727a07585de02420913e82aa85ddd2ae7322175d2cfda6ac0878a17f061cb605e6a7db57a languageName: node linkType: hard From db55575cc37273d634c3794889f1b02432836596 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 8 Mar 2023 16:20:09 +0100 Subject: [PATCH 186/498] Fixed a bug that prevent update of existing teams. --- .../competition-builder-team-dialog.component.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index ce7ba2529..dafb848bd 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -45,6 +45,7 @@ export class CompetitionBuilderTeamDialogComponent { @Inject(MAT_DIALOG_DATA) private team?: ApiTeam ) { this.form = new FormGroup({ + id: new FormControl(team?.id), name: new FormControl(team?.name, [Validators.required, Validators.minLength(3)]), color: new FormControl(team?.color ? team.color : CompetitionBuilderTeamDialogComponent.randomColor(), [ Validators.required, @@ -56,9 +57,7 @@ export class CompetitionBuilderTeamDialogComponent { users: new FormControl(team?.users != null ? team.users : []), userInput: new FormControl(''), }); - this.availableUsers = this.userService.getApiV2UserList() - //getApiV1UserList() - .pipe( + this.availableUsers = this.userService.getApiV2UserList().pipe( map((value) => { return value.filter((user) => user.role !== 'JUDGE' && user.role !== 'VIEWER'); }), @@ -148,10 +147,10 @@ export class CompetitionBuilderTeamDialogComponent { fetchData() { return { + id: this.form.get('id').value, name: this.form.get('name').value, color: this.form.get('color').value, logoData: this.form.get('logoData').value, - // logoId: this.form.get('logoId').value, users: this.form.get('users').value, } as ApiTeam; } From 1fbc593d60e2c8a4f2a582051f0fe67420575b92 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 8 Mar 2023 16:20:55 +0100 Subject: [PATCH 187/498] DbEvaluationTemplate now has a canBeEdited() method that is used to enforce immutability once a DbEvaluation for that template exists. --- .../AbstractEvaluationTemplateHandler.kt | 5 +- .../DeleteEvaluationTemplateHandler.kt | 9 +- .../ListEvaluationTemplatesHandler.kt | 2 +- .../rest/handler/template/ListTasksHandler.kt | 2 +- .../rest/handler/template/ListTeamHandler.kt | 2 +- .../template/ShowEvaluationTemplateHandler.kt | 2 +- .../UpdateEvaluationTemplateHandler.kt | 10 +- .../competition/ApiEvaluationOverview.kt | 4 +- .../competition/ApiEvaluationTemplate.kt | 3 +- .../model/template/DbEvaluationTemplate.kt | 15 +- doc/oas-client.json | 322 ++++++++++++++---- doc/oas.json | 175 +++++----- 12 files changed, 377 insertions(+), 174 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt index 185593860..90caea364 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt @@ -31,13 +31,14 @@ abstract class AbstractEvaluationTemplateHandler(protected val store: TransientE override val apiVersion = "v2" /** Convenience method to extract [DbEvaluationTemplate]'s ID from [Context]. */ - private fun evaluationTemplateId(ctx: Context): TemplateId = + private fun templateIdFromContext(ctx: Context): TemplateId = ctx.pathParamMap().getOrElse("templateId") { throw ErrorStatusException(404, "Parameter 'templateId' is missing!'", ctx) } /** Convenience method to extract [DbEvaluationTemplate] from [Context]. */ - protected fun competitionFromContext(ctx: Context): DbEvaluationTemplate = evaluationTemplateById(evaluationTemplateId(ctx), ctx) + protected fun evaluationTemplateFromContext(ctx: Context): DbEvaluationTemplate + = evaluationTemplateById(templateIdFromContext(ctx), ctx) /** Convenience method to extract [DbEvaluationTemplate] by ID. */ protected fun evaluationTemplateById(id: TemplateId, ctx: Context): DbEvaluationTemplate diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index 7de88bfcc..b1274edbb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -2,9 +2,9 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.template.DbEvaluationTemplate -import dev.dres.utilities.extensions.evaluationId import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -35,8 +35,11 @@ class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva ] ) override fun doDelete(ctx: Context): SuccessStatus = this.store.transactional { - val competitionToDelete = competitionFromContext(ctx) - competitionToDelete.delete() + val template = evaluationTemplateFromContext(ctx) + if (!template.canBeEdited()) { + throw ErrorStatusException(400, "Evaluation template ${template.id} can no longer be deleted.", ctx) + } + template.delete() SuccessStatus("Evaluation template with ID ${ctx.pathParam("templateId")} was deleted successfully.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index 72f3e31ba..425f84970 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -34,7 +34,7 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEval ) override fun doGet(ctx: Context) = this.store.transactional(true) { DbEvaluationTemplate.all().asSequence().map { - ApiEvaluationOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) + ApiEvaluationOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size(), it.canBeEdited()) }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index cc8ea433f..80dc8af9b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -37,6 +37,6 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplate ) override fun doGet(ctx: Context) = this.store.transactional(true) { - competitionFromContext(ctx).tasks.asSequence().map { it.toApi() }.toList() + evaluationTemplateFromContext(ctx).tasks.asSequence().map { it.toApi() }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index b089dc3e6..403893ac5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -37,6 +37,6 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateH ) override fun doGet(ctx: Context) = this.store.transactional(true) { - competitionFromContext(ctx).teams.asSequence().map { it.toApi() }.toList() + evaluationTemplateFromContext(ctx).teams.asSequence().map { it.toApi() }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index 6420a3e9a..b44bde09f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -34,6 +34,6 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvalu methods = [HttpMethod.GET] ) override fun doGet(ctx: Context)= this.store.transactional(true) { - competitionFromContext(ctx).toApi() + evaluationTemplateFromContext(ctx).toApi() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 4bac0fc06..ee2b7d7f4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -1,9 +1,7 @@ package dev.dres.api.rest.handler.template -import com.github.kittinunf.fuel.util.decodeBase64 import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.competition.ApiEvaluationTemplate -import dev.dres.api.rest.types.competition.team.ApiTeam import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -22,11 +20,6 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.query.* -import java.awt.Image -import java.awt.image.BufferedImage -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import javax.imageio.ImageIO /** * A [AbstractEvaluationTemplateHandler] that can be used to create a new [DbEvaluationTemplate]. @@ -65,6 +58,9 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C /* Store change. */ this.store.transactional { val existing = this.evaluationTemplateById(apiValue.id, ctx) + if (!existing.canBeEdited()) { + throw ErrorStatusException(400, "Evaluation template ${apiValue.id} can no longer be edited.", ctx) + } /* Update core information. */ existing.name = apiValue.name diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt index 034757e2e..4faa5cfbe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt @@ -6,6 +6,6 @@ import dev.dres.data.model.template.DbEvaluationTemplate * An overview over a [DbEvaluationTemplate]. * * @author Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ -data class ApiEvaluationOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int) \ No newline at end of file +data class ApiEvaluationOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int, val editable: Boolean) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt index 8475c9961..6dd45f0ed 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt @@ -24,6 +24,7 @@ data class ApiEvaluationTemplate( val tasks: List, val teams: List, val teamGroups: List, - val judges: List + val judges: List, + val canBeEdited: Boolean? = null/* Optional, because not required when sent by the client. */ ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 76c15ed63..91b1f9ef1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -11,6 +11,8 @@ import dev.dres.data.model.template.task.DbTaskType import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.run.DbEvaluation +import dev.dres.data.model.run.DbTask import dev.dres.data.model.template.interfaces.EvaluationTemplate import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard @@ -39,10 +41,7 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio set(value) { this.id = value } /** The name held by this [DbEvaluationTemplate]. Must be unique!*/ - var name by xdRequiredStringProp(unique = true, trimmed = true) - - /** If set, this [DbEvaluationTemplate] is considered a template!*/ - var isTemplate by xdBooleanProp() + var name by xdRequiredStringProp(trimmed = true) /** An optional description of this [DbEvaluationTemplate]. */ var description by xdStringProp(trimmed = false) @@ -76,6 +75,7 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio id = this.id, name = this.name, description = this.description, + canBeEdited = this.canBeEdited(), taskTypes = this.taskTypes.asSequence().map { it.toApi() }.toList(), taskGroups = this.taskGroups.asSequence().map { it.toApi() }.toList(), tasks = this.tasks.asSequence().map { it.toApi() }.toList(), @@ -84,6 +84,13 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio judges = this.judges.asSequence().map { it.id }.toList() ) + /** + * Checks if this [DbEvaluationTemplate] can be edited. This is the case only, if no active [DbEvaluation] exists that uses it. + * + * This is a convenience method and requires an active transaction context. + */ + fun canBeEdited() = DbEvaluation.filter { it.template.id eq this@DbEvaluationTemplate.id }.none() + /** * Generates and returns a list of all [DbMediaItem] for this [DbEvaluationTemplate]. * diff --git a/doc/oas-client.json b/doc/oas-client.json index fe2d44ac5..b0208bc29 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2748,15 +2748,132 @@ "security" : [ ] } }, - "/api/v2/template/{templateId}" : { - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", "parameters" : [ { - "name" : "templateId", + "name" : "teamId", "in" : "path", - "description" : "The evaluation template ID.", + "description" : "The team ID.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -2764,6 +2881,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -2808,7 +2935,9 @@ }, "deprecated" : false, "security" : [ ] - }, + } + }, + "/api/v2/template/{templateId}" : { "get" : { "tags" : [ "Template" ], "summary" : "Loads the detailed definition of a specific evaluation template.", @@ -2869,6 +2998,66 @@ "deprecated" : false, "security" : [ ] }, + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, "patch" : { "tags" : [ "Template" ], "summary" : "Updates an existing evaluation template.", @@ -3008,7 +3197,7 @@ "/api/v2/template/{templateId}/team/list" : { "get" : { "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific competition.", + "summary" : "Lists all the teams of a specific evaluation template.", "operationId" : "getApiV2TemplateByTemplateIdTeamList", "parameters" : [ { "name" : "templateId", @@ -3071,21 +3260,11 @@ } }, "/api/v2/user" : { - "post" : { + "get" : { "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3097,16 +3276,6 @@ } } }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "500" : { "description" : "Server Error", "content" : { @@ -3121,11 +3290,21 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "post" : { "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3137,6 +3316,16 @@ } } }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "500" : { "description" : "Server Error", "content" : { @@ -3245,14 +3434,14 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User ID", + "description" : "User's UID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -3260,16 +3449,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -3281,18 +3460,8 @@ } } }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -3315,14 +3484,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -3330,6 +3499,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -3341,8 +3520,18 @@ } } }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { - "description" : "If the user could not be found.", + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -3645,6 +3834,9 @@ "items" : { "type" : "string" } + }, + "canBeEdited" : { + "type" : "boolean" } }, "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] diff --git a/doc/oas.json b/doc/oas.json index 81ff92da8..fa7cde027 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -388,10 +388,10 @@ } }, "/api/v2/collection" : { - "post" : { + "patch" : { "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -438,10 +438,10 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "post" : { "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -525,10 +525,10 @@ } }, "/api/v2/collection/{collectionId}" : { - "delete" : { + "get" : { "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -546,13 +546,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -561,8 +561,8 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -575,10 +575,10 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "delete" : { "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -596,13 +596,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -611,8 +611,8 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -4225,10 +4225,10 @@ } }, "/api/v2/template/{templateId}" : { - "delete" : { + "get" : { "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4246,7 +4246,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } @@ -4285,10 +4285,10 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "delete" : { "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4306,7 +4306,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -4547,21 +4547,11 @@ } }, "/api/v2/user" : { - "post" : { + "get" : { "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4573,16 +4563,6 @@ } } }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "500" : { "description" : "Server Error", "content" : { @@ -4597,11 +4577,21 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "post" : { "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4613,6 +4603,16 @@ } } }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "500" : { "description" : "Server Error", "content" : { @@ -4781,14 +4781,14 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User ID", + "description" : "User's UID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -4796,16 +4796,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4817,18 +4807,8 @@ } } }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -4851,14 +4831,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -4866,6 +4846,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4877,8 +4867,18 @@ } } }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { - "description" : "If the user could not be found.", + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -5181,6 +5181,9 @@ "items" : { "type" : "string" } + }, + "canBeEdited" : { + "type" : "boolean" } }, "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] From b31d3729876d524ebb11f41a3d95b1a71b917447 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 8 Mar 2023 16:21:29 +0100 Subject: [PATCH 188/498] Removed todo, since this has been addressed in another way. --- .../rest/handler/evaluation/admin/CreateEvaluationHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index ab3be49bb..1cb716e96 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -99,7 +99,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs /* Prepare evaluation. */ val evaluation = DbEvaluation.new { this.name = message.name - this.template = template /* TODO: Create copy. */ + this.template = template this.type = message.type.toDb() this.allowRepeatedTasks = message.properties.allowRepeatedTasks this.participantCanView = message.properties.participantCanView From 0c024368a8c11f94cd44c1ab6d5371bbbbda28a3 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 13:59:21 +0100 Subject: [PATCH 189/498] Added check to not persist submissions that are empty after transformation --- .../kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt | 5 +++++ .../kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 2c500a8da..5e0d8a125 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -518,6 +518,11 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Apply transformations to submissions */ val transformedSubmission = task.transformer.transform(submission) + /* Check if there are answers left after transformation */ + if (transformedSubmission.answers.isEmpty()) { + throw IllegalStateException("Submission contains no valid answer sets") + } + /* At this point, the submission is considered valid and is persisted */ /* Validator is applied to each answer set */ transformedSubmission.answerSets().forEach { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 318675acc..24596b3d0 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -462,6 +462,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Apply transformations to submissions */ val transformedSubmission = task.transformer.transform(submission) + /* Check if there are answers left after transformation */ + if (transformedSubmission.answers.isEmpty()) { + throw IllegalStateException("Submission contains no valid answer sets") + } + /* At this point, the submission is considered valid and is persisted */ /* Validator is applied to each answer set */ transformedSubmission.answerSets().forEach { From c1ec3c9208c2c46c64d11a03c25e805b064776fc Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 14:09:40 +0100 Subject: [PATCH 190/498] RunManager.postSubmission no longer returns VerdictStatus as it doesn't match new semantics anymore --- .../submission/LegacySubmissionHandler.kt | 5 ++++- .../handler/submission/SubmissionHandler.kt | 19 +++++-------------- .../run/InteractiveAsynchronousRunManager.kt | 4 ++-- .../run/InteractiveSynchronousRunManager.kt | 3 +-- .../dev/dres/run/NonInteractiveRunManager.kt | 2 +- .../main/kotlin/dev/dres/run/RunManager.kt | 3 +-- 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 84be5ff6b..45c80c9ef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -130,7 +130,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v val submission = toSubmission(userId, run, time, ctx) val rac = RunActionContext.runActionContext(ctx, run) - val result = try { + try { run.postSubmission(rac, submission) } catch (e: SubmissionRejectedException) { throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) @@ -150,6 +150,9 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS)) { //pre-generate preview generatePreview(submission.answerSets().first()) } + + val result = DbSubmission.filter { it.id eq submission.submissionId }.firstOrNull()?.answerSets?.first()?.status?.let { VerdictStatus.fromDb(it) } ?: VerdictStatus.INDETERMINATE + submission to result } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 5a0ce30d2..1f5e59d74 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.Config @@ -23,7 +24,7 @@ import io.javalin.http.bodyAsClass import jetbrains.exodus.database.TransientEntityStore import org.slf4j.LoggerFactory -class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): PostRestHandler, AccessManagedRestHandler { +class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): PostRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.PARTICIPANT) @@ -34,7 +35,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con private val logger = LoggerFactory.getLogger(this.javaClass) - override fun doPost(ctx: Context): SuccessfulSubmissionsStatus { + override fun doPost(ctx: Context): SuccessStatus { return this.store.transactional { @@ -50,7 +51,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con throw ErrorStatusException(400, "Invalid submission, cannot parse: ${e.message}", ctx) } - val result = try { + try { runManager.postSubmission(rac, apiSubmission) } catch (e: SubmissionRejectedException) { throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) @@ -64,19 +65,9 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con DbAuditLogger.submission(apiSubmission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) - logger.info("Submission ${apiSubmission.submissionId} received status $result.") - return@transactional when (result) { - VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") - VerdictStatus.WRONG -> SuccessfulSubmissionsStatus(ApiVerdictStatus.WRONG, "Submission incorrect! Try again") - VerdictStatus.INDETERMINATE -> { - ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") - } + return@transactional SuccessStatus("Submission received") - VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(ApiVerdictStatus.UNDECIDABLE,"Submission undecidable. Try again!") - else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) - } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 5e0d8a125..356f6468a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -500,7 +500,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ - override fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: ApiSubmission) = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } require(submission.answerSets().count() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ @@ -537,7 +537,7 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) - return transformedSubmission.answerSets().first().status() + } /** diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 24596b3d0..9889f7a06 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -448,7 +448,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param context The [RunActionContext] used for the invocation * @param submission [ApiSubmission] that should be registered. */ - override fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus = this.stateLock.read { + override fun postSubmission(context: RunActionContext, submission: ApiSubmission) = this.stateLock.read { /* Register submission. */ val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") @@ -486,7 +486,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) - return submission.answerSets().first().status() } /** diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index d4e759584..caaa93b4f 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -167,7 +167,7 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation */ override fun tasks(context: RunActionContext): List = this.evaluation.tasks - override fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus { + override fun postSubmission(context: RunActionContext, submission: ApiSubmission) { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index cd775678a..695b95a21 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -109,10 +109,9 @@ interface RunManager : Runnable { * @param context The [RunActionContext] used for the invocation * @param submission The [ApiSubmission] to be posted. * - * @return [DbVerdictStatus] of the [ApiSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(context: RunActionContext, submission: ApiSubmission): VerdictStatus + fun postSubmission(context: RunActionContext, submission: ApiSubmission) /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. From 3737b04a891fc04fbf64a76f82039e796bf1d353 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Mar 2023 16:06:34 +0100 Subject: [PATCH 191/498] Lock status is now properly displayed. --- .../competition-builder.component.html | 22 +++++----- .../competition-builder.component.ts | 2 + .../competition-list.component.html | 26 +++++++----- .../competition-list.component.ts | 2 +- .../src/app/competition/competition.module.ts | 40 ++++++++++--------- 5 files changed, 53 insertions(+), 39 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder.component.html index e3342afa2..171f03f63 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.html @@ -3,17 +3,21 @@ -

Edit competition {{ competitionId }}

-

Edit competition {{ competitionId }} (unsaved changes)

+ +
+

Edit evaluation template {{ competitionId }} + (unsaved changes) + lock +

-
diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 8e57a8071..4faefb936 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -240,6 +240,8 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat this.competitionService.getApiV2TemplateByTemplateId(this.competitionId).subscribe( (c) => { this.competition = c; + + this.form.disable() this.form.get('name').setValue(c.name); this.form.get('description').setValue(c.description); // TODO fetch other stuff diff --git a/frontend/src/app/competition/competition-list/competition-list.component.html b/frontend/src/app/competition/competition-list/competition-list.component.html index 9ccc70aa6..b5ac67bb1 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.html +++ b/frontend/src/app/competition/competition-list/competition-list.component.html @@ -1,21 +1,21 @@
-

Competitions

+

Evaluation Templates

-
@@ -31,24 +31,23 @@ - - @@ -83,6 +82,13 @@ {{ row.teamCount }} + + Status + + lock + + + diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts index 79a274ff3..346ca8aff 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ b/frontend/src/app/competition/competition-list/competition-list.component.ts @@ -24,7 +24,7 @@ import { }) export class CompetitionListComponent implements AfterViewInit { /** */ - displayedColumns = ['actions', 'id', 'name', 'description', 'taskCount', 'teamCount']; + displayedColumns = ['actions', 'id', 'name', 'description', 'taskCount', 'teamCount', 'status']; competitions: ApiEvaluationOverview[] = []; waitingForRun = false; diff --git a/frontend/src/app/competition/competition.module.ts b/frontend/src/app/competition/competition.module.ts index 884bbc59c..abf21bf40 100644 --- a/frontend/src/app/competition/competition.module.ts +++ b/frontend/src/app/competition/competition.module.ts @@ -19,27 +19,29 @@ import { CompetitionStartDialogComponent } from './competition-list/competition- import { MatSelectModule } from '@angular/material/select'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { SharedModule } from '../shared/shared.module'; +import {MatChipsModule} from "@angular/material/chips"; @NgModule({ - imports: [ - MatTableModule, - MatIconModule, - MatButtonModule, - MatTooltipModule, - MatDialogModule, - MatFormFieldModule, - MatInputModule, - FormsModule, - ReactiveFormsModule, - CommonModule, - MatListModule, - MatProgressSpinnerModule, - MatMenuModule, - CompetitionBuilderModule, - MatSelectModule, - MatCheckboxModule, - SharedModule, - ], + imports: [ + MatTableModule, + MatIconModule, + MatButtonModule, + MatTooltipModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + FormsModule, + ReactiveFormsModule, + CommonModule, + MatListModule, + MatProgressSpinnerModule, + MatMenuModule, + CompetitionBuilderModule, + MatSelectModule, + MatCheckboxModule, + SharedModule, + MatChipsModule, + ], exports: [CompetitionListComponent], declarations: [CompetitionListComponent, CompetitionCreateDialogComponent, CompetitionStartDialogComponent], providers: [], From ac8f5eee4a1988b0f15ac668792ad7a1498d1131 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 13 Mar 2023 16:06:48 +0100 Subject: [PATCH 192/498] Simplification --- backend/src/main/kotlin/dev/dres/run/RunExecutor.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index d601508e0..bec1a81cc 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -177,8 +177,7 @@ object RunExecutor : Consumer { this.connectedClients.remove(connection.httpSessionId) this.runManagerLock.read { for (m in this.runManagers) { - if (this.observingClients[m.key]?.contains(connection) == true) { - this.observingClients[m.key]?.remove(connection) + if (this.observingClients[m.key]?.remove(connection) == true) { m.value.wsMessageReceived(session, ClientMessage(m.key, ClientMessageType.UNREGISTER)) /* Send implicit unregister message associated with a disconnect. */ } } From 3e0a2ad6d04bd6c5118727d1918f6217870d5285 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 16:07:02 +0100 Subject: [PATCH 193/498] All versions of AnswerSet now have an id --- .../api/rest/handler/submission/LegacySubmissionHandler.kt | 2 ++ .../kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt | 3 ++- .../main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt | 4 +++- .../kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt | 1 + .../test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt | 1 + .../test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt | 2 ++ .../kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt | 2 ++ 7 files changed, 13 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 45c80c9ef..51ea01fff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -33,6 +33,7 @@ import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.nio.file.Files import java.nio.file.Paths +import java.util.* /** * An [GetRestHandler] used to process [DbSubmission]s. @@ -351,6 +352,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v /* Create AnswerSet. */ answerSets.add( ApiAnswerSet( + id = UUID.randomUUID().toString(), status = ApiVerdictStatus.INDETERMINATE, taskId = task.taskId, answers = listOf(answer) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index d68424328..33dea1aea 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -18,6 +18,7 @@ import kotlinx.dnq.query.firstOrNull * @version 1.0.0 */ data class ApiAnswerSet( + override val id: AnswerSetId, var status: ApiVerdictStatus, override val taskId: TaskId, val answers: List @@ -43,7 +44,7 @@ data class ApiAnswerSet( * * @return [DbAnswerSet] */ - fun toNewDb(): DbAnswerSet { + fun toNewDb(): DbAnswerSet { //id is ignored here, since the db element id newly generated return DbAnswerSet.new { this.status = this@ApiAnswerSet.status.toDb() this.task = DbTask.filter { it.id eq this@ApiAnswerSet.taskId }.first() diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index 306356d23..a2746ac55 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -3,7 +3,9 @@ package dev.dres.data.model.submissions import dev.dres.data.model.run.Task import dev.dres.data.model.run.TaskId -interface AnswerSet { //TODO +typealias AnswerSetId = String +interface AnswerSet { + val id : AnswerSetId val taskId: TaskId val submission: Submission diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 18a5cb39a..2c76feab5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -49,6 +49,7 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { * @return [ApiAnswerSet] */ fun toApi(): ApiAnswerSet = ApiAnswerSet( + id = this.id, status = this.status.toApi(), taskId = this.taskId, answers = this.answers.asSequence().map { it.toApi() }.toList() diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 57c067aa6..80db78ff1 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -40,6 +40,7 @@ class AvsTaskScorerTest { private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long, taskId: String) = listOf( ApiAnswerSet( + "dummyId", status, taskId, listOf( diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 1a77bb36b..253a7e19e 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -31,6 +31,7 @@ class KisTaskScorerTest { private val wrongAnswer = listOf( ApiAnswerSet( + "wrong", ApiVerdictStatus.WRONG, "task", listOf( @@ -45,6 +46,7 @@ class KisTaskScorerTest { private val correctAnswer = listOf( ApiAnswerSet( + "correct", ApiVerdictStatus.CORRECT, "task", listOf( diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index cf4d16ba6..d7f502962 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -53,6 +53,7 @@ class LegacyAvsTaskScorerTest { private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem) = listOf( ApiAnswerSet( + "dummyId", status, "task", listOf( @@ -67,6 +68,7 @@ class LegacyAvsTaskScorerTest { private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long) = listOf( ApiAnswerSet( + "", status, "task", listOf( From 8a3371acaea5a7f3ac92c82bf8f314b531c9a77b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 16:29:47 +0100 Subject: [PATCH 194/498] Changed semantics of override submission mechanism --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 5 +- ....kt => OverrideAnswerSetVerdictHandler.kt} | 54 +-- .../rest/types/evaluation/ApiVerdictStatus.kt | 2 +- .../dev/dres/run/audit/DbAuditLogger.kt | 17 +- doc/oas.json | 325 +++++++++--------- 5 files changed, 205 insertions(+), 198 deletions(-) rename backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/{OverrideSubmissionHandler.kt => OverrideAnswerSetVerdictHandler.kt} (54%) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 4c40a5fea..3e5446cac 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -41,7 +41,6 @@ import io.javalin.apibuilder.ApiBuilder.* import io.javalin.http.staticfiles.Location import io.javalin.community.ssl.SSLPlugin import io.javalin.openapi.CookieAuth -import io.javalin.openapi.OpenApiInfo import io.javalin.openapi.plugin.* import io.javalin.openapi.plugin.swagger.SwaggerConfiguration import io.javalin.openapi.plugin.swagger.SwaggerPlugin @@ -50,8 +49,6 @@ import org.eclipse.jetty.server.* import org.eclipse.jetty.util.thread.QueuedThreadPool import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory -import java.util.function.BiConsumer -import java.util.function.Consumer /** * This is a singleton instance of the RESTful API @@ -160,7 +157,7 @@ object RestApi { StopTaskHandler(store), AdjustDurationHandler(store), AdjustPropertiesHandler(store), - OverrideSubmissionHandler(store), + OverrideAnswerSetVerdictHandler(store), ForceViewerHandler(store), ListViewersHandler(store), ListSubmissionsHandler(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt similarity index 54% rename from backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt rename to backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt index 33536b961..cf54b1598 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideSubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt @@ -1,10 +1,10 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PatchRestHandler -import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.DbVerdictStatus @@ -16,6 +16,9 @@ import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.any +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first /** * A [PatchRestHandler] used to overwrite [DbVerdictStatus] information. @@ -25,58 +28,57 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class OverrideSubmissionHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { - override val route: String = "evaluation/admin/{evaluationId}/submission/override" +class OverrideAnswerSetVerdictHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { + override val route: String = "evaluation/admin/{evaluationId}/override/{answerSetId}" @OpenApi( - summary = "Override the submission status for a given submission.", - path = "/api/v2/evaluation/admin/{evaluationId}/submission/override", + summary = "Override the verdict status of an AnswerSet.", + path = "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.PATCH], pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), + OpenApiParam("answerSetId", String::class, "The ID of the AnswerSet.", required = true, allowEmptyValue = false) ], - requestBody = OpenApiRequestBody([OpenApiContent(ApiSubmission::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiVerdictStatus::class)]), tags = ["Evaluation Administrator"], responses = [ - OpenApiResponse("200", [OpenApiContent(ApiSubmission::class)]), + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doPatch(ctx: Context): ApiSubmission { + override fun doPatch(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() + val answerSetId = ctx.pathParamMap()["answerSetId"] ?: throw ErrorStatusException(400, "Parameter 'answerSetId' is missing!'", ctx) val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - /* TODO: Make this work for batched submissions! */ - /* Extract HTTP body. */ - val submissionInfo = try { - ctx.bodyAsClass() + val apiVerdictStatus = try { + ctx.bodyAsClass() } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - /* Perform sanity check. */ - if (submissionInfo.answers.first().status == ApiVerdictStatus.INDETERMINATE ) { + /* Perform sanity check. */ + if (apiVerdictStatus == ApiVerdictStatus.INDETERMINATE ) { throw ErrorStatusException(400, "Submission status can not be set to INDETERMINATE.", ctx) } return this.store.transactional { val rac = RunActionContext.runActionContext(ctx, evaluationManager) - /* Sanity check to see, whether the submission exists */ - if (evaluationManager.allSubmissions(rac).none { it.id == submissionInfo.submissionId }) { - throw ErrorStatusException(404, "The given submission $submissionInfo was not found.", ctx) - } - if (evaluationManager.updateSubmission(rac, submissionInfo.submissionId, submissionInfo.answers.first().status.toDb())) { - val submission = evaluationManager.allSubmissions(rac).single { it.id == submissionInfo.submissionId } - DbAuditLogger.overrideSubmission(submission, DbAuditLogSource.REST, ctx.sessionToken()) - submission.toApi() - } else { - throw ErrorStatusException(500, "Could not update the submission. Please see the backend's log.", ctx) - } + val dbSubmission = evaluationManager.allSubmissions(rac).find { submission -> submission.answerSets.filter { it.id eq answerSetId }.any() } ?: + throw ErrorStatusException(404, "No AnswerSet with Id '$answerSetId' found.", ctx) + + val answerSet = dbSubmission.answerSets.filter { it.id eq answerSetId }.first() + val verdictStatus = apiVerdictStatus.toDb() + answerSet.status = verdictStatus + + DbAuditLogger.overrideVerdict(answerSet, verdictStatus, DbAuditLogSource.REST, ctx.sessionToken()) + + SuccessStatus("Set status of AnswerSet '$answerSetId' to '${apiVerdictStatus.name}'") } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt index 23d5a61c0..70f8a2ab8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt @@ -9,7 +9,7 @@ import dev.dres.data.model.submissions.DbVerdictStatus * @author Ralph Gasser * @version 1.0.0 */ -enum class ApiVerdictStatus() { +enum class ApiVerdictStatus { CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; /** diff --git a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt index d5d8c9ed0..89509ee75 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt @@ -12,7 +12,6 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.AnswerSetValidator -import kotlinx.dnq.query.first /** * Audit logging instance of DRES. Requires one-time initialisation @@ -156,20 +155,16 @@ object DbAuditLogger { } /** - * Logs a submission override to DRES. - * - * @param submission The [DbSubmission] that was overriden (new snapshot). - * @param api The [DbAuditLogSource] - * @param sessionToken The identifier of the user session. + * Logs a AnswerSet VerdictStatus override to DRES. */ - fun overrideSubmission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?) { + fun overrideVerdict(answerSet: DbAnswerSet, verdict: DbVerdictStatus, api: DbAuditLogSource, sessionToken: SessionToken?) { DbAuditLogEntry.new { this.type = DbAuditLogType.SUBMISSION_STATUS_OVERWRITE this.source = api - this.submissionId = submission.id - this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId - this.taskId = submission.answerSets.first().task.id /* TODO: Multiple verdicts. */ - this.description = "Verdict: ${submission.answerSets.first().status.description}" + this.submissionId = answerSet.submission.submissionId + this.evaluationId = answerSet.task.evaluation.evaluationId + this.taskId =answerSet.task.id + this.description = "Set verdict of '${answerSet.id}' to ${verdict.description}" this.session = sessionToken } } diff --git a/doc/oas.json b/doc/oas.json index fa7cde027..3c0d9ba45 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -388,10 +388,10 @@ } }, "/api/v2/collection" : { - "patch" : { + "post" : { "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -438,10 +438,10 @@ "deprecated" : false, "security" : [ ] }, - "post" : { + "patch" : { "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -525,10 +525,10 @@ } }, "/api/v2/collection/{collectionId}" : { - "get" : { + "delete" : { "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -546,13 +546,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -561,8 +561,8 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -575,10 +575,10 @@ "deprecated" : false, "security" : [ ] }, - "delete" : { + "get" : { "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -596,13 +596,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -611,8 +611,8 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -1152,15 +1152,25 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -1168,13 +1178,23 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -1214,11 +1234,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1230,23 +1250,13 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationOverview" } } } @@ -1286,15 +1296,15 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "The evaluation ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -1302,6 +1312,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -1332,17 +1352,27 @@ } } } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1353,16 +1383,6 @@ "schema" : { "type" : "string" } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } } ], "responses" : { "200" : { @@ -1370,10 +1390,7 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -1397,27 +1414,17 @@ } } } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { - "patch" : { + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the submission status for a given submission.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -1428,24 +1435,27 @@ "schema" : { "type" : "string" } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } } } } @@ -4225,10 +4235,10 @@ } }, "/api/v2/template/{templateId}" : { - "get" : { + "delete" : { "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4246,7 +4256,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -4285,10 +4295,10 @@ "deprecated" : false, "security" : [ ] }, - "delete" : { + "get" : { "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4306,7 +4316,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } @@ -4547,11 +4557,21 @@ } }, "/api/v2/user" : { - "get" : { + "post" : { "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4563,6 +4583,16 @@ } } }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "500" : { "description" : "Server Error", "content" : { @@ -4577,21 +4607,11 @@ "deprecated" : false, "security" : [ ] }, - "post" : { + "get" : { "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4603,16 +4623,6 @@ } } }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "500" : { "description" : "Server Error", "content" : { @@ -4781,14 +4791,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -4796,6 +4806,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4807,8 +4827,18 @@ } } }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { - "description" : "If the user could not be found.", + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -4831,14 +4861,14 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User ID", + "description" : "User's UID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -4846,16 +4876,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4867,18 +4887,8 @@ } } }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -5476,6 +5486,9 @@ "type" : "object", "additionalProperties" : false, "properties" : { + "id" : { + "type" : "string" + }, "status" : { "$ref" : "#/components/schemas/ApiVerdictStatus" }, @@ -5492,7 +5505,7 @@ "$ref" : "#/components/schemas/ApiSubmission" } }, - "required" : [ "status", "taskId", "answers", "submission" ] + "required" : [ "id", "status", "taskId", "answers", "submission" ] }, "ApiAnswerType" : { "type" : "string", From c60acdbfc9a519aaa8e2bd827b4e752e574a876a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 16:51:52 +0100 Subject: [PATCH 195/498] Cleanup in DbAuditLogger and judgement mechanism --- .../api/rest/types/evaluation/ApiAnswerSet.kt | 6 ++- .../dev/dres/run/audit/DbAuditLogger.kt | 29 ++--------- .../interfaces/JudgementValidator.kt | 2 +- .../judged/BasicJudgementValidator.kt | 50 +++++++++---------- 4 files changed, 35 insertions(+), 52 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index 33dea1aea..41f3c6273 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -9,6 +9,7 @@ import kotlinx.dnq.query.addAll import kotlinx.dnq.query.filter import kotlinx.dnq.query.first import kotlinx.dnq.query.firstOrNull +import java.util.* /** * The RESTful API equivalent for the type of [ApiAnswerSet]. @@ -18,7 +19,7 @@ import kotlinx.dnq.query.firstOrNull * @version 1.0.0 */ data class ApiAnswerSet( - override val id: AnswerSetId, + override val id: AnswerSetId = UUID.randomUUID().toString(), var status: ApiVerdictStatus, override val taskId: TaskId, val answers: List @@ -44,8 +45,9 @@ data class ApiAnswerSet( * * @return [DbAnswerSet] */ - fun toNewDb(): DbAnswerSet { //id is ignored here, since the db element id newly generated + fun toNewDb(): DbAnswerSet { return DbAnswerSet.new { + this.id = this@ApiAnswerSet.id this.status = this@ApiAnswerSet.status.toDb() this.task = DbTask.filter { it.id eq this@ApiAnswerSet.taskId }.first() this.answers.addAll( diff --git a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt index 89509ee75..6c534d7e7 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt @@ -11,7 +11,6 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator -import dev.dres.run.validation.interfaces.AnswerSetValidator /** * Audit logging instance of DRES. Requires one-time initialisation @@ -130,28 +129,10 @@ object DbAuditLogger { this.source = api this.submissionId = submission.submissionId this.evaluationId = submission.evaluationId - this.taskId = submission.answerSets().first().taskId /* TODO: Multiple verdicts. */ this.session = sessionToken this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.evaluationId, submission.answerSets().first().taskId, submission)) - } - - /** - * Logs the validation of a [DbSubmission] to DRES. - * - * @param submission The [DbSubmission] the submission that was validated - * @param validator The [AnswerSetValidator] instance. - */ - fun validateSubmission(submission: Submission, validator: AnswerSetValidator) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.SUBMISSION_VALIDATION - this.source = DbAuditLogSource.INTERNAL - this.submissionId = submission.submissionId - this.evaluationId = submission.evaluationId - this.taskId = submission.answerSets().first().taskId /* TODO: Multiple verdicts. */ - this.description = "Validator: ${validator::class.simpleName}, Verdict: ${submission.answerSets().first().status()}" /* TODO: Here name, there ID. Why? */ - } + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.evaluationId, null, submission)) } /** @@ -172,18 +153,18 @@ object DbAuditLogger { /** * Logs a submission override to DRES. * - * @param answerSet The [DbSubmission] that was overriden (new snapshot). + * @param answerSet The [DbSubmission] that was overwritten (new snapshot). * @param validator The [JudgementValidator] instance. * @param token The token generated by the judgement sub-system */ - fun prepareJudgement(answerSet: AnswerSet, validator: JudgementValidator, token: String) { + fun prepareJudgement(answerSet: DbAnswerSet, validator: JudgementValidator, token: String) { DbAuditLogEntry.new { this.type = DbAuditLogType.PREPARE_JUDGEMENT this.source = DbAuditLogSource.INTERNAL this.submissionId = answerSet.submission.submissionId - this.evaluationId = "" //FIXME lookup? + this.evaluationId = answerSet.task.evaluation.evaluationId this.taskId = answerSet.taskId - this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${answerSet.status()}" + this.description = "Token: $token, Validator: ${validator.id}, AnswerSet: ${answerSet.id}, Verdict: ${answerSet.status()}" } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index 7aa0533e6..a49a6a6d3 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -31,7 +31,7 @@ interface JudgementValidator { * * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - fun next(queue: String): Pair? + fun next(queue: String): Pair? /** * Places a verdict for the [Submission] identified by the given token. diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 7396f27f3..98790770c 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -5,6 +5,10 @@ import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.AnswerSetValidator +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -40,13 +44,13 @@ open class BasicJudgementValidator( private val updateLock = ReentrantReadWriteLock() /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ - private val queue: Queue = LinkedList() + private val queue: Queue = LinkedList() /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ private val queuedItemRanges: MutableMap> = HashMap() - /** Internal map of all [DbAnswerSet]s that have been retrieved by a judge and are pending a verdict. */ - private val waiting = HashMap() + /** Internal map of all [AnswerSetId]s that have been retrieved by a judge and are pending a verdict. */ + private val waiting = HashMap() /** Helper structure to keep track when a request needs to be re-scheduled */ private val timeouts = mutableListOf>() @@ -87,12 +91,6 @@ open class BasicJudgementValidator( return this.queue.isNotEmpty() } - /** - * Enqueues a [DbSubmission] with the internal judgment queue and updates its [DbVerdictStatus] - * to [DbVerdictStatus.INDETERMINATE]. - * - * @param submission The [DbSubmission] to validate. - */ override fun validate(answerSet: AnswerSet) = this.updateLock.read { //only validate submissions which are not already validated @@ -107,7 +105,7 @@ open class BasicJudgementValidator( answerSet.status(cachedStatus) } else if (itemRange !in queuedItemRanges.keys) { updateLock.write { - this.queue.offer(answerSet) + this.queue.offer(answerSet.id) answerSet.status(VerdictStatus.INDETERMINATE) this.queuedItemRanges[itemRange] = mutableListOf(answerSet) } @@ -126,18 +124,16 @@ open class BasicJudgementValidator( * * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - override fun next(queue: String): Pair? = updateLock.write { + override fun next(queue: String): Pair? = updateLock.write { checkTimeOuts() - val next = this.queue.poll() - return if (next != null) { - val token = UUID.randomUUID().toString() - this.waiting[token] = next - this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) - DbAuditLogger.prepareJudgement(next, this, token) - Pair(token, next) - } else { - null - } + val nextAnswerSetId = this.queue.poll() ?: return@write null + val next = DbAnswerSet.query(DbAnswerSet::id eq nextAnswerSetId).firstOrNull() ?: return@write null + val token = UUID.randomUUID().toString() + this.waiting[token] = nextAnswerSetId + this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) + DbAuditLogger.prepareJudgement(next, this, token) + Pair(token, next) + } /** @@ -153,10 +149,14 @@ open class BasicJudgementValidator( /** * */ - fun processSubmission(token: String, status: VerdictStatus): AnswerSet = this.updateLock.write { - val verdict = this.waiting[token] + fun processSubmission(token: String, status: VerdictStatus): DbAnswerSet = this.updateLock.write { + + val nextAnswerSetId = this.waiting[token] ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged - val itemRange = ItemRange(verdict.answers().first()) //TODO reason about semantics + + val answerSet = DbAnswerSet.query(DbAnswerSet::id eq nextAnswerSetId).firstOrNull() ?: throw IllegalStateException("DbAnswerSet with id '$nextAnswerSetId' not found") + + val itemRange = ItemRange(answerSet.answers().first()) //TODO reason about semantics //add to cache this.cache[itemRange] = status @@ -168,7 +168,7 @@ open class BasicJudgementValidator( val otherSubmissions = this.queuedItemRanges.remove(itemRange) otherSubmissions?.forEach { it.status(status) } - return@write verdict + return@write answerSet } /** From 84e726f7c69f0bf1c883301bfb997d8d4473836d Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 17:01:41 +0100 Subject: [PATCH 196/498] SubmissionFilter now explicitly uses ApiSubmission --- .../main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt | 3 ++- .../kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt | 3 ++- .../dev/dres/run/filter/CorrectPerTeamMemberFilter.kt | 3 ++- .../kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt | 3 ++- .../main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt | 3 ++- .../kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt | 3 ++- .../kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt | 3 ++- .../src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt | 5 +++-- .../dev/dres/run/filter/SubmissionFilterAggregator.kt | 6 +++--- .../main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt | 3 ++- .../dev/dres/run/filter/SubmissionRejectedException.kt | 5 +++-- .../kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt | 5 +++-- .../kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt | 5 +++-- 13 files changed, 31 insertions(+), 19 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt index dc924da95..317cf1d52 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission @@ -26,7 +27,7 @@ class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { override val reason = "Maximum number of correct submissions ($limit) exceeded for the team." constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "1").toIntOrNull() ?: 1) - override fun test(submission: Submission): Boolean { + override fun test(submission: ApiSubmission): Boolean { return submission.answerSets().all { answer -> answer.task().answerSets().filter { (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt index 2d8d21c8a..010b75b36 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission @@ -15,7 +16,7 @@ class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { override val reason: String = "Maximum number of correct submissions ($limit) exceeded for this item." - override fun test(submission: Submission): Boolean { + override fun test(submission: ApiSubmission): Boolean { val submittedItems = submission.answerSets().flatMap { it.answers() }.mapNotNull { it.item }.toSet() return submission.answerSets().all { answerSet -> answerSet.task().answerSets().filter { taskAnswerSets -> diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt index c3542c21f..cd5150a1c 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission @@ -25,7 +26,7 @@ class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "1").toIntOrNull() ?: 1) override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member." - override fun test(submission: Submission): Boolean { + override fun test(submission: ApiSubmission): Boolean { return submission.answerSets().all { answer -> answer.task().answerSets().filter { (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId && it.submission.memberId == submission.memberId diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index 5a8076a69..2c3a0a427 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import kotlinx.dnq.query.asSequence @@ -17,7 +18,7 @@ class DuplicateSubmissionFilter : SubmissionFilter { override val reason = "Duplicate submission received." - override fun test(submission: Submission): Boolean { //TODO semantics unclear + override fun test(submission: ApiSubmission): Boolean { //TODO semantics unclear // return submission.answerSets.asSequence().all { verdict -> // verdict.task.submissions.filter {set -> // set.answers.filter { diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt index b3a5b1911..40c38efcf 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerType @@ -14,6 +15,6 @@ import kotlinx.dnq.query.asSequence */ class ItemSubmissionFilter : SubmissionFilter { override val reason = "Submission does include temporal information, but whole item was expected" - override fun test(submission: Submission): Boolean + override fun test(submission: ApiSubmission): Boolean = submission.answerSets().any { it.answers().any { it.type() == AnswerType.ITEM } } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index f211a64c1..dc790d14b 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.task.options.DbSubmissionOption @@ -20,7 +21,7 @@ class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi /** * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? */ - override fun test(submission: Submission): Boolean { + override fun test(submission: ApiSubmission): Boolean { return submission.answerSets().all { answerSet -> answerSet.task().answerSets().filter { it.submission.teamId == submission.teamId }.count() < max } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 159bd21f6..7ff4942b1 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.submissions.Submission @@ -26,7 +27,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi /** * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? */ - override fun test(submission: Submission): Boolean { + override fun test(submission: ApiSubmission): Boolean { return submission.answerSets().all { answerSet -> answerSet.task().answerSets().filter { (it.submission.teamId == submission.teamId) and (it.status() == VerdictStatus.WRONG) }.count() < max } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt index 8d34d5afd..1994cae0a 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import org.slf4j.LoggerFactory @@ -12,7 +13,7 @@ import java.util.function.Predicate * @author Ralph Gasser * @version 1.1.0 */ -interface SubmissionFilter : Predicate { +interface SubmissionFilter : Predicate { companion object { private val LOGGER = LoggerFactory.getLogger(this::class.java) } @@ -25,7 +26,7 @@ interface SubmissionFilter : Predicate { * @param submission The [DbSubmission] to check. * @throws SubmissionRejectedException on failure */ - fun acceptOrThrow(submission: Submission) { + fun acceptOrThrow(submission: ApiSubmission) { if (!this.test(submission)) { LOGGER.info("Submission $${submission.submissionId} was rejected by filter: $reason") throw SubmissionRejectedException(submission, reason) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt index b3ff61b74..cd87e1e95 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt @@ -1,16 +1,16 @@ package dev.dres.run.filter -import dev.dres.data.model.submissions.Submission +import dev.dres.api.rest.types.evaluation.ApiSubmission class SubmissionFilterAggregator(private val filters: List) : SubmissionFilter { override val reason = "" //will never be relevant - override fun acceptOrThrow(submission: Submission) { + override fun acceptOrThrow(submission: ApiSubmission) { for (filter in filters) { filter.acceptOrThrow(submission) } } - override fun test(t: Submission): Boolean = filters.all { it.test(t) } + override fun test(t: ApiSubmission): Boolean = filters.all { it.test(t) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt index 43743dbfb..37884bc9e 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.task.options.DbSubmissionOption @@ -14,7 +15,7 @@ class SubmissionRateFilter(private val minDelayMS: Int = 500) : SubmissionFilter override val reason = "Not enough time has passed since last submission, gap needs to be at least $minDelayMS ms" - override fun test(t: Submission): Boolean { + override fun test(t: ApiSubmission): Boolean { // FIXME TODO("Not sure about the semantic here. Do we need a data model extension for this?") /*val mostRecentSubmissionTime = t.task!!.submissions.maxByOrNull { it.timestamp }?.timestamp ?: 0 diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt index b7eb885ed..091be13fc 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt @@ -1,12 +1,13 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission /** - * An exception that is thrown, when a [DbSubmission] is rejected by a [SubmissionFilter]. + * An exception that is thrown, when a [Submission] is rejected by a [SubmissionFilter]. * * @author Ralph Gasser * @version 1.0.0 */ -class SubmissionRejectedException(s: Submission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file +class SubmissionRejectedException(s: ApiSubmission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt index d1b1eb4bc..cc4d3307a 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerType @@ -16,6 +17,6 @@ import kotlinx.dnq.query.asSequence class TemporalSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include temporal information." - override fun test(submission: Submission): Boolean - = submission.answerSets().all { set -> set.answers().all { it.type() == AnswerType.TEMPORAL && it.start != null && it.end != null } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + override fun test(submission: ApiSubmission): Boolean + = submission.answerSets().all { set -> set.answers().all { it.type() == AnswerType.TEMPORAL && it.start != null && it.end != null } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt index aa6e195a6..24d46dbf6 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerType @@ -16,6 +17,6 @@ class TextualSubmissionFilter : SubmissionFilter { override val reason = "Submission does not include textual information (or is an empty submission)" - override fun test(submission: Submission): Boolean - = submission.answerSets().all { set -> set.answers().all { it.text != null && it.type() == AnswerType.TEXT } } /* TODO: Probably needs adjustment if this is supposed work with batch submissions. */ + override fun test(submission: ApiSubmission): Boolean + = submission.answerSets().all { set -> set.answers().all { it.text != null && it.type() == AnswerType.TEXT } } } From 4b29a890c309c894457212f450fb1ee5e1fc03c0 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 17:48:03 +0100 Subject: [PATCH 197/498] Reworked Submission Filters --- .../dev/dres/data/model/submissions/Answer.kt | 8 +++++ .../dres/data/model/submissions/AnswerSet.kt | 21 +++++++++++++ .../dres/run/filter/AllSubmissionFilter.kt | 3 +- .../run/filter/DuplicateSubmissionFilter.kt | 30 +++++++++++-------- .../run/filter/MaximumTotalPerTeamFilter.kt | 3 -- .../run/filter/MaximumWrongPerTeamFilter.kt | 4 +-- .../dres/run/filter/SubmissionRateFilter.kt | 23 +++++++++----- 7 files changed, 66 insertions(+), 26 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt index 48df1c6c6..a570d09ea 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt @@ -22,4 +22,12 @@ interface Answer { } fun type(): AnswerType + + infix fun eq(answer: Answer) : Boolean = + answer.type() == this.type() && + answer.item?.mediaItemId == this.item?.mediaItemId && + answer.item?.collection?.id == this.item?.collection?.id && + answer.start == this.start && + answer.end == this.end && + answer.text == this.text } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index a2746ac55..f975865c4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -15,4 +15,25 @@ interface AnswerSet { fun status() : VerdictStatus fun status(status: VerdictStatus) + + /** + * checks if the answers of a given [AnswerSet] have the same content as + */ + infix fun equivalent(answerSet: AnswerSet): Boolean { + + if (this.answers().count() != answerSet.answers().count()) { + return false + } + + val tmp = this.answers().toMutableList() + + //pairwise comparison + answerSet.answers().forEach { answer -> + //this assumes that there are no duplicates within an AnswerSet + tmp.removeIf { it eq answer } + } + + return tmp.isEmpty() + + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt index f25582323..fd4171ef3 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt @@ -1,5 +1,6 @@ package dev.dres.run.filter +import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.Submission @@ -13,5 +14,5 @@ import dev.dres.data.model.submissions.Submission object AllSubmissionFilter : SubmissionFilter { override val reason = "" //will never be relevant - override fun test(t: Submission): Boolean = true + override fun test(t: ApiSubmission): Boolean = true } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index 2c3a0a427..d09a73ee8 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -18,16 +18,22 @@ class DuplicateSubmissionFilter : SubmissionFilter { override val reason = "Duplicate submission received." - override fun test(submission: ApiSubmission): Boolean { //TODO semantics unclear -// return submission.answerSets.asSequence().all { verdict -> -// verdict.task.submissions.filter {set -> -// set.answers.filter { -// (it.text eq verdict.text) and (it.item eq verdict.item) and (it.start le (verdict.start -// ?: Long.MAX_VALUE)) and (it.end ge (verdict.end ?: Long.MIN_VALUE)) -// } -// }.isEmpty -// } - - return true - } + override fun test(submission: ApiSubmission): Boolean = + + submission.answers.groupBy { it.taskId }.all { + + val task = it.value.firstOrNull()?.task() ?: return@all true + + val presentSubmissions = task.answerSets().filter { it.submission.teamId == submission.teamId } + + presentSubmissions.forEach { presentAnswerSet -> + if (it.value.any { it equivalent presentAnswerSet }) { //any overlap in answerSets + return@all false + } + } + + true + } + + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index dc790d14b..557875047 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -18,9 +18,6 @@ class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi override val reason = "Maximum total number of submissions ($max) exceeded for the team" - /** - * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? - */ override fun test(submission: ApiSubmission): Boolean { return submission.answerSets().all { answerSet -> answerSet.task().answerSets().filter { it.submission.teamId == submission.teamId }.count() < max diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 7ff4942b1..57267df29 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -24,9 +24,7 @@ class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : Submissi constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) override val reason = "Maximum number of wrong submissions ($max) exceeded for the team" - /** - * TODO: This filter now takes all [Verdict]s into account. Is this desired behaviour? - */ + override fun test(submission: ApiSubmission): Boolean { return submission.answerSets().all { answerSet -> answerSet.task().answerSets().filter { (it.submission.teamId == submission.teamId) and (it.status() == VerdictStatus.WRONG) }.count() < max diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt index 37884bc9e..934f0c803 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt @@ -11,14 +11,23 @@ class SubmissionRateFilter(private val minDelayMS: Int = 500) : SubmissionFilter val PARAMETER_KEY_DELAY = "${DbSubmissionOption.MINIMUM_TIME_GAP.description}.delay" } - constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_DELAY, "500").toIntOrNull() ?: 500) + constructor(parameters: Map) : this( + parameters.getOrDefault(PARAMETER_KEY_DELAY, "500").toIntOrNull() ?: 500 + ) override val reason = "Not enough time has passed since last submission, gap needs to be at least $minDelayMS ms" - override fun test(t: ApiSubmission): Boolean { - // FIXME - TODO("Not sure about the semantic here. Do we need a data model extension for this?") - /*val mostRecentSubmissionTime = t.task!!.submissions.maxByOrNull { it.timestamp }?.timestamp ?: 0 - return (t.timestamp - mostRecentSubmissionTime) >= minDelayMS*/ - } + override fun test(submission: ApiSubmission): Boolean = + + submission.answers.groupBy { it.taskId }.all { + + val task = it.value.firstOrNull()?.task() ?: return@all true + + val mostRecentSubmissionTime = task.answerSets().filter { it.submission.teamId == submission.teamId } + .maxOfOrNull { it.submission.timestamp } ?: return@all true + + return (submission.timestamp - mostRecentSubmissionTime) >= minDelayMS + + } + } From 62b5e3e5df6ca5a33d859bda3d4a4ea1f74953c2 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 20:27:39 +0100 Subject: [PATCH 198/498] Cleanup in InferredAveragePrecisionScorer --- .../scorer/InferredAveragePrecisionScorer.kt | 102 ++++++++---------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index a05629a09..c18192f2d 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -1,89 +1,79 @@ package dev.dres.run.score.scorer +import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable -import dev.dres.run.score.scorer.TaskScorer /** + * + * Computes the Inferred Average Precision as used in TREC based on an ordered, partially assessed list of [AnswerSet]s. + * See https://www-nlpir.nist.gov/projects/tv2006/infap/inferredAP.pdf for details. + * * @author Luca Rossetto * @version 1.0.0 */ class InferredAveragePrecisionScorer(override val scoreable: Scoreable) : TaskScorer { - - private val epsilon = 0.01 //TODO check what TRECVID uses override fun scoreMap(submissions: Sequence): Map { - TODO("Not yet implemented") + return this.scoreable.teams.associateWith { teamId -> + val answerSets = submissions.filter { it.teamId == teamId }.flatMap { it.answerSets() }.filter { it.taskId == this.scoreable.taskId } + infAP(answerSets) + } } - // TODO: Fix and make purpose and functionality of class explicit through proper documentation. - //see https://www-nlpir.nist.gov/projects/tv2006/infap/inferredAP.pdf - /*fun infAP(elements: List): Double { + companion object { - if (elements.isEmpty()) { - return 0.0 - } + private const val epsilon = 1e-6 //TODO check what TRECVID uses + private fun infAP(elements: Sequence): Double { + + if (elements.none()) { + return 0.0 + } - var infAPSum = 0.0 - var judgements = 0 - var correct = 0 - var wrong = 0 + var infAPSum = 0.0 + var judgements = 0 + var correct = 0 + var wrong = 0 - elements.forEachIndexed { index, statusAspect -> + elements.forEachIndexed { index, answerSet -> - val k = index + 1.0 - when(statusAspect.status) { - VerdictStatus.CORRECT -> { - ++judgements // |d100| - ++correct // |rel| + val k = index + 1.0 + when (answerSet.status()) { + VerdictStatus.CORRECT -> { + ++judgements // |d100| + ++correct // |rel| - val ap = if (index == 0){ //special case for first document - 1.0 //all are relevant so far, since there is only one so far and it is relevant - } else { - (1.0 / k) + ((k - 1.0) / k) * ((judgements / (k - 1.0)) * ((correct + epsilon) / (correct + wrong + 2.0 * epsilon))) - } + val ap = if (index == 0) { //special case for first document + 1.0 //all are relevant so far, since there is only one so far, and it is relevant + } else { + (1.0 / k) + ((k - 1.0) / k) * ((judgements / (k - 1.0)) * ((correct + epsilon) / (correct + wrong + 2.0 * epsilon))) + } - println(ap) + infAPSum += ap - infAPSum += ap + } + VerdictStatus.WRONG -> { + ++judgements + ++wrong // |nonrel| + } + + else -> {} } - VerdictStatus.WRONG -> { - ++judgements - ++wrong // |nonrel| - } - else -> {} + } - } + if (correct == 0) { + return 0.0 + } - if (correct == 0){ - return 0.0 - } + return infAPSum / correct - return infAPSum / correct + } } - fun score(submissions: List): Double = infAP(submissions) - fun score(batch: ResultBatch<*>): Double = infAP(batch.results) - */ - - - /*override fun computeScores(batch: ResultBatch<*>): Double = this.lastScoresLock.write { - val score = score(batch) - this.lastScores[batch.teamId to batch.name] = score - return@write score - }*/ - -// private var lastScores: MutableMap, Double> = mutableMapOf() -// -// private val lastScoresLock = ReentrantReadWriteLock() -// -// -// override fun scores(): List = this.lastScoresLock.read { -// this.lastScores.map { ScoreEntry(it.key.first, it.key.second, it.value) } -// } } \ No newline at end of file From 4077cd01b1788a2184051efd27cbca2bd4b79f78 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 13 Mar 2023 20:41:18 +0100 Subject: [PATCH 199/498] Removed unused StreamEventHandlers --- backend/src/main/kotlin/dev/dres/DRES.kt | 6 +- .../handlers/ResultLogStatisticsHandler.kt | 86 -------------- .../handlers/SubmissionStatisticsHandler.kt | 86 -------------- .../handlers/TeamCombinationScoreHandler.kt | 112 ------------------ 4 files changed, 1 insertion(+), 289 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 4d6e5ef82..662e02063 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -24,11 +24,7 @@ import dev.dres.data.model.run.DbEvaluationType import dev.dres.data.model.run.DbTask import dev.dres.data.model.submissions.* import dev.dres.run.RunExecutor -import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor -import dev.dres.run.eventstream.handlers.ResultLogStatisticsHandler -import dev.dres.run.eventstream.handlers.SubmissionStatisticsHandler -import dev.dres.run.eventstream.handlers.TeamCombinationScoreHandler import dev.dres.utilities.FFmpegUtil import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.XdModel @@ -68,7 +64,7 @@ object DRES { RunExecutor.init(store) /* Initialize EventStreamProcessor */ - EventStreamProcessor.register(SubmissionStatisticsHandler(), ResultLogStatisticsHandler(store), TeamCombinationScoreHandler()) + EventStreamProcessor.register( /* Add handlers here */) EventStreamProcessor.init() /* Initialize Rest API. */ diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt deleted file mode 100644 index 424a29a7f..000000000 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/ResultLogStatisticsHandler.kt +++ /dev/null @@ -1,86 +0,0 @@ -package dev.dres.run.eventstream.handlers - -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.run.eventstream.StreamEvent -import dev.dres.run.eventstream.StreamEventHandler -import jetbrains.exodus.database.TransientEntityStore -import java.io.File -import java.io.PrintWriter - -class ResultLogStatisticsHandler(private val store: TransientEntityStore) : StreamEventHandler { - - private val writer = PrintWriter(File("statistics/result_log_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - - private val lastActiveTask = mutableMapOf() - private val lastActiveTargets = mutableMapOf>>() - - - init { - writer.println("timestamp,task,session,item,segment,frame,reportedRank,listRank,inTime") - } - - override fun handle(event: StreamEvent) { - - /* TODO: Fix / and maybe design + document in a way such that one understands what is going on... :-) - - when (event) { - is TaskStartEvent -> { - lastActiveTask[event.runId] = event.taskTemplate - lastActiveTargets[event.runId] = when(event.taskTemplate.target) { - is TaskDescriptionTarget.JudgementTaskDescriptionTarget, is TaskDescriptionTarget.VoteTaskDescriptionTarget, -> return //no analysis possible - is TaskDescriptionTarget.MediaItemTarget -> listOf(event.taskTemplate.target.item to null) - is TaskDescriptionTarget.VideoSegmentTarget -> listOf(event.taskTemplate.target.item to event.taskTemplate.target.temporalRange) - is TaskDescriptionTarget.MultipleMediaItemTarget -> event.taskTemplate.target.items.map { it to null } - is TaskDescriptionTarget.TextTaskDescriptionTarget -> return //TODO maybe some analysis would be possible, needs restructuring - } - } - is QueryResultLogEvent -> { - - val relevantTask = lastActiveTask[event.runId] ?: return - val relevantTargets = lastActiveTargets[event.runId] ?: return - - val correctItems = event.queryResultLog.results.mapIndexed { - index, queryResult -> - if ( relevantTargets.any { it.first.name == queryResult.item } ) - index to queryResult else null }.filterNotNull() - - if (correctItems.isEmpty()) { - return - } - - val temporalTargets = relevantTargets.filter { it.second != null } - - if (temporalTargets.isEmpty()) { //consider only items - correctItems.forEach { - writer.println("${System.currentTimeMillis()},${relevantTask.name},${event.session},${it.second.item},${it.second.segment},${it.second.frame},${it.second.rank},${it.first},n/a") - } - } else { // consider also temporal range - val relevantTemporalTargets = temporalTargets.filter { it.first.name == relevantTask.name } - - correctItems.forEach { - val correctTime = (it.second.segment != null || it.second.frame != null) && relevantTemporalTargets.any { target -> - val segments = this.segmentIndex[target.first.id].firstOrNull() ?: return@any false - val segment = TemporalRange(if (it.second.segment != null) { - TimeUtil.shotToTime(it.second.segment.toString(), segments) - } else { - TimeUtil.timeToSegment(TemporalPoint.Frame.toMilliseconds(it.second.frame!!, (target.first as MediaItem.VideoItem).fps), segments) - } ?: return@any false ) - - segment.overlaps(target.second!!) - } - writer.println("${System.currentTimeMillis()},${relevantTask.name},${event.session},${it.second.item},${it.second.segment},${it.second.frame},${it.second.rank},${it.first},$correctTime") - } - } - - writer.flush() - - } - - else -> { /* ignore */ } - - } */ - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt deleted file mode 100644 index dfba798d2..000000000 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/SubmissionStatisticsHandler.kt +++ /dev/null @@ -1,86 +0,0 @@ -package dev.dres.run.eventstream.handlers - -import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.run.eventstream.* -import kotlinx.dnq.query.first -import kotlinx.dnq.query.size -import org.slf4j.LoggerFactory -import java.io.File -import java.io.PrintWriter - -class SubmissionStatisticsHandler : StreamEventHandler { - - private val writer = PrintWriter(File("statistics/submission_statistics_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - - private val submissionTaskMap = mutableMapOf>() - private val taskStartMap = mutableMapOf() - private val taskNameMap = mutableMapOf() - - private val logger = LoggerFactory.getLogger(this.javaClass) - - init { - writer.println("task,team,type,value") - } - - override fun handle(event: StreamEvent) { - when (event) { - is TaskStartEvent -> { - submissionTaskMap[event.taskId] = mutableListOf() - taskStartMap[event.taskId] = event.timeStamp - taskNameMap[event.taskId] = event.taskTemplate.name - } - is SubmissionEvent -> if (event.taskId != null && taskStartMap.containsKey(event.taskId)){ - submissionTaskMap[event.taskId]!!.add(event.submission) - } - is TaskEndEvent -> { - val submissions = submissionTaskMap[event.taskId] - val start = taskStartMap[event.taskId] - val name = taskNameMap[event.taskId] - - if (submissions == null || start == null || name == null) { - logger.info("Task '{}' not found in previously started tasks. Already ended previously?", name) - return - } - - computeStatistics(submissions, start, name) - submissionTaskMap.remove(event.taskId) - taskStartMap.remove(event.taskId) - taskNameMap.remove(event.taskId) - } - else -> {/* ignore */ - } - } - } - - /** - * TODO: Check and maybe generalise. - * - * I assume here, that there this handler requires a single verdict per submission. Is this a valid assumption? - */ - private fun computeStatistics(submissions: List, taskStart: Long, task: String) { - val submissionsByTeam = submissions.groupBy { it.teamId } - submissionsByTeam.mapValues { it.value.size }.forEach{ - (teamId, count) -> writer.println("$task,${teamId},\"totalSubmissionsPerTeam\",$count") - } - submissionsByTeam.mapValues { - it.value.firstOrNull { s -> - require(s.answerSets().count() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.answerSets().first().status() == VerdictStatus.CORRECT - }?.timestamp?.minus(taskStart) }.filter { it.value != null }.forEach{ - (teamId, time) -> writer.println("$task,${teamId},\"timeUntilCorrectSubmission\",$time") - } - submissionsByTeam.mapValues { - it.value.indexOfFirst { s -> - require(s.answerSets().count() == 1) { "SubmissionStatisticsHandler can only process single-verdict submissions." } - s.answerSets().first().status() == VerdictStatus.CORRECT - } - }.forEach{ - (teamId, count) -> writer.println("$task,${teamId},\"incorrectBeforeCorrectSubmissions\",$count") - } - writer.flush() - } -} diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt deleted file mode 100644 index 3bf4632e9..000000000 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/handlers/TeamCombinationScoreHandler.kt +++ /dev/null @@ -1,112 +0,0 @@ -package dev.dres.run.eventstream.handlers - -import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission -import dev.dres.run.eventstream.* -import java.io.File -import java.io.PrintWriter - -/** - * - */ -class TeamCombinationScoreHandler : StreamEventHandler { - - private val writer = PrintWriter(File("statistics/combined_team_scores_${System.currentTimeMillis()}.csv").also { it.parentFile.mkdirs() }) - - /** - * - */ - private val tasks = mutableMapOf() - - /** - * - */ - - private val taskStartMap = mutableMapOf() - - /** - * - */ - private val submissionTaskMap = mutableMapOf>() - - init { - writer.println("task,team1,team2,score") - } - - override fun handle(event: StreamEvent) { - - when(event){ - is TaskStartEvent -> { - tasks[event.taskId] = event.taskTemplate - taskStartMap[event.taskId] = event.timeStamp - submissionTaskMap[event.taskId] = mutableListOf() - } - is SubmissionEvent -> if (event.taskId != null && tasks.containsKey(event.taskId)){ - submissionTaskMap[event.taskId]!!.add(event.submission) - } - is TaskEndEvent -> { - - val taskDescription = tasks[event.taskId] ?: return - - - - val submissions = submissionTaskMap[event.taskId] ?: return - - val teams = submissions.map { it.teamId }.toSet().toList().sortedBy { it } - - val combinations = teams.mapIndexed { firstIndex, uidA -> - teams.mapIndexed {secondIndex, uidB -> if (firstIndex > secondIndex) (uidA to uidB) else null} - }.flatten().filterNotNull().map { EvaluationId() to it }.toMap() - - /* TODO: Fix. Not quite sure what is going on here. */ - /*val combinedSubmissions = submissions.flatMap { submission -> - combinations.map { - if (it.value.first == submission.team.teamId || it.value.second == submission.team.teamId) { - when (submission) { - is Submission.Item -> submission.copy(teamId = it.key).apply { this.status = submission.status } - is Submission.Temporal -> submission.copy(teamId = it.key).apply { this.status = submission.status } - is Submission.Text -> submission.copy(teamId = it.key).apply { this.status = submission.status } - } - } else { - null - } - }.filterNotNull() - } - - when(scorer) { - is RecalculatingSubmissionTaskScorer -> { - scorer.computeScores( - combinedSubmissions, - TaskContext( - combinations.keys, - taskStartMap[event.taskId]!!, - taskDescription.duration, - event.timeStamp - ) - ) - } - is IncrementalSubmissionTaskScorer -> { - combinedSubmissions.forEach { scorer.update(it) } - } - else -> throw IllegalStateException("unsupported scorer type $scorer") - } - - val scores = scorer.teamScoreMap().mapKeys { combinations[it.key]!! } - - scores.forEach { - writer.println("${event.taskId.string},${it.key.first.string},${it.key.second.string},${it.value}") - } - - writer.flush() - - tasks.remove(event.taskId) - submissionTaskMap.remove(event.taskId) - taskStartMap.remove(event.taskId) */ - - } - else -> { /* ignore */ } - } - } -} \ No newline at end of file From d3d353415e51a12c5a0199944bff6ab24139c505 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 17 Mar 2023 10:18:46 +0100 Subject: [PATCH 200/498] Added explicit, persistent status to DbTask and DbEvaluation. --- .../types/evaluation/ApiEvaluationStatus.kt | 13 ++++++++ .../dev/dres/data/model/run/DbEvaluation.kt | 6 +++- .../dres/data/model/run/DbEvaluationStatus.kt | 33 +++++++++++++++++++ .../dres/data/model/run/DbEvaluationType.kt | 2 +- .../kotlin/dev/dres/data/model/run/DbTask.kt | 5 ++- .../dev/dres/data/model/run/DbTaskStatus.kt | 25 ++++++++++++++ .../kotlin/dev/dres/data/model/run/Task.kt | 2 -- .../data/model/run/interfaces/Evaluation.kt | 2 -- 8 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt index dc0f197c8..55c446795 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt @@ -1,5 +1,7 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.data.model.run.DbEvaluationStatus +import dev.dres.data.model.run.DbEvaluationType import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus @@ -29,4 +31,15 @@ enum class ApiEvaluationStatus { ACTIVE -> RunManagerStatus.ACTIVE TERMINATED -> RunManagerStatus.TERMINATED } + + /** + * Converts this [ApiEvaluationStatus] to a [DbEvaluationStatus] representation. Requires an ongoing transaction. + * + * @return [DbEvaluationStatus] + */ + fun toDb(): DbEvaluationStatus = when(this) { + CREATED -> DbEvaluationStatus.CREATED + ACTIVE -> DbEvaluationStatus.ACTIVE + TERMINATED -> DbEvaluationStatus.TERMINATED + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 2143d19a3..fc61206c9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -14,7 +14,7 @@ import kotlinx.dnq.query.asSequence * Represents a [DbEvaluation], i.e., a concrete instance of a [DbEvaluationTemplate], as executed by DRES. * * @author Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { companion object : XdNaturalEntityType() @@ -22,6 +22,7 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { override fun constructor() { super.constructor() created = System.currentTimeMillis() + status = DbEvaluationStatus.CREATED } /** The [EvaluationId] of this [DbEvaluation]. */ @@ -35,6 +36,9 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { /** The [DbEvaluationType] of this [DbEvaluation]. */ var type by xdLink1(DbEvaluationType) + /** The [DbEvaluationStatus] of this [DbEvaluation]. */ + var status by xdLink1(DbEvaluationStatus) + /** The [DbEvaluationTemplate] backing this [DbEvaluation]. */ var template by xdLink1(DbEvaluationTemplate) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt new file mode 100644 index 000000000..34b4a18dc --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt @@ -0,0 +1,33 @@ +package dev.dres.data.model.run + +import dev.dres.api.rest.types.evaluation.ApiEvaluationStatus +import dev.dres.api.rest.types.evaluation.ApiEvaluationType +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + + +/** + * Enumeration of the status of a [DbEvaluation]. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class DbEvaluationStatus(entity: Entity): XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val CREATED by enumField { description = "CREATED" } + val ACTIVE by enumField { description = "ACTIVE" } + val TERMINATED by enumField { description = "TERMINATED" } + } + + var description by xdRequiredStringProp(unique = true) + private set + + /** + * Converts this [DbEvaluationType] to a RESTful API representation [ApiEvaluationType]. + * + * @return [ApiEvaluationType] + */ + fun toApi() = ApiEvaluationStatus.values().find { it.toDb() == this } ?: throw IllegalStateException("Evaluation status ${this.description} is not supported.") +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt index d21f723e7..7b194694d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationType.kt @@ -27,5 +27,5 @@ class DbEvaluationType(entity: Entity) : XdEnumEntity(entity) { * * @return [ApiEvaluationType] */ - fun toApi() = ApiEvaluationType.values().find { it.toDb() == this } ?: throw IllegalStateException("Run type ${this.description} is not supported.") + fun toApi() = ApiEvaluationType.values().find { it.toDb() == this } ?: throw IllegalStateException("Evaluation type ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index 2e0cc0c83..4d31ad4b8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -22,7 +22,7 @@ import kotlinx.dnq.query.asSequence class DbTask(entity: Entity) : PersistentEntity(entity), Task { companion object : XdNaturalEntityType() - /** The [EvaluationId] of this [DbTask]. */ + /** The [TaskId] of this [DbTask]. */ override var taskId: TaskId get() = this.id set(value) { this.id = value } @@ -30,6 +30,9 @@ class DbTask(entity: Entity) : PersistentEntity(entity), Task { /** The [DbEvaluation] this [DbTask] belongs to. */ override var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) + /** The [DbTaskStatus] of this [DbTask]. */ + var status by xdLink1(DbTaskStatus) + /** Timestamp of when this [DbEvaluation] started. */ override var started by xdNullableLongProp() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt new file mode 100644 index 000000000..b168f0bca --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt @@ -0,0 +1,25 @@ +package dev.dres.data.model.run + +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEnumEntity +import kotlinx.dnq.XdEnumEntityType +import kotlinx.dnq.xdRequiredStringProp + +/** +* Enumeration of the status of a [DbTask]. +* +* @author Ralph Gasser +* @version 1.0.0 +*/ +class DbTaskStatus (entity: Entity): XdEnumEntity(entity) { + companion object : XdEnumEntityType() { + val CREATED by DbTaskStatus.enumField { description = "CREATED" } + val PREPARING by DbTaskStatus.enumField { description = "PREPARING" } + val RUNNING by DbTaskStatus.enumField { description = "RUNNING" } + val ENDED by DbTaskStatus.enumField { description = "ENDED" } + val IGNORED by DbTaskStatus.enumField { description = "IGNORED" } + } + + var description by xdRequiredStringProp(unique = true) + private set +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt index cb098c5ab..f94b3bd45 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/Task.kt @@ -7,7 +7,6 @@ import dev.dres.data.model.template.task.TaskTemplate typealias TaskId = String interface Task { //TODO - val taskId: TaskId val started: Long? val ended: Long? @@ -15,5 +14,4 @@ interface Task { //TODO val template: TaskTemplate fun answerSets(): Sequence - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt index 083a22ecf..8a21f41bb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/Evaluation.kt @@ -3,7 +3,5 @@ package dev.dres.data.model.run.interfaces typealias EvaluationId = String interface Evaluation { - val evaluationId: EvaluationId - } \ No newline at end of file From 033f9be2ebe2bf77d40c259d130e55860e4b6592 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 17 Mar 2023 10:39:02 +0100 Subject: [PATCH 201/498] Made necessary adjustments to use DbTaskStatus and DbEvaluationStatus internally. --- backend/src/main/kotlin/dev/dres/DRES.kt | 6 +-- .../evaluation/admin/NextTaskHandler.kt | 5 +-- .../evaluation/viewer/GetTaskTargetHandler.kt | 4 +- .../handler/preview/AbstractPreviewHandler.kt | 15 +------- .../types/evaluation/ApiEvaluationState.kt | 8 +--- .../types/evaluation/ApiEvaluationStatus.kt | 4 +- .../rest/types/evaluation/ApiTaskOverview.kt | 5 +-- .../rest/types/evaluation/ApiTaskStatus.kt | 38 +++++++++++++------ .../dev/dres/data/model/run/AbstractTask.kt | 33 ++++++++-------- .../dres/data/model/run/DbEvaluationStatus.kt | 4 +- .../dev/dres/data/model/run/DbTaskStatus.kt | 12 +++++- .../dres/data/model/run/interfaces/TaskRun.kt | 9 ++--- .../run/InteractiveAsynchronousRunManager.kt | 2 +- .../run/InteractiveSynchronousRunManager.kt | 36 +++++++++++------- .../main/kotlin/dev/dres/run/TaskStatus.kt | 28 -------------- 15 files changed, 97 insertions(+), 112 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/run/TaskStatus.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 662e02063..e931e2b60 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -19,9 +19,7 @@ import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.DbMediaSegment import dev.dres.data.model.media.DbMediaType -import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.DbEvaluationType -import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.* import dev.dres.data.model.submissions.* import dev.dres.run.RunExecutor import dev.dres.run.eventstream.EventStreamProcessor @@ -97,6 +95,7 @@ object DRES { DbAuditLogType, DbConfiguredOption, DbEvaluation, + DbEvaluationStatus, DbEvaluationType, DbEvaluationTemplate, DbHint, @@ -111,6 +110,7 @@ object DRES { DbSubmission, DbSubmissionOption, DbTask, + DbTaskStatus, DbTaskGroup, DbTaskType, DbTaskOption, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index f7547c397..ac0716fe1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -6,10 +6,10 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveAsynchronousRunManager -import dev.dres.run.TaskStatus import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -51,12 +51,11 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl /* Important: Check that user can actually change this manager. */ synchronousAdminCheck(evaluationManager, ctx) - return this.store.transactional(true) { val rac = RunActionContext.runActionContext(ctx, evaluationManager) if (evaluationManager is InteractiveAsynchronousRunManager && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) - && evaluationManager.currentTask(rac)?.status != TaskStatus.ENDED) { + && evaluationManager.currentTask(rac)?.status !in setOf(DbTaskStatus.ENDED, DbTaskStatus.IGNORED)) { throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 839318330..7af766dad 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -8,9 +8,9 @@ import dev.dres.api.rest.types.competition.tasks.ApiTargetContent import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.Config +import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.RunActionContext import dev.dres.run.InteractiveRunManager -import dev.dres.run.TaskStatus import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -55,7 +55,7 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val config: Conf if (task == null) { task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) } - if (task.status != TaskStatus.ENDED) { + if (task.status != DbTaskStatus.ENDED) { throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index c88c68c4e..80a4fe8e9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -28,13 +28,6 @@ import java.nio.file.Paths * @version 2.0.0 */ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, config: Config) : GetRestHandler, AccessManagedRestHandler { - companion object { - /** Placeholder for when image is missing. */ - private val MISSING_IMAGE = this::class.java.getResourceAsStream("/img/missing.png").use { it!!.readAllBytes() } - - /** Placeholder for when image is waiting to be loaded. */ - private val WAITING_IMAGE = this::class.java.getResourceAsStream("/img/loading.png").use { it!!.readAllBytes() } - } /** All [AbstractCollectionHandler]s require [ApiRole.VIEWER]. */ override val permittedRoles = setOf(ApiRole.VIEWER) @@ -51,7 +44,7 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, /** * Handles a request for a preview based on an [CollectionId] and a [DbMediaItem]'s name. Fetching of the [DbMediaItem] takes - * place in a transaction context. However, the (potentially) long running media processing is executed outside. + * place in a transaction context. However, the (potentially) long-running media processing is executed outside. * * @param collectionId [CollectionId] of the [DbMediaCollection]. * @param itemName Name of the [DbMediaItem] @@ -74,7 +67,6 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, * @param ctx The request [Context] */ protected fun handlePreviewRequest(item: DbMediaItem, time: Long?, ctx: Context) { - val basePath = Paths.get(item.collection.path) if (item.type == DbMediaType.IMAGE) { //TODO scale down image if too large @@ -95,17 +87,14 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, } - val imgPath = cacheDir.resolve("${time}.jpg") - + val imgPath = cacheDir.resolve("$time.jpg") if (Files.exists(imgPath)) { //if file is available, send contents immediately ctx.header("Cache-Control", "max-age=31622400") ctx.sendFile(imgPath.toFile()) } else { //if not, schedule and return error - FFmpegUtil.extractFrame(basePath.resolve(item.location), time, imgPath) ctx.status(408) ctx.header("Cache-Control", "max-age=30") - } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt index 437bc708f..e4892afbe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt @@ -22,13 +22,7 @@ data class ApiEvaluationState( constructor(run: InteractiveRunManager, context: RunActionContext) : this( run.id, run.status.toApi(), - when(run.currentTask(context)?.status) { - TaskStatus.CREATED -> ApiTaskStatus.CREATED - TaskStatus.PREPARING -> ApiTaskStatus.PREPARING - TaskStatus.RUNNING -> ApiTaskStatus.RUNNING - TaskStatus.ENDED -> ApiTaskStatus.ENDED - null -> ApiTaskStatus.NO_TASK - }, + run.currentTask(context)?.status?.toApi() ?: ApiTaskStatus.NO_TASK, try { ApiTaskTemplateInfo(run.currentTaskTemplate(context)) } catch (e: IllegalArgumentException) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt index 55c446795..7cef00aec 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationStatus.kt @@ -6,7 +6,9 @@ import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus /** - * The collection auf statuses a [RunManager] can be in. API version of [RunManagerStatus] + * The collection auf statuses an [ApiEvaluation] and the associated [RunManager] can be in. + * + * API version of [DbEvaluationStatus] * * @author Ralph Gasser * @version 1.0.0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt index 0d542f166..de07c16ff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt @@ -1,7 +1,6 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.run.TaskStatus data class ApiTaskOverview( val id:String, @@ -10,7 +9,7 @@ data class ApiTaskOverview( val group: String, val duration: Long, val taskId: String, - val status: TaskStatus, + val status: ApiTaskStatus, val started: Long?, val ended: Long?) { constructor(task: TaskRun) : this( @@ -20,7 +19,7 @@ data class ApiTaskOverview( task.template.taskGroup.type.name, task.template.duration, task.taskId, - task.status, + task.status.toApi(), task.started, task.ended) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt index 2879e28e5..6bd60a48c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskStatus.kt @@ -1,15 +1,29 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.run.TaskStatus - - -/** - * - */ -enum class ApiTaskStatus(val status: TaskStatus?) { - NO_TASK(null), - CREATED(TaskStatus.CREATED), - PREPARING(TaskStatus.PREPARING), - RUNNING(TaskStatus.PREPARING), - ENDED(TaskStatus.ENDED); +import dev.dres.data.model.run.DbTaskStatus + + +/** The collection auf statuses an [ApiTask] can be in. +* +* API version of [DbTaskStatus] +* +* @author Ralph Gasser +* @version 1.0.0 +*/ +enum class ApiTaskStatus { + NO_TASK, CREATED, PREPARING, RUNNING, ENDED, IGNORED; + + /** + * Converts this [ApiTaskStatus] to a [DbTaskStatus] representation. Requires an ongoing transaction. + * + * @return [Db] + */ + fun toDb(): DbTaskStatus = when(this) { + CREATED -> DbTaskStatus.CREATED + PREPARING -> DbTaskStatus.PREPARING + RUNNING -> DbTaskStatus.RUNNING + ENDED -> DbTaskStatus.ENDED + IGNORED -> DbTaskStatus.IGNORED + else -> throw IllegalArgumentException("The API task status $this cannot be converted to a persistent representation.") + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 9023e54f5..099b7967f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -6,7 +6,6 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId -import dev.dres.run.TaskStatus import dev.dres.run.filter.SubmissionFilter import dev.dres.run.transformer.SubmissionTransformer import dev.dres.run.validation.interfaces.AnswerSetValidator @@ -19,7 +18,7 @@ import kotlinx.dnq.util.findById * An abstract [Run] implementation that can be used by different subtypes. * * @author Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ abstract class AbstractTask(task: DbTask): TaskRun { @@ -49,6 +48,13 @@ abstract class AbstractTask(task: DbTask): TaskRun { */ final override val templateId: TemplateId = this.task.template.templateId + /** The current [DbTaskStatus] of this [AbstractTask]. This is a transient property. */ + final override var status: DbTaskStatus = task.status + protected set(value) { + field = value + this.task.status = value /* Update backing database field. */ + } + /** * Timestamp of when this [AbstractTask] was started. * @@ -79,24 +85,15 @@ abstract class AbstractTask(task: DbTask): TaskRun { final override val template: DbTaskTemplate get() = this.task.template - /** The current status of this [AbstractTask]. This is a transient property. */ - @Volatile - final override var status: TaskStatus = when { - this.started != null && this.ended != null -> TaskStatus.ENDED - this.started != null && this.ended == null -> TaskStatus.RUNNING - else -> TaskStatus.CREATED - } - protected set - - /** The [SubmissionFilter] used to filter [Submission]s. */ + /** The [SubmissionFilter] used to filter [DbSubmission]s. */ abstract val filter: SubmissionFilter + /** The [SubmissionTransformer] used to convert [DbSubmission]s. */ abstract val transformer: SubmissionTransformer - /** The [AnswerSetValidator] used to validate [Submission]s. */ + /** The [AnswerSetValidator] used to validate [DbAnswerSet]s. */ abstract val validator: AnswerSetValidator - /** * Prepares this [TaskRun] for later starting. */ @@ -107,7 +104,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { if (this.hasStarted) { throw IllegalStateException("Run has already been started.") } - this.status = TaskStatus.PREPARING + this.status = DbTaskStatus.PREPARING } /** @@ -118,7 +115,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { throw IllegalStateException("Run has already been started.") } this.started = System.currentTimeMillis() - this.status = TaskStatus.RUNNING + this.status = DbTaskStatus.RUNNING } /** @@ -129,7 +126,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { this.started = System.currentTimeMillis() } this.ended = System.currentTimeMillis() - this.status = TaskStatus.ENDED + this.status = DbTaskStatus.ENDED } /** @@ -140,7 +137,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { throw IllegalStateException("Run has not yet ended.") } this.ended = null - this.status = TaskStatus.RUNNING + this.status = DbTaskStatus.RUNNING } /** Returns a [Sequence] of all [DbSubmission]s connected to this [AbstractTask]. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt index 34b4a18dc..ce2432ae5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluationStatus.kt @@ -25,9 +25,9 @@ class DbEvaluationStatus(entity: Entity): XdEnumEntity(entity) { private set /** - * Converts this [DbEvaluationType] to a RESTful API representation [ApiEvaluationType]. + * Converts this [DbEvaluationStatus] to a RESTful API representation [ApiEvaluationStatus]. * - * @return [ApiEvaluationType] + * @return [ApiEvaluationStatus] */ fun toApi() = ApiEvaluationStatus.values().find { it.toDb() == this } ?: throw IllegalStateException("Evaluation status ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt index b168f0bca..f0da723c5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTaskStatus.kt @@ -1,5 +1,8 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.evaluation.ApiEvaluationStatus +import dev.dres.api.rest.types.evaluation.ApiEvaluationType +import dev.dres.api.rest.types.evaluation.ApiTaskStatus import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity import kotlinx.dnq.XdEnumEntityType @@ -11,7 +14,7 @@ import kotlinx.dnq.xdRequiredStringProp * @author Ralph Gasser * @version 1.0.0 */ -class DbTaskStatus (entity: Entity): XdEnumEntity(entity) { +class DbTaskStatus(entity: Entity): XdEnumEntity(entity) { companion object : XdEnumEntityType() { val CREATED by DbTaskStatus.enumField { description = "CREATED" } val PREPARING by DbTaskStatus.enumField { description = "PREPARING" } @@ -22,4 +25,11 @@ class DbTaskStatus (entity: Entity): XdEnumEntity(entity) { var description by xdRequiredStringProp(unique = true) private set + + /** + * Converts this [DbTaskStatus] to a RESTful API representation [ApiTaskStatus]. + * + * @return [ApiTaskStatus] + */ + fun toApi() = ApiTaskStatus.values().find { it.toDb() == this } ?: throw IllegalStateException("Task status ${this.description} is not supported.") } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index bbe959a99..08ba863d9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,14 +1,13 @@ package dev.dres.data.model.run.interfaces +import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.team.TeamId -import dev.dres.run.TaskStatus import dev.dres.run.score.Scoreable import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.TaskScorer typealias TaskId = String @@ -28,6 +27,9 @@ interface TaskRun: Run, Scoreable { /** The unique [TemplateId] that identifies the task template underpinning [TaskRun]. */ val templateId: TemplateId + /** The current [DbTaskStatus] of this [TaskRun]. This is typically a transient property. */ + val status: DbTaskStatus + /** Reference to the [EvaluationRun] this [IATaskRun] belongs to. */ val competition: EvaluationRun @@ -40,9 +42,6 @@ interface TaskRun: Run, Scoreable { /** The [CachingTaskScorer] used to update score for this [IATaskRun]. */ val scorer: CachingTaskScorer - /** The current status of this [TaskRun]. This is typically a transient property. */ - val status: TaskStatus - /** * Prepares this [TaskRun] for later starting. */ diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 356f6468a..5c0b61b9a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -728,5 +728,5 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * */ private fun teamHasPreparingTask(teamId: TeamId) = - this.evaluation.currentTaskForTeam(teamId)?.status == TaskStatus.PREPARING + this.evaluation.currentTaskForTeam(teamId)?.status == DbTaskStatus.PREPARING } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 9889f7a06..a6652c741 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -259,7 +259,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override fun abortTask(context: RunActionContext) = this.stateLock.write { checkStatus(RunManagerStatus.ACTIVE) - assureTaskPreparingOrRunning() + assertTaskPreparingOrRunning() checkContext(context) /* End TaskRun and persist. */ @@ -284,9 +284,9 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ override fun currentTask(context: RunActionContext) = this.stateLock.read { when (this.evaluation.currentTask?.status) { - TaskStatus.PREPARING, - TaskStatus.RUNNING, - TaskStatus.ENDED -> this.evaluation.currentTask + DbTaskStatus.PREPARING, + DbTaskStatus.RUNNING, + DbTaskStatus.ENDED -> this.evaluation.currentTask else -> null } } @@ -357,7 +357,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - return if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { + return if (this.evaluation.currentTask?.status == DbTaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") max( @@ -376,7 +376,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { - return if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { + return if (this.evaluation.currentTask?.status == DbTaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) @@ -399,7 +399,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ override fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean = this.stateLock.read { checkStatus(RunManagerStatus.ACTIVE) - assureTaskPreparingOrRunning() + assertTaskPreparingOrRunning() checkContext(context) return try { @@ -425,7 +425,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.read { when (message.type) { ClientMessageType.ACK -> { - if (this.evaluation.currentTask?.status == TaskStatus.PREPARING) { + if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { this.readyLatch.setReady(connection) } } @@ -588,7 +588,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ - if (this.evaluation.currentTask?.status == TaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { + if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { /* TODO (Potential race condition): It can occur, that this part of the code is entered without the corresponding task being committed (status change in memory manifest faster than persistent information). */ this.stateLock.write { this.store.transactional { @@ -602,7 +602,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ - if (this.evaluation.currentTask?.status == TaskStatus.RUNNING) { + if (this.evaluation.currentTask?.status == DbTaskStatus.RUNNING) { this.stateLock.write { val task = this.evaluation.currentTask!! val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) @@ -657,12 +657,22 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (this.status !in status) throw IllegalRunStateException(this.status) } - private fun assureTaskPreparingOrRunning() { + /** + * Internal assertion method that makes sure that a task is either preparing or running. + * + * @throws IllegalArgumentException If task is neither preparing nor running. + */ + private fun assertTaskPreparingOrRunning() { val status = this.evaluation.currentTask?.status - if (status != TaskStatus.RUNNING && status != TaskStatus.PREPARING) throw IllegalStateException("Task not preparing or running") + if (status != DbTaskStatus.RUNNING && status != DbTaskStatus.PREPARING) throw IllegalStateException("Task is neither preparing nor running.") } + /** + * Internal assertion method that makes sure that no task is running. + * + * @throws IllegalArgumentException If task is neither preparing nor running. + */ private fun assureNoRunningTask() { - if (this.evaluation.tasks.any { it.status == TaskStatus.RUNNING }) throw IllegalStateException("Task running!") + if (this.evaluation.tasks.any { it.status == DbTaskStatus.RUNNING }) throw IllegalStateException("Task is already running!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt b/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt deleted file mode 100644 index cb6b64c64..000000000 --- a/backend/src/main/kotlin/dev/dres/run/TaskStatus.kt +++ /dev/null @@ -1,28 +0,0 @@ -package dev.dres.run - -import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.DbTask - -/** - * The status of a [DbTask] within an [DbEvaluation]. - * - * @author Luca Rossetto - * @version 1.0.0. - */ -enum class TaskStatus { - /** - * A [DbTask] was freshly created and is ready for execution. - */ - CREATED, - - /** - * A [DbTask] is currently being prepared for execution. - */ - PREPARING, - - /** A [DbTask] is currently being executed. */ - RUNNING, - - /** A [DbTask] has been completed. */ - ENDED; -} \ No newline at end of file From 2a78c767042d8af1da575dfa0721587e6995fa6e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 17 Mar 2023 11:16:50 +0100 Subject: [PATCH 202/498] Implemented instantiation approach for DbEvaluationTemplate. --- .../admin/CreateEvaluationHandler.kt | 9 +- .../DeleteEvaluationTemplateHandler.kt | 3 - .../ListEvaluationTemplatesHandler.kt | 6 +- .../UpdateEvaluationTemplateHandler.kt | 3 - .../competition/ApiEvaluationOverview.kt | 2 +- .../competition/ApiEvaluationTemplate.kt | 3 +- .../dev/dres/data/model/run/DbEvaluation.kt | 2 +- .../kotlin/dev/dres/data/model/run/DbTask.kt | 4 +- .../model/template/DbEvaluationTemplate.kt | 103 +++++++++++++++--- .../model/template/task/DbTaskTemplate.kt | 56 +++++++++- 10 files changed, 156 insertions(+), 35 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 1cb716e96..58ba947b5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -20,6 +20,7 @@ import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.and import kotlinx.dnq.query.eq import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query @@ -67,10 +68,10 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs /* Prepare run manager. */ val evaluationId = this.store.transactional { - val template = DbEvaluationTemplate.query(DbEvaluationTemplate::id eq message.templateId).firstOrNull() - ?: throw ErrorStatusException(404, "Competition with ID ${message.templateId} not found.'", ctx) - /* ensure that only one synchronous run of a competition is happening at any given time */ + val template = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq message.templateId) and (DbEvaluationTemplate::instance eq false)).firstOrNull() + ?: throw ErrorStatusException(404, "Evaluation template with ID ${message.templateId} could not be found.'", ctx) + /* ensure that only one synchronous run of a competition is happening at any given time */ if (message.type == ApiEvaluationType.SYNCHRONOUS && RunExecutor.managers().any { it is InteractiveSynchronousRunManager && it.template == template && it.status != RunManagerStatus.TERMINATED } @@ -99,7 +100,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, config: Config) : Abs /* Prepare evaluation. */ val evaluation = DbEvaluation.new { this.name = message.name - this.template = template + this.template = template.toInstance() this.type = message.type.toDb() this.allowRepeatedTasks = message.properties.allowRepeatedTasks this.participantCanView = message.properties.participantCanView diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index b1274edbb..b25f7668e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -36,9 +36,6 @@ class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva ) override fun doDelete(ctx: Context): SuccessStatus = this.store.transactional { val template = evaluationTemplateFromContext(ctx) - if (!template.canBeEdited()) { - throw ErrorStatusException(400, "Evaluation template ${template.id} can no longer be deleted.", ctx) - } template.delete() SuccessStatus("Evaluation template with ID ${ctx.pathParam("templateId")} was deleted successfully.") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index 425f84970..904a9e4bf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -8,6 +8,8 @@ import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.query import kotlinx.dnq.query.size /** @@ -33,8 +35,8 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEval methods = [HttpMethod.GET] ) override fun doGet(ctx: Context) = this.store.transactional(true) { - DbEvaluationTemplate.all().asSequence().map { - ApiEvaluationOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size(), it.canBeEdited()) + DbEvaluationTemplate.query(DbEvaluationTemplate::instance eq true).asSequence().map { + ApiEvaluationOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) }.toList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index ee2b7d7f4..5ccff0fc8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -58,9 +58,6 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C /* Store change. */ this.store.transactional { val existing = this.evaluationTemplateById(apiValue.id, ctx) - if (!existing.canBeEdited()) { - throw ErrorStatusException(400, "Evaluation template ${apiValue.id} can no longer be edited.", ctx) - } /* Update core information. */ existing.name = apiValue.name diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt index 4faa5cfbe..3f33c0b1e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationOverview.kt @@ -8,4 +8,4 @@ import dev.dres.data.model.template.DbEvaluationTemplate * @author Ralph Gasser * @version 1.1.0 */ -data class ApiEvaluationOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int, val editable: Boolean) \ No newline at end of file +data class ApiEvaluationOverview(val id: String, val name: String, val description: String?, val taskCount: Int, val teamCount: Int) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt index 6dd45f0ed..8475c9961 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/ApiEvaluationTemplate.kt @@ -24,7 +24,6 @@ data class ApiEvaluationTemplate( val tasks: List, val teams: List, val teamGroups: List, - val judges: List, - val canBeEdited: Boolean? = null/* Optional, because not required when sent by the client. */ + val judges: List ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index fc61206c9..4e72df2c6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -31,7 +31,7 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { set(value) { this.id = value } /** The name held by this [DbEvaluation]. Must be unique!*/ - var name by xdRequiredStringProp(unique = true, trimmed = true) + var name by xdRequiredStringProp(trimmed = true) /** The [DbEvaluationType] of this [DbEvaluation]. */ var type by xdLink1(DbEvaluationType) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index 4d31ad4b8..c5bc0cc37 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -6,11 +6,13 @@ import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.team.DbTeam import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence - +import kotlinx.dnq.util.isInstanceOf +import java.lang.IllegalStateException /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 91b1f9ef1..f91f4f480 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -14,13 +14,17 @@ import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.DbTask import dev.dres.data.model.template.interfaces.EvaluationTemplate +import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.query.* +import java.lang.IllegalStateException +import java.util.* typealias TemplateId = String @@ -32,7 +36,7 @@ typealias TemplateId = String * @version 2.0.0 * @author Luca Rossetto & Ralph Gasser */ -class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), EvaluationTemplate{ +class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), EvaluationTemplate { companion object: XdNaturalEntityType() /** The [TemplateId] of this [DbEvaluationTemplate]. */ @@ -46,24 +50,27 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio /** An optional description of this [DbEvaluationTemplate]. */ var description by xdStringProp(trimmed = false) + /** Flag indicating, whether this [DbTaskTemplate] is a concrete instance or a general template. */ + var instance by xdBooleanProp() + /** The [DbTaskType]s defined within this [DbEvaluationTemplate]. */ val taskTypes by xdChildren0_N(DbTaskType::evaluation) /** The [DbTaskGroup]s that are part of this [DbEvaluationTemplate]. */ val taskGroups by xdChildren0_N(DbTaskGroup::evaluation) - /** The [DbTaskTemplate]s contained in this [DbEvaluationTemplate]*/ - val tasks by xdChildren0_N(DbTaskTemplate::evaluation) - /** The [DbTeam]s that are part of this [DbEvaluationTemplate]. */ - val teams by xdChildren0_N(DbTeam::evaluation) + val teamGroups by xdChildren0_N(DbTeamGroup::evaluation) /** The [DbTeam]s that are part of this [DbEvaluationTemplate]. */ - val teamGroups by xdChildren0_N(DbTeamGroup::evaluation) + val teams by xdChildren0_N(DbTeam::evaluation) /** The [DbUser]s that act as judge for this [DbEvaluationTemplate] */ val judges by xdLink0_N(DbUser, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) + /** The [DbTaskTemplate]s contained in this [DbEvaluationTemplate]*/ + val tasks by xdChildren0_N(DbTaskTemplate::evaluation) + /** * Converts this [DbEvaluationTemplate] to a RESTful API representation [ApiEvaluationTemplate]. * @@ -75,21 +82,89 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio id = this.id, name = this.name, description = this.description, - canBeEdited = this.canBeEdited(), taskTypes = this.taskTypes.asSequence().map { it.toApi() }.toList(), taskGroups = this.taskGroups.asSequence().map { it.toApi() }.toList(), - tasks = this.tasks.asSequence().map { it.toApi() }.toList(), - teams = this.teams.asSequence().map { it.toApi() }.toList(), teamGroups = this.teamGroups.asSequence().map { it.toApi() }.toList(), - judges = this.judges.asSequence().map { it.id }.toList() + teams = this.teams.asSequence().map { it.toApi() }.toList(), + judges = this.judges.asSequence().map { it.id }.toList(), + tasks = this.tasks.asSequence().map { it.toApi() }.toList(), ) /** - * Checks if this [DbEvaluationTemplate] can be edited. This is the case only, if no active [DbEvaluation] exists that uses it. - * - * This is a convenience method and requires an active transaction context. + * Creates a [DbTaskTemplate] instance and returns it. */ - fun canBeEdited() = DbEvaluation.filter { it.template.id eq this@DbEvaluationTemplate.id }.none() + fun toInstance(): DbEvaluationTemplate { + require(!this.instance) { throw IllegalStateException("Cannot create an instance of an EvaluationTemplate that is an instance itself.") } + val evaluation = DbEvaluationTemplate.new { + id = UUID.randomUUID().toString() + instance = true + name = this@DbEvaluationTemplate.name + description = this@DbEvaluationTemplate.description + judges.addAll(this@DbEvaluationTemplate.judges) + } + + /* Copy task types. */ + this.taskTypes.asSequence().forEach { t -> + evaluation.taskTypes.add( + DbTaskType.new { + name = t.name + duration = t.duration + target = t.target + score = t.score + hints.addAll(t.hints) + submission.addAll(t.submission) + options.addAll(t.options) + t.configurations.asSequence().forEach { c -> + configurations.add(DbConfiguredOption.new { + key = c.key + value = c.value + }) + } + } + ) + } + + /* Copy task groups. */ + this.taskGroups.asSequence().forEach { g -> + evaluation.taskGroups.add( + DbTaskGroup.new { + name = g.name + type = evaluation.taskTypes.query((DbTaskType::name eq g.type.name)).first() + } + ) + } + + /* Copy team groups. */ + this.teamGroups.asSequence().forEach { g -> + evaluation.teamGroups.add( + DbTeamGroup.new { + id = UUID.randomUUID().toString() + name = g.name + defaultAggregator = g.defaultAggregator + } + ) + } + + /* Copy teams. */ + this.teams.asSequence().forEach { t -> + evaluation.teams.add( + DbTeam.new { + id = UUID.randomUUID().toString() + name = t.name + color = t.color + logo = t.logo + group = DbTeamGroup.query((DbTeamGroup::name eq t.group?.name)).firstOrNull() + users.addAll(t.users) + } + ) + } + + /* Copy tasks. */ + this.tasks.asSequence().forEach { + evaluation.tasks.add(it.toInstance(evaluation)) + } + return evaluation + } /** * Generates and returns a list of all [DbMediaItem] for this [DbEvaluationTemplate]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 9f76753b7..ceb862210 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -8,16 +8,19 @@ import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.run.DbTask import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.* import kotlinx.dnq.simple.min import java.io.FileNotFoundException import java.io.IOException +import java.lang.IllegalStateException import java.lang.Long.max +import java.util.* /** * Basic description of a [TaskRun] as executed in DRES. Defines basic attributes such as its name, its duration, @@ -38,9 +41,6 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { /** The name held by this [DbTeam]. Must be unique!*/ var name by xdRequiredStringProp(unique = false, trimmed = true) - /** If set, this [DbEvaluationTemplate] is considered a template!*/ - var template by xdBooleanProp() - /** The [DbTaskGroup] this [DbTaskTemplate] belongs to. */ var taskGroup by xdLink1(DbTaskGroup) @@ -59,6 +59,54 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { /** The [DbHint]s that act as clues to find the target media. */ val hints by xdChildren0_N(DbHint::task) + /** + * Creates a [DbTaskTemplate] instance and returns it. + * + * @return [DbTaskTemplate] instance + */ + fun toInstance(forEvaluation: DbEvaluationTemplate): DbTaskTemplate { + require(!this.evaluation.instance) { throw IllegalStateException("Cannot create an instance of an TaskTemplate that is an instance itself.") } + require(forEvaluation.instance) { throw IllegalStateException("Cannot attach an instance of an TaskTemplate to an EvaluationTemplate that is not an instance itself.") } + val copy = DbTaskTemplate.new { + this.id = UUID.randomUUID().toString() + this.name = this@DbTaskTemplate.name + this.collection = this@DbTaskTemplate.collection + this.duration = this@DbTaskTemplate.duration + this.taskGroup = forEvaluation.taskGroups.query(DbTaskGroup::name eq this@DbTaskTemplate.taskGroup.name).first() + } + + /* Copy task targets. */ + this.targets.asSequence().forEach { + copy.targets.add( + DbTaskTemplateTarget.new { + this.type = it.type + this.start = it.start + this.end = it.end + this.item = it.item + this.text = it.text + } + ) + } + + /* Copy task hints. */ + this.hints.asSequence().forEach { + copy.hints.add( + DbHint.new { + this.type = it.type + this.start = it.start + this.end = it.end + this.item = it.item + this.text = it.text + this.path = it.path + this.temporalRangeStart = it.temporalRangeStart + this.temporalRangeEnd = it.temporalRangeEnd + } + ) + } + + return copy + } + /** * Generates and returns a [ApiHintContent] object to be used by the RESTful interface. * From 5bb3595f76324742c8086361803cb197ec4a09fb Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 17 Mar 2023 11:27:26 +0100 Subject: [PATCH 203/498] Updated OAS. --- doc/oas-client.json | 164 ++++++++++++++++++++------------------- doc/oas.json | 183 +++++++++++++++++++++----------------------- 2 files changed, 173 insertions(+), 174 deletions(-) diff --git a/doc/oas-client.json b/doc/oas-client.json index b0208bc29..35fdaeada 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -490,15 +490,25 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -506,13 +516,23 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -552,11 +572,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -568,23 +588,13 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationOverview" } } } @@ -624,15 +634,15 @@ "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "The evaluation ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -640,6 +650,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -670,17 +690,27 @@ } } } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -691,16 +721,6 @@ "schema" : { "type" : "string" } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } } ], "responses" : { "200" : { @@ -708,10 +728,7 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -735,27 +752,17 @@ } } } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { - "patch" : { + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the submission status for a given submission.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -766,24 +773,27 @@ "schema" : { "type" : "string" } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiSubmission" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } } } } @@ -3834,9 +3844,6 @@ "items" : { "type" : "string" } - }, - "canBeEdited" : { - "type" : "boolean" } }, "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] @@ -4129,6 +4136,9 @@ "type" : "object", "additionalProperties" : false, "properties" : { + "id" : { + "type" : "string" + }, "status" : { "$ref" : "#/components/schemas/ApiVerdictStatus" }, @@ -4145,7 +4155,7 @@ "$ref" : "#/components/schemas/ApiSubmission" } }, - "required" : [ "status", "taskId", "answers", "submission" ] + "required" : [ "id", "status", "taskId", "answers", "submission" ] }, "ApiAnswerType" : { "type" : "string", @@ -4301,7 +4311,7 @@ "type" : "string" }, "status" : { - "$ref" : "#/components/schemas/TaskStatus" + "$ref" : "#/components/schemas/ApiTaskStatus" }, "started" : { "type" : "integer", @@ -4316,7 +4326,7 @@ }, "ApiTaskStatus" : { "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] }, "ApiTaskTemplateInfo" : { "type" : "object", @@ -4872,10 +4882,6 @@ "RunManagerStatus" : { "type" : "string", "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "TaskStatus" : { - "type" : "string", - "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] } }, "securitySchemes" : { } diff --git a/doc/oas.json b/doc/oas.json index 3c0d9ba45..547fcc7a9 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -388,10 +388,10 @@ } }, "/api/v2/collection" : { - "post" : { + "patch" : { "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -438,10 +438,10 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "post" : { "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", "parameters" : [ ], "requestBody" : { "content" : { @@ -525,10 +525,10 @@ } }, "/api/v2/collection/{collectionId}" : { - "delete" : { + "get" : { "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -546,13 +546,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" } } } }, - "400" : { - "description" : "Bad Request", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -561,8 +561,8 @@ } } }, - "401" : { - "description" : "Unauthorized", + "404" : { + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -575,10 +575,10 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "delete" : { "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", "parameters" : [ { "name" : "collectionId", "in" : "path", @@ -596,13 +596,13 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + "$ref" : "#/components/schemas/SuccessStatus" } } } }, - "401" : { - "description" : "Unauthorized", + "400" : { + "description" : "Bad Request", "content" : { "application/json" : { "schema" : { @@ -611,8 +611,8 @@ } } }, - "404" : { - "description" : "Not Found", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -4235,10 +4235,10 @@ } }, "/api/v2/template/{templateId}" : { - "delete" : { + "get" : { "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4256,7 +4256,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/ApiEvaluationTemplate" } } } @@ -4295,10 +4295,10 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "delete" : { "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", "parameters" : [ { "name" : "templateId", "in" : "path", @@ -4316,7 +4316,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" + "$ref" : "#/components/schemas/SuccessStatus" } } } @@ -4557,21 +4557,11 @@ } }, "/api/v2/user" : { - "post" : { + "get" : { "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4583,16 +4573,6 @@ } } }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "500" : { "description" : "Server Error", "content" : { @@ -4607,11 +4587,21 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "post" : { "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4623,6 +4613,16 @@ } } }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "500" : { "description" : "Server Error", "content" : { @@ -4791,14 +4791,14 @@ "deprecated" : false, "security" : [ ] }, - "patch" : { + "get" : { "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User ID", + "description" : "User's UID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -4806,16 +4806,6 @@ "type" : "string" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, "responses" : { "200" : { "description" : "OK", @@ -4827,18 +4817,8 @@ } } }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, "404" : { - "description" : "Not Found", + "description" : "If the user could not be found.", "content" : { "application/json" : { "schema" : { @@ -4861,14 +4841,14 @@ "deprecated" : false, "security" : [ ] }, - "get" : { + "patch" : { "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", "parameters" : [ { "name" : "userId", "in" : "path", - "description" : "User's UID", + "description" : "User ID", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -4876,6 +4856,16 @@ "type" : "string" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, "responses" : { "200" : { "description" : "OK", @@ -4887,8 +4877,18 @@ } } }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, "404" : { - "description" : "If the user could not be found.", + "description" : "Not Found", "content" : { "application/json" : { "schema" : { @@ -5191,9 +5191,6 @@ "items" : { "type" : "string" } - }, - "canBeEdited" : { - "type" : "boolean" } }, "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] @@ -5661,7 +5658,7 @@ "type" : "string" }, "status" : { - "$ref" : "#/components/schemas/TaskStatus" + "$ref" : "#/components/schemas/ApiTaskStatus" }, "started" : { "type" : "integer", @@ -5676,7 +5673,7 @@ }, "ApiTaskStatus" : { "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] }, "ApiTaskTemplateInfo" : { "type" : "object", @@ -6232,10 +6229,6 @@ "RunManagerStatus" : { "type" : "string", "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "TaskStatus" : { - "type" : "string", - "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] } }, "securitySchemes" : { From 379c2f1a4852e49352ac7ccd14ad26d00447d324 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 17 Mar 2023 11:30:02 +0100 Subject: [PATCH 204/498] Rolledback UI support for 'locked' templates. --- .../competition-builder.component.html | 2 -- .../competition-list/competition-list.component.html | 8 +------- .../competition-list/competition-list.component.ts | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder.component.html index 171f03f63..f659c364c 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.html @@ -7,7 +7,6 @@

Edit evaluation template {{ competitionId }} (unsaved changes) - lock

@@ -17,7 +16,6 @@

Edit evaluation template {{ competitionId }} - - @@ -57,8 +57,8 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect ID - - {{ row.id.substring(0, 8) }} + + {{ row.mediaItemId.substring(0, 8) }} From 976b0b5386530ba66ce221d83cd2cf07d4ef612f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 18 Mar 2023 09:48:43 +0100 Subject: [PATCH 215/498] Fixed a bug that led to an error if the user starting the task (ADMIN) is not associated with a team. --- .../kotlin/dev/dres/data/model/run/RunActionContext.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 15d2e832e..050f65104 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -9,10 +9,7 @@ import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.first -import kotlinx.dnq.query.query +import kotlinx.dnq.query.* /** * The [RunActionContext] captures and encapsulates information usually required during the interaction with a [RunManager]. @@ -40,7 +37,8 @@ data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) val roles = AccessManager.rolesOfSession(ctx.sessionToken()).mapNotNull { it.toDb() }.toSet() - val teamId = runManager.template.teams.filter { it.users.contains(DbUser.query(DbUser::id eq userId).first()) }.first().teamId + val user = DbUser.query(DbUser::id eq userId).firstOrNull() ?: throw ErrorStatusException(500, "Specified user does not exist in database. This is a programmer's error!", ctx) + val teamId = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull()?.teamId return RunActionContext(userId, teamId, roles) } } From 722273a094c7217fb42a687fc4c97b9f34a72b2f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 18 Mar 2023 09:49:15 +0100 Subject: [PATCH 216/498] Fixed a bug during the PREPARE phase, where a status check lead to an exception due to a missing transaction. --- .../dev/dres/run/InteractiveSynchronousRunManager.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 3b701d420..eef51704a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -425,8 +425,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.stateLock.read { when (message.type) { ClientMessageType.ACK -> { - if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { - this.readyLatch.setReady(connection) + this.store.transactional(true) { + if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { + this.readyLatch.setReady(connection) + } } } ClientMessageType.REGISTER -> this.readyLatch.register(connection) @@ -545,7 +547,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /* 3) Yield to other threads. */ - Thread.sleep(250) + Thread.sleep(500) /* Reset error counter. */ errorCounter = 0 @@ -591,7 +593,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { - /* TODO (Potential race condition): It can occur, that this part of the code is entered without the corresponding task being committed (status change in memory manifest faster than persistent information). */ this.stateLock.write { this.evaluation.currentTask!!.start() DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate(), DbAuditLogSource.INTERNAL, null) From c73ea8adaaf23bd4a5d37bab5de1b4ff60399f2b Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 18 Mar 2023 12:08:44 +0100 Subject: [PATCH 217/498] Added overlay that reflects if WebSocket connection is dropped. --- .../src/app/viewer/run-viewer.component.html | 4 ++ .../src/app/viewer/run-viewer.component.ts | 68 ++++++++++++++++--- frontend/src/app/viewer/viewer.module.ts | 4 +- frontend/src/styles.scss | 5 ++ 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index dd6eb9a38..4d371ecf9 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -174,3 +174,7 @@ >

+ + +

âš¡Viewer is out of sync. Please wait for WebSocket connection to be re-established!âš¡

+
diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index b009170d3..afaf3a069 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -1,6 +1,6 @@ -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import {AfterViewInit, Component, Inject, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { interval, merge, Observable, of, Subscription, zip } from 'rxjs'; +import {BehaviorSubject, interval, merge, Observable, of, Subscription, zip} from 'rxjs'; import { catchError, delay, @@ -27,16 +27,21 @@ import { Widget } from './model/run-viewer-widgets'; import { DOCUMENT } from '@angular/common'; import {Title} from '@angular/platform-browser'; import {ApiEvaluationInfo, ApiEvaluationState, ApiTaskTemplateInfo, EvaluationService} from '../../../openapi'; +import {Overlay, OverlayRef} from "@angular/cdk/overlay"; +import {TemplatePortal} from "@angular/cdk/portal"; @Component({ selector: 'app-run-viewer', templateUrl: './run-viewer.component.html', - styleUrls: ['./run-viewer.component.scss'], + styleUrls: ['./run-viewer.component.scss'] }) -export class RunViewerComponent implements OnInit, OnDestroy { +export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /** The WebSocketSubject that represent the WebSocket connection to the DRES endpoint. */ webSocketSubject: WebSocketSubject; + /** A {@link BehaviorSubject} that reflect the WebSocket connection status. */ + webSocketConnectionSubject: BehaviorSubject = new BehaviorSubject(false); + /** Observable for incoming WebSocket messages. */ webSocket: Observable; @@ -57,16 +62,33 @@ export class RunViewerComponent implements OnInit, OnDestroy { /** Observable that fires whenever a task ends. Emits the task description of the task that just ended. */ taskEnded: Observable; + /** Observable of the {@link Widget} that should be displayed on the left-hand side. */ leftWidget: Observable; + /** Observable of the {@link Widget} that should be displayed on the right-hand side. */ rightWidget: Observable; + /** Observable of the {@link Widget} that should be displayed at the center. */ centerWidget: Observable; + /** Observable of the {@link Widget} that should be displayed at the bottom. */ bottomWidget: Observable; + + /** Reference to the {@link OverlayRef}. Initialized in ngAfterViewInit(). */ + private overlayRef: OverlayRef = null + + /** Reference to the overlay template. Only available once view has been loaded. */ + @ViewChild('overlayTemplate') + private overlayTemplateRef: TemplateRef; + + /** Reference to the overlay template. Only available once view has been loaded. */ + private overlaySubscription: Subscription; + + /** Internal WebSocket subscription for pinging the server. */ private pingSubscription: Subscription; + /** Cached config */ private p: any; @@ -80,20 +102,25 @@ export class RunViewerComponent implements OnInit, OnDestroy { private runService: EvaluationService, private snackBar: MatSnackBar, private titleService: Title, - @Inject(DOCUMENT) private document: Document + private overlay: Overlay, + @Inject(DOCUMENT) private document: Document, + private _viewContainerRef: ViewContainerRef ) { /** Initialize basic WebSocketSubject. */ const wsurl = this.config.webSocketUrl; + const connectionSubject = this.webSocketConnectionSubject this.webSocketSubject = webSocket({ url: wsurl, openObserver: { next(openEvent) { console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} established!`); + connectionSubject.next(true) }, }, closeObserver: { next(closeEvent: CloseEvent) { console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} closed: ${closeEvent.reason}.`); + connectionSubject.next(false) }, }, } as WebSocketSubjectConfig); @@ -156,8 +183,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { /* Basic observable for web socket messages received from the DRES server. */ this.webSocket = this.evaluationId.pipe( flatMap((evaluationId) => - this.webSocketSubject - .multiplex( + this.webSocketSubject.multiplex( () => { return { evaluationId: evaluationId, type: 'REGISTER' } as IWsClientMessage; }, @@ -175,7 +201,7 @@ export class RunViewerComponent implements OnInit, OnDestroy { e ) ), - delay(1000) + delay(5000) ) ), map((m) => m as IWsServerMessage), @@ -258,15 +284,41 @@ export class RunViewerComponent implements OnInit, OnDestroy { tap(([i, evaluationId]) => this.webSocketSubject.next({ evaluationId: evaluationId, type: 'PING' } as IWsClientMessage)) ) .subscribe(); + } /** + * Prepare the overlay that is being displayed when WebSocket connection times out. + */ + ngAfterViewInit() { + this.overlayRef = this.overlay.create({ + positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(), + scrollStrategy: this.overlay.scrollStrategies.block(), + backdropClass: "overlay", + hasBackdrop: true + }); + + /* Create subscription for WebSocket connection status and show overlay. */ + this.overlaySubscription = this.webSocketConnectionSubject.subscribe((status) => { + if (status == false) { + this.overlayRef.attach(new TemplatePortal(this.overlayTemplateRef, this._viewContainerRef)) + } else { + this.overlayRef.detach() + } + }) + } + + /** * Unregisters this RunViewerComponent on view destruction and cleans the WebSocket subscription. */ ngOnDestroy(): void { /* Unregister Ping service. */ this.pingSubscription.unsubscribe(); this.pingSubscription = null; + this.overlaySubscription.unsubscribe() + this.overlaySubscription = null + this.overlayRef?.dispose() + this.overlayRef = null this.titleService.setTitle('DRES'); } diff --git a/frontend/src/app/viewer/viewer.module.ts b/frontend/src/app/viewer/viewer.module.ts index 899112c5b..cf9f10dd9 100644 --- a/frontend/src/app/viewer/viewer.module.ts +++ b/frontend/src/app/viewer/viewer.module.ts @@ -22,6 +22,7 @@ import { NgApexchartsModule } from 'ng-apexcharts'; import { CompetitionScoreboardViewerComponent } from './competition-scoreboard-viewer/competition-scoreboard-viewer.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { SharedModule } from '../shared/shared.module'; +import {FullscreenOverlayContainer, OverlayContainer, OverlayModule} from "@angular/cdk/overlay"; @NgModule({ imports: [ @@ -45,9 +46,10 @@ import { SharedModule } from '../shared/shared.module'; NgApexchartsModule, BrowserAnimationsModule, SharedModule, + OverlayModule ], exports: [RunViewerComponent], declarations: [RunViewerComponent, TaskViewerComponent, TeamsViewerComponent, CompetitionScoreboardViewerComponent], - providers: [], + providers: [{provide: OverlayContainer, useClass: FullscreenOverlayContainer}], }) export class ViewerModule {} diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index ffe4b1e26..71c93a6c1 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -11,6 +11,11 @@ body { @import '@angular/material/prebuilt-themes/purple-green.css'; +.overlay { + background-color: black; + opacity: 0.75; +} + /* */ #container { margin-top: 25px; /* Previous 100px seems far too much */ From 29f63e6729995ada95609b4544d2203d5b61b4ab Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 18 Mar 2023 12:08:58 +0100 Subject: [PATCH 218/498] Removed MISSING_IMAGE constant. --- .../dres/api/rest/handler/preview/AbstractPreviewHandler.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index 6378d4a59..c286d5a54 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -29,9 +29,6 @@ import java.nio.file.Files abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, private val cache: CacheManager) : GetRestHandler, AccessManagedRestHandler { companion object { - /** Placeholder for when image is missing. */ - private val MISSING_IMAGE = this::class.java.getResourceAsStream("/img/missing.png").use { it!!.readAllBytes() } - /** Placeholder for when image is waiting to be loaded. */ private val WAITING_IMAGE = this::class.java.getResourceAsStream("/img/loading.png").use { it!!.readAllBytes() } } From d67db013782be70e203d06ed7c4e123003f5f678 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 18 Mar 2023 12:35:09 +0100 Subject: [PATCH 219/498] Content type of PNG is explicitly set. --- .../dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt index c286d5a54..6fb8a7fa2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/AbstractPreviewHandler.kt @@ -73,6 +73,7 @@ abstract class AbstractPreviewHandler(protected val store: TransientEntityStore, } else -> { ctx.status(202) + ctx.header("Content-Type", "image/png") ctx.header("Cache-Control", "no-store") ctx.outputStream().write(WAITING_IMAGE) } From f78e19a257a2a46c818d3809ac4314259e766b8d Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 19 Mar 2023 10:13:09 +0100 Subject: [PATCH 220/498] New features for CacheManager: - Added method to cleanup cache directory. - Added option to configure several aspects of the cache and generated previews. --- backend/src/main/kotlin/dev/dres/DRES.kt | 4 +- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 2 +- .../dres/api/cli/MediaCollectionCommand.kt | 15 +--- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 2 +- .../collection/ListExternalItemHandler.kt | 2 - .../evaluation/viewer/GetTaskTargetHandler.kt | 1 - .../handler/submission/SubmissionHandler.kt | 5 +- .../UpdateEvaluationTemplateHandler.kt | 2 +- .../dev/dres/data/migration/Migration.kt | 2 +- .../dev/dres/data/model/config/CacheConfig.kt | 50 ++++++++++++ .../dres/data/model/{ => config}/Config.kt | 6 +- .../dres/data/model/template/task/DbHint.kt | 12 --- .../model/template/task/DbTaskTemplate.kt | 6 -- .../template/task/DbTaskTemplateTarget.kt | 9 --- .../dev/dres/mgmt/cache/CacheManager.kt | 76 ++++++++++++++----- .../main/kotlin/dev/dres/run/RunExecutor.kt | 2 +- 16 files changed, 120 insertions(+), 76 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt rename backend/src/main/kotlin/dev/dres/data/model/{ => config}/Config.kt (87%) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index ea94fd932..c658a5fd6 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -3,7 +3,7 @@ package dev.dres import dev.dres.api.cli.Cli import dev.dres.api.cli.OpenApiCommand import dev.dres.api.rest.RestApi -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.data.model.admin.DbRole import dev.dres.data.model.admin.DbUser import dev.dres.data.model.audit.DbAuditLogEntry @@ -78,7 +78,7 @@ object DRES { val store = this.prepareDatabase(config) /* Initialize the global Cache Manager. */ - val global = CacheManager(config) + val global = CacheManager(config, store) /* Initialize RunExecutor. */ RunExecutor.init(config, store, global) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index d05a901be..3ecfe38cc 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -7,7 +7,7 @@ import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.core.subcommands import com.github.ajalt.clikt.output.CliktHelpFormatter import com.github.ajalt.clikt.output.HelpFormatter -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.mgmt.cache.CacheManager import jetbrains.exodus.database.TransientEntityStore import org.jline.builtins.Completers diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index b89435754..60d51fd0f 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -16,9 +16,8 @@ import com.github.kokorin.jaffree.ffprobe.FFprobeResult import com.jakewharton.picnic.table import dev.dres.DRES import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.data.model.media.* -import dev.dres.mgmt.cache.CacheManager import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.slf4j.LoggerFactory @@ -347,18 +346,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va help = "Video file types (endings) to be considered in the scan" ).convert { it.lowercase() }.multiple() - /** The path to the FFmpeg binary used by this [Scan] instance. */ - private val ffmpegBin = when { - this@MediaCollectionCommand.config.ffmpegBinary != null && Files.isDirectory(this@MediaCollectionCommand.config.ffmpegBinary) -> this@MediaCollectionCommand.config.ffmpegBinary /* Explicitly configured. */ - Files.isDirectory(DRES.APPLICATION_ROOT.parent.resolve("ffmpeg")) -> DRES.APPLICATION_ROOT.parent.resolve("ffmpeg") /* Distribution */ - Files.isDirectory(DRES.APPLICATION_ROOT.parent.parent.parent.resolve("ext/ffmpeg")) -> DRES.APPLICATION_ROOT.parent.parent.parent.resolve("ext/ffmpeg") /* Debug mode. */ - Files.isDirectory(Paths.get("ext/ffmpeg")) -> Paths.get("ext/ffmpeg") - Files.isDirectory(Paths.get("ffmpeg")) -> Paths.get("ffmpeg") - else -> throw IllegalStateException("Could not find valid FFmpeg binary path.") - } - private fun analyze(videoPath: Path, countFrames: Boolean = false): FFprobeResult = - FFprobe.atPath(this.ffmpegBin) + FFprobe.atPath(this@MediaCollectionCommand.config.cache.ffmpegPath()) .setInput(videoPath) .setShowStreams(true) .setCountFrames(countFrames) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 2c9d85e7e..a406f6faa 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -33,7 +33,7 @@ import dev.dres.api.rest.handler.system.LogoutHandler import dev.dres.api.rest.handler.users.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.mgmt.cache.CacheManager import dev.dres.run.RunExecutor import dev.dres.utilities.NamedThreadFactory diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index 12a6431a7..bee601e06 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -3,12 +3,10 @@ package dev.dres.api.rest.handler.collection import dev.dres.DRES import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.Config import io.javalin.http.Context import io.javalin.openapi.* import java.nio.file.FileVisitOption import java.nio.file.Files -import java.nio.file.Paths import kotlin.streams.toList /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index f57af5db4..0c7acf9cb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -9,7 +9,6 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.Config import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.RunActionContext diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 1f5e59d74..b8f32f397 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -4,15 +4,12 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 5ccff0fc8..50ea739b5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.types.competition.ApiEvaluationTemplate import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.data.model.admin.DbUser import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.DbEvaluationTemplate diff --git a/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt b/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt index de9640a46..4f5a2c1e0 100644 --- a/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt +++ b/backend/src/main/kotlin/dev/dres/data/migration/Migration.kt @@ -1,6 +1,6 @@ package dev.dres.data.migration -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import jetbrains.exodus.database.TransientEntityStore /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt b/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt new file mode 100644 index 000000000..fe9e0ab19 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt @@ -0,0 +1,50 @@ +package dev.dres.data.model.config + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import dev.dres.DRES +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +/** + * Configuration related to the handling of the preview cache by DRES. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +data class CacheConfig( + /** Interval in ms at which cache cleanup is triggered. Defaults to 1 minute. */ + val cleanupIntervalMs: Long = 6000L, + + /** Threshold after which unused files are evicted from cache. */ + val evictionThresholdMs: Long = 30000L, + + /** The maximum number of threads used for generating preview images and videos. */ + val maxRenderingThreads: Int = 2, + + /** Custom path to FFmpeg. If not set, default paths will be tried instead. */ + val ffmpegPath: Path? = null, + + /** The maximum bounding box for a logo. Defaults to 500px. */ + val logoMaxSize: Int = 500, + + /** The maximum bounding box for a preview image. Defaults to 500px. */ + val previewImageMaxSize: Int = 500, + + /** The maximum bounding box for a preview video. Defaults to 480p. */ + val previewVideoMaxSize: Int = 480, +) { + + /** + * Returns the path to FFmpeg. + */ + fun ffmpegPath() = when { + this.ffmpegPath != null && Files.isDirectory(ffmpegPath) -> ffmpegPath /* Explicitly configured. */ + Files.isDirectory(DRES.APPLICATION_ROOT.parent.resolve("ffmpeg")) -> DRES.APPLICATION_ROOT.parent.resolve("ffmpeg") /* Distribution */ + Files.isDirectory(DRES.APPLICATION_ROOT.parent.parent.parent.resolve("ext/ffmpeg")) -> DRES.APPLICATION_ROOT.parent.parent.parent.resolve("ext/ffmpeg") /* Debug mode. */ + Files.isDirectory(Paths.get("ext/ffmpeg")) -> Paths.get("ext/ffmpeg") + Files.isDirectory(Paths.get("ffmpeg")) -> Paths.get("ffmpeg") + else -> throw IllegalStateException("Could not find valid FFmpeg binary path.") + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt similarity index 87% rename from backend/src/main/kotlin/dev/dres/data/model/Config.kt rename to backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index 97cfee5d4..de94ecfea 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -1,8 +1,7 @@ -package dev.dres.data.model +package dev.dres.data.model.config import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.ObjectMapper -import java.io.File import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption @@ -14,8 +13,7 @@ data class Config( val enableSsl: Boolean = true, val keystorePath: String = "keystore.jks", val keystorePassword: String = "password", - val logoMaxSize: Int = 1000, - val ffmpegBinary: Path? = null + val cache: CacheConfig= CacheConfig() ) { companion object{ diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index 6757d1369..c9d3f0450 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -1,25 +1,13 @@ package dev.dres.data.model.template.task -import dev.dres.DRES import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.api.rest.types.competition.tasks.ApiHint -import dev.dres.api.rest.types.task.ApiContentElement -import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.Config import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange -import dev.dres.mgmt.cache.CacheManager import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.query.FilteringContext.le import kotlinx.dnq.simple.requireIf -import java.io.FileNotFoundException -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.util.* /** * Represents the hint given by a [DbTaskTemplate], e.g., a media item or text that is shown. diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 9c5cf8c6c..7b4bfeed8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -2,13 +2,9 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.competition.tasks.* import dev.dres.api.rest.types.competition.team.ApiTeam -import dev.dres.api.rest.types.task.ApiContentElement -import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.Config import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection -import dev.dres.data.model.run.DbTask import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.template.TemplateId @@ -16,8 +12,6 @@ import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.* import kotlinx.dnq.simple.min -import java.io.FileNotFoundException -import java.io.IOException import java.lang.IllegalStateException import java.lang.Long.max import java.util.* diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt index 0a87fd11d..9eb61c124 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt @@ -2,9 +2,6 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.api.rest.types.competition.tasks.ApiTarget -import dev.dres.api.rest.types.task.ApiContentElement -import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.Config import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.media.time.TemporalPoint @@ -12,12 +9,6 @@ import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.simple.requireIf -import java.io.FileNotFoundException -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths -import java.nio.file.StandardOpenOption -import java.util.* /** * Represents the target of a [DbTaskTemplate], i.e., the [DbMediaItem] or part thereof that is considered correct. diff --git a/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt index f409557f1..78309f23b 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt @@ -4,13 +4,19 @@ import com.github.kokorin.jaffree.ffmpeg.FFmpeg import com.github.kokorin.jaffree.ffmpeg.UrlInput import com.github.kokorin.jaffree.ffmpeg.UrlOutput import dev.dres.DRES -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.media.MediaItemType +import dev.dres.data.model.run.DbEvaluation +import dev.dres.data.model.run.DbEvaluationStatus import dev.dres.utilities.CompletedFuture import dev.dres.utilities.FailedFuture +import jetbrains.exodus.database.TransientEntityStore import jetbrains.exodus.kotlin.synchronized +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct import org.slf4j.LoggerFactory import org.slf4j.MarkerFactory import java.awt.Image @@ -28,21 +34,14 @@ import javax.imageio.ImageIO * @author Ralph Gasser * @version 1.0.0 */ -class CacheManager(config: Config) { +class CacheManager(private val config: Config, private val store: TransientEntityStore) { companion object { private val LOGGER = LoggerFactory.getLogger(PreviewImageFromVideoRequest::class.java) private val MARKER = MarkerFactory.getMarker("FFMPEG") } /** The path to the FFmpeg binary used by this [CacheManager] instance. */ - private val ffmpegBin = when { - config.ffmpegBinary != null && Files.isDirectory(config.ffmpegBinary) -> config.ffmpegBinary /* Explicitly configured. */ - Files.isDirectory(DRES.APPLICATION_ROOT.parent.resolve("ffmpeg")) -> DRES.APPLICATION_ROOT.parent.resolve("ffmpeg") /* Distribution */ - Files.isDirectory(DRES.APPLICATION_ROOT.parent.parent.parent.resolve("ext/ffmpeg")) -> DRES.APPLICATION_ROOT.parent.parent.parent.resolve("ext/ffmpeg") /* Debug mode. */ - Files.isDirectory(Paths.get("ext/ffmpeg")) -> Paths.get("ext/ffmpeg") - Files.isDirectory(Paths.get("ffmpeg")) -> Paths.get("ffmpeg") - else -> throw IllegalStateException("Could not find valid FFmpeg binary path.") - } + private val ffmpegBin = this.config.cache.ffmpegPath() /** The [Path] to the cache location. */ private val cacheLocation = DRES.DATA_ROOT.resolve("cache") @@ -51,7 +50,7 @@ class CacheManager(config: Config) { private val inTransit = ConcurrentHashMap>() /** The [ExecutorService] used by this [CacheManager]. */ - private val executor: ExecutorService = Executors.newFixedThreadPool(2) + private val executor: ExecutorService = Executors.newFixedThreadPool(this.config.cache.maxRenderingThreads) init { println("Found FFmpeg at ${this.ffmpegBin}...") @@ -71,7 +70,7 @@ class CacheManager(config: Config) { fun asyncPreviewImage(item: DbMediaItem, frame: Long = 0): Future { require(frame >= 0) { "Frame numbers cannot be negative." } /** The output path generated by this [PreviewImageFromVideoRequest]. */ - val output = this@CacheManager.cacheLocation.resolve("${item.mediaItemId}_${frame}.jpg") + val output = this@CacheManager.cacheLocation.resolve("${item.mediaItemId}_${frame}-${frame}.jpg") if (Files.exists(output)) { return CompletedFuture(output) } @@ -128,7 +127,48 @@ class CacheManager(config: Config) { } /** - * Clears the cache directory associated with this [CacheManager] + * Cleans the cache directory associated with this [CacheManager]. The cleanup mechanism makes sure, that all + * as part of an ongoing evaluation are preserved and not deleted. + * + * Requires an active transaction. + */ + fun cleanup() = try { + /* Fetch all items related to a hint preview. */ + val blackList = mutableSetOf() + this.store.transactional(true) { + DbEvaluation.filter { it.status eq DbEvaluationStatus.ACTIVE } + .flatMapDistinct { it.template.tasks } + .flatMapDistinct { it.hints } + .filter { it.item ne null } + .asSequence().forEach { + blackList.add(this@CacheManager.cacheLocation.resolve("${it.item!!.mediaItemId}_${it.start ?: 0}-${it.end ?: 0}")) + } + + /* Fetch all items related to a target preview. */ + DbEvaluation.filter { it.status eq DbEvaluationStatus.ACTIVE } + .flatMapDistinct { it.template.tasks } + .flatMapDistinct { it.targets } + .filter { it.item ne null } + .asSequence().forEach { + blackList.add(this@CacheManager.cacheLocation.resolve("${it.item!!.mediaItemId}_${it.range?.start ?: 0}-${it.range?.end ?: 0}")) + } + } + + /* Delete all files that have not been blacklisted. */ + Files.walk(this.cacheLocation).use { walk -> + walk.sorted(Comparator.reverseOrder()).forEach { + val time = Files.getLastModifiedTime(it) + if ((System.currentTimeMillis() - time.toMillis()) > 300000L && !blackList.contains(it)) { + Files.delete(it) + } + } + } + } catch(e: Throwable) { + LOGGER.warn("Failed to cleanup cache directory ${this.cacheLocation}: ${e.message}") + } + + /** + * Clears the cache directory associated with this [CacheManager]. */ fun clear() = try { Files.walk(this.cacheLocation).use { walk -> @@ -145,7 +185,6 @@ class CacheManager(config: Config) { */ fun stop() { this.executor.shutdown() - this.clear() this.executor.awaitTermination(5000, TimeUnit.MILLISECONDS) } @@ -166,7 +205,7 @@ class CacheManager(config: Config) { /** * A [Callable] that generates a preview image from an image file. */ - inner class PreviewImageFromImageRequest constructor(input: Path, output: Path, val size: Int = 500): AbstractPreviewRequest(input, output) { + inner class PreviewImageFromImageRequest constructor(input: Path, output: Path, private val size: Int = this@CacheManager.config.cache.previewImageMaxSize): AbstractPreviewRequest(input, output) { override fun call(): Path = try {/* Try to read image. */ Files.newInputStream(this.input, StandardOpenOption.READ).use {i -> @@ -197,15 +236,15 @@ class CacheManager(config: Config) { /** * A [Callable] that generates a preview image from a video file. */ - inner class PreviewImageFromVideoRequest constructor(input: Path, output: Path, private val start: Long): AbstractPreviewRequest(input, output) { + inner class PreviewImageFromVideoRequest constructor(input: Path, output: Path, private val start: Long, private val size: Int = this@CacheManager.config.cache.previewImageMaxSize): AbstractPreviewRequest(input, output) { override fun call(): Path = try { FFmpeg.atPath(this@CacheManager.ffmpegBin). addInput(UrlInput.fromPath(this.input)) .addOutput(UrlOutput.toPath(this.output)) .setOverwriteOutput(true) .addArguments("-ss", millisecondToTimestamp(this.start)) - .addArguments("-vframes", "1") - .addArguments("-filter:v", "scale=120:-1") + .addArguments("-frames:v", "1") + .addArguments("-filter:v", "scale=${this@CacheManager.config.cache.previewImageMaxSize}:-1") .setOutputListener { l -> LOGGER.debug(MARKER, l); } .execute() this.output @@ -234,6 +273,7 @@ class CacheManager(config: Config) { .addArguments("-c:v", "libx264") .addArguments("-c:a", "aac") .addArguments("-b:v", "2000k") + .addArguments("-filter:v", "scale=${this@CacheManager.config.cache.previewVideoMaxSize}:-1") .addArguments("-tune", "zerolatency") .addArguments("-preset", "slow") .setOutputListener { l -> LOGGER.debug(MARKER, l); } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 1641b49cd..7c32d20f1 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType -import dev.dres.data.model.Config +import dev.dres.data.model.config.Config import dev.dres.data.model.run.* import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.EvaluationId From 8a747037ec06fe041f11a7abd4fced3dd76634ab Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 23 Mar 2023 14:15:58 +0100 Subject: [PATCH 221/498] KisTaskScorer no longer considers extended task durations Closes #389 --- .../main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 69c369951..4a7479ac2 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -34,16 +34,15 @@ class KisTaskScorer( * @return A [Map] of [TeamId] to calculated task score. */ override fun scoreMap(submissions: Sequence): Map { - val taskDuration = this.scoreable.duration + val taskDuration = this.scoreable.duration.toDouble() val taskStartTime = this.scoreable.started ?: throw IllegalArgumentException("No task start time specified.") - val tDur = max(taskDuration * 1000L, (scoreable.ended ?: 0) - taskStartTime).toDouble() //actual duration of task, in case it was extended during competition return this.scoreable.teams.associateWith { teamId -> val verdicts = submissions.filter { it.teamId == teamId }.sortedBy { it.timestamp }.flatMap { sub -> - sub.answerSets().filter { (it.status() == VerdictStatus.CORRECT) or (it.status() == VerdictStatus.WRONG) }.asSequence() + sub.answerSets().filter { (it.status() == VerdictStatus.CORRECT) or (it.status() == VerdictStatus.WRONG) } }.toList() val firstCorrect = verdicts.indexOfFirst { it.status() == VerdictStatus.CORRECT } val score = if (firstCorrect > -1) { - val timeFraction = 1.0 - (verdicts[firstCorrect].submission.timestamp - taskStartTime) / tDur + val timeFraction = 1.0 - (verdicts[firstCorrect].submission.timestamp - taskStartTime) / taskDuration max( 0.0, this.maxPointsAtTaskEnd + From 7cb94e5dd5340a6bf7d3a4254bd89dbaedc0cb64 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 24 Mar 2023 16:01:25 +0100 Subject: [PATCH 222/498] Addressing #367 by using milliseconds --- .../run-admin-submissions-list.component.html | 4 ++-- .../app/services/pipes/format-time-pipe.pipe.ts | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html index b76f4fa8f..d2b2fb74c 100644 --- a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html +++ b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html @@ -92,14 +92,14 @@

Submission overview for task {{ (taskId | async).substr(0, 8) }}

Start - {{ submission.start != null ? (submission.start / 1000 | formatTime) : 'N/A' }} + {{ submission.start != null ? (submission.start | formatTime) : 'N/A' }}ms End - {{ submission.end != null ? (submission.end / 1000 | formatTime) : 'N/A' }} + {{ submission.end != null ? (submission.end | formatTime) : 'N/A' }}ms diff --git a/frontend/src/app/services/pipes/format-time-pipe.pipe.ts b/frontend/src/app/services/pipes/format-time-pipe.pipe.ts index 33b467f24..cbb14e85d 100644 --- a/frontend/src/app/services/pipes/format-time-pipe.pipe.ts +++ b/frontend/src/app/services/pipes/format-time-pipe.pipe.ts @@ -8,18 +8,21 @@ import { Pipe, PipeTransform } from '@angular/core'; }) export class FormatTimePipePipe implements PipeTransform { transform(value: number): string { - const hrs = Math.floor(value / 3600); - const mins = Math.floor((value % 3600) / 60); - const secs = Math.floor(value % 60); + const hrs = Math.floor((value/1000) / 3600); + const mins = Math.floor(((value/1000) % 3600) / 60); + const secs = Math.floor((value/1000) % 60); + const ms = Math.floor(value % 1000); let out = ''; /* Hours if present */ if (hrs > 0) { - out += '' + (hrs < 10 ? '0' : '') + hrs + ':'; + out += '' + (''+hrs).padStart(2, '0') + ':'; } /* Minutes */ - out += '' + (mins < 10 ? '0' : '') + mins + ':'; - /* seconds */ - out += '' + (secs < 10 ? '0' : '') + secs; + out += '' + (''+mins).padStart(2, '0') + ':'; + /* Seconds */ + out += '' + (''+secs).padStart(2, '0') + '.'; + /* Milliseconds */ + out += '' + (''+ms).padStart(3,'0'); return out; } } From 12431e6e9a93012cf9f80f426e8747919a191bdf Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 24 Mar 2023 16:03:08 +0100 Subject: [PATCH 223/498] Addressing #375 by changing 'Abort' to 'End' --- frontend/src/app/run/run-admin-view.component.html | 4 ++-- frontend/src/app/run/run-admin-view.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index fb22cd2a6..479bab8cc 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -12,8 +12,8 @@
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index aa95f73fb..8097e8f38 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -6,6 +6,8 @@ import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { AppConfig } from './app.config'; import {ApiRole, ApiUser} from '../../openapi'; +import { MatDialog } from "@angular/material/dialog"; +import { ServerInfoComponent } from "./shared/server-info/server-info.component"; @Component({ selector: 'app-root', @@ -24,7 +26,8 @@ export class AppComponent { private authenticationService: AuthenticationService, private router: Router, private snackBar: MatSnackBar, - public config: AppConfig + public config: AppConfig, + private dialog: MatDialog ) { this.user = this.authenticationService.user; this.loggedIn = this.authenticationService.isLoggedIn; @@ -49,4 +52,8 @@ export class AppComponent { public profile() { this.router.navigate(['/user']); } + + public openInfoDialog(){ + this.dialog.open(ServerInfoComponent, { width: '600px' }); + } } diff --git a/frontend/src/app/shared/server-info/server-info.component.html b/frontend/src/app/shared/server-info/server-info.component.html new file mode 100644 index 000000000..c9cc397bb --- /dev/null +++ b/frontend/src/app/shared/server-info/server-info.component.html @@ -0,0 +1,54 @@ +

DRES Server Information

+
+
+
+ DRES version: + {{(info | async )?.version}} +
+
+ Start time: + {{(info | async )?.startTime}} +
+
+ Uptime: + {{(info | async )?.uptime}} +
+
+ +
+ JVM: + {{(info | async )?.jvm}} +
+
+ OS: + {{(info | async )?.os}} +
+
+ Cores: + {{(info | async )?.cores}} +
+
+ Server threads: + {{(info | async )?.availableSeverThreads}} +
+
+ Total memory: + {{(info | async )?.totalMemory}} +
+
+ Free memory: + {{(info | async )?.freeMemory}} +
+
+ Load: + {{(info | async )?.load}} +
+
+ Start arguments: + {{(info | async )?.args}} +
+
+
+
+ +
diff --git a/frontend/src/app/shared/server-info/server-info.component.scss b/frontend/src/app/shared/server-info/server-info.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/shared/server-info/server-info.component.ts b/frontend/src/app/shared/server-info/server-info.component.ts new file mode 100644 index 000000000..d9c71f0af --- /dev/null +++ b/frontend/src/app/shared/server-info/server-info.component.ts @@ -0,0 +1,47 @@ +import { AfterViewInit, Component, OnDestroy, OnInit } from "@angular/core"; +import { AuthenticationService } from "../../services/session/authentication.sevice"; +import { Observable, Subscription } from "rxjs"; +import { ApiRole, DresInfo, StatusService } from "../../../../openapi"; + +@Component({ + selector: "app-server-info", + templateUrl: "./server-info.component.html", + styleUrls: ["./server-info.component.scss"] +}) +export class ServerInfoComponent implements OnInit, OnDestroy, AfterViewInit { + + public info: Observable; + + private authSub: Subscription; + private infoSub: Subscription; + public isAdmin: boolean; + + + constructor(private auth: AuthenticationService, private status: StatusService) { + this.authSub = this.auth.user.subscribe( + u => { + this.isAdmin = u?.role === ApiRole.ADMIN + } + ); + } + + ngOnInit(): void { + this.infoSub = this.status.getApiV2StatusInfo().subscribe(info => { + this.info = new Observable(subscriber => { + subscriber.next(info); + }); + }); + } + + ngAfterViewInit() { + + } + + ngOnDestroy(): void { + this.infoSub?.unsubscribe(); + this.infoSub = null; + this.authSub?.unsubscribe(); + this.authSub = null; + } + +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 43d84a993..27d53c8e6 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -13,6 +13,8 @@ import { MatDialogModule } from '@angular/material/dialog'; import { DynamicTableComponent } from './dynamic-table/dynamic-table.component'; import {MatTableModule} from '@angular/material/table'; import { ActionableDynamicTable } from './actionable-dynamic-table/actionable-dynamic-table.component'; +import { ServerInfoComponent } from './server-info/server-info.component'; +import { FlexModule } from "@angular/flex-layout"; @NgModule({ declarations: [ @@ -23,6 +25,7 @@ import { ActionableDynamicTable } from './actionable-dynamic-table/actionable-dy ConfirmationDialogComponent, DynamicTableComponent, ActionableDynamicTable, + ServerInfoComponent, ], exports: [ BackButtonComponent, @@ -33,6 +36,6 @@ import { ActionableDynamicTable } from './actionable-dynamic-table/actionable-dy DownloadButtonComponent, UploadJsonButtonComponent, ], - imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule], + imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule, FlexModule] }) export class SharedModule {} From 36e520d78302fdcc45eba70c1265ab30744ba85e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 24 Mar 2023 16:35:07 +0100 Subject: [PATCH 225/498] Bump to kotlin 1.8.+ and robustness for windows --- backend/build.gradle | 4 ++-- backend/src/main/kotlin/dev/dres/DRES.kt | 3 ++- gradle.properties | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 4407bf944..27f86eb11 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -47,8 +47,8 @@ compileTestKotlin { } dependencies { - def javalin = '5.3.2' - def javalinOpenapi = '5.3.3-SNAPSHOT' + def javalin = '5.4.2' + def javalinOpenapi = '5.4.2' def log4jVersion = '2.17.1' ///// Frontend files (produced by sub-project). diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index c658a5fd6..2bd5fb57e 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -31,6 +31,7 @@ import kotlinx.dnq.util.initMetaData import java.io.File import java.nio.file.Path import java.nio.file.Paths +import kotlin.io.path.absolute /** @@ -65,7 +66,7 @@ object DRES { val config = if (args.isNotEmpty()) { val configPath = Paths.get(args[0]) val config = Config.read(configPath) - DATA_ROOT = configPath.parent + DATA_ROOT = configPath.absolute().parent config } else { Config() diff --git a/gradle.properties b/gradle.properties index d361cde29..f2ddb277b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ version_jaffree=0.11.0 version_javalin=5.1.2 version_jline3=3.21.0 version_junit=5.9.1 -version_kotlin=1.7.20 +version_kotlin=1.8.10 version_kotlin_csv=1.6.0 version_log4j=2.18.0 version_picnic=0.6.0 From 901b09707c6f36c8b19ae2abe75a705bc51ef4e2 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 24 Mar 2023 16:38:31 +0100 Subject: [PATCH 226/498] Added build-process generated yarn-lock to ignorefiles --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3744893a6..9725c4f58 100644 --- a/.gitignore +++ b/.gitignore @@ -304,3 +304,6 @@ local.config.json ## Do not include the generated client bindings frontend/openapi/ + +## Do not include the generated yarn.lock when using the build process +yarn.lock From e24d6f171ee5b4bb2740a40f45f220c607fdb46e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 24 Mar 2023 22:41:03 +0100 Subject: [PATCH 227/498] Added old team table in new builder --- .../actionable-dynamic-table.component.html | 18 ++-- .../actionable-dynamic-table.component.ts | 9 +- .../teams-list/teams-list.component.html | 48 +++++++++ .../teams-list/teams-list.component.scss | 0 .../teams-list/teams-list.component.ts | 98 +++++++++++++++++++ .../template-builder-components.module.ts | 6 +- .../template-information.component.ts | 16 ++- .../template-builder.component.html | 4 +- .../template-builder.component.ts | 1 + .../template-builder.service.ts | 15 +-- 10 files changed, 192 insertions(+), 23 deletions(-) create mode 100644 frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html create mode 100644 frontend/src/app/template/template-builder/components/teams-list/teams-list.component.scss create mode 100644 frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index d9f68b123..162448149 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -9,14 +9,16 @@

{{title}}

- - - - + + + + + +
{{ element[column.property] }} diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts index c4a9cc774..718e2401d 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts @@ -6,13 +6,18 @@ export class ActionableDynamicTableColumnDefinition { type: ActionableDynamicTableColumnType; header: string; - action?: ActionableDynamicTableActionType + actions?: ActionableDynamicTableActionType[] + /** + * The name of the template to use in case this has the type CUSTOM + */ + customName?: string; } export enum ActionableDynamicTableColumnType { TEXT = 'text', NUMBER = 'number', - ACTION = 'action' + ACTION = 'action', + CUSTOM = 'custom' } export enum ActionableDynamicTableActionType{ diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html new file mode 100644 index 000000000..d15003695 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html @@ -0,0 +1,48 @@ +
+

Teams

+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
Logo + The {{team.name}}'s logo + Name{{ team.name }}Action + + + +
diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.scss b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts new file mode 100644 index 000000000..161b76d18 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts @@ -0,0 +1,98 @@ +import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { ApiTeam, UserService } from "../../../../../../openapi"; +import { MatTable } from "@angular/material/table"; +import { Observable } from "rxjs"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { MatDialog } from "@angular/material/dialog"; +import { AppConfig } from "../../../../app.config"; +import { filter, map } from "rxjs/operators"; +import { + CompetitionBuilderTeamDialogComponent +} from "../../../../competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component"; + +@Component({ + selector: 'app-teams-list', + templateUrl: './teams-list.component.html', + styleUrls: ['./teams-list.component.scss'] +}) +export class TeamsListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + + displayedColumns = ['logo', 'name', 'action']; + + @ViewChild('teamTable') + teamTable: MatTable; + + teams: Observable = new Observable((o) => o.next([])); + constructor( + builderService: TemplateBuilderService, + private userService: UserService, + private dialog: MatDialog, + private config: AppConfig + ) { + super(builderService); + } + + ngOnInit(): void { + this.onInit(); + } + + onChange() { + this.teams = this.builderService.templateAsObservable().pipe( + map((t) => { + if (t) { + return t.teams; + } else { + return []; + } + }) + ); + } + + ngOnDestroy() { + this.onDestroy(); + } + + /** + * Generates a URL for the logo of the team. + */ + public teamLogo(team: ApiTeam): string { + if ( team.logoData != null) { + return team.logoData; + } else { + return this.config.resolveApiUrl(`/template/logo/${team.id}`); + } + } + + public trackByTeamId(index: number, team: ApiTeam){ + return team.id; + } + + public addTeam(){ + const dialogRef = this.dialog.open(CompetitionBuilderTeamDialogComponent, {width: '600px'}); + dialogRef.afterClosed().pipe(filter((t) => t != null)).subscribe((t) => { + this.builderService.getTemplate().teams.push(t); + this.builderService.update(); + this.teamTable.renderRows(); + }) + } + + public editTeam(team: ApiTeam){ + const index = this.builderService.getTemplate().teams.indexOf(team); + if(index > -1){ + const dialogRef = this.dialog.open(CompetitionBuilderTeamDialogComponent, {data: team, width: '600px'}); + dialogRef.afterClosed().pipe(filter((t) => t != null)).subscribe((t)=>{ + this.builderService.getTemplate().teams[index] = t; + this.builderService.update(); + this.teamTable.renderRows(); + }) + } + } + + public removeTeam(team: ApiTeam){ + this.builderService.getTemplate().teams.splice(this.builderService.getTemplate().teams.indexOf(team), 1); + this.builderService.update(); + this.teamTable.renderRows(); + } + +} diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index 72b024d7c..e21e97a75 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -12,12 +12,14 @@ import {MatIconModule} from '@angular/material/icon'; import {MatMenuModule} from '@angular/material/menu'; import {MatAutocompleteModule} from '@angular/material/autocomplete'; import {MatTableModule} from '@angular/material/table'; +import { TeamsListComponent } from './teams-list/teams-list.component'; @NgModule({ declarations: [ TemplateInformationComponent, - JudgesListComponent + JudgesListComponent, + TeamsListComponent ], imports: [ CommonModule, @@ -32,7 +34,7 @@ import {MatTableModule} from '@angular/material/table'; MatAutocompleteModule, MatTableModule ], - exports: [TemplateInformationComponent, JudgesListComponent] + exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent] }) export class TemplateBuilderComponentsModule { } diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts index a94a75fe4..ecbbc3218 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts @@ -16,6 +16,8 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone private changeSub: Subscription; + private initOngoing = true; + constructor( private templateService: TemplateService, builder: TemplateBuilderService @@ -25,7 +27,6 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone ngOnInit(): void { this.onInit(); - this.changeSub = this.form.valueChanges.subscribe(() => { this.builderService.markDirty(); }); @@ -38,9 +39,18 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone onChange() { if(this.builderService.getTemplate()){ - this.form.get('name').setValue(this.builderService.getTemplate().name); - this.form.get('description').setValue(this.builderService.getTemplate().description); + if(this.form.get('name').value !== this.builderService.getTemplate().name){ + this.form.get('name').setValue(this.builderService.getTemplate().name, {emitEvent: !this.initOngoing}); + } + if(this.form.get('description').value !== this.builderService.getTemplate().description){ + this.form.get('description').setValue(this.builderService.getTemplate().description, {emitEvent: !this.initOngoing}); + } + // We need to check the end of init here + if(this.initOngoing){ + this.initOngoing = false; + } } } + } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 1f09f759d..48669f1bf 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -3,7 +3,7 @@ -

Edit competition {{(builderService.templateAsObservable() | async)?.id}} (unsaved changes)

+

Edit competition {{(builderService.templateAsObservable() | async)?.id}} (unsaved changes)

@@ -35,7 +35,7 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}}
-

TEAMS

+
diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index b4b0498bd..8181d4284 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -72,6 +72,7 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i this.templateService.patchApiV2TemplateByTemplateId(this.builderService.getTemplate().id, this.builderService.getTemplate()).subscribe((s) => { this.snackBar.open(s.description, null, {duration: 5000}); this.builderService.unmarkDirty(); + console.log("TemplateBuilder: Saved successfully", this.builderService.isDirty()) }, (r) => this.snackBar.open(`Error: ${r?.error?.description}`, null, {duration: 5000})); } diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index cec2d2f40..9fb918134 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -16,14 +16,13 @@ export class TemplateBuilderService { private shouldLogDirtyChanges = true; private templateSubject: BehaviorSubject = new BehaviorSubject(null); - private dirty = false; + private dirtySubject: BehaviorSubject = new BehaviorSubject(false); constructor() { } public initialise(template: ApiEvaluationTemplate){ this.templateSubject.next(template); - this.markDirty(); // might be a little early } public getTemplate(){ @@ -57,22 +56,26 @@ export class TemplateBuilderService { } public checkDirty(){ - if(!this.dirty){ + if(!this.dirtySubject.value){ return true; } return confirm('There are unsaved changes in this evaluation template that will be lost. Do you really want to proceed?') } public markDirty(){ - this.dirty = true; + this.dirtySubject.next(true); } public unmarkDirty(){ - this.dirty = false; + this.dirtySubject.next(false); } public isDirty(){ - return this.dirty; + return this.dirtySubject.value; + } + + public dirty(){ + return this.dirtySubject.asObservable(); } public removeTaskType(taskType: ApiTaskType){ From f1e4433235c5c98516aef32f8984a50fd58948bd Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 25 Mar 2023 11:33:53 +0100 Subject: [PATCH 228/498] Added task type list. might require re-styling --- ...tion-builder-task-type-dialog.component.ts | 6 +- .../task-types-list.component.html | 34 ++++++ .../task-types-list.component.scss | 0 .../task-types-list.component.ts | 109 ++++++++++++++++++ .../template-builder-components.module.ts | 34 +++--- .../template-builder.component.html | 5 +- .../template-builder.service.ts | 2 + 7 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html create mode 100644 frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss create mode 100644 frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts index 8d9c97ada..1c899ee43 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts @@ -161,7 +161,9 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV for (let configurationKey in this.data?.configuration) { const keyParts = configurationKey.split('.'); console.log(configurationKey, this?.data.configuration[configurationKey]) - parameters.push([keyParts[0] as ApiSubmissionOption, keyParts[1], this?.data.configuration[configurationKey]]); + const param: [string,string,string] = [keyParts[0] as ApiSubmissionOption, keyParts[1], this?.data.configuration[configurationKey]]; + console.log(param); + parameters.push(param); } /* --- Legacy. Keep to check validity @@ -226,7 +228,7 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV /* Parameters: Optional */ parameters: new FormArray( - parameters.map((v) => new FormArray([new FormControl(v[0]), new FormControl(v[1])])) + parameters.map((v) => new FormArray([new FormControl(v[0]), new FormControl(v[1]), new FormControl(v[2])])) ), }); } diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html new file mode 100644 index 000000000..e5a8126ae --- /dev/null +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html @@ -0,0 +1,34 @@ +
+

Task types

+
+ + + + + + + + + + + + +
+ + +
+
+

{{taskType.name}}

+
+ +
+
+ Scoring: {{taskType.scoreOption}} + Hints: {{h}}, + Target: {{taskType.targetOption}} +
+
+
+
diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts new file mode 100644 index 000000000..a87afec2b --- /dev/null +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts @@ -0,0 +1,109 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { MatDialog } from "@angular/material/dialog"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { ApiTaskType } from "../../../../../../openapi"; +import { Observable } from "rxjs"; +import { filter, map } from "rxjs/operators"; +import { + CompetitionBuilderTaskTypeDialogComponent +} from "../../../../competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component"; + +@Component({ + selector: "app-task-types-list", + templateUrl: "./task-types-list.component.html", + styleUrls: ["./task-types-list.component.scss"] +}) +export class TaskTypesListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + + public static TKIS_PRESET = { + name: 'Textual Known Item Search', + duration: 420, + targetOption: "SINGLE_MEDIA_SEGMENT", + scoreOption: "KIS", + hintOptions: ["TEXT"], + submissionOptions: ["NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "TEMPORAL_SUBMISSION"], + taskOptions: ["HIDDEN_RESULTS"], + configuration: {["LIMIT_CORRECT_PER_TEAM.limit"]: "1"} + } as ApiTaskType; + + public static VKIS_PRESET = { + name: 'Visual Known Item Search', + duration: 300, + targetOption: "SINGLE_MEDIA_SEGMENT", + scoreOption: "KIS", + hintOptions: ["VIDEO_ITEM_SEGMENT"], + submissionOptions: ["NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "TEMPORAL_SUBMISSION"], + taskOptions: [], + configuration: {["LIMIT_CORRECT_PER_TEAM.limit"]: "1"} + } as ApiTaskType; + + public static AVS_PRESET = { + name: 'Ad-hoc Video Search', + duration: 300, + targetOption: "JUDGEMENT", + scoreOption: "AVS", + hintOptions: ["TEXT"], + submissionOptions: ["NO_DUPLICATES", "TEMPORAL_SUBMISSION"], + taskOptions: ["MAP_TO_SEGMENT"] + } as ApiTaskType; + + public static LSC_PRSET = { + name: 'Lifelog Search Challenge Topic', + duration: 300, + targetOption: "SINGLE_MEDIA_ITEM", // TODO MULTIPLE_MEDIA_ITEMS is missing + scoreOption: "KIS", + hintOptions: ["TEXT"], + submissionOptions: ["NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM"], + taskOptions: ["HIDDEN_RESULTS"], + configuration: {["LIMIT_CORRECT_PER_TEAM.limit"]: "1"} + } as ApiTaskType; + + types: Observable = new Observable((o) => o.next([])); + tkisPreset = TaskTypesListComponent.TKIS_PRESET; + vkisPreset = TaskTypesListComponent.VKIS_PRESET; + avsPreset = TaskTypesListComponent.AVS_PRESET; + + lscPreset = TaskTypesListComponent.LSC_PRSET; + + constructor( + builder: TemplateBuilderService, + private dialog: MatDialog + ) { + super(builder); + } + + ngOnDestroy(): void { + this.onDestroy(); + } + + ngOnInit(): void { + this.onInit(); + } + + onChange() { + this.types = this.builderService.templateAsObservable().pipe(map((t) => { + if (t) { + return t.taskTypes; + } else { + return []; + } + })); + } + + public addTaskType(type?: ApiTaskType) { + const dialogRef = this.dialog.open(CompetitionBuilderTaskTypeDialogComponent, { data: type ?? null, width: "750px" }); + dialogRef.afterClosed() + .pipe(filter((t) => t != null)) + .subscribe((t) => { + this.builderService.getTemplate().taskTypes.push(t); + this.builderService.update(); + }); + } + + public remove(taskType: ApiTaskType) { + this.builderService.removeTaskType(taskType); + } + +} diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index e21e97a75..26259e567 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -13,28 +13,32 @@ import {MatMenuModule} from '@angular/material/menu'; import {MatAutocompleteModule} from '@angular/material/autocomplete'; import {MatTableModule} from '@angular/material/table'; import { TeamsListComponent } from './teams-list/teams-list.component'; +import { TaskTypesListComponent } from './task-types-list/task-types-list.component'; +import { MatListModule } from "@angular/material/list"; @NgModule({ declarations: [ TemplateInformationComponent, JudgesListComponent, - TeamsListComponent + TeamsListComponent, + TaskTypesListComponent ], - imports: [ - CommonModule, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - FlexModule, - MatButtonModule, - MatTooltipModule, - MatIconModule, - MatMenuModule, - MatAutocompleteModule, - MatTableModule - ], - exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent] + imports: [ + CommonModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + FlexModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatMenuModule, + MatAutocompleteModule, + MatTableModule, + MatListModule + ], + exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, TaskTypesListComponent] }) export class TemplateBuilderComponentsModule { } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 48669f1bf..9371c6c04 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -40,7 +40,10 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} - types and groups +
+ +
groups
+
tasks diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 9fb918134..9d75a3508 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -22,6 +22,7 @@ export class TemplateBuilderService { } public initialise(template: ApiEvaluationTemplate){ + this.unmarkDirty(); this.templateSubject.next(template); } @@ -82,6 +83,7 @@ export class TemplateBuilderService { this.getTemplate().taskTypes.splice(this.getTemplate().taskTypes.indexOf(taskType), 1); this.getTemplate().taskGroups.filter((g) => g.type === taskType.name) .forEach((g) => this.removeTaskGroup(g)); + this.update(this.getTemplate()) } public removeTaskGroup(taskGroup: ApiTaskGroup){ From fc984a8e56219cebad3f592e8d1f3adbe9cd39c7 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 25 Mar 2023 15:33:54 +0100 Subject: [PATCH 229/498] Re-added task group list --- .../actionable-dynamic-table.component.html | 10 +-- .../actionable-dynamic-table.component.ts | 47 +++++------ frontend/src/app/shared/shared.module.ts | 1 + .../task-groups-list.component.html | 13 +++ .../task-groups-list.component.scss | 0 .../task-groups-list.component.ts | 84 +++++++++++++++++++ .../template-builder-components.module.ts | 10 ++- .../template-builder.component.html | 2 +- 8 files changed, 132 insertions(+), 35 deletions(-) create mode 100644 frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html create mode 100644 frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.scss create mode 100644 frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index 162448149..fc4812ac9 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -3,14 +3,14 @@

{{title}}

- - +
+
{{column.header}} - - + + @@ -18,7 +18,7 @@

{{title}}

remove
-
+
{{ element[column.property] }} diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts index 718e2401d..783fefcbb 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts @@ -2,9 +2,10 @@ import {Component, Input, OnInit, ViewChild} from '@angular/core'; import {MatTable} from '@angular/material/table'; export class ActionableDynamicTableColumnDefinition { - property: string; - type: ActionableDynamicTableColumnType; + key: string; header: string; + type: ActionableDynamicTableColumnType; + property?: string; actions?: ActionableDynamicTableActionType[] /** @@ -25,12 +26,6 @@ export enum ActionableDynamicTableActionType{ REMOVE = 'remove' } -export interface ActionableDynamicTableHandler{ - add(): T; - edit(obj: T); - beforeRemove(obj: T): boolean; -} - @Component({ selector: 'app-actionable-dynamic-table', templateUrl: './actionable-dynamic-table.component.html', @@ -53,36 +48,36 @@ export class ActionableDynamicTable { @Input() public trackByProperty?: string; - private handler: ActionableDynamicTableHandler; + @Input() + public onEdit?: (element: T) => void; + + @Input() + public onRemove?: (element: T) => void; + + @ViewChild('table') table: MatTable constructor() { } - public setHandler(handler: ActionableDynamicTableHandler){ - this.handler = handler; - } - - onEdit(element: T){ - if(this.handler){ - this.handler.edit(element); + edit(element: T){ + if(this.onEdit){ + this.onEdit(element); + this.table.renderRows(); } } - onAdd(){ - if(this.handler){ - const newElement = this.handler.add(); - this.dataSource.push(newElement); + + remove(element: T){ + if(this.onRemove){ + this.onRemove(element); + this.table.renderRows(); } } - onRemove(element: T){ - if(this.handler){ - if(this.handler.beforeRemove(element)){ - this.dataSource.splice(this.dataSource.indexOf(element), 1); - } - } + public renderRows(){ + this.table.renderRows(); } diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 27d53c8e6..895572a83 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -35,6 +35,7 @@ import { FlexModule } from "@angular/flex-layout"; ApiStatusComponent, DownloadButtonComponent, UploadJsonButtonComponent, + ActionableDynamicTable ], imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule, FlexModule] }) diff --git a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html new file mode 100644 index 000000000..19f0ffba4 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html @@ -0,0 +1,13 @@ + + + diff --git a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.scss b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts new file mode 100644 index 000000000..fdd44bd0f --- /dev/null +++ b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts @@ -0,0 +1,84 @@ +import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { MatDialog } from "@angular/material/dialog"; +import { filter, map } from "rxjs/operators"; +import { Observable } from "rxjs"; +import { ApiTaskGroup } from "../../../../../../openapi"; +import { + CompetitionBuilderTaskGroupDialogComponent, + CompetitionBuilderTaskGroupDialogData +} from "../../../../competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component"; +import { + ActionableDynamicTable, + ActionableDynamicTableActionType, + ActionableDynamicTableColumnDefinition, + ActionableDynamicTableColumnType, +} from "../../../../shared/actionable-dynamic-table/actionable-dynamic-table.component"; + +@Component({ + selector: "app-task-groups-list", + templateUrl: "./task-groups-list.component.html", + styleUrls: ["./task-groups-list.component.scss"] +}) +export class TaskGroupsListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + groups: Observable; + + columns: ActionableDynamicTableColumnDefinition[] = [ + {key: 'name', header: 'Name', property: 'name', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'type', header: 'Type', property: 'type', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'actions', header: 'Actions', type: ActionableDynamicTableColumnType.ACTION, actions: [ActionableDynamicTableActionType.REMOVE],} + ]; + + displayedColumns= ['name', 'type', 'actions']; + + @ViewChild("groupTable") groupTable: ActionableDynamicTable; + + constructor( + builderService: TemplateBuilderService, + private dialog: MatDialog + ) { + super(builderService); + } + + ngOnInit(): void { + this.onInit(); + + } + + ngOnDestroy(): void { + this.onDestroy(); + } + + onChange() { + this.groups = this.builderService.templateAsObservable().pipe(map((t) => { + if (t) { + return t.taskGroups; + } else { + return []; + } + })); + } + + public remove = (group: ApiTaskGroup) => this.removeTaskGroup(group); + + public removeTaskGroup(group: ApiTaskGroup) { + this.builderService.removeTaskGroup(group); + this.groupTable.renderRows(); + } + + public addTaskGroup() { + const dialogRef = this.dialog.open(CompetitionBuilderTaskGroupDialogComponent, { + data: { + types: this.builderService.getTemplate().taskTypes, + group: null + } as CompetitionBuilderTaskGroupDialogData, width: "600px" + }); + dialogRef.afterClosed().pipe(filter((g) => g != null)).subscribe((g) => { + this.builderService.getTemplate().taskGroups.push(g); + this.builderService.update(); + this.groupTable.renderRows(); + }); + } + +} diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index 26259e567..c65d8c225 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -15,6 +15,8 @@ import {MatTableModule} from '@angular/material/table'; import { TeamsListComponent } from './teams-list/teams-list.component'; import { TaskTypesListComponent } from './task-types-list/task-types-list.component'; import { MatListModule } from "@angular/material/list"; +import { TaskGroupsListComponent } from './task-groups-list/task-groups-list.component'; +import { SharedModule } from "../../../shared/shared.module"; @NgModule({ @@ -22,7 +24,8 @@ import { MatListModule } from "@angular/material/list"; TemplateInformationComponent, JudgesListComponent, TeamsListComponent, - TaskTypesListComponent + TaskTypesListComponent, + TaskGroupsListComponent ], imports: [ CommonModule, @@ -36,9 +39,10 @@ import { MatListModule } from "@angular/material/list"; MatMenuModule, MatAutocompleteModule, MatTableModule, - MatListModule + MatListModule, + SharedModule ], - exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, TaskTypesListComponent] + exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, TaskTypesListComponent, TaskGroupsListComponent] }) export class TemplateBuilderComponentsModule { } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 9371c6c04..d71afbb29 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -42,7 +42,7 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}}
-
groups
+
From 25294000605fb2f4692d56c10ac9e83aa9e3c6f0 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 25 Mar 2023 15:39:28 +0100 Subject: [PATCH 230/498] Fixed user chip not displaying username --- .../competition-builder-team-dialog.component.html | 2 +- .../competition-builder-team-dialog.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html index 7b4fa0a34..33fccee2e 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html @@ -14,7 +14,7 @@

Add team

[removable]="true" (removed)="removeUser(user)" > - {{ (userForId(user) | async)?.username }} + {{ user.username }} cancel diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index dafb848bd..a0451355f 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -96,7 +96,7 @@ export class CompetitionBuilderTeamDialogComponent { * * @param user The selected user. */ - public removeUser(user: number): void { + public removeUser(user: ApiUser): void { const index = this.form.get('users').value.indexOf(user); if (index >= 0) { this.form.get('users').value.splice(index, 1); From 94d133d5475ca2ca2c76e5010e2273ab57218ae0 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 25 Mar 2023 17:29:49 +0100 Subject: [PATCH 231/498] WiP: First iteration of task template editor. It doesnt work properly yet --- .../competition-form.builder.ts | 4 +- .../competition-builder.module.ts | 2 +- .../task-template-editor.component.html | 560 ++++++++++++++++++ .../task-template-editor.component.scss | 0 .../task-template-editor.component.ts | 365 ++++++++++++ .../task-templates-list.component.html | 72 +++ .../task-templates-list.component.scss | 0 .../task-templates-list.component.ts | 115 ++++ .../template-builder-components.module.ts | 18 +- .../template-builder.component.html | 5 +- .../template-builder.component.ts | 19 +- .../template-builder.service.ts | 12 + .../src/app/user/login/login.component.html | 2 +- 13 files changed, 1162 insertions(+), 12 deletions(-) create mode 100644 frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html create mode 100644 frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss create mode 100644 frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts create mode 100644 frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html create mode 100644 frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss create mode 100644 frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 4503d2e30..fde91e066 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -192,7 +192,7 @@ export class CompetitionFormBuilder { targets: (this.form.get('target') as FormArray).controls.map((t) => { return { type: t.get('type').value, - target: t.get('mediaItem') ? t.get('mediaItem').value.mediaItemId : null, + target: t.get('mediaItem')?.value?.mediaItemId ?? null, range: t.get('segment_start') && t.get('segment_start') ? ({ @@ -237,7 +237,7 @@ export class CompetitionFormBuilder { */ private initializeForm() { this.form = new FormGroup({ - id: new FormControl(this.data?.id), + id: new FormControl(this.data?.id ?? `_${Date.now()}`), name: new FormControl(this.data?.name, [Validators.required]), duration: new FormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), mediaCollection: new FormControl(this.data?.collectionId, [Validators.required]), diff --git a/frontend/src/app/competition/competition-builder/competition-builder.module.ts b/frontend/src/app/competition/competition-builder/competition-builder.module.ts index bb9c33259..bd8527e33 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.module.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.module.ts @@ -66,7 +66,7 @@ import { NgxSliderModule } from '@angular-slider/ngx-slider'; SharedModule, ColorPickerModule, ], - exports: [CompetitionBuilderComponent], + exports: [CompetitionBuilderComponent, VideoPlayerSegmentBuilderComponent], declarations: [ CompetitionBuilderComponent, CompetitionBuilderTeamDialogComponent, diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html new file mode 100644 index 000000000..03e2ae8f0 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -0,0 +1,560 @@ + + + +

Currently no task selected

+
+ +
+

UID: {{ form.get('id').value }}

+

Task group / type: {{ taskGroup.name }} / {{ taskType.name }}

+

+ + Name + + + + + + + {{ mediaCollection.name }} (ID: {{ mediaCollection.id }}) + + + + + + Duration [s] + + +

+ +
+

+ Target + + + + +

+
+ + + + + + + + +

+ + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} + + + + + Please select a valid media item i.e. use the autocomplete to select a value. + + + + + +

+

+ + + + + + + + + {{ unit }} + + + + + + +

+
+ + + +
+ +
+
+
+
+
+
+
+
+

+ + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.id }} + + + + + Please select a valid media item i.e. use the autocomplete to select a value. + + + + + + +

+ +

+ + + + + + + + + {{ unit }} + + + + + + +

+

+ +

+
+ +
+
+ +
+ +
+
+
+
+ +
+

+ Query description + + + + +

+
+
At least one query component is required!
+
+
+
+

+ + + + + + + + + + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} + + + Please select a valid media item, i.e., use the autocomplete to select a value. + + + + + + + + + + + + {{ path }} + + + Please select a valid media item, i.e., use the autocomplete to select a value. + + + + + + + + + + + + + + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} + + + Please select a valid media item i.e. use the autocomplete to select a value. + + + + + + + + + + + + + {{ unit }} + + + + + + +

+
+
+
+ +
+
+ +
+
+
+
+
+ + diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts new file mode 100644 index 000000000..8780c3efc --- /dev/null +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -0,0 +1,365 @@ +import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { Observable } from "rxjs"; +import { + ApiHintOption, ApiHintType, + ApiMediaCollection, ApiMediaItem, ApiTargetOption, + ApiTaskGroup, + ApiTaskTemplate, + ApiTaskType, ApiTemporalPoint, ApiTemporalRange, + ApiTemporalUnit, + CollectionService +} from "../../../../../../openapi"; +import { FormControl, FormGroup } from "@angular/forms"; +import { + CompetitionFormBuilder +} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; +import { + VideoPlayerSegmentBuilderData +} from "../../../../competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component"; +import { AppConfig } from "../../../../app.config"; +import { MatDialog, MatDialogConfig } from "@angular/material/dialog"; +import { filter, first } from "rxjs/operators"; +import { TimeUtilities } from "../../../../utilities/time.utilities"; +import { + AdvancedBuilderDialogComponent, + AdvancedBuilderDialogData +} from "../../../../competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component"; + +@Component({ + selector: 'app-task-template-editor', + templateUrl: './task-template-editor.component.html', + styleUrls: ['./task-template-editor.component.scss'] +}) +export class TaskTemplateEditorComponent implements OnInit { + + @Input() + public task?: ApiTaskTemplate; + + @Input() + public taskType: ApiTaskType; + @Input() + public taskGroup: ApiTaskGroup; + + form: FormGroup; + + units = [ApiTemporalUnit.FRAME_NUMBER, ApiTemporalUnit.SECONDS, ApiTemporalUnit.MILLISECONDS, ApiTemporalUnit.TIMECODE] + + mediaCollectionSource: Observable; + + formBuilder: CompetitionFormBuilder; + + @ViewChild('videoPlayer', {static: false}) video: ElementRef; + + viewLayout = 'list'; + + showVideo = false; + videoSegmentData: VideoPlayerSegmentBuilderData; + + private imagePreviewMap = new Set(); + + constructor(private builderService: TemplateBuilderService, + public collectionService: CollectionService, + public config: AppConfig, + private dialog: MatDialog) {} + + ngOnInit(): void { + if(!this.isInactive() ){ + this.init(); + } + } + + public init(){ + this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, this.task); + this.form = this.formBuilder.form; + this.form.valueChanges.subscribe(newValue => { + this.builderService.updateTask(this.formBuilder.fetchFormData()) + this.builderService.markDirty() + }); + this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); + } + + public isInactive(){ + const hasNoType = this.taskType == null + const hasNoGroup = this.taskGroup == null + return (hasNoGroup && hasNoType) + } + + public isFormValid(){ + return this.form.valid; + } + + public fetchData(){ + return this.formBuilder.fetchFormData(); + } + + private static randInt(min: number, max: number): number { + min = Math.floor(min); + max = Math.ceil(max); + return Math.round(Math.random() * (max - min + 1) + min); + } + + uploaded = (taskData: string) => { + const task = JSON.parse(taskData) as ApiTaskTemplate; + this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, task); + this.form = this.formBuilder.form; + console.log('Loaded task: ' + JSON.stringify(task)); + }; + + /** + * Handler for (+) button for query target form component. + */ + public addQueryTarget(targetType: ApiTargetOption) { + this.formBuilder.addTargetForm(targetType); + } + + /** + * Handler for (-) button for query target form component. + * + * @param index The index of the query target to remove. + */ + public removeQueryTarget(index: number) { + this.formBuilder.removeTargetForm(index); + } + + /** + * Handler for (+) button for query hint form component. + */ + public addQueryComponent(componentType: ApiHintType, previous: number = null) { + this.formBuilder.addComponentForm(componentType, previous); + } + + /** + * Handler for (-) button for query hint form components. + * + * @param index The index of the query component to remove. + */ + public removeQueryComponent(index: number) { + this.formBuilder.removeComponentForm(index); + } + + /** + * Converts a MediaItem to its display value for the autocomplete field. + * + * @param value MediaItem to convert + */ + public mediaItemToDisplay(value: ApiMediaItem) { + if (value) { + return `${value.name} (${value.type})`; + } else { + return ''; + } + } + + + /** + * The form data as json + */ + asJson(): string { + return JSON.stringify(this.formBuilder.fetchFormData()); + } + + fileProvider = () => (this.formBuilder.fetchFormData()?.name ? this.formBuilder.fetchFormData().name : 'task-download.json'); + + downloadProvider = () => this.asJson(); + + /** + * Picks a ranomd {@link MediaItem} from the list. + * + * @param collectionId The ID of the collection to pick a {@link MediaItem} from. + * @param target The target {@link FormControl} to apply the value to. + */ + public pickRandomMediaItem(collectionId: string, target: FormControl) { + this.collectionService + .getApiV2CollectionByCollectionIdRandom(collectionId) + .pipe(first()) + .subscribe((value) => { + target.setValue(value); + }); + } + + /** + * Picks a random segment within the given {@link MediaItem} . + * + * @param item The {@link VideoItem} to pick the segment for. + * @param startControl The target {@link FormControl} to apply the value to. + * @param endControl The target {@link FormControl} to apply the value to. + * @param unitControl The target {@link FormControl} to apply the value to. + */ + public pickRandomSegment(item: ApiMediaItem, startControl: FormControl, endControl: FormControl, unitControl: FormControl) { + const start = TaskTemplateEditorComponent.randInt(1, item.durationMs / 1000 / 2); // always in first half + let end = 1; + do { + end = start + TaskTemplateEditorComponent.randInt(5, item.durationMs / 1000); // Arbitrary 5 seconds minimal length + } while (end > item.durationMs / 1000); + startControl.setValue(start); + endControl.setValue(end); + unitControl.setValue('SECONDS'); + } + + toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + /* Add to toggleVideoPlayer button if + [disabled]="!target.get('mediaItem').value && !target.get('segment_start').value && !target.get('segment_end').value" + */ + /* + convert segmentStart / end based on unit to seconds + pass everything to dialog. let dialog handle and take result as temporal range + */ + let start = -1; + let end = -1; + const unit = unitControl?.value ? (unitControl.value as ApiTemporalUnit) : ApiTemporalUnit.SECONDS; + if (startControl && startControl.value) { + if (unitControl.value === 'TIMECODE') { + start = TimeUtilities.timeCode2Milliseconds(startControl.value, mediaItem.fps) / 1000; + } else { + start = + TimeUtilities.point2Milliseconds({ value: startControl.value, unit } as ApiTemporalPoint, mediaItem.fps) / 1000; + } + // start = Number.parseInt(startControl.value, 10); + } + if (endControl && endControl.value) { + if (unitControl.value === 'TIMECODE') { + end = TimeUtilities.timeCode2Milliseconds(endControl.value, mediaItem.fps) / 1000; + } else { + end = TimeUtilities.point2Milliseconds({ value: endControl.value, unit } as ApiTemporalPoint, mediaItem.fps) / 1000; + } + } + + console.log('Start=' + start + ', End=' + end); + // const config = { + // width: '800px', data: {mediaItem, segmentStart: start, segmentEnd: end} + // } as MatDialogConfig; + // const dialogRef = this.dialog.open(VideoPlayerSegmentBuilderDialogComponent, config); + /*dialogRef.afterClosed().pipe( + filter(r => r != null)) + .subscribe((r: TemporalRange) => { + console.log(`Finished: ${r}`); + startControl.setValue(r.start.value); + endControl.setValue(r.end.value); + unitControl.setValue(TemporalPoint.UnitEnum.SECONDS); + });*/ + this.videoSegmentData = { mediaItem, segmentStart: start, segmentEnd: end } as VideoPlayerSegmentBuilderData; + this.showVideo = !this.showVideo; + } + + onRangeChange(range: ApiTemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + startControl?.setValue(range.start.value); + endControl?.setValue(range.end.value); + unitControl?.setValue(ApiTemporalUnit.SECONDS); + console.log('Range updated'); + } + + isImageMediaItem(mi: ApiMediaItem): boolean { + if (mi) { + return mi.type === 'IMAGE'; + } else { + return false; + } + } + + /** + * Check whether the given index is currently listed as active preview + * + * @param index + */ + isPreviewActive(index: number): boolean { + return this.imagePreviewMap.has(index); + } + + togglePreview(index: number) { + if (this.imagePreviewMap.has(index)) { + this.imagePreviewMap.delete(index); + } else { + this.imagePreviewMap.add(index); + } + } + + getImageUrl(mi: ApiMediaItem) { + if (mi && mi.type === 'IMAGE') { + return this.config.resolveApiUrl(`/media/${mi.collectionId}/${mi.mediaItemId}`); + } + return ''; + } + + + batchAddTargets() { + const config = { + width: '400px', + height: '600px', + data: { builder: this.formBuilder }, + } as MatDialogConfig; + const dialogRef = this.dialog.open(AdvancedBuilderDialogComponent, config); + dialogRef + .afterClosed() + .pipe(filter((r) => r != null)) + .subscribe((r: Array) => { + this.formBuilder.removeTargetForm(0); + const mediaCollectionId = this.formBuilder.form.get('mediaCollection').value; + this.collectionService.postApiV2CollectionByCollectionIdResolve(mediaCollectionId, r).subscribe((items) => { + items.forEach((item) => { + const form = this.formBuilder.addTargetForm("MULTI"); + console.log(`Adding new mediaItem as target ${mediaCollectionId}/${item.name}`); + form.get('mediaItem').setValue(item); + }); + }); + /*r.forEach((name, idx) => { + const form = this.builder.addTargetForm(ConfiguredOptionTargetOption.OptionEnum.MULTIPLE_MEDIA_ITEMS); + console.log(`${mediaCollectionId} ? ${name}`); + const nameNoExt = name.substring(0, name.lastIndexOf('.')); + this.collectionService.getApiV1CollectionWithCollectionidWithStartswith(mediaCollectionId, nameNoExt) + .subscribe(item => { + console.log(`Added ${item[0]}`); + form.get('mediaItem').setValue(item[0]); + } + ); + });*/ + }); + } + + timeUnitChanged($event, startElementRef: HTMLInputElement, endElementRef: HTMLInputElement) { + console.log($event); + const type = $event.value === 'TIMECODE' ? 'text' : 'number'; + console.log('New type: ' + type); + if (startElementRef) { + startElementRef.type = type; + } + if (endElementRef) { + endElementRef.type = type; + } + } + + /** + * Handler for 'close' button. + */ + private pathForItem(item: ApiMediaItem): string { + // units = ['FRAME_NUMBER', 'SECONDS', 'MILLISECONDS', 'TIMECODE']; + let timeSuffix = ''; + switch (this.form.get('time_unit').value) { + case 'FRAME_NUMBER': + const start = Math.round(this.form.get('start').value / item.fps); + const end = Math.round(this.form.get('end').value / item.fps); + timeSuffix = `#t=${start},${end}`; + break; + case 'SECONDS': + timeSuffix = `#t=${this.form.get('start').value},${this.form.get('end').value}`; + break; + case 'MILLISECONDS': + timeSuffix = `#t=${Math.round(this.form.get('start').value / 1000)},${Math.round(this.form.get('end').value / 1000)}`; + break; + case 'TIMECODE': + console.log('Not yet supported'); // TODO make it! + break; + default: + console.error(`The time unit ${this.form.get('time_unit').value} is not supported`); + } + return ''; + } + + public renderTextTargetTooltip() { + return `The textual task target. + Regex are allowed and have to be enclosed with single backslashes (\\). + Java Regex matching is used.`; + } + +} diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html new file mode 100644 index 000000000..54f3fd301 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html @@ -0,0 +1,72 @@ +
+

Tasks

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name{{ task.name }}Group{{ task.taskGroup }}Type{{ task.taskType }}Duration [s]{{ task.duration }}Action + + + + +
diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts new file mode 100644 index 000000000..6db086841 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -0,0 +1,115 @@ +import { Component, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { ApiEvaluationTemplate, ApiTaskGroup, ApiTaskTemplate, ApiTaskType } from "../../../../../../openapi"; +import { MatTable } from "@angular/material/table"; +import { Observable } from "rxjs"; +import { SelectionModel } from "@angular/cdk/collections"; +import { map, tap } from "rxjs/operators"; + +export interface TaskTemplateEditorLauncher{ + editTask(taskType: ApiTaskType, taskGroup: ApiTaskGroup, task?: ApiTaskTemplate); +} + +@Component({ + selector: "app-task-templates-list", + templateUrl: "./task-templates-list.component.html", + styleUrls: ["./task-templates-list.component.scss"] +}) +export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { + + @Input() + editorLauncher: TaskTemplateEditorLauncher + + // TODO After dynact table fanciness (conditional multi component projection), rewrite to use dynact table + + @ViewChild('taskTable') + taskTable: MatTable; + tasks = new Observable((o) => o.next([])); + displayedColumns = ['name', 'group', 'type', 'duration', 'actions']; + + groups = new Observable((o) => o.next([])); + + selection = new SelectionModel(false, [], false); + + + constructor(builder: TemplateBuilderService) { + super(builder); + } + + ngOnInit(): void { + this.onInit(); + } + + ngOnDestroy(): void { + this.onDestroy(); + } + + public addTask(group: ApiTaskGroup){ + const type = this.builderService.getTemplate().taskTypes.find((v) => v.name === group.type); + this.editorLauncher.editTask(type, group, null); + } + + public editTask(task: ApiTaskTemplate){ + const index = this.builderService.getTemplate().tasks.indexOf(task); + if(index > -1){ + // task exists + this.selection.toggle(task); + this.editorLauncher.editTask( + this.builderService.getTemplate().taskTypes.find((v) => v.name === task.taskType), + this.builderService.getTemplate().taskGroups.find((v) => v.name === task.taskGroup), + task + ); + } + } + + public moveTaskUp(task: ApiTaskTemplate){ + const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); + if(oldIndex > 0){ + const buffer = this.builderService.getTemplate().tasks[oldIndex - 1]; + this.builderService.getTemplate().tasks[oldIndex - 1] = task; + this.builderService.getTemplate().tasks[oldIndex] = buffer; + this.builderService.update(); + this.taskTable.renderRows(); + } + } + + public moveTaskDown(task: ApiTaskTemplate){ + const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); + if(oldIndex < this.builderService.getTemplate().tasks.length - 1){ + const buffer = this.builderService.getTemplate().tasks[oldIndex + 1]; + this.builderService.getTemplate().tasks[oldIndex + 1] = task; + this.builderService.getTemplate().tasks[oldIndex] = buffer; + this.builderService.update(); + this.taskTable.renderRows(); + } + } + + public tasksLength(){ + return this.builderService.getTemplate().tasks.length; + } + + public removeTask(task: ApiTaskTemplate){ + this.builderService.removeTask(task); + } + + onChange() { + this.tasks = this.builderService.templateAsObservable().pipe(map((t) => { + if(t){ + console.log("templates list: updated", t.tasks) + return t.tasks; + }else{ + return []; + } + })); + this.taskTable?.renderRows(); + this.groups = this.builderService.templateAsObservable().pipe(map((t) => { + if(t){ + return t.taskGroups; + }else{ + return []; + } + })); + + } +} diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index c65d8c225..80612240c 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -17,6 +17,12 @@ import { TaskTypesListComponent } from './task-types-list/task-types-list.compon import { MatListModule } from "@angular/material/list"; import { TaskGroupsListComponent } from './task-groups-list/task-groups-list.component'; import { SharedModule } from "../../../shared/shared.module"; +import { TaskTemplatesListComponent } from './tasks-list/task-templates-list.component'; +import { TaskTemplateEditorComponent } from './task-template-editor/task-template-editor.component'; +import { MatSelectModule } from "@angular/material/select"; +import { MatButtonToggleModule } from "@angular/material/button-toggle"; +import { MatGridListModule } from "@angular/material/grid-list"; +import { CompetitionBuilderModule } from "../../../competition/competition-builder/competition-builder.module"; @NgModule({ @@ -25,7 +31,9 @@ import { SharedModule } from "../../../shared/shared.module"; JudgesListComponent, TeamsListComponent, TaskTypesListComponent, - TaskGroupsListComponent + TaskGroupsListComponent, + TaskTemplatesListComponent, + TaskTemplateEditorComponent ], imports: [ CommonModule, @@ -40,9 +48,13 @@ import { SharedModule } from "../../../shared/shared.module"; MatAutocompleteModule, MatTableModule, MatListModule, - SharedModule + SharedModule, + MatSelectModule, + MatButtonToggleModule, + MatGridListModule, + CompetitionBuilderModule ], - exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, TaskTypesListComponent, TaskGroupsListComponent] + exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, TaskTypesListComponent, TaskGroupsListComponent, TaskTemplatesListComponent, TaskTemplateEditorComponent] }) export class TemplateBuilderComponentsModule { } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index d71afbb29..bcf53094e 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -46,7 +46,10 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} - tasks +
+ + +
diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index 8181d4284..215ab9f97 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -1,23 +1,27 @@ -import {Component, HostListener, OnDestroy, OnInit} from '@angular/core'; +import { Component, HostListener, OnDestroy, OnInit, ViewChild } from "@angular/core"; import {AbstractTemplateBuilderComponent} from './components/abstract-template-builder.component'; import {DeactivationGuarded} from '../../services/can-deactivate.guard'; import {Observable, Subscription} from 'rxjs'; -import {DownloadService, TemplateService, UserService} from '../../../../openapi'; +import { ApiTaskGroup, ApiTaskTemplate, ApiTaskType, DownloadService, TemplateService, UserService } from "../../../../openapi"; import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; import {MatSnackBar} from '@angular/material/snack-bar'; import {TemplateBuilderService} from './template-builder.service'; import {take} from 'rxjs/operators'; +import { TaskTemplateEditorLauncher } from "./components/tasks-list/task-templates-list.component"; +import { TaskTemplateEditorComponent } from "./components/task-template-editor/task-template-editor.component"; @Component({ selector: 'app-template-builder', templateUrl: './template-builder.component.html', styleUrls: ['./template-builder.component.scss'] }) -export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy, DeactivationGuarded { +export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy, DeactivationGuarded, TaskTemplateEditorLauncher { onChange() { } + @ViewChild('taskTemplateEditor', {static: true}) taskEditor: TaskTemplateEditorComponent; + routeSub: Subscription; changeSub: Subscription; @@ -69,7 +73,7 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i public save(){ // FIXME re-enable form validation. possibly on the form-builder? console.log("save") - this.templateService.patchApiV2TemplateByTemplateId(this.builderService.getTemplate().id, this.builderService.getTemplate()).subscribe((s) => { + this.templateService.patchApiV2TemplateByTemplateId(this.builderService.getTemplate().id, this.builderService.getTemplateCleaned()).subscribe((s) => { this.snackBar.open(s.description, null, {duration: 5000}); this.builderService.unmarkDirty(); console.log("TemplateBuilder: Saved successfully", this.builderService.isDirty()) @@ -98,4 +102,11 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i } } + editTask(taskType: ApiTaskType, taskGroup: ApiTaskGroup, task?: ApiTaskTemplate) { + this.taskEditor.taskType = taskType; + this.taskEditor.taskGroup = taskGroup; + this.taskEditor.task = task; + this.taskEditor.init(); + } + } diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 9d75a3508..5a81edf0b 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -30,6 +30,13 @@ export class TemplateBuilderService { return this.templateSubject.getValue(); } + public getTemplateCleaned(){ + const template = this.templateSubject.getValue(); + console.log(template.tasks); + template.tasks.filter((t) => t.id.startsWith('_')).forEach((t) => t.id = ''); + return template; + } + public templateAsObservable(){ return this.templateSubject.asObservable(); } @@ -41,8 +48,13 @@ export class TemplateBuilderService { } public updateTask(task: ApiTaskTemplate){ + console.log('update task', task); + if(this.getTemplate().tasks == null || this.getTemplate().tasks.length === 0){ + this.templateSubject.getValue().tasks.push(task); + } const idx = this.getTemplate().tasks.findIndex(t => t.id === task.id); this.templateSubject.getValue().tasks[idx] = task; + this.update(this.getTemplate()); this.markDirty(); } diff --git a/frontend/src/app/user/login/login.component.html b/frontend/src/app/user/login/login.component.html index ca50561c5..9fe31c0d3 100644 --- a/frontend/src/app/user/login/login.component.html +++ b/frontend/src/app/user/login/login.component.html @@ -5,7 +5,7 @@

Username: - +

From 3e9342f6f2af1d6e2be87e16740fb4d005b4856e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Mar 2023 12:50:27 +0200 Subject: [PATCH 232/498] Fixed an erroneous default value. --- .../competition-builder-task-dialog/competition-form.builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index fde91e066..e59f8ee5e 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -237,7 +237,7 @@ export class CompetitionFormBuilder { */ private initializeForm() { this.form = new FormGroup({ - id: new FormControl(this.data?.id ?? `_${Date.now()}`), + id: new FormControl(this.data?.id), name: new FormControl(this.data?.name, [Validators.required]), duration: new FormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), mediaCollection: new FormControl(this.data?.collectionId, [Validators.required]), From f2f179388e131c05b4b60a8bcc00535edcc266ea Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Mar 2023 13:12:16 +0200 Subject: [PATCH 233/498] Fixed issue with conversion between DbHint and ApiHint. --- .../src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index c9d3f0450..ea216755a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -64,6 +64,7 @@ class DbHint(entity: Entity) : XdEntity(entity) { type = this.type.toApi(), start = this.start, end = this.end, + description = this.text, mediaItem = this.item?.id, dataType = this.type.mimeType, path = this.path, From 3617bf5d101f998f454210957d5248c96a80b42e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Mar 2023 13:12:28 +0200 Subject: [PATCH 234/498] Fixed small issues. --- .../competition-form.builder.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index e59f8ee5e..f7f571fd8 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -177,7 +177,7 @@ export class CompetitionFormBuilder { type: c.get('type').value, start: c.get('start').value, end: c.get('end').value, - mediaItem: c.get('mediaItem') ? c.get('mediaItem').value.mediaItemId : null, + mediaItem: c.get('mediaItem')?.value?.mediaItemId ?? null, range: c.get('segment_start') && c.get('segment_end') ? ({ @@ -205,9 +205,7 @@ export class CompetitionFormBuilder { } as ApiTaskTemplate; /* Set ID of set. */ - if (this.form.get('id').value) { - data.id = this.form.get('id').value; - } + data.id = this.form.get('id')?.value ?? null; return data; } From 612e0c78bf25ba93e5ddf750d33a1d33a6f8139f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Mar 2023 13:32:21 +0200 Subject: [PATCH 235/498] Changed delete logic for task templates. --- .../rest/handler/template/UpdateEvaluationTemplateHandler.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 50ea739b5..8b03e2fbc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -110,7 +110,9 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C /* Update task information. */ val taskIds = apiValue.tasks.mapNotNull { it.id }.toTypedArray() - existing.tasks.removeAll(DbTaskTemplate.query(DbTaskTemplate::evaluation eq existing and not(DbTaskTemplate::id.containsIn(*taskIds)))) + DbTaskTemplate.query(DbTaskTemplate::evaluation eq existing and not(DbTaskTemplate::id.containsIn(*taskIds))).asSequence().forEach { + it.delete() + } for (task in apiValue.tasks) { val t = if (task.id != null) { existing.tasks.filter { it.id eq task.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown task ${task.id} for evaluation ${apiValue.id}.", ctx) From cc35e40869e1e227fcc59fb84c0810ae5ded9869 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sun, 26 Mar 2023 13:49:37 +0200 Subject: [PATCH 236/498] Fixed issue when creating AVS task. --- .../competition-builder-task-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html index faf97a2e1..cf3464f0b 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.html @@ -265,7 +265,7 @@

/>
+

+ +

Name diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index a2ffd002a..55162a439 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -35,7 +35,7 @@ import { export class TaskTemplateEditorComponent implements OnInit, OnDestroy { @Input() - public task?: ApiTaskTemplate; + public task: ApiTaskTemplate; @Input() public taskType: ApiTaskType; @@ -68,14 +68,12 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { ngOnInit(): void { this.taskSub = this.builderService.selectedTaskTemplateAsObservable().subscribe((t)=>{ - console.log("Editor selected template sub", t); if(t){ this.task = t; this.taskGroup = this.builderService.selectedTaskGroup; this.taskType = this.builderService.selectedTaskType; this.init(); }else{ - console.log("Editor: task unselected") this.task = null; this.taskGroup = null; this.taskType = null; @@ -89,12 +87,10 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { } public init(){ - console.log("INIT") - const newTask = this.task.id === undefined && this.task.name === undefined && this.task.taskGroup !== undefined && this.task.taskType !== undefined; - this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, newTask ? null : this.task); + this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, this.task); this.form = this.formBuilder.form; this.form.valueChanges.subscribe(newValue => { - this.builderService.updateTask(this.formBuilder.fetchFormData()) + this.formBuilder.storeFormData(); this.builderService.markDirty() }); this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); @@ -118,7 +114,6 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { const task = JSON.parse(taskData) as ApiTaskTemplate; this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, task); this.form = this.formBuilder.form; - console.log('Loaded task: ' + JSON.stringify(task)); }; /** @@ -261,7 +256,6 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { startControl?.setValue(range.start.value); endControl?.setValue(range.end.value); unitControl?.setValue(ApiTemporalUnit.SECONDS); - console.log('Range updated'); } isImageMediaItem(mi: ApiMediaItem): boolean { diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss index e69de29bb..3dbfdd8c0 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss @@ -0,0 +1,7 @@ +.mat-row:hover { + background: rgba(255,255,255,0.1); +} + +.dres-selected-row { + background: rgba(255,255,255,0.2); +} diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index 90c015014..56ba83510 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -63,15 +63,16 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent taskType: string; }; newTask.taskGroup = group.name; + newTask.targets = []; + newTask.hints = []; newTask.taskType = this.builderService.findTypeForGroup(group).name; this.builderService.selectTaskTemplate(newTask); - //this.selection.toggle(newTask); + this.selection.toggle(newTask); } public editTask(task: ApiTaskTemplate){ - console.log("Edit"); this.builderService.selectTaskTemplate(task); - //this.selection.toggle(task); + this.selection.toggle(task); } public moveTaskUp(task: ApiTaskTemplate){ diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 4470da053..1c2bf8896 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -139,9 +139,24 @@ export class TemplateBuilderService { public updateTask(task: ApiTaskTemplate){ console.log('update task', task); console.log('update task, all', this.getTemplate().tasks); - const idx = this.getTemplate().tasks.indexOf(task); + let idx: number; + if(task.id){ + for (let i = 0; i Date: Mon, 27 Mar 2023 14:45:22 +0200 Subject: [PATCH 251/498] Added close button logic to task template editor --- .../task-template-editor/task-template-editor.component.html | 2 +- .../task-template-editor/task-template-editor.component.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index bfaa18cdc..5874cabe4 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -17,7 +17,7 @@

-
diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index 55162a439..89c26b7a4 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -370,4 +370,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { Java Regex matching is used.`; } + close() { + this.builderService.selectTaskTemplate(null); + } } From 9efd73ceb20c44a6a6a29b01441d2577e8a48984 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 27 Mar 2023 14:45:37 +0200 Subject: [PATCH 252/498] Regenerated OAS --- doc/legacy.oas.json | 6046 ++++++++++++++++++++ doc/oas-client.json | 9780 ++++++++++++++++----------------- doc/oas.json | 12486 +++++++++++++++++++++--------------------- 3 files changed, 17179 insertions(+), 11133 deletions(-) create mode 100644 doc/legacy.oas.json diff --git a/doc/legacy.oas.json b/doc/legacy.oas.json new file mode 100644 index 000000000..9f64a21c5 --- /dev/null +++ b/doc/legacy.oas.json @@ -0,0 +1,6046 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "DRES API", + "summary" : null, + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", + "termsOfService" : null, + "contact" : null, + "license" : null, + "version" : "2.0.0" + }, + "paths" : { + "/api/v2/audit/info" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", + "operationId" : "getApiV2AuditInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "The audit log info.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AuditLogInfo" + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user executes the call.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/log/list/limit/{limit}/{page}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query.", + "operationId" : "getApiV2AuditLogListLimitByLimitByPage", + "parameters" : [ { + "name" : "limit", + "in" : "path", + "description" : "The maximum number of results. Default: 500", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "page", + "in" : "path", + "description" : "The page index offset, relative to the limit.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/log/list/since/{since}/{upto}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", + "operationId" : "getApiV2AuditLogListSinceBySinceByUpto", + "parameters" : [ { + "name" : "since", + "in" : "path", + "description" : "Timestamp of the earliest audit log to include", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "upto", + "in" : "path", + "description" : "Timestamp of the latest audit log to include.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "query", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiV2CollectionList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiV2CollectionByCollectionIdRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", + "operationId" : "postApiV2CollectionByCollectionIdResolve", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name the item(s) should start with.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/override" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the submission status for a given submission.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdSubmissionOverride", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2ExternalByStartsWith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaItemId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItemByMediaItemId", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Media item ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV2Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific competition.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiAuditLogEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiAuditLogType" + }, + "source" : { + "$ref" : "#/components/schemas/ApiAuditLogSource" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "competitionId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "submissionId" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "id", "type", "source", "timestamp" ] + }, + "ApiAuditLogSource" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "ApiAuditLogType" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + }, + "AuditLogInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "size" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + }, + "required" : [ "name" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "collection" : { + "$ref" : "#/components/schemas/MediaItemCollection" + } + }, + "required" : [ "name", "type", "collectionId", "location", "collection" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "type" : "string" + } + }, + "required" : [ "teams" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + }, + "submission" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + }, + "required" : [ "status", "taskId", "answers", "submission" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "currentTemplate" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "evaluationId" : { + "type" : "string" + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/TaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "mediaType" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "endTime" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "UserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "MediaItemCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + } + }, + "required" : [ "id" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "TaskStatus" : { + "type" : "string", + "enum" : [ "CREATED", "PREPARING", "RUNNING", "ENDED" ] + } + }, + "securitySchemes" : { + "CookieAuth" : { + "name" : "SESSIONID", + "in" : "cookie", + "type" : "apiKey" + } + } + }, + "servers" : [ ], + "security" : [ ] +} diff --git a/doc/oas-client.json b/doc/oas-client.json index 35fdaeada..853d251c1 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1,4891 +1,4891 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : "DRES API (client)", - "summary" : null, - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", - "termsOfService" : null, - "contact" : null, - "license" : null, - "version" : "1.0" - }, - "paths" : { - "/api/v1/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV1Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the verdict status of an AnswerSet.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "answerSetId", - "in" : "path", - "description" : "The ID of the AnswerSet.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeam", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/list" : { - "get" : { - "tags" : [ "Template", "Team" ], - "summary" : "Lists all the teams across all evaluations.", - "operationId" : "getApiV2TemplateTeamList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/{teamId}" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeamByTeamId", - "parameters" : [ { - "name" : "teamId", - "in" : "path", - "description" : "The team ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiAuditLogEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiAuditLogType" - }, - "source" : { - "$ref" : "#/components/schemas/ApiAuditLogSource" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "competitionId" : { - "type" : "string" - }, - "userId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "id", "type", "source", "timestamp" ] - }, - "ApiAuditLogSource" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "ApiAuditLogType" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] - }, - "AuditLogInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "size" : { - "type" : "integer", - "format" : "int32" - }, - "latest" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - } - }, - "required" : [ "name" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "mediaItemId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - }, - "collection" : { - "$ref" : "#/components/schemas/MediaItemCollection" - } - }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "collection" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "type" : "string" - } - }, - "required" : [ "teams" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - }, - "submission" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - }, - "required" : [ "id", "status", "taskId", "answers", "submission" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "evaluationStatus" : { - "$ref" : "#/components/schemas/ApiEvaluationStatus" - }, - "taskStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "currentTemplate" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "evaluationId" : { - "type" : "string" - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "mediaType" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "validator" : { - "type" : "string" - }, - "collection" : { - "type" : "string" - }, - "item" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "endTime" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "UserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "MediaItemCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - } - }, - "required" : [ "id" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - } - }, - "securitySchemes" : { } - }, - "servers" : [ ], - "security" : null +{ + "openapi" : "3.0.3", + "info" : { + "title" : "DRES API (client)", + "summary" : null, + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0", + "termsOfService" : null, + "contact" : null, + "license" : null, + "version" : "1.0" + }, + "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", + "parameters" : [ { + "name" : "teamId", + "in" : "path", + "description" : "The team ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiAuditLogEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiAuditLogType" + }, + "source" : { + "$ref" : "#/components/schemas/ApiAuditLogSource" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "competitionId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "submissionId" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "id", "type", "source", "timestamp" ] + }, + "ApiAuditLogSource" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "ApiAuditLogType" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + }, + "AuditLogInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "size" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + }, + "required" : [ "name" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "mediaItemId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "collection" : { + "$ref" : "#/components/schemas/MediaItemCollection" + } + }, + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "collection" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "type" : "string" + } + }, + "required" : [ "teams" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + }, + "submission" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + }, + "required" : [ "id", "status", "taskId", "answers", "submission" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "currentTemplate" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "evaluationId" : { + "type" : "string" + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "mediaType" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "endTime" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "UserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "MediaItemCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + } + }, + "required" : [ "id" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + } + }, + "securitySchemes" : { } + }, + "servers" : [ ], + "security" : null } \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 547fcc7a9..d77b7c9df 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1,6244 +1,6244 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : "DRES API", - "summary" : null, - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", - "termsOfService" : null, - "contact" : null, - "license" : null, - "version" : "2.0.0" - }, - "paths" : { - "/api/v1/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV1Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/info" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", - "operationId" : "getApiV2AuditInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "The audit log info.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/AuditLogInfo" - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user executes the call.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/log/list/limit/{limit}/{page}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query.", - "operationId" : "getApiV2AuditLogListLimitByLimitByPage", - "parameters" : [ { - "name" : "limit", - "in" : "path", - "description" : "The maximum number of results. Default: 500", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "page", - "in" : "path", - "description" : "The page index offset, relative to the limit.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAuditLogEntry" - } - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user starts the call", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/log/list/since/{since}/{upto}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query", - "operationId" : "getApiV2AuditLogListSinceBySinceByUpto", - "parameters" : [ { - "name" : "since", - "in" : "path", - "description" : "Timestamp of the earliest audit log to include", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "upto", - "in" : "path", - "description" : "Timestamp of the latest audit log to include.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAuditLogEntry" - } - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user starts the call", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "operationId" : "getApiV2CollectionList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/random" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "operationId" : "getApiV2CollectionByCollectionIdRandom", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", - "operationId" : "postApiV2CollectionByCollectionIdResolve", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", - "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", - "in" : "path", - "description" : "Name the item(s) should start with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given evaluation.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the verdict status of an AnswerSet.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "answerSetId", - "in" : "path", - "description" : "The ID of the AnswerSet.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a list of available scoreboard names for the given evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", - "operationId" : "getApiV2ExternalByStartsWith", - "parameters" : [ { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a media item to the specified media collection.", - "operationId" : "postApiV2MediaItem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Tries to delete a specific media item.", - "operationId" : "deleteApiV2MediaItemByMediaId", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaItemId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "getApiV2MediaItemByMediaItemId", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Media item ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the score overviews of a specific evaluation run.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/current" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeries" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns team group aggregated values of the current task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroupValue" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeam", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/list" : { - "get" : { - "tags" : [ "Template", "Team" ], - "summary" : "Lists all the teams across all evaluations.", - "operationId" : "getApiV2TemplateTeamList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/{teamId}" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeamByTeamId", - "parameters" : [ { - "name" : "teamId", - "in" : "path", - "description" : "The team ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "operationId" : "getApiV2UserList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "operationId" : "getApiV2UserSessionActiveList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiAuditLogEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiAuditLogType" - }, - "source" : { - "$ref" : "#/components/schemas/ApiAuditLogSource" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "competitionId" : { - "type" : "string" - }, - "userId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "id", "type", "source", "timestamp" ] - }, - "ApiAuditLogSource" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "ApiAuditLogType" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] - }, - "AuditLogInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "size" : { - "type" : "integer", - "format" : "int32" - }, - "latest" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - } - }, - "required" : [ "name" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "mediaItemId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - }, - "collection" : { - "$ref" : "#/components/schemas/MediaItemCollection" - } - }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "collection" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "type" : "string" - } - }, - "required" : [ "teams" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - }, - "submission" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - }, - "required" : [ "id", "status", "taskId", "answers", "submission" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "evaluationStatus" : { - "$ref" : "#/components/schemas/ApiEvaluationStatus" - }, - "taskStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "currentTemplate" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "evaluationId" : { - "type" : "string" - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "mediaType" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "validator" : { - "type" : "string" - }, - "collection" : { - "type" : "string" - }, - "item" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "endTime" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "UserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "MediaItemCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - } - }, - "required" : [ "id" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - } - }, - "securitySchemes" : { - "CookieAuth" : { - "name" : "SESSIONID", - "in" : "cookie", - "type" : "apiKey" - } - } - }, - "servers" : [ ], - "security" : [ ] +{ + "openapi" : "3.0.3", + "info" : { + "title" : "DRES API", + "summary" : null, + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", + "termsOfService" : null, + "contact" : null, + "license" : null, + "version" : "2.0.0" + }, + "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/info" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", + "operationId" : "getApiV2AuditInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "The audit log info.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AuditLogInfo" + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user executes the call.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/log/list/limit/{limit}/{page}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query.", + "operationId" : "getApiV2AuditLogListLimitByLimitByPage", + "parameters" : [ { + "name" : "limit", + "in" : "path", + "description" : "The maximum number of results. Default: 500", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "page", + "in" : "path", + "description" : "The page index offset, relative to the limit.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/audit/log/list/since/{since}/{upto}" : { + "get" : { + "tags" : [ "Audit" ], + "summary" : "Lists all audit logs matching the query", + "operationId" : "getApiV2AuditLogListSinceBySinceByUpto", + "parameters" : [ { + "name" : "since", + "in" : "path", + "description" : "Timestamp of the earliest audit log to include", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "upto", + "in" : "path", + "description" : "Timestamp of the latest audit log to include.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "The audit logs", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAuditLogEntry" + } + } + } + } + }, + "403" : { + "description" : "Whenever a non-admin user starts the call", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiV2CollectionList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiV2CollectionByCollectionIdRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", + "operationId" : "postApiV2CollectionByCollectionIdResolve", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name the item(s) should start with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of a run.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2ExternalByStartsWith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaItemId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItemByMediaItemId", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Media item ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available competitions with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", + "parameters" : [ { + "name" : "teamId", + "in" : "path", + "description" : "The team ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiAuditLogEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiAuditLogType" + }, + "source" : { + "$ref" : "#/components/schemas/ApiAuditLogSource" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "competitionId" : { + "type" : "string" + }, + "userId" : { + "type" : "string" + }, + "submissionId" : { + "type" : "string" + }, + "session" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "id", "type", "source", "timestamp" ] + }, + "ApiAuditLogSource" : { + "type" : "string", + "enum" : [ "REST", "CLI", "INTERNAL" ] + }, + "ApiAuditLogType" : { + "type" : "string", + "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] + }, + "AuditLogInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "size" : { + "type" : "integer", + "format" : "int32" + }, + "latest" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "size" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + }, + "required" : [ "name" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "mediaItemId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "collection" : { + "$ref" : "#/components/schemas/MediaItemCollection" + } + }, + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "collection" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "type" : "string" + } + }, + "required" : [ "teams" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + }, + "submission" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + }, + "required" : [ "id", "status", "taskId", "answers", "submission" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "currentTemplate" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "evaluationId" : { + "type" : "string" + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "mediaType" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "validator" : { + "type" : "string" + }, + "collection" : { + "type" : "string" + }, + "item" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "endTime" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "mediaType", "validator", "collection", "item", "taskDescription" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "UserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "MediaItemCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + } + }, + "required" : [ "id" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + } + }, + "securitySchemes" : { + "CookieAuth" : { + "name" : "SESSIONID", + "in" : "cookie", + "type" : "apiKey" + } + } + }, + "servers" : [ ], + "security" : [ ] } \ No newline at end of file From b746b52da62e79dee56937c43907b5ed085b0747 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 27 Mar 2023 16:01:50 +0200 Subject: [PATCH 253/498] Table-fied task type list --- .../task-types-list.component.html | 29 ++++++------------- .../task-types-list.component.ts | 28 ++++++++++++++++-- .../task-templates-list.component.html | 2 +- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html index e5a8126ae..a4c9d10c6 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html @@ -1,6 +1,11 @@ -
-

Task types

-
+ @@ -15,20 +20,4 @@

Task types

-
- - -
-
-

{{taskType.name}}

-
- -
-
- Scoring: {{taskType.scoreOption}} - Hints: {{h}}, - Target: {{taskType.targetOption}} -
-
-
-
+ diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts index a87afec2b..7c2c59174 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts @@ -1,14 +1,20 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; import { TemplateBuilderService } from "../../template-builder.service"; import { MatDialog } from "@angular/material/dialog"; import { MatSnackBar } from "@angular/material/snack-bar"; -import { ApiTaskType } from "../../../../../../openapi"; +import { ApiTaskGroup, ApiTaskType } from "../../../../../../openapi"; import { Observable } from "rxjs"; import { filter, map } from "rxjs/operators"; import { CompetitionBuilderTaskTypeDialogComponent } from "../../../../competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component"; +import { + ActionableDynamicTable, + ActionableDynamicTableActionType, + ActionableDynamicTableColumnDefinition, + ActionableDynamicTableColumnType +} from "../../../../shared/actionable-dynamic-table/actionable-dynamic-table.component"; @Component({ selector: "app-task-types-list", @@ -67,6 +73,18 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp lscPreset = TaskTypesListComponent.LSC_PRSET; + columns: ActionableDynamicTableColumnDefinition[] = [ + {key: 'name', header: 'Name', property: 'name', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'duration', header: 'Duration', property: 'duration', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'target', header: 'Target', property: 'targetOption', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'score', header: 'Score', property: 'scoreOption', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'actions', header: 'Actions', type: ActionableDynamicTableColumnType.ACTION, actions: [ActionableDynamicTableActionType.REMOVE],} + ]; + + displayedColumns= ['name', 'duration', 'target', 'score', 'actions']; + + @ViewChild("typesTable") table: ActionableDynamicTable; + constructor( builder: TemplateBuilderService, private dialog: MatDialog @@ -92,6 +110,8 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp })); } + //public remove = (type: ApiTaskType) => this.remove(type); + public addTaskType(type?: ApiTaskType) { const dialogRef = this.dialog.open(CompetitionBuilderTaskTypeDialogComponent, { data: type ?? null, width: "750px" }); dialogRef.afterClosed() @@ -99,11 +119,15 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp .subscribe((t) => { this.builderService.getTemplate().taskTypes.push(t); this.builderService.update(); + this.table?.renderRows(); }); } public remove(taskType: ApiTaskType) { this.builderService.removeTaskType(taskType); + this.table?.renderRows(); } + + } diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html index d79bfd820..54f3fd301 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html @@ -1,5 +1,5 @@
-

Tasks

+

Tasks

@@ -421,7 +421,7 @@

- + @@ -469,6 +469,52 @@

+ + + + + + + + + + + {{ unit }} + + + + + + @@ -478,7 +524,7 @@

- + Date: Sat, 1 Apr 2023 10:07:01 +0200 Subject: [PATCH 259/498] Fixed visualisation of submissions for "blinded" configurations. --- .../dres/api/cli/MediaCollectionCommand.kt | 22 ++++++++++--------- .../dres/data/model/submissions/DbAnswer.kt | 18 ++++++++++----- .../data/model/submissions/DbAnswerSet.kt | 7 +++--- .../data/model/submissions/DbSubmission.kt | 2 +- .../app/viewer/teams-viewer.component.html | 14 +++++++++--- .../src/app/viewer/teams-viewer.component.ts | 2 +- 6 files changed, 42 insertions(+), 23 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index c6a7c9b35..d0d590063 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -702,17 +702,19 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va .groupBy(ListSegment::video) .forEach { (videoName, segments) -> val videoItem = collection.items.filter { it.name eq videoName }.firstOrNull() - val fps = videoItem?.fps!! - videoItem.segments.addAll( - segments.map { - inserted += 1 - DbMediaSegment.new { - this.name = it.name - this.start = it.start - this.end = it.end + if (videoItem != null) { + val fps = videoItem.fps!! + videoItem.segments.addAll( + segments.map { + inserted += 1 + DbMediaSegment.new { + this.name = it.name + this.start = it.start + this.end = it.end + } } - } - ) + ) + } } println("Done! Read $inserted valid segments.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt index d2bb18b0f..0c7741739 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt @@ -31,11 +31,19 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { override fun type(): AnswerType = AnswerType.fromDb(this.type) - fun toApi() = ApiAnswer( + /** + * Converts this [DbAnswer] to a RESTful API representation [ApiAnswer]. + * + * This is a convenience method and requires an active transaction context. + * + * @param blind True, if a "blind" [ApiAnswer] should be generated. + * @return [ApiAnswer] + */ + fun toApi(blind: Boolean = false) = ApiAnswer( type = this.type().toApi(), - item = this.item?.toApi(), - start = this.start, - end = this.end, - text = this.text + item = if (blind) { null } else { this.item?.toApi() }, + start = if (blind) { null } else { this.start }, + end = if (blind) { null } else { this.end }, + text = if (blind) { null } else { this.text } ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 2c76feab5..f864ca8e6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -42,16 +42,17 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { } /** - * Converts this [DbVerdictStatus] to a RESTful API representation [ApiAnswerSet]. + * Converts this [DbAnswerSet] to a RESTful API representation [ApiAnswerSet]. * * This is a convenience method and requires an active transaction context. * + * @param blind True, if a "blind" [ApiAnswerSet] should be generated. * @return [ApiAnswerSet] */ - fun toApi(): ApiAnswerSet = ApiAnswerSet( + fun toApi(blind: Boolean = false): ApiAnswerSet = ApiAnswerSet( id = this.id, status = this.status.toApi(), taskId = this.taskId, - answers = this.answers.asSequence().map { it.toApi() }.toList() + answers = this.answers.asSequence().map { it.toApi(blind) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 9855ee8d6..6a1dbe534 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -66,7 +66,7 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { memberId = this.user.id, memberName = this.user.username, timestamp = this.timestamp, - answers = if (blind) { emptyList() } else { this.answerSets.asSequence().map { it.toApi() }.toList()}, + answers = this.answerSets.asSequence().map { it.toApi(blind) }.toList(), evaluationId = this.evaluationId ) } diff --git a/frontend/src/app/viewer/teams-viewer.component.html b/frontend/src/app/viewer/teams-viewer.component.html index 482d13d00..f084929d1 100644 --- a/frontend/src/app/viewer/teams-viewer.component.html +++ b/frontend/src/app/viewer/teams-viewer.component.html @@ -28,9 +28,17 @@

{{ team.name }}

- Preview for submission {{answer.submissionId}}. - Preview for submission {{answer.submissionId}}. -

{{ answer.previewText }}

+
+ Preview for submission {{answer.submissionId}}. +

?

+
+
+ Preview for submission {{answer.submissionId}}. +

?

+
+
+

{{ answer.previewText }}

+
diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index d40d28d25..9c7ad91ce 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -249,7 +249,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { * Generates a URL for the preview image of a submission. */ public previewOfItem(item: ApiMediaItem, start: number): string { - return this.config.resolveApiUrl(`/preview/${item.mediaItemId}/${start == null ? 0 : start}`) + return this.config.resolveApiUrl(`/preview/${item.mediaItemId}/${start == null ? 0 : start}`) } /** From b950230b56e0bb28021cbad7210b4f9fb45ca958 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Sat, 1 Apr 2023 10:51:11 +0200 Subject: [PATCH 260/498] Fixed some minor inconsistencies that could occur upon reactivation of run. --- .../dev/dres/data/model/run/AbstractTask.kt | 16 ++++++++-------- .../run/InteractiveSynchronousEvaluation.kt | 10 +++++----- .../dres/run/InteractiveSynchronousRunManager.kt | 10 ++++++---- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 099b7967f..0a5323fa1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -49,9 +49,9 @@ abstract class AbstractTask(task: DbTask): TaskRun { final override val templateId: TemplateId = this.task.template.templateId /** The current [DbTaskStatus] of this [AbstractTask]. This is a transient property. */ - final override var status: DbTaskStatus = task.status + final override var status: DbTaskStatus + get() = this.task.status protected set(value) { - field = value this.task.status = value /* Update backing database field. */ } @@ -60,9 +60,9 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Setter requires active database transaction! */ - final override var started: Long? = task.started + final override var started: Long? + get() = this.task.started protected set(value) { - field = value this.task.started = value /* Update backing database field. */ } @@ -71,9 +71,9 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Setter requires active database transaction! */ - final override var ended: Long? = task.ended + final override var ended: Long? + get() = this.task.ended protected set(value) { - field = value this.task.ended = value /* Update backing database field. */ } @@ -111,7 +111,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { * Starts this [AbstractTask]. */ override fun start() { - if (this.hasStarted) { + if (this.started != null || this.status == DbTaskStatus.RUNNING) { throw IllegalStateException("Run has already been started.") } this.started = System.currentTimeMillis() @@ -122,7 +122,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { * Ends this [AbstractTask]. */ override fun end() { - if (!this.isRunning) { + if (this.started == null) { this.started = System.currentTimeMillis() } this.ended = System.currentTimeMillis() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 1531785a9..98c92e1dc 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -25,6 +25,7 @@ import dev.dres.run.transformer.SubmissionTaskMatchFilter import dev.dres.run.transformer.SubmissionTransformer import dev.dres.run.transformer.SubmissionTransformerAggregator import kotlinx.dnq.query.* +import kotlinx.dnq.query.FilteringContext.le import java.lang.IndexOutOfBoundsException import java.util.LinkedList @@ -64,15 +65,14 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ private val templates = this.description.tasks.asSequence().map { it.templateId }.toList() - - /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ - var templateIndex: Int = 0 - private set - /** Returns the last [TaskRun]. */ val currentTask: AbstractInteractiveTask? get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex] } + /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ + var templateIndex: Int = this.currentTask?.templateId?.let { this.templates.indexOf(it) } ?: 0 + private set + /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ override val scoreboards: List diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 9ac1ca7ac..b9dd0f29e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -118,10 +118,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.updatables.add(this.scoreboardsUpdatable) this.updatables.add(this.endTaskUpdatable) - - /** End ongoing runs upon initialization (in case server crashed during task execution). */ - if (this.evaluation.currentTask?.isRunning == true) { - this.evaluation.currentTask?.end() + /** End ongoing tasks upon initialization (in case server crashed during task execution). */ + for (task in this.evaluation.tasks) { + if (task.isRunning || task.status == DbTaskStatus.RUNNING) { + task.end() + } } /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ @@ -214,6 +215,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override fun goTo(context: RunActionContext, index: Int) { checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() + this.evaluation.tasks.any { it.status == DbTaskStatus.RUNNING } if (index >= 0 && index < this.template.tasks.size()) { /* Update active task. */ this.evaluation.goTo(index) From 0baf3c0c3a47133c4e6ee326f5b31239d886e639 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 3 Apr 2023 13:48:27 +0200 Subject: [PATCH 261/498] Fixed external task hints in new builder --- .../competition-form.builder.ts | 45 ++++++++++++++++++- .../task-template-editor.component.html | 6 +-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index ad6a2483c..859e33642 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -458,6 +458,10 @@ export class CompetitionFormBuilder { * @return The new {@link FormGroup} */ private imageItemComponentForm(index: number, initialize?: ApiHint): FormGroup { + if(initialize?.path && !initialize?.mediaItem){ + /* Handle external image */ + return this.externalImageItemComponentForm(index, initialize); + } const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); if ( !initialize?.mediaItem && @@ -487,6 +491,8 @@ export class CompetitionFormBuilder { }); } + + return new FormGroup({ start: new FormControl(initialize?.start, [ Validators.required, @@ -507,6 +513,10 @@ export class CompetitionFormBuilder { * @return The new {@link FormGroup} */ private videoItemComponentForm(index: number, initialize?: ApiHint): FormGroup { + if(initialize?.path && !initialize?.mediaItem){ + /* handle external video */ + return this.externalVideoItemComponentForm(index, initialize); + } /* Initialize media item based on target. */ const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); if ( @@ -661,7 +671,7 @@ export class CompetitionFormBuilder { ) ); - return new FormGroup({ + const group = new FormGroup({ start: new FormControl(initialize?.start, [ Validators.required, Validators.min(0), @@ -671,6 +681,39 @@ export class CompetitionFormBuilder { type: new FormControl('VIDEO', [Validators.required]), external: new FormControl(true), path: pathFormControl, + segment_start: new FormControl(initialize?.range.start.value, [Validators.required]), + segment_end: new FormControl(initialize?.range.end.value, [Validators.required]), + segment_time_unit: new FormControl( + initialize?.range.start.unit ? initialize?.range.start.unit : 'SECONDS', + Validators.required + ), }); + /* Initialize start, end and time unit based on target. */ + // fetch target time unit + const targetTimeUnit = (this.form.get('target') as FormArray).controls[0].get('segment_time_unit').value; + if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { + group.get('segment_time_unit').setValue(targetTimeUnit, {emitEvent: false}); + } + + if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { + group.get('segment_start').setValue((this.form.get('target') as FormArray).controls[0].get('segment_start').value, {emitEvent: false}); + } + + if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { + group.get('segment_end').setValue((this.form.get('target') as FormArray).controls[0].get('segment_end').value, {emitEvent: false}); + } + + /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ + group.get('end').setValue(this.taskType.duration, {emitEvent: false}); + + group + .get('segment_start') + .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + group + .get('segment_end') + .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + group.get('segment_start').updateValueAndValidity(); + group.get('segment_end').updateValueAndValidity(); + return group; } } diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 14825e4a9..5ebdfe3a5 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -451,7 +451,7 @@

- + {{ unit }} - + From 4864331c23968c8788e4fa5929d26dbc7630626b Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 4 Apr 2023 12:40:58 +0200 Subject: [PATCH 262/498] Added default collection in new builder --- ...mpetition-builder-task-dialog.component.ts | 8 +++-- .../competition-form.builder.ts | 10 ++++--- .../task-template-editor.component.ts | 4 +-- .../template-information.component.html | 14 +++++++++ .../template-information.component.ts | 30 +++++++++++++++---- .../template-builder.service.ts | 8 +++++ 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index 96bebce23..b2601f8e1 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -21,6 +21,7 @@ import { ApiTaskType, ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit, CollectionService } from '../../../../../openapi'; +import { TemplateBuilderService } from "../../../template/template-builder/template-builder.service"; /** * Its expected that the taskGroup and taskType properties are correctly given @@ -55,9 +56,10 @@ export class CompetitionBuilderTaskDialogComponent { public collectionService: CollectionService, @Inject(MAT_DIALOG_DATA) public data: CompetitionBuilderTaskDialogData, private dialog: MatDialog, - public config: AppConfig + public config: AppConfig, + private builderService: TemplateBuilderService // To make the compiler happy ) { - this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.data.task); + this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.builderService, this.data.task); this.form = this.builder.form; this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); } @@ -70,7 +72,7 @@ export class CompetitionBuilderTaskDialogComponent { uploaded = (taskData: string) => { const task = JSON.parse(taskData) as ApiTaskTemplate; - this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, task); + this.builder = new CompetitionFormBuilder(this.data.taskGroup, this.data.taskType, this.collectionService, this.builderService, task); this.form = this.builder.form; console.log('Loaded task: ' + JSON.stringify(task)); }; diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 859e33642..f918b0843 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -13,6 +13,7 @@ import { ApiTaskType, ApiTemporalPoint, ApiTemporalRange, CollectionService } from '../../../../../openapi'; +import { TemplateBuilderService } from "../../../template/template-builder/template-builder.service"; export class CompetitionFormBuilder { /** The default duration of a query hint. This is currently a hard-coded constant. */ @@ -21,9 +22,6 @@ export class CompetitionFormBuilder { /** The {@link FormGroup} held by this {@link CompetitionFormBuilder}. */ public form: FormGroup; - /** List of data sources managed by this CompetitionFormBuilder. */ - private dataSources = new Map>(); - /** * Constructor for CompetitionFormBuilder. * @@ -36,11 +34,15 @@ export class CompetitionFormBuilder { private taskGroup: ApiTaskGroup, private taskType: ApiTaskType, private collectionService: CollectionService, + private builderService: TemplateBuilderService, private data?: ApiTaskTemplate ) { this.initializeForm(); } + /** List of data sources managed by this CompetitionFormBuilder. */ + private dataSources = new Map>(); + /** * Returns the duration value to init with: * either the set task duration (if this is for editing) @@ -279,7 +281,7 @@ export class CompetitionFormBuilder { id: new FormControl(this.data?.id), name: new FormControl(this.data?.name, [Validators.required]), duration: new FormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), - mediaCollection: new FormControl(this.data?.collectionId, [Validators.required]), + mediaCollection: new FormControl(this.data?.collectionId ?? this.builderService.defaultCollection, [Validators.required]), }); this.form.addControl('target', this.formForTarget()); this.form.addControl('components', this.formForQueryComponents()); diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index a950f30c2..2501124b3 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -84,7 +84,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { } public init(){ - this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, this.task); + this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, this.builderService, this.task); this.form = this.formBuilder.form; this.form.valueChanges.subscribe(newValue => { this.formBuilder.storeFormData(); @@ -109,7 +109,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { uploaded = (taskData: string) => { const task = JSON.parse(taskData) as ApiTaskTemplate; - this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, task); + this.formBuilder = new CompetitionFormBuilder(this.taskGroup, this.taskType, this.collectionService, this.builderService, task); this.form = this.formBuilder.form; }; diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.html b/frontend/src/app/template/template-builder/components/template-information/template-information.component.html index 10afb6241..33f9575bd 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.html +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.html @@ -12,5 +12,19 @@

+

+ + Default collection for tasks within this evaluation template + + + {{ mediaCollection.name }} (ID: {{ mediaCollection.id }}) + + + +

diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts index ecbbc3218..094f0dfce 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts @@ -1,8 +1,8 @@ import {Component, OnDestroy, OnInit} from '@angular/core'; import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; import {FormControl, FormGroup} from '@angular/forms'; -import {Subscription} from 'rxjs'; -import {TemplateService} from '../../../../../../openapi'; +import { Observable, Subscription } from "rxjs"; +import { ApiMediaCollection, CollectionService, TemplateService } from "../../../../../../openapi"; import {TemplateBuilderService} from '../../template-builder.service'; @Component({ @@ -12,14 +12,21 @@ import {TemplateBuilderService} from '../../template-builder.service'; }) export class TemplateInformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { - form: FormGroup = new FormGroup({name: new FormControl(''), description: new FormControl('')}); + form: FormGroup = new FormGroup({ + name: new FormControl(''), + description: new FormControl(''), + collection: new FormControl('') + }); private changeSub: Subscription; private initOngoing = true; + mediaCollectionSource: Observable; + constructor( private templateService: TemplateService, + private collectionService: CollectionService, builder: TemplateBuilderService ) { super(builder); @@ -27,9 +34,22 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone ngOnInit(): void { this.onInit(); - this.changeSub = this.form.valueChanges.subscribe(() => { - this.builderService.markDirty(); + this.changeSub = this.form.valueChanges.subscribe((value) => { + let isDirty = false; + if(value.name !== this.builderService.getTemplate().name){ + isDirty = true; + } + if(value.description !== this.builderService.getTemplate().description){ + isDirty = true; + } + if(value.collection && value.collection?.length > 0){ + this.builderService.defaultCollection = value.collection; + } + if(isDirty){ + this.builderService.markDirty(); + } }); + this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); } ngOnDestroy() { diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 1c2bf8896..8637d282a 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -12,6 +12,13 @@ import { map } from "rxjs/operators"; providedIn: 'root' }) export class TemplateBuilderService { + set defaultCollection(value: string) { + this._defaultCollection = value; + } + get defaultCollection(): string { + return this._defaultCollection; + } + private _defaultCollection: string = ''; get selectedTaskGroup(): ApiTaskGroup { return this._selectedTaskGroup; } @@ -215,4 +222,5 @@ export class TemplateBuilderService { this.getTemplate().tasks.splice(this.getTemplate().tasks.indexOf(task), 1); this.update(this.getTemplate()); } + } From 3412b39539d45ebe90b1068e0b2dc5c91562c70e Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 4 Apr 2023 13:09:14 +0200 Subject: [PATCH 263/498] Various minor bugfixes --- frontend/src/app/run/abstract-run-list.component.ts | 1 + .../app/run/run-admin-toolbar/run-admin-toolbar.component.ts | 4 ++-- .../template/template-builder/template-builder.component.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index fb62b6ca5..66beece51 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -47,6 +47,7 @@ export class AbstractRunListComponent { * @param runId ID of the run to navigate to. */ public navigateToViewer(runId: string) { + console.log("Navigate (AbstractList): ", runId) /* TODO: Setup depends on type of competition run. */ this.router.navigate([ '/evaluation/viewer', diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index 1e94a7f5b..c5c999960 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -84,10 +84,10 @@ export class RunAdminToolbarComponent implements OnInit { public navigateToViewer() { const runId = this.runId.value; - + console.log("Navigate (Admin): ", runId) /* TODO: Setup depends on type of competition run. */ this.router.navigate([ - '/run/viewer', + '/evaluation/viewer', runId, { center: 'player', diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index 407a2c82c..1f6c5d6bc 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -44,8 +44,8 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i ngOnDestroy(): void { this.onDestroy(); - this.changeSub.unsubscribe(); - this.routeSub.unsubscribe(); + this.changeSub?.unsubscribe(); + this.routeSub?.unsubscribe(); } ngOnInit(): void { From 503fc89f5f4f65dc503ebd30d1049bcebbc3d441 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 13 Apr 2023 09:51:06 +0200 Subject: [PATCH 264/498] Updated to angular 14 --- frontend/angular.json | 5 +- frontend/package.json | 27 +- .../collection-builder-dialog.component.ts | 14 +- .../media-item-builder-dialog.component.ts | 24 +- ...mpetition-builder-task-dialog.component.ts | 12 +- .../competition-form.builder.ts | 180 +- ...ompetition-builder-task-group.component.ts | 10 +- ...tion-builder-task-type-dialog.component.ts | 58 +- ...mpetition-builder-team-dialog.component.ts | 20 +- .../competition-builder.component.ts | 4 +- .../competition-create-dialog.component.ts | 8 +- .../competition-start-dialog.component.ts | 16 +- .../task-template-editor.component.ts | 12 +- .../template-information.component.ts | 10 +- ...in-user-create-or-edit-dialog.component.ts | 10 +- .../src/app/user/login/login.component.ts | 8 +- .../src/app/user/profile/profile.component.ts | 8 +- frontend/yarn.lock | 2797 +++++++++-------- 18 files changed, 1723 insertions(+), 1500 deletions(-) diff --git a/frontend/angular.json b/frontend/angular.json index ec5cb5d0c..53319d70f 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -119,10 +119,11 @@ } } }, - "defaultProject": "dres-frontend", "cli": { "analytics": "6d219313-be38-49ad-a2fb-7d6b7b72fabe", "packageManager": "yarn", - "defaultCollection": "@angular-eslint/schematics" + "schematicCollections": [ + "@angular-eslint/schematics" + ] } } diff --git a/frontend/package.json b/frontend/package.json index 124eba2ea..95257e03d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,18 +21,17 @@ }, "private": true, "dependencies": { - "@angular-slider/ngx-slider": "2.0.x", - "@angular/animations": "13.2.x", + "@angular/animations": "14.3.0", "@angular/cdk": "13.2.x", - "@angular/common": "13.2.x", - "@angular/compiler": "13.2.x", - "@angular/core": "13.2.x", + "@angular/common": "14.3.0", + "@angular/compiler": "14.3.0", + "@angular/core": "14.3.0", "@angular/flex-layout": "^13.0.0-beta.38", - "@angular/forms": "13.2.x", + "@angular/forms": "14.3.0", "@angular/material": "13.2.x", - "@angular/platform-browser": "13.2.x", - "@angular/platform-browser-dynamic": "13.2.x", - "@angular/router": "13.2.x", + "@angular/platform-browser": "14.3.0", + "@angular/platform-browser-dynamic": "14.3.0", + "@angular/router": "14.3.0", "angularx-qrcode": "13.0.x", "apexcharts": "3.37.x", "ng-apexcharts": "1.7.x", @@ -42,15 +41,15 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "13.3.x", + "@angular-devkit/build-angular": "14.2.11", "@angular-eslint/builder": "13.2.0", "@angular-eslint/eslint-plugin": "13.2.0", "@angular-eslint/eslint-plugin-template": "13.2.0", "@angular-eslint/schematics": "13.2.0", "@angular-eslint/template-parser": "13.2.0", - "@angular/cli": "13.2.x", - "@angular/compiler-cli": "13.2.x", - "@angular/language-service": "13.2.x", + "@angular/cli": "14.2.11", + "@angular/compiler-cli": "14.3.0", + "@angular/language-service": "14.3.0", "@types/jasmine": "3.6.x", "@types/jasminewd2": "2.0.x", "@types/node": "12.11.x", @@ -79,7 +78,7 @@ "stylelint-config-standard": "^25.0.0", "stylelint-config-standard-scss": "^3.0.0", "ts-node": "8.3.x", - "typescript": "4.5.x" + "typescript": "4.8.4" }, "packageManager": "yarn@3.2.0" } diff --git a/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts index 2dea523f6..88ea7df7f 100644 --- a/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.ts @@ -1,6 +1,6 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import {ApiMediaCollection} from '../../../../../openapi'; @Component({ @@ -9,17 +9,17 @@ import {ApiMediaCollection} from '../../../../../openapi'; styleUrls: ['./collection-builder-dialog.component.scss'], }) export class CollectionBuilderDialogComponent implements OnInit { - form: FormGroup; + form: UntypedFormGroup; constructor( public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: ApiMediaCollection ) { - this.form = new FormGroup({ - id: new FormControl(data?.id), - name: new FormControl(data?.name, [Validators.required, Validators.minLength(3)]), - description: new FormControl(data?.description), - basePath: new FormControl(data?.basePath), + this.form = new UntypedFormGroup({ + id: new UntypedFormControl(data?.id), + name: new UntypedFormControl(data?.name, [Validators.required, Validators.minLength(3)]), + description: new UntypedFormControl(data?.description), + basePath: new UntypedFormControl(data?.basePath), }); } diff --git a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts index 1d15c694f..b135a8ddf 100644 --- a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts +++ b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {ApiMediaItem, ApiMediaType} from '../../../../../openapi'; @@ -14,7 +14,7 @@ export interface MediaItemBuilderData { styleUrls: ['./media-item-builder-dialog.component.scss'], }) export class MediaItemBuilderDialogComponent implements OnInit { - form: FormGroup; + form: UntypedFormGroup; types = Object.values(ApiMediaType).sort((a, b) => a.localeCompare(b)); @@ -22,26 +22,26 @@ export class MediaItemBuilderDialogComponent implements OnInit { public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: MediaItemBuilderData ) { - this.form = new FormGroup({ - id: new FormControl(data?.item?.mediaItemId), - name: new FormControl(data?.item?.name, [Validators.required, Validators.minLength(3)]), - location: new FormControl(data?.item?.location, Validators.required), - type: new FormControl({ value: data?.item?.type, disabled: this.isEditing() }, [Validators.required]), - collectionId: new FormControl(data.collectionId), + this.form = new UntypedFormGroup({ + id: new UntypedFormControl(data?.item?.mediaItemId), + name: new UntypedFormControl(data?.item?.name, [Validators.required, Validators.minLength(3)]), + location: new UntypedFormControl(data?.item?.location, Validators.required), + type: new UntypedFormControl({ value: data?.item?.type, disabled: this.isEditing() }, [Validators.required]), + collectionId: new UntypedFormControl(data.collectionId), }); if (data?.item?.type === ApiMediaType.VIDEO) { - this.form.addControl('durationMs', new FormControl(data?.item?.durationMs, [Validators.required, Validators.min(1)])); + this.form.addControl('durationMs', new UntypedFormControl(data?.item?.durationMs, [Validators.required, Validators.min(1)])); this.form.addControl( 'fps', - new FormControl(data?.item?.fps, [Validators.required, Validators.min(1), Validators.max(200)]) + new UntypedFormControl(data?.item?.fps, [Validators.required, Validators.min(1), Validators.max(200)]) ); // Arbitrarily limiting to 200 fps } } enableVideoItemControls(enable: boolean) { if (enable) { - this.form.addControl('durationMs', new FormControl(0, [Validators.required, Validators.min(1)])); - this.form.addControl('fps', new FormControl(0, [Validators.required, Validators.min(1), Validators.max(200)])); + this.form.addControl('durationMs', new UntypedFormControl(0, [Validators.required, Validators.min(1)])); + this.form.addControl('fps', new UntypedFormControl(0, [Validators.required, Validators.min(1), Validators.max(200)])); } else { this.form.removeControl('durationMs'); this.form.removeControl('fps'); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index b2601f8e1..1b53e5719 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -1,6 +1,6 @@ import { Component, ElementRef, Inject, ViewChild } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; import { filter, first } from 'rxjs/operators'; import { AppConfig } from '../../../app.config'; @@ -39,7 +39,7 @@ export interface CompetitionBuilderTaskDialogData { styleUrls: ['./competition-builder-task-dialog.component.scss'], }) export class CompetitionBuilderTaskDialogComponent { - form: FormGroup; + form: UntypedFormGroup; units = ['FRAME_NUMBER', 'SECONDS', 'MILLISECONDS', 'TIMECODE']; /** Data source for list of {@link MediaCollection}. Loaded upon construction of the dialog. */ mediaCollectionSource: Observable; @@ -167,7 +167,7 @@ export class CompetitionBuilderTaskDialogComponent { * @param collectionId The ID of the collection to pick a {@link MediaItem} from. * @param target The target {@link FormControl} to apply the value to. */ - public pickRandomMediaItem(collectionId: string, target: FormControl) { + public pickRandomMediaItem(collectionId: string, target: UntypedFormControl) { this.collectionService .getApiV2CollectionByCollectionIdRandom(collectionId) .pipe(first()) @@ -184,7 +184,7 @@ export class CompetitionBuilderTaskDialogComponent { * @param endControl The target {@link FormControl} to apply the value to. * @param unitControl The target {@link FormControl} to apply the value to. */ - public pickRandomSegment(item: ApiMediaItem, startControl: FormControl, endControl: FormControl, unitControl: FormControl) { + public pickRandomSegment(item: ApiMediaItem, startControl: UntypedFormControl, endControl: UntypedFormControl, unitControl: UntypedFormControl) { const start = CompetitionBuilderTaskDialogComponent.randInt(1, item.durationMs / 1000 / 2); // always in first half let end = 1; do { @@ -195,7 +195,7 @@ export class CompetitionBuilderTaskDialogComponent { unitControl.setValue('SECONDS'); } - toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { /* Add to toggleVideoPlayer button if [disabled]="!target.get('mediaItem').value && !target.get('segment_start').value && !target.get('segment_end').value" */ @@ -240,7 +240,7 @@ export class CompetitionBuilderTaskDialogComponent { this.showVideo = !this.showVideo; } - onRangeChange(range: ApiTemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + onRangeChange(range: ApiTemporalRange, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { startControl?.setValue(range.start.value); endControl?.setValue(range.end.value); unitControl?.setValue(ApiTemporalUnit.SECONDS); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index f918b0843..3e8ea5d00 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -1,4 +1,4 @@ -import { AbstractControl, FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; import { filter, first, switchMap } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { RequireMatch } from './require-match'; @@ -20,7 +20,7 @@ export class CompetitionFormBuilder { private static DEFAULT_HINT_DURATION = 30; /** The {@link FormGroup} held by this {@link CompetitionFormBuilder}. */ - public form: FormGroup; + public form: UntypedFormGroup; /** * Constructor for CompetitionFormBuilder. @@ -72,7 +72,7 @@ export class CompetitionFormBuilder { * @param afterIndex The {@link FormControl} to insert the new {@link FormControl} after. */ public addComponentForm(type: ApiHintType, afterIndex: number = null, external : boolean = false) { - const array = this.form.get('components') as FormArray; + const array = this.form.get('components') as UntypedFormArray; const newIndex = afterIndex ? afterIndex + 1 : array.length; let component = null; switch (type) { @@ -121,7 +121,7 @@ export class CompetitionFormBuilder { * @param type The {@link TaskType.TargetTypeEnum} to add a {@link FormGroup} for. */ public addTargetForm(type: ApiTargetOption | 'MULTI') { - const array = this.form.get('target') as FormArray; + const array = this.form.get('target') as UntypedFormArray; const newIndex = array.length; switch (type) { // FIXME to make compiler happy. obviously this is semantically not appropriate @@ -144,7 +144,7 @@ export class CompetitionFormBuilder { * @param index Index to remove. */ public removeComponentForm(index: number) { - const array = this.form.get('components') as FormArray; + const array = this.form.get('components') as UntypedFormArray; if (array.length > index) { array.removeAt(index); } @@ -156,7 +156,7 @@ export class CompetitionFormBuilder { * @param index Index to remove. */ public removeTargetForm(index: number) { - const array = this.form.get('target') as FormArray; + const array = this.form.get('target') as UntypedFormArray; if (array.length > index) { array.removeAt(index); } @@ -173,7 +173,7 @@ export class CompetitionFormBuilder { taskType: this.taskGroup.type /* Cannot be edited! */, duration: this.form.get('duration').value, collectionId: this.form.get('mediaCollection').value, - hints: (this.form.get('components') as FormArray).controls.map((c) => { + hints: (this.form.get('components') as UntypedFormArray).controls.map((c) => { return { type: c.get('type').value, start: c.get('start').value, @@ -190,7 +190,7 @@ export class CompetitionFormBuilder { path: c.get('path') ? c.get('path').value : null, } as ApiHint; }), - targets: (this.form.get('target') as FormArray).controls.map((t) => { + targets: (this.form.get('target') as UntypedFormArray).controls.map((t) => { return { type: t.get('type').value, target: t.get('mediaItem')?.value?.mediaItemId ?? null, @@ -217,7 +217,7 @@ export class CompetitionFormBuilder { this.data.taskType = this.taskGroup.type /* Cannot be edited! */; this.data.duration= this.form.get('duration').value; this.data.collectionId = this.form.get('mediaCollection').value; - this.data.hints = (this.form.get('components') as FormArray).controls.map((c) => { + this.data.hints = (this.form.get('components') as UntypedFormArray).controls.map((c) => { return { type: c.get('type').value, start: c.get('start').value, @@ -234,7 +234,7 @@ export class CompetitionFormBuilder { path: c.get('path') ? c.get('path').value : null, } as ApiHint; }); - this.data.targets= (this.form.get('target') as FormArray).controls.map((t) => { + this.data.targets= (this.form.get('target') as UntypedFormArray).controls.map((t) => { return { type: t.get('type').value, /** Either its the mediaItem's ID or its text that is stored in 'mediaItem' form control */ @@ -259,7 +259,7 @@ export class CompetitionFormBuilder { }; } - private temporalPointValidator(unitControl: FormControl): ValidatorFn { + private temporalPointValidator(unitControl: UntypedFormControl): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { if (unitControl.value === 'TIMECODE') { if (TimeUtilities.timeCodeRegex.test(`${control.value}`)) { @@ -277,11 +277,11 @@ export class CompetitionFormBuilder { * Initializes the {@link FormGroup}. */ private initializeForm() { - this.form = new FormGroup({ - id: new FormControl(this.data?.id), - name: new FormControl(this.data?.name, [Validators.required]), - duration: new FormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), - mediaCollection: new FormControl(this.data?.collectionId ?? this.builderService.defaultCollection, [Validators.required]), + this.form = new UntypedFormGroup({ + id: new UntypedFormControl(this.data?.id), + name: new UntypedFormControl(this.data?.name, [Validators.required]), + duration: new UntypedFormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), + mediaCollection: new UntypedFormControl(this.data?.collectionId ?? this.builderService.defaultCollection, [Validators.required]), }); this.form.addControl('target', this.formForTarget()); this.form.addControl('components', this.formForQueryComponents()); @@ -293,31 +293,31 @@ export class CompetitionFormBuilder { private formForTarget() { switch (this.taskType.targetOption) { case 'SINGLE_MEDIA_ITEM': - return new FormArray([this.singleMediaItemTargetForm(0, this.data?.targets[0] ?? null)]); + return new UntypedFormArray([this.singleMediaItemTargetForm(0, this.data?.targets[0] ?? null)]); case 'SINGLE_MEDIA_SEGMENT': - return new FormArray([this.singleMediaSegmentTargetForm(this.data?.targets[0] ?? null)]); + return new UntypedFormArray([this.singleMediaSegmentTargetForm(this.data?.targets[0] ?? null)]); case 'JUDGEMENT': - return new FormArray([new FormGroup({type: new FormControl(ApiTargetType.JUDGEMENT)})]); + return new UntypedFormArray([new UntypedFormGroup({type: new UntypedFormControl(ApiTargetType.JUDGEMENT)})]); case 'VOTE': - return new FormArray([new FormGroup({type: new FormControl(ApiTargetType.JUDGEMENT_WITH_VOTE)})]); + return new UntypedFormArray([new UntypedFormGroup({type: new UntypedFormControl(ApiTargetType.JUDGEMENT_WITH_VOTE)})]); case 'TEXT': - const text: FormGroup[] = []; + const text: UntypedFormGroup[] = []; if (this.data?.targets) { this.data?.targets?.forEach((d, i) => text.push(this.singleTextTargetForm(d))); } else { console.log('no target'); text.push(this.singleTextTargetForm()); } - return new FormArray(text); + return new UntypedFormArray(text); default: // Handling multiple here, since it's the default. - const content: FormGroup[] = []; + const content: UntypedFormGroup[] = []; if (this.data?.targets) { this.data?.targets?.forEach((t, i) => content.push(this.singleMediaItemTargetForm(i, t))); } else { content.push(this.singleMediaItemTargetForm(0)); } - return new FormArray(content); + return new UntypedFormArray(content); } } @@ -327,10 +327,10 @@ export class CompetitionFormBuilder { * @param index Index of the FormControl * @param initialize The optional {RestTaskDescriptionTargetItem} containing the data to initialize the form with. */ - private singleMediaItemTargetForm(index: number, initialize?: ApiTarget): FormGroup { + private singleMediaItemTargetForm(index: number, initialize?: ApiTarget): UntypedFormGroup { /* Prepare auto complete field. */ - const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); - const typeFormControl = new FormControl(ApiTargetType.MEDIA_ITEM); + const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); + const typeFormControl = new UntypedFormControl(ApiTargetType.MEDIA_ITEM); this.dataSources.set( `target.${index}.mediaItem`, @@ -352,7 +352,7 @@ export class CompetitionFormBuilder { }); } - return new FormGroup({ type: typeFormControl, mediaItem: mediaItemFormControl }, [RequireMatch]); + return new UntypedFormGroup({ type: typeFormControl, mediaItem: mediaItemFormControl }, [RequireMatch]); } /** @@ -362,8 +362,8 @@ export class CompetitionFormBuilder { */ private singleMediaSegmentTargetForm(initialize?: ApiTarget) { /* Prepare auto complete field. */ - const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); - const typeFormControl = new FormControl(ApiTargetType.MEDIA_ITEM_TEMPORAL_RANGE); + const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); + const typeFormControl = new UntypedFormControl(ApiTargetType.MEDIA_ITEM_TEMPORAL_RANGE); this.dataSources.set( `target.0.mediaItem`, @@ -385,12 +385,12 @@ export class CompetitionFormBuilder { }); } - const formGroup = new FormGroup({ + const formGroup = new UntypedFormGroup({ type: typeFormControl, mediaItem: mediaItemFormControl, - segment_start: new FormControl(initialize?.range.start.value, [Validators.required]), - segment_end: new FormControl(initialize?.range.end.value, [Validators.required]), - segment_time_unit: new FormControl( + segment_start: new UntypedFormControl(initialize?.range.start.value, [Validators.required]), + segment_end: new UntypedFormControl(initialize?.range.end.value, [Validators.required]), + segment_time_unit: new UntypedFormControl( initialize?.range.start.unit ? initialize?.range.start.unit : 'SECONDS', [Validators.required] ), @@ -398,10 +398,10 @@ export class CompetitionFormBuilder { formGroup .get('segment_start') - .setValidators([Validators.required, this.temporalPointValidator(formGroup.get('segment_time_unit') as FormControl)]); + .setValidators([Validators.required, this.temporalPointValidator(formGroup.get('segment_time_unit') as UntypedFormControl)]); formGroup .get('segment_end') - .setValidators([Validators.required, this.temporalPointValidator(formGroup.get('segment_time_unit') as FormControl)]); + .setValidators([Validators.required, this.temporalPointValidator(formGroup.get('segment_time_unit') as UntypedFormControl)]); formGroup.get('segment_start').updateValueAndValidity(); formGroup.get('segment_end').updateValueAndValidity(); @@ -409,14 +409,14 @@ export class CompetitionFormBuilder { } private singleTextTargetForm(initialize?: ApiTarget) { - const textFormControl = new FormControl(null, [Validators.required]); - const typeFormControl = new FormControl(ApiTargetType.TEXT); + const textFormControl = new UntypedFormControl(null, [Validators.required]); + const typeFormControl = new UntypedFormControl(ApiTargetType.TEXT); console.log(initialize?.target); textFormControl.setValue(initialize?.target, {emitEvent: false}); - return new FormGroup({ + return new UntypedFormGroup({ type: typeFormControl, mediaItem: textFormControl, }); @@ -449,7 +449,7 @@ export class CompetitionFormBuilder { } } } - return new FormArray(array, [Validators.required]); + return new UntypedFormArray(array, [Validators.required]); } /** @@ -459,17 +459,17 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private imageItemComponentForm(index: number, initialize?: ApiHint): FormGroup { + private imageItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { if(initialize?.path && !initialize?.mediaItem){ /* Handle external image */ return this.externalImageItemComponentForm(index, initialize); } - const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); + const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); if ( !initialize?.mediaItem && (this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetOption === 'SINGLE_MEDIA_ITEM') ) { - mediaItemFormControl.setValue((this.form.get('target') as FormArray).controls[0].get('mediaItem').value, {emitEvent: false}); + mediaItemFormControl.setValue((this.form.get('target') as UntypedFormArray).controls[0].get('mediaItem').value, {emitEvent: false}); } /* Prepare data source. */ @@ -495,14 +495,14 @@ export class CompetitionFormBuilder { - return new FormGroup({ - start: new FormControl(initialize?.start, [ + return new UntypedFormGroup({ + start: new UntypedFormControl(initialize?.start, [ Validators.required, Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), - type: new FormControl('IMAGE', [Validators.required]), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + type: new UntypedFormControl('IMAGE', [Validators.required]), mediaItem: mediaItemFormControl, }); } @@ -514,18 +514,18 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private videoItemComponentForm(index: number, initialize?: ApiHint): FormGroup { + private videoItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { if(initialize?.path && !initialize?.mediaItem){ /* handle external video */ return this.externalVideoItemComponentForm(index, initialize); } /* Initialize media item based on target. */ - const mediaItemFormControl = new FormControl(null, [Validators.required, RequireMatch]); + const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); if ( !initialize?.mediaItem && (this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetOption === 'SINGLE_MEDIA_ITEM') ) { - mediaItemFormControl.setValue((this.form.get('target') as FormArray).controls[0].get('mediaItem').value, {emitEvent: false}); + mediaItemFormControl.setValue((this.form.get('target') as UntypedFormArray).controls[0].get('mediaItem').value, {emitEvent: false}); } /* Prepare data source. */ @@ -550,22 +550,22 @@ export class CompetitionFormBuilder { } /* Prepare FormGroup. */ - const group = new FormGroup({ - start: new FormControl(initialize?.start, [ + const group = new UntypedFormGroup({ + start: new UntypedFormControl(initialize?.start, [ Validators.required, Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [ + end: new UntypedFormControl(initialize?.end, [ Validators.required, Validators.min(0), Validators.max(this.taskType.duration), ]), - type: new FormControl('VIDEO', [Validators.required]), + type: new UntypedFormControl('VIDEO', [Validators.required]), mediaItem: mediaItemFormControl, - segment_start: new FormControl(initialize?.range.start.value, [Validators.required]), - segment_end: new FormControl(initialize?.range.end.value, [Validators.required]), - segment_time_unit: new FormControl( + segment_start: new UntypedFormControl(initialize?.range.start.value, [Validators.required]), + segment_end: new UntypedFormControl(initialize?.range.end.value, [Validators.required]), + segment_time_unit: new UntypedFormControl( initialize?.range.start.unit ? initialize?.range.start.unit : 'SECONDS', Validators.required ), @@ -573,17 +573,17 @@ export class CompetitionFormBuilder { /* Initialize start, end and time unit based on target. */ // fetch target time unit - const targetTimeUnit = (this.form.get('target') as FormArray).controls[0].get('segment_time_unit').value; + const targetTimeUnit = (this.form.get('target') as UntypedFormArray).controls[0].get('segment_time_unit').value; if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { group.get('segment_time_unit').setValue(targetTimeUnit, {emitEvent: false}); } if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_start').setValue((this.form.get('target') as FormArray).controls[0].get('segment_start').value, {emitEvent: false}); + group.get('segment_start').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_start').value, {emitEvent: false}); } if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_end').setValue((this.form.get('target') as FormArray).controls[0].get('segment_end').value, {emitEvent: false}); + group.get('segment_end').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_end').value, {emitEvent: false}); } /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ @@ -591,10 +591,10 @@ export class CompetitionFormBuilder { group .get('segment_start') - .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as UntypedFormControl)]); group .get('segment_end') - .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as UntypedFormControl)]); group.get('segment_start').updateValueAndValidity(); group.get('segment_end').updateValueAndValidity(); @@ -608,16 +608,16 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private textItemComponentForm(index: number, initialize?: ApiHint): FormGroup { - return new FormGroup({ - start: new FormControl(initialize?.start, [ + private textItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { + return new UntypedFormGroup({ + start: new UntypedFormControl(initialize?.start, [ Validators.required, Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), - type: new FormControl('TEXT', [Validators.required]), - description: new FormControl(initialize?.description, [Validators.required]), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + type: new UntypedFormControl('TEXT', [Validators.required]), + description: new UntypedFormControl(initialize?.description, [Validators.required]), }); } @@ -627,9 +627,9 @@ export class CompetitionFormBuilder { * @param index The position of the new {@link FormGroup} (for data source). * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. */ - private externalImageItemComponentForm(index: number, initialize?: ApiHint): FormGroup { + private externalImageItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { /* Prepare form control. */ - const pathFormControl = new FormControl(initialize?.path, [Validators.required]); + const pathFormControl = new UntypedFormControl(initialize?.path, [Validators.required]); /* Prepare data source. */ this.dataSources.set( @@ -640,15 +640,15 @@ export class CompetitionFormBuilder { ) ); - return new FormGroup({ - start: new FormControl(initialize?.start, [ + return new UntypedFormGroup({ + start: new UntypedFormControl(initialize?.start, [ Validators.required, Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), - type: new FormControl('IMAGE', [Validators.required]), - external: new FormControl(true), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + type: new UntypedFormControl('IMAGE', [Validators.required]), + external: new UntypedFormControl(true), path: pathFormControl, }); } @@ -660,9 +660,9 @@ export class CompetitionFormBuilder { * @param initialize The {@link RestTaskDescriptionComponent} to populate data from. * @return The new {@link FormGroup} */ - private externalVideoItemComponentForm(index: number, initialize?: ApiHint): FormGroup { + private externalVideoItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { /* Prepare form control. */ - const pathFormControl = new FormControl(initialize?.path, [Validators.required]); + const pathFormControl = new UntypedFormControl(initialize?.path, [Validators.required]); /* Prepare data source. */ this.dataSources.set( @@ -673,36 +673,36 @@ export class CompetitionFormBuilder { ) ); - const group = new FormGroup({ - start: new FormControl(initialize?.start, [ + const group = new UntypedFormGroup({ + start: new UntypedFormControl(initialize?.start, [ Validators.required, Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new FormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), - type: new FormControl('VIDEO', [Validators.required]), - external: new FormControl(true), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + type: new UntypedFormControl('VIDEO', [Validators.required]), + external: new UntypedFormControl(true), path: pathFormControl, - segment_start: new FormControl(initialize?.range.start.value, [Validators.required]), - segment_end: new FormControl(initialize?.range.end.value, [Validators.required]), - segment_time_unit: new FormControl( + segment_start: new UntypedFormControl(initialize?.range.start.value, [Validators.required]), + segment_end: new UntypedFormControl(initialize?.range.end.value, [Validators.required]), + segment_time_unit: new UntypedFormControl( initialize?.range.start.unit ? initialize?.range.start.unit : 'SECONDS', Validators.required ), }); /* Initialize start, end and time unit based on target. */ // fetch target time unit - const targetTimeUnit = (this.form.get('target') as FormArray).controls[0].get('segment_time_unit').value; + const targetTimeUnit = (this.form.get('target') as UntypedFormArray).controls[0].get('segment_time_unit').value; if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { group.get('segment_time_unit').setValue(targetTimeUnit, {emitEvent: false}); } if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_start').setValue((this.form.get('target') as FormArray).controls[0].get('segment_start').value, {emitEvent: false}); + group.get('segment_start').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_start').value, {emitEvent: false}); } if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_end').setValue((this.form.get('target') as FormArray).controls[0].get('segment_end').value, {emitEvent: false}); + group.get('segment_end').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_end').value, {emitEvent: false}); } /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ @@ -710,10 +710,10 @@ export class CompetitionFormBuilder { group .get('segment_start') - .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as UntypedFormControl)]); group .get('segment_end') - .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as FormControl)]); + .setValidators([Validators.required, this.temporalPointValidator(group.get('segment_time_unit') as UntypedFormControl)]); group.get('segment_start').updateValueAndValidity(); group.get('segment_end').updateValueAndValidity(); return group; diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts index 62facaaa6..ecdb91e67 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-group-dialog/competition-builder-task-group.component.ts @@ -1,6 +1,6 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import {ApiTaskGroup, ApiTaskType} from '../../../../../openapi'; export interface CompetitionBuilderTaskGroupDialogData { @@ -17,7 +17,7 @@ export class CompetitionBuilderTaskGroupDialogComponent { readonly supportedTaskTypes: ApiTaskType[] = []; /** FromGroup for this dialog. */ - form: FormGroup; + form: UntypedFormGroup; constructor( public dialogRef: MatDialogRef, @@ -56,9 +56,9 @@ export class CompetitionBuilderTaskGroupDialogComponent { } private init() { - this.form = new FormGroup({ - name: new FormControl(this.data?.group?.name, [Validators.required, Validators.minLength(3)]), - type: new FormControl(this.typeFromName(this.data?.group?.type), [Validators.required]), + this.form = new UntypedFormGroup({ + name: new UntypedFormControl(this.data?.group?.name, [Validators.required, Validators.minLength(3)]), + type: new UntypedFormControl(this.typeFromName(this.data?.group?.type), [Validators.required]), }); } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts index 1c899ee43..88e8c6502 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormArray, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { MatCheckboxChange } from '@angular/material/checkbox'; import {ApiHintOption, ApiScoreOption, ApiSubmissionOption, ApiTargetOption, ApiTaskOption, ApiTaskType} from '../../../../../openapi'; @@ -19,7 +19,7 @@ interface ActivatedType { }) export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterViewInit { /** FromGroup for this dialog. */ - form: FormGroup; + form: UntypedFormGroup; /** * Dynamically generated list of all target types. Since TargetType is an enum, values is required as this is the "underscore sensitive" @@ -56,13 +56,13 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV * @param name */ onCheckboxChange(e: MatCheckboxChange, name: string) { - const arr: FormArray = this.form.get(name) as FormArray; + const arr: UntypedFormArray = this.form.get(name) as UntypedFormArray; if (e.checked) { - arr.push(new FormControl(e.source.value)); + arr.push(new UntypedFormControl(e.source.value)); } else { let i = 0; - arr.controls.forEach((item: FormControl) => { + arr.controls.forEach((item: UntypedFormControl) => { if (item.value === e.source.value) { arr.removeAt(i); return; @@ -105,16 +105,16 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV * Adds a new parameter entry to the list of parameter entries. */ public addParameter() { - (this.form.get('parameters') as FormArray).controls.push( - new FormArray([new FormControl(null), new FormControl(null), new FormControl(null)]) + (this.form.get('parameters') as UntypedFormArray).controls.push( + new UntypedFormArray([new UntypedFormControl(null), new UntypedFormControl(null), new UntypedFormControl(null)]) ); } /** * Removes a parameter entry from the list of parameter entries. */ - public removeParameter(entry: FormArray) { - const array = (this.form.get('parameters') as FormArray).controls; + public removeParameter(entry: UntypedFormArray) { + const array = (this.form.get('parameters') as UntypedFormArray).controls; const index = array.indexOf(entry); if (index >= 0) { array.splice(index, 1); @@ -128,9 +128,9 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV const array = []; array.push(this.form.get('target').value); array.push(this.form.get('scoring').value); - (this.form.get('components') as FormArray).controls.forEach((c) => array.push(c.value)); - (this.form.get('filters') as FormArray).controls.forEach((c) => array.push(c.value)); - (this.form.get('options') as FormArray).controls.forEach((c) => array.push(c.value)); + (this.form.get('components') as UntypedFormArray).controls.forEach((c) => array.push(c.value)); + (this.form.get('filters') as UntypedFormArray).controls.forEach((c) => array.push(c.value)); + (this.form.get('options') as UntypedFormArray).controls.forEach((c) => array.push(c.value)); return array; } @@ -199,36 +199,36 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV */ /* Prepare empty FormControl. */ - this.form = new FormGroup({ + this.form = new UntypedFormGroup({ /* Name. Required */ - name: new FormControl(this.data?.name, [Validators.required, Validators.minLength(3)]), + name: new UntypedFormControl(this.data?.name, [Validators.required, Validators.minLength(3)]), /* Default Duration. Required */ - defaultTaskDuration: new FormControl(this.data?.duration, [Validators.required, Validators.min(1)]), + defaultTaskDuration: new UntypedFormControl(this.data?.duration, [Validators.required, Validators.min(1)]), /* Target Type. Required */ - target: new FormControl(this.data?.targetOption, [Validators.required]), + target: new UntypedFormControl(this.data?.targetOption, [Validators.required]), /* Components: Required, at least one */ components: this.data?.hintOptions - ? new FormArray( - this.data?.hintOptions?.map((v) => new FormControl(v)), + ? new UntypedFormArray( + this.data?.hintOptions?.map((v) => new UntypedFormControl(v)), [Validators.minLength(1)] ) - : new FormArray([]), + : new UntypedFormArray([]), /* Scoring: Required */ - scoring: new FormControl(this.data?.scoreOption, [Validators.required]), + scoring: new UntypedFormControl(this.data?.scoreOption, [Validators.required]), /* Submission Filters: Optional*/ - filters: this.data?.submissionOptions ? new FormArray(this.data.submissionOptions.map((v) => new FormControl(v))) : new FormArray([]), + filters: this.data?.submissionOptions ? new UntypedFormArray(this.data.submissionOptions.map((v) => new UntypedFormControl(v))) : new UntypedFormArray([]), /* Options: Optional */ - options: this.data?.taskOptions ? new FormArray(this.data.taskOptions.map((v) => new FormControl(v))) : new FormArray([]), + options: this.data?.taskOptions ? new UntypedFormArray(this.data.taskOptions.map((v) => new UntypedFormControl(v))) : new UntypedFormArray([]), /* Parameters: Optional */ - parameters: new FormArray( - parameters.map((v) => new FormArray([new FormControl(v[0]), new FormControl(v[1]), new FormControl(v[2])])) + parameters: new UntypedFormArray( + parameters.map((v) => new UntypedFormArray([new UntypedFormControl(v[0]), new UntypedFormControl(v[1]), new UntypedFormControl(v[2])])) ), }); } @@ -241,14 +241,14 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV name: this.form.get('name').value, duration: this.form.get('defaultTaskDuration').value, targetOption: this.form.get('target').value as ApiTargetOption, - hintOptions: (this.form.get('components') as FormArray).controls.map((c) => { + hintOptions: (this.form.get('components') as UntypedFormArray).controls.map((c) => { return c.value as ApiHintOption; }) as Array, scoreOption: this.form.get('scoring').value as ApiScoreOption, - submissionOptions: (this.form.get('filters') as FormArray).controls.map((c) => { + submissionOptions: (this.form.get('filters') as UntypedFormArray).controls.map((c) => { return c.value as ApiSubmissionOption; }) as Array, - taskOptions: (this.form.get('options') as FormArray).controls.map((c) => { + taskOptions: (this.form.get('options') as UntypedFormArray).controls.map((c) => { return c.value as ApiTaskOption; }) as Array, configuration: this.fetchConfigurationParameters() @@ -263,8 +263,8 @@ export class CompetitionBuilderTaskTypeDialogComponent implements OnInit, AfterV */ private fetchConfigurationParameters(): any { const obj = {}; - (this.form.get('parameters') as FormArray).controls.forEach((c) => { - const cast = (c as FormArray).controls; + (this.form.get('parameters') as UntypedFormArray).controls.forEach((c) => { + const cast = (c as UntypedFormArray).controls; obj[`${cast[0].value}.${cast[1].value}`] = cast[2].value; }); return obj; diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index a0451355f..eea6812ec 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { map, shareReplay } from 'rxjs/operators'; @@ -12,7 +12,7 @@ import {ApiTeam, ApiUser, UserService} from '../../../../../openapi'; templateUrl: './competition-builder-team-dialog.component.html', }) export class CompetitionBuilderTeamDialogComponent { - form: FormGroup; + form: UntypedFormGroup; logoName = ''; availableUsers: Observable; colorPalette = [ @@ -44,18 +44,18 @@ export class CompetitionBuilderTeamDialogComponent { private config: AppConfig, @Inject(MAT_DIALOG_DATA) private team?: ApiTeam ) { - this.form = new FormGroup({ - id: new FormControl(team?.id), - name: new FormControl(team?.name, [Validators.required, Validators.minLength(3)]), - color: new FormControl(team?.color ? team.color : CompetitionBuilderTeamDialogComponent.randomColor(), [ + this.form = new UntypedFormGroup({ + id: new UntypedFormControl(team?.id), + name: new UntypedFormControl(team?.name, [Validators.required, Validators.minLength(3)]), + color: new UntypedFormControl(team?.color ? team.color : CompetitionBuilderTeamDialogComponent.randomColor(), [ Validators.required, Validators.minLength(7), Validators.maxLength(7), ]), - logoId: new FormControl(/*team?.logoId*/''), - logoData: new FormControl(team?.logoData), - users: new FormControl(team?.users != null ? team.users : []), - userInput: new FormControl(''), + logoId: new UntypedFormControl(/*team?.logoId*/''), + logoData: new UntypedFormControl(team?.logoData), + users: new UntypedFormControl(team?.users != null ? team.users : []), + userInput: new UntypedFormControl(''), }); this.availableUsers = this.userService.getApiV2UserList().pipe( map((value) => { diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index c2ccdb16e..63e7cedf0 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -2,7 +2,7 @@ import { Component, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/ import { ActivatedRoute, Router, RouterStateSnapshot } from '@angular/router'; import { filter, map, shareReplay, take } from 'rxjs/operators'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; import { CompetitionBuilderTeamDialogComponent } from './competition-builder-team-dialog/competition-builder-team-dialog.component'; @@ -133,7 +133,7 @@ export class CompetitionBuilderComponent implements OnInit, OnDestroy, Deactivat displayedColumnsTeams: string[] = ['logo', 'name', 'action']; displayedColumnsJudges: string[] = ['name', 'action']; displayedColumnsTasks: string[] = ['name', 'group', 'type', 'duration', 'action']; - form: FormGroup = new FormGroup({ name: new FormControl(''), description: new FormControl('') }); + form: UntypedFormGroup = new UntypedFormGroup({ name: new UntypedFormControl(''), description: new UntypedFormControl('') }); dirty = false; routeSubscription: Subscription; changeSubscription: Subscription; diff --git a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts index 36b475b92..27dfff45e 100644 --- a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts +++ b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import {ApiCreateEvaluation} from '../../../../openapi'; @Component({ @@ -8,9 +8,9 @@ import {ApiCreateEvaluation} from '../../../../openapi'; templateUrl: 'competition-create-dialog.component.html', }) export class CompetitionCreateDialogComponent { - form: FormGroup = new FormGroup({ - name: new FormControl(''), - description: new FormControl(''), + form: UntypedFormGroup = new UntypedFormGroup({ + name: new UntypedFormControl(''), + description: new UntypedFormControl(''), }); participantsCanView = true; diff --git a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts index e7675a364..e7b836110 100644 --- a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts +++ b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatDialogRef } from '@angular/material/dialog'; import {ApiEvaluationType} from '../../../../openapi'; @@ -17,13 +17,13 @@ export interface CompetitionStartDialogResult { templateUrl: 'competition-start-dialog.component.html', }) export class CompetitionStartDialogComponent { - form: FormGroup = new FormGroup({ - name: new FormControl(''), - type: new FormControl(''), - participantsCanView: new FormControl(true), - shuffleTasks: new FormControl(false), - allowRepeatedTasks: new FormControl(false), - limit: new FormControl(0) + form: UntypedFormGroup = new UntypedFormGroup({ + name: new UntypedFormControl(''), + type: new UntypedFormControl(''), + participantsCanView: new UntypedFormControl(true), + shuffleTasks: new UntypedFormControl(false), + allowRepeatedTasks: new UntypedFormControl(false), + limit: new UntypedFormControl(0) }); runTypes: ApiEvaluationType[] = ['SYNCHRONOUS', 'ASYNCHRONOUS']; diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index 2501124b3..3745fb096 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -11,7 +11,7 @@ import { ApiTemporalUnit, CollectionService } from "../../../../../../openapi"; -import { FormControl, FormGroup } from "@angular/forms"; +import { UntypedFormControl, UntypedFormGroup } from "@angular/forms"; import { CompetitionFormBuilder } from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; @@ -39,7 +39,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { public taskType: ApiTaskType; public taskGroup: ApiTaskGroup; - form: FormGroup; + form: UntypedFormGroup; units = [ApiTemporalUnit.FRAME_NUMBER, ApiTemporalUnit.SECONDS, ApiTemporalUnit.MILLISECONDS, ApiTemporalUnit.TIMECODE] @@ -192,7 +192,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { * @param collectionId The ID of the collection to pick a {@link MediaItem} from. * @param target The target {@link FormControl} to apply the value to. */ - public pickRandomMediaItem(collectionId: string, target: FormControl) { + public pickRandomMediaItem(collectionId: string, target: UntypedFormControl) { this.collectionService .getApiV2CollectionByCollectionIdRandom(collectionId) .pipe(first()) @@ -209,7 +209,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { * @param endControl The target {@link FormControl} to apply the value to. * @param unitControl The target {@link FormControl} to apply the value to. */ - public pickRandomSegment(item: ApiMediaItem, startControl: FormControl, endControl: FormControl, unitControl: FormControl) { + public pickRandomSegment(item: ApiMediaItem, startControl: UntypedFormControl, endControl: UntypedFormControl, unitControl: UntypedFormControl) { const start = TaskTemplateEditorComponent.randInt(1, item.durationMs / 1000 / 2); // always in first half let end = 1; do { @@ -220,7 +220,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { unitControl.setValue('SECONDS'); } - toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { /* Add to toggleVideoPlayer button if [disabled]="!target.get('mediaItem').value && !target.get('segment_start').value && !target.get('segment_end').value" */ @@ -265,7 +265,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { this.showVideo = !this.showVideo; } - onRangeChange(range: ApiTemporalRange, startControl?: FormControl, endControl?: FormControl, unitControl?: FormControl) { + onRangeChange(range: ApiTemporalRange, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { startControl?.setValue(range.start.value); endControl?.setValue(range.end.value); unitControl?.setValue(ApiTemporalUnit.SECONDS); diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts index 094f0dfce..20931dcea 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts @@ -1,6 +1,6 @@ import {Component, OnDestroy, OnInit} from '@angular/core'; import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; -import {FormControl, FormGroup} from '@angular/forms'; +import {UntypedFormControl, UntypedFormGroup} from '@angular/forms'; import { Observable, Subscription } from "rxjs"; import { ApiMediaCollection, CollectionService, TemplateService } from "../../../../../../openapi"; import {TemplateBuilderService} from '../../template-builder.service'; @@ -12,10 +12,10 @@ import {TemplateBuilderService} from '../../template-builder.service'; }) export class TemplateInformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { - form: FormGroup = new FormGroup({ - name: new FormControl(''), - description: new FormControl(''), - collection: new FormControl('') + form: UntypedFormGroup = new UntypedFormGroup({ + name: new UntypedFormControl(''), + description: new UntypedFormControl(''), + collection: new UntypedFormControl('') }); private changeSub: Subscription; diff --git a/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts b/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts index d5483f788..0ffc153b0 100644 --- a/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts +++ b/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.ts @@ -1,6 +1,6 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import {ApiRole, ApiUser, UserRequest} from '../../../../openapi'; @Component({ @@ -9,10 +9,10 @@ import {ApiRole, ApiUser, UserRequest} from '../../../../openapi'; styleUrls: ['./admin-user-create-or-edit-dialog.component.scss'], }) export class AdminUserCreateOrEditDialogComponent { - form: FormGroup = new FormGroup({ - username: new FormControl(''), - password: new FormControl(''), - role: new FormControl(''), + form: UntypedFormGroup = new UntypedFormGroup({ + username: new UntypedFormControl(''), + password: new UntypedFormControl(''), + role: new UntypedFormControl(''), }); roles = [ApiRole.ADMIN, ApiRole.JUDGE, ApiRole.PARTICIPANT, ApiRole.VIEWER]; diff --git a/frontend/src/app/user/login/login.component.ts b/frontend/src/app/user/login/login.component.ts index d1d8c9cd6..3567addc3 100644 --- a/frontend/src/app/user/login/login.component.ts +++ b/frontend/src/app/user/login/login.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { AuthenticationService } from '../../services/session/authentication.sevice'; @@ -11,9 +11,9 @@ import { Subscription } from 'rxjs'; styleUrls: ['./login.component.scss'], }) export class LoginComponent implements OnInit, OnDestroy { - form: FormGroup = new FormGroup({ - username: new FormControl(''), - password: new FormControl(''), + form: UntypedFormGroup = new UntypedFormGroup({ + username: new UntypedFormControl(''), + password: new UntypedFormControl(''), }); private returnUrl = '/'; diff --git a/frontend/src/app/user/profile/profile.component.ts b/frontend/src/app/user/profile/profile.component.ts index a66fc3690..7eb6b4bbc 100644 --- a/frontend/src/app/user/profile/profile.component.ts +++ b/frontend/src/app/user/profile/profile.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatSnackBar } from '@angular/material/snack-bar'; import { first, flatMap, tap } from 'rxjs/operators'; import { AuthenticationService } from '../../services/session/authentication.sevice'; @@ -21,9 +21,9 @@ export class ProfileComponent implements OnInit, OnDestroy { private userSub: Subscription; - form: FormGroup = new FormGroup({ - username: new FormControl({ value: '', disabled: !this.editing }), - password: new FormControl({ value: '', disabled: !this.editing }), + form: UntypedFormGroup = new UntypedFormGroup({ + username: new UntypedFormControl({ value: '', disabled: !this.editing }), + password: new UntypedFormControl({ value: '', disabled: !this.editing }), }); constructor( diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b31fe15aa..39eed5fcd 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5,7 +5,14 @@ __metadata: version: 6 cacheKey: 8 -"@ampproject/remapping@npm:2.2.0, @ampproject/remapping@npm:^2.2.0": +"@adobe/css-tools@npm:^4.0.1": + version: 4.2.0 + resolution: "@adobe/css-tools@npm:4.2.0" + checksum: dc5cc92ba3d562e7ffddb79d6d222c7e00b65f255fd2725b3d71490ff268844be322f917415d8c4ab39eca646343b632058db8bd5b1d646193fcc94d1d3e420b + languageName: node + linkType: hard + +"@ampproject/remapping@npm:2.2.0, @ampproject/remapping@npm:^2.1.0, @ampproject/remapping@npm:^2.2.0": version: 2.2.0 resolution: "@ampproject/remapping@npm:2.2.0" dependencies: @@ -15,104 +22,92 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1302.6": - version: 0.1302.6 - resolution: "@angular-devkit/architect@npm:0.1302.6" +"@angular-devkit/architect@npm:0.1402.11": + version: 0.1402.11 + resolution: "@angular-devkit/architect@npm:0.1402.11" dependencies: - "@angular-devkit/core": 13.2.6 + "@angular-devkit/core": 14.2.11 rxjs: 6.6.7 - checksum: d6f1b706bc56bfc8c1ab4c25bff52714728895ac58ea52c6ad7d0ffa73e55326982f7f024ab2bb14cb7ba5a330b05e797df004f4404995a22c26163d106e9a42 + checksum: c494678e47aa80662acdd5b1d68ecae892add773cca4690f0e43b344fbce3d4414d9f50fc642906b3ad4c89a6067b2c6672154114ed4f9e9b3d1d99f5f8e71ed languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1303.10": - version: 0.1303.10 - resolution: "@angular-devkit/architect@npm:0.1303.10" - dependencies: - "@angular-devkit/core": 13.3.10 - rxjs: 6.6.7 - checksum: fa07d923c8a9d37511bb6fb6c88bd0df6044958c57ba7995c0cc610c2a8c6d2f84a3abb1aa4318015fd068a1e49414bfea4743882068b22d15cf8dab2a53c661 - languageName: node - linkType: hard - -"@angular-devkit/build-angular@npm:13.3.x": - version: 13.3.10 - resolution: "@angular-devkit/build-angular@npm:13.3.10" +"@angular-devkit/build-angular@npm:14.2.11": + version: 14.2.11 + resolution: "@angular-devkit/build-angular@npm:14.2.11" dependencies: "@ampproject/remapping": 2.2.0 - "@angular-devkit/architect": 0.1303.10 - "@angular-devkit/build-webpack": 0.1303.10 - "@angular-devkit/core": 13.3.10 - "@babel/core": 7.16.12 - "@babel/generator": 7.16.8 - "@babel/helper-annotate-as-pure": 7.16.7 - "@babel/plugin-proposal-async-generator-functions": 7.16.8 - "@babel/plugin-transform-async-to-generator": 7.16.8 - "@babel/plugin-transform-runtime": 7.16.10 - "@babel/preset-env": 7.16.11 - "@babel/runtime": 7.16.7 - "@babel/template": 7.16.7 - "@discoveryjs/json-ext": 0.5.6 - "@ngtools/webpack": 13.3.10 - ansi-colors: 4.1.1 + "@angular-devkit/architect": 0.1402.11 + "@angular-devkit/build-webpack": 0.1402.11 + "@angular-devkit/core": 14.2.11 + "@babel/core": 7.18.10 + "@babel/generator": 7.18.12 + "@babel/helper-annotate-as-pure": 7.18.6 + "@babel/plugin-proposal-async-generator-functions": 7.18.10 + "@babel/plugin-transform-async-to-generator": 7.18.6 + "@babel/plugin-transform-runtime": 7.18.10 + "@babel/preset-env": 7.18.10 + "@babel/runtime": 7.18.9 + "@babel/template": 7.18.10 + "@discoveryjs/json-ext": 0.5.7 + "@ngtools/webpack": 14.2.11 + ansi-colors: 4.1.3 babel-loader: 8.2.5 babel-plugin-istanbul: 6.1.1 browserslist: ^4.9.1 - cacache: 15.3.0 - circular-dependency-plugin: 5.2.2 - copy-webpack-plugin: 10.2.1 - core-js: 3.20.3 + cacache: 16.1.2 + copy-webpack-plugin: 11.0.0 critters: 0.0.16 - css-loader: 6.5.1 - esbuild: 0.14.22 - esbuild-wasm: 0.14.22 - glob: 7.2.0 - https-proxy-agent: 5.0.0 - inquirer: 8.2.0 - jsonc-parser: 3.0.0 + css-loader: 6.7.1 + esbuild: 0.15.5 + esbuild-wasm: 0.15.5 + glob: 8.0.3 + https-proxy-agent: 5.0.1 + inquirer: 8.2.4 + jsonc-parser: 3.1.0 karma-source-map-support: 1.4.0 - less: 4.1.2 - less-loader: 10.2.0 + less: 4.1.3 + less-loader: 11.0.0 license-webpack-plugin: 4.0.2 loader-utils: 3.2.1 - mini-css-extract-plugin: 2.5.3 - minimatch: 3.0.5 + mini-css-extract-plugin: 2.6.1 + minimatch: 5.1.0 open: 8.4.0 ora: 5.4.1 parse5-html-rewriting-stream: 6.0.1 piscina: 3.2.0 - postcss: 8.4.5 - postcss-import: 14.0.2 - postcss-loader: 6.2.1 - postcss-preset-env: 7.2.3 + postcss: 8.4.16 + postcss-import: 15.0.0 + postcss-loader: 7.0.1 + postcss-preset-env: 7.8.0 regenerator-runtime: 0.13.9 resolve-url-loader: 5.0.0 rxjs: 6.6.7 - sass: 1.49.9 - sass-loader: 12.4.0 - semver: 7.3.5 - source-map-loader: 3.0.1 + sass: 1.54.4 + sass-loader: 13.0.2 + semver: 7.3.7 + source-map-loader: 4.0.0 source-map-support: 0.5.21 - stylus: 0.56.0 - stylus-loader: 6.2.0 + stylus: 0.59.0 + stylus-loader: 7.0.0 terser: 5.14.2 text-table: 0.2.0 tree-kill: 1.2.2 - tslib: 2.3.1 - webpack: 5.70.0 - webpack-dev-middleware: 5.3.0 - webpack-dev-server: 4.7.3 + tslib: 2.4.0 + webpack: 5.76.1 + webpack-dev-middleware: 5.3.3 + webpack-dev-server: 4.11.0 webpack-merge: 5.8.0 webpack-subresource-integrity: 5.1.0 peerDependencies: - "@angular/compiler-cli": ^13.0.0 || ^13.3.0-rc.0 - "@angular/localize": ^13.0.0 || ^13.3.0-rc.0 - "@angular/service-worker": ^13.0.0 || ^13.3.0-rc.0 + "@angular/compiler-cli": ^14.0.0 + "@angular/localize": ^14.0.0 + "@angular/service-worker": ^14.0.0 karma: ^6.3.0 - ng-packagr: ^13.0.0 + ng-packagr: ^14.0.0 protractor: ^7.0.0 tailwindcss: ^2.0.0 || ^3.0.0 - typescript: ">=4.4.3 <4.7" + typescript: ">=4.6.2 <4.9" dependenciesMeta: esbuild: optional: true @@ -129,71 +124,51 @@ __metadata: optional: true tailwindcss: optional: true - checksum: 242f0b9789e22da42d21e368da299ab3c14ab06cace81dbb5b1894730d632ee59e55e6030eb105ad636ba76b8f3e494f59c84c061680abd1e53f74d723f44a0c + checksum: b420ae49d561bf8c21203abc1eed2a9078cc3dd6ccbf6d18ab2662a31c6a84dd14a084b614cf18033fe0ca7bfb31ccad2e2e9b9595ee37f9c7435cfe6c36ab31 languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1303.10": - version: 0.1303.10 - resolution: "@angular-devkit/build-webpack@npm:0.1303.10" +"@angular-devkit/build-webpack@npm:0.1402.11": + version: 0.1402.11 + resolution: "@angular-devkit/build-webpack@npm:0.1402.11" dependencies: - "@angular-devkit/architect": 0.1303.10 + "@angular-devkit/architect": 0.1402.11 rxjs: 6.6.7 peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^4.0.0 - checksum: ede18e93d996509dc44b94d0fc7bafa0c96b3c0486e69631008992f6f1efdded251635e655eebd168ffd1ffc9ff25f6c6491fb6c72910dca8b07c948fb9fbe66 + checksum: 4619dfb2f958a9150b2e43e8d1a1e5e8905f3e8db5b8c8bc82e5c0cdbf4ce7ead35add634773fcb775a205c7d4469246b3b7d08dba0a346724c3482d9e1087e5 languageName: node linkType: hard -"@angular-devkit/core@npm:13.2.6": - version: 13.2.6 - resolution: "@angular-devkit/core@npm:13.2.6" +"@angular-devkit/core@npm:14.2.11": + version: 14.2.11 + resolution: "@angular-devkit/core@npm:14.2.11" dependencies: - ajv: 8.9.0 + ajv: 8.11.0 ajv-formats: 2.1.1 - fast-json-stable-stringify: 2.1.0 - magic-string: 0.25.7 + jsonc-parser: 3.1.0 rxjs: 6.6.7 - source-map: 0.7.3 + source-map: 0.7.4 peerDependencies: chokidar: ^3.5.2 peerDependenciesMeta: chokidar: optional: true - checksum: ec8589dbd7b2b1c444229be9915c6a1f5ddb3ee26dcc43612f930125d93c7b2be1aabba09e0ba46008883908cd908c7883e67d534313d979cb3ad86b30e06019 + checksum: 5672fe0f44b557e637d97a12c1aea9c23b154005430dda4e5c80e0f0f73ff465e115e726d398f2d9008b2cf81c152083402ac234f1454a76db3f748c057a1c72 languageName: node linkType: hard -"@angular-devkit/core@npm:13.3.10": - version: 13.3.10 - resolution: "@angular-devkit/core@npm:13.3.10" +"@angular-devkit/schematics@npm:14.2.11": + version: 14.2.11 + resolution: "@angular-devkit/schematics@npm:14.2.11" dependencies: - ajv: 8.9.0 - ajv-formats: 2.1.1 - fast-json-stable-stringify: 2.1.0 - magic-string: 0.25.7 - rxjs: 6.6.7 - source-map: 0.7.3 - peerDependencies: - chokidar: ^3.5.2 - peerDependenciesMeta: - chokidar: - optional: true - checksum: 25140170e2adefa05bce8a7db30c4b1d26d4739d8be0cc5d09fd29ad4cb424ee1831993806cfbba4302749be2d117faf3f9bbfcb9814eda38a1a0a31f80211f2 - languageName: node - linkType: hard - -"@angular-devkit/schematics@npm:13.2.6": - version: 13.2.6 - resolution: "@angular-devkit/schematics@npm:13.2.6" - dependencies: - "@angular-devkit/core": 13.2.6 - jsonc-parser: 3.0.0 - magic-string: 0.25.7 + "@angular-devkit/core": 14.2.11 + jsonc-parser: 3.1.0 + magic-string: 0.26.2 ora: 5.4.1 rxjs: 6.6.7 - checksum: 0aeade80952a08ddf461f6e3b8fb3643089e5691c5aecb3b330610a197cdb3b0b2f2032a131128d9355146ad6a46a10af2e986dd5eac7d93a7e89374e772a6da + checksum: 6162af280476fb2bfa2e752f9941addfd623bb88b1cf6adc1cd834b20376f3d4df3762e40a9959ca41b19e2a4062b5dcee9c9b9381ca5062cd42e459b34fa58b languageName: node linkType: hard @@ -285,29 +260,14 @@ __metadata: languageName: node linkType: hard -"@angular-slider/ngx-slider@npm:2.0.x": - version: 2.0.4 - resolution: "@angular-slider/ngx-slider@npm:2.0.4" - dependencies: - detect-passive-events: ^2.0.3 - rxjs: ^6.5.2 - tslib: ^1.9.0 - peerDependencies: - "@angular/common": ">=6.1.0" - "@angular/core": ">=6.1.0" - "@angular/forms": ">=6.1.0" - checksum: 5dc10802e2976edb9e45618c27646f57498014162b25f69231291abf126bf86de7511e77736f1fd2b48db95568fcfe94b56d60cd123269b51346e9c9eea27352 - languageName: node - linkType: hard - -"@angular/animations@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/animations@npm:13.2.7" +"@angular/animations@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/animations@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.7 - checksum: e444c814cdbfd2c1360e9f7f01d853baeccf17551eba178fd30b5c29b8e95386fe7d315ccc951b8f0c5d2cfa04091f5ecffecde71f6a2a3137c711218d4cae58 + "@angular/core": 14.3.0 + checksum: fb609ea6ced8ba612938e24b47e1eb1d1c32ede7841b4829b4817629f9b2692d4b69e88ab10a0f04cec143f1ca68d0e8bbba0d863106f22e959515ed7a5172f3 languageName: node linkType: hard @@ -328,50 +288,51 @@ __metadata: languageName: node linkType: hard -"@angular/cli@npm:13.2.x": - version: 13.2.6 - resolution: "@angular/cli@npm:13.2.6" +"@angular/cli@npm:14.2.11": + version: 14.2.11 + resolution: "@angular/cli@npm:14.2.11" dependencies: - "@angular-devkit/architect": 0.1302.6 - "@angular-devkit/core": 13.2.6 - "@angular-devkit/schematics": 13.2.6 - "@schematics/angular": 13.2.6 + "@angular-devkit/architect": 0.1402.11 + "@angular-devkit/core": 14.2.11 + "@angular-devkit/schematics": 14.2.11 + "@schematics/angular": 14.2.11 "@yarnpkg/lockfile": 1.1.0 - ansi-colors: 4.1.1 - debug: 4.3.3 - ini: 2.0.0 - inquirer: 8.2.0 - jsonc-parser: 3.0.0 - npm-package-arg: 8.1.5 - npm-pick-manifest: 6.1.1 + ansi-colors: 4.1.3 + debug: 4.3.4 + ini: 3.0.0 + inquirer: 8.2.4 + jsonc-parser: 3.1.0 + npm-package-arg: 9.1.0 + npm-pick-manifest: 7.0.1 open: 8.4.0 ora: 5.4.1 - pacote: 12.0.3 - resolve: 1.22.0 - semver: 7.3.5 + pacote: 13.6.2 + resolve: 1.22.1 + semver: 7.3.7 symbol-observable: 4.0.0 uuid: 8.3.2 + yargs: 17.5.1 bin: ng: bin/ng.js - checksum: 66765d8ead86045949dc3b8fe45577e9ae705354975da4e6bfdce51654102f313dde667f67ae2106cd79e810bec44192e4cca2f75a19522fac3561314a10d84c + checksum: 9754cb2cc97f63a4ef4f8ad7200156e3a47a404abbfa54346eb7b0e38fb7a58e104eeeb551240b4c7934ddb3e002550eaa90fc63d445bddec18bd2a3109f2eec languageName: node linkType: hard -"@angular/common@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/common@npm:13.2.7" +"@angular/common@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/common@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 13.2.7 + "@angular/core": 14.3.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: dbf2c324b3e3a549312d5d196da02cb07854fc75c7db5afeeb5efd7d432dff0d9539b097a1f150a2e8e8291cc85f0405a3e5cf7ec7a888a86f6f66a2213e269e + checksum: 92e61f5b1425f6992d117f288909208ae5a2ac27e2b16ae758797d8f8eff6532e933f7292a21276877c532abf75aad21eae8132afc92ae84ddc4f1f0d5932287 languageName: node linkType: hard -"@angular/compiler-cli@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/compiler-cli@npm:13.2.7" +"@angular/compiler-cli@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/compiler-cli@npm:14.3.0" dependencies: "@babel/core": ^7.17.2 chokidar: ^3.0.0 @@ -384,34 +345,39 @@ __metadata: tslib: ^2.3.0 yargs: ^17.2.1 peerDependencies: - "@angular/compiler": 13.2.7 - typescript: ">=4.4.2 <4.6" + "@angular/compiler": 14.3.0 + typescript: ">=4.6.2 <4.9" bin: ng-xi18n: bundles/src/bin/ng_xi18n.js ngc: bundles/src/bin/ngc.js ngcc: bundles/ngcc/main-ngcc.js - checksum: 9c7f207285174bd2e5bbb52363eadbaf143bc1582dca44fa941fc8c29cefd580173a8aa9444f152dc9b96bd8a267415890cd4509a08b786211a14a3f85cf1254 + checksum: 4c67467680856b8a3fed3105ef178b851435fadb265e83cdc54374d0beda3d5cd4020179b9d2450dbf707f3b72d0e331d2f5a5045641b49d58c269b95c1acb58 languageName: node linkType: hard -"@angular/compiler@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/compiler@npm:13.2.7" +"@angular/compiler@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/compiler@npm:14.3.0" dependencies: tslib: ^2.3.0 - checksum: 6d63ede056a8625d237e9895c2c532009b90b4a3b0ed232a84cea5a6005021c4cd91746fe38398af72c8b6794107386bba56e1b1c65163635bde5f325fb67674 + peerDependencies: + "@angular/core": 14.3.0 + peerDependenciesMeta: + "@angular/core": + optional: true + checksum: 24667875b12e34b57f95b1ac1995284267aeeefb15bba0f2d2c7f242d3aba5b2be47824a5beb54d4b502ae4caf22850d0a040bd75db71bff0fc63fd69187d149 languageName: node linkType: hard -"@angular/core@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/core@npm:13.2.7" +"@angular/core@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/core@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: rxjs: ^6.5.3 || ^7.4.0 - zone.js: ~0.11.4 - checksum: 7601052ae8c2206a6c26a1e1f92077cc2cf2c84aeed2562b68b3f45066adac1fb581622b175475e4979ca5aee5ce3bc9c9d5cb95325ca61b6546c5366d10adcc + zone.js: ~0.11.4 || ~0.12.0 + checksum: 07c2adfee1ef53a93e5c08ad140f28248c5b4c118a716a5acfef4254e0595e3d52001df872341308200ba08600098f050ac0293bf89adb547c64b4aadfc9e8cd languageName: node linkType: hard @@ -430,24 +396,24 @@ __metadata: languageName: node linkType: hard -"@angular/forms@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/forms@npm:13.2.7" +"@angular/forms@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/forms@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.7 - "@angular/core": 13.2.7 - "@angular/platform-browser": 13.2.7 + "@angular/common": 14.3.0 + "@angular/core": 14.3.0 + "@angular/platform-browser": 14.3.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: bea0ae8de61695b341846982821a7463ee9cded9f8afb1e3fd1c344935898682ce73743adfdadc4d2be075cb77fab2af2b1fe4ab4c5ec3cc8bd371c71c99876b + checksum: 2981590e5d71b18a71eb2828f303ee044068071aa2d0f473a89cc99cb1be7d031e5ce869608ef0494a29991212696133ac705aadaa8370344ce81d5bce9a60c2 languageName: node linkType: hard -"@angular/language-service@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/language-service@npm:13.2.7" - checksum: 2054f85287c822143ba891659148dfa5934b8abe2186d5b8a7544d6e890e7cec7d8afaaeddd76c004cc4e4b45414b24d4902ef176aa1dfca9b28c0598246968f +"@angular/language-service@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/language-service@npm:14.3.0" + checksum: cd0a0494dc51947fc51e261b9e12005c2d48b259dd4058da5c3a0ca02deeb9da5f77d5cbc135d187ed4ff65ce720085861a84d6c8d1d55c775293319d02e0a2e languageName: node linkType: hard @@ -468,47 +434,47 @@ __metadata: languageName: node linkType: hard -"@angular/platform-browser-dynamic@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/platform-browser-dynamic@npm:13.2.7" +"@angular/platform-browser-dynamic@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/platform-browser-dynamic@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.7 - "@angular/compiler": 13.2.7 - "@angular/core": 13.2.7 - "@angular/platform-browser": 13.2.7 - checksum: f8423844f0c63d618b8861e2911163c266ce972fb9ebcc8e0bb49d8a176e9abedb5019a9b300100d7d7f054784c343410dd80ac7f1e62946910e5484513cec7c + "@angular/common": 14.3.0 + "@angular/compiler": 14.3.0 + "@angular/core": 14.3.0 + "@angular/platform-browser": 14.3.0 + checksum: c18ad42030c8728c2123837f20e72cbbd44e6810ab62d9ee5ecd498136f30581a39112fe338583990587ab31ccc01d1cc5f15bfeea0544b294a7754aed31e76d languageName: node linkType: hard -"@angular/platform-browser@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/platform-browser@npm:13.2.7" +"@angular/platform-browser@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/platform-browser@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/animations": 13.2.7 - "@angular/common": 13.2.7 - "@angular/core": 13.2.7 + "@angular/animations": 14.3.0 + "@angular/common": 14.3.0 + "@angular/core": 14.3.0 peerDependenciesMeta: "@angular/animations": optional: true - checksum: 92c9020c6cd0b3e61e251fff6d86d8173e8af26c95b11f82548a81afc5b305507bd366b2a107d7596477b9d82a1b39397bdd20cf39f5779902ccbbd1dba6a816 + checksum: f46cf20606f7ab0e883df95d4a904b45972e9749668f5f80817f8a61f74ac07147a34f11d3542934e9b8c2055809d1488154b58876cb44a304e0da37c68f9152 languageName: node linkType: hard -"@angular/router@npm:13.2.x": - version: 13.2.7 - resolution: "@angular/router@npm:13.2.7" +"@angular/router@npm:14.3.0": + version: 14.3.0 + resolution: "@angular/router@npm:14.3.0" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 13.2.7 - "@angular/core": 13.2.7 - "@angular/platform-browser": 13.2.7 + "@angular/common": 14.3.0 + "@angular/core": 14.3.0 + "@angular/platform-browser": 14.3.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: ac3403e65a6eb420b040179e0ccb927744b654b0c335f58b7b0e144f53b0af5d5273284fdc143265c17a872680b94be304b61d031910f19375bafadd8b1389af + checksum: 8c0a26916fc4ba5a4b0b466a6fda83372521886f471a719a0092451dbeac9c0f5d83244d82cf6de5dd00cfd29b53d34ddb206e3696a3a9ab45030a71d37b02fe languageName: node linkType: hard @@ -528,14 +494,53 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.16.8, @babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5": +"@babel/code-frame@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/code-frame@npm:7.21.4" + dependencies: + "@babel/highlight": ^7.18.6 + checksum: e5390e6ec1ac58dcef01d4f18eaf1fd2f1325528661ff6d4a5de8979588b9f5a8e852a54a91b923846f7a5c681b217f0a45c2524eb9560553160cd963b7d592c + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5": version: 7.21.0 resolution: "@babel/compat-data@npm:7.21.0" checksum: dbf632c532f9c75ba0be7d1dc9f6cd3582501af52f10a6b90415d634ec5878735bd46064c91673b10317af94d4cc99c4da5bd9d955978cdccb7905fc33291e4d languageName: node linkType: hard -"@babel/core@npm:7.16.12, @babel/core@npm:^7.12.3": +"@babel/compat-data@npm:^7.18.8": + version: 7.21.4 + resolution: "@babel/compat-data@npm:7.21.4" + checksum: 5f8b98c66f2ffba9f3c3a82c0cf354c52a0ec5ad4797b370dc32bdcd6e136ac4febe5e93d76ce76e175632e2dbf6ce9f46319aa689fcfafa41b6e49834fa4b66 + languageName: node + linkType: hard + +"@babel/core@npm:7.18.10": + version: 7.18.10 + resolution: "@babel/core@npm:7.18.10" + dependencies: + "@ampproject/remapping": ^2.1.0 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.18.10 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-module-transforms": ^7.18.9 + "@babel/helpers": ^7.18.9 + "@babel/parser": ^7.18.10 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.18.10 + "@babel/types": ^7.18.10 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.1 + semver: ^6.3.0 + checksum: 3a3fcd878430a9e1cb165f755c89fff45acc4efe4dd3a2ba356e89af331cb1947886b9782d56902a49af19ba3c24f08cf638a632699b9c5a4d8305c57c6a150d + languageName: node + linkType: hard + +"@babel/core@npm:^7.12.3": version: 7.16.12 resolution: "@babel/core@npm:7.16.12" dependencies: @@ -581,7 +586,18 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:7.16.8, @babel/generator@npm:^7.16.8": +"@babel/generator@npm:7.18.12": + version: 7.18.12 + resolution: "@babel/generator@npm:7.18.12" + dependencies: + "@babel/types": ^7.18.10 + "@jridgewell/gen-mapping": ^0.3.2 + jsesc: ^2.5.1 + checksum: 07dd71d255144bb703a80ab0156c35d64172ce81ddfb70ff24e2be687b052080233840c9a28d92fa2c33f7ecb8a8b30aef03b807518afc53b74c7908bf8859b1 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/generator@npm:7.16.8" dependencies: @@ -592,6 +608,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.18.10, @babel/generator@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/generator@npm:7.21.4" + dependencies: + "@babel/types": ^7.21.4 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: 9ffbb526a53bb8469b5402f7b5feac93809b09b2a9f82fcbfcdc5916268a65dae746a1f2479e03ba4fb0776facd7c892191f63baa61ab69b2cfdb24f7b92424d + languageName: node + linkType: hard + "@babel/generator@npm:^7.21.0, @babel/generator@npm:^7.21.1": version: 7.21.1 resolution: "@babel/generator@npm:7.21.1" @@ -604,16 +632,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:7.16.7": - version: 7.16.7 - resolution: "@babel/helper-annotate-as-pure@npm:7.16.7" - dependencies: - "@babel/types": ^7.16.7 - checksum: d235be963fed5d48a8a4cfabc41c3f03fad6a947810dbcab9cebed7f819811457e10d99b4b2e942ad71baa7ee8e3cd3f5f38a4e4685639ddfddb7528d9a07179 - languageName: node - linkType: hard - -"@babel/helper-annotate-as-pure@npm:^7.18.6": +"@babel/helper-annotate-as-pure@npm:7.18.6, @babel/helper-annotate-as-pure@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" dependencies: @@ -677,7 +696,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.1, @babel/helper-define-polyfill-provider@npm:^0.3.2, @babel/helper-define-polyfill-provider@npm:^0.3.3": +"@babel/helper-define-polyfill-provider@npm:^0.3.2, @babel/helper-define-polyfill-provider@npm:^0.3.3": version: 0.3.3 resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" dependencies: @@ -737,7 +756,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.18.6": +"@babel/helper-module-imports@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-module-imports@npm:7.18.6" dependencies: @@ -746,7 +765,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0, @babel/helper-module-transforms@npm:^7.21.2": +"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.18.9, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0, @babel/helper-module-transforms@npm:^7.21.2": version: 7.21.2 resolution: "@babel/helper-module-transforms@npm:7.21.2" dependencies: @@ -771,14 +790,14 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.20.2 resolution: "@babel/helper-plugin-utils@npm:7.20.2" checksum: f6cae53b7fdb1bf3abd50fa61b10b4470985b400cc794d92635da1e7077bb19729f626adc0741b69403d9b6e411cddddb9c0157a709cc7c4eeb41e663be5d74b languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.16.8": +"@babel/helper-remap-async-to-generator@npm:^7.18.6, @babel/helper-remap-async-to-generator@npm:^7.18.9": version: 7.18.9 resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" dependencies: @@ -847,7 +866,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.18.6": +"@babel/helper-validator-option@npm:^7.18.6": version: 7.21.0 resolution: "@babel/helper-validator-option@npm:7.21.0" checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 @@ -866,7 +885,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.21.0": +"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.18.9, @babel/helpers@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helpers@npm:7.21.0" dependencies: @@ -897,7 +916,16 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.16.7": +"@babel/parser@npm:^7.18.10, @babel/parser@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/parser@npm:7.21.4" + bin: + parser: ./bin/babel-parser.js + checksum: de610ecd1bff331766d0c058023ca11a4f242bfafefc42caf926becccfb6756637d167c001987ca830dd4b34b93c629a4cef63f8c8c864a8564cdfde1989ac77 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" dependencies: @@ -908,7 +936,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.16.7": +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.18.9": version: 7.20.7 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.20.7" dependencies: @@ -921,20 +949,35 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:7.16.8, @babel/plugin-proposal-async-generator-functions@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.8" +"@babel/plugin-proposal-async-generator-functions@npm:7.18.10": + version: 7.18.10 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.18.10" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-remap-async-to-generator": ^7.16.8 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-remap-async-to-generator": ^7.18.9 "@babel/plugin-syntax-async-generators": ^7.8.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: abd2c2c67de262720d37c5509dafe2ce64d6cee2dc9a8e863bbba1796b77387214442f37618373c6a4521ca624bfc7dcdbeb1376300d16f2a474405ee0ca2e69 + checksum: 3a6c25085021053830f6c57780118d3337935ac3309eef7f09b11e413d189eed8119d50cbddeb4c8c02f42f8cc01e62a4667b869be6e158f40030bafb92a0629 languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.16.7": +"@babel/plugin-proposal-async-generator-functions@npm:^7.18.10": + version: 7.20.7 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 + languageName: node + linkType: hard + +"@babel/plugin-proposal-class-properties@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: @@ -946,7 +989,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-static-block@npm:^7.16.7": +"@babel/plugin-proposal-class-static-block@npm:^7.18.6": version: 7.21.0 resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" dependencies: @@ -959,7 +1002,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-dynamic-import@npm:^7.16.7": +"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" dependencies: @@ -971,7 +1014,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-export-namespace-from@npm:^7.16.7": +"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": version: 7.18.9 resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" dependencies: @@ -983,7 +1026,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-json-strings@npm:^7.16.7": +"@babel/plugin-proposal-json-strings@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" dependencies: @@ -995,7 +1038,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-logical-assignment-operators@npm:^7.16.7": +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.18.9": version: 7.20.7 resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" dependencies: @@ -1007,7 +1050,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.7": +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" dependencies: @@ -1019,7 +1062,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-numeric-separator@npm:^7.16.7": +"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" dependencies: @@ -1031,7 +1074,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.16.7": +"@babel/plugin-proposal-object-rest-spread@npm:^7.18.9": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: @@ -1046,7 +1089,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-catch-binding@npm:^7.16.7": +"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" dependencies: @@ -1058,7 +1101,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.16.7, @babel/plugin-proposal-optional-chaining@npm:^7.20.7": +"@babel/plugin-proposal-optional-chaining@npm:^7.18.9, @babel/plugin-proposal-optional-chaining@npm:^7.20.7": version: 7.21.0 resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" dependencies: @@ -1071,7 +1114,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.16.11": +"@babel/plugin-proposal-private-methods@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" dependencies: @@ -1083,7 +1126,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.16.7": +"@babel/plugin-proposal-private-property-in-object@npm:^7.18.6": version: 7.21.0 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" dependencies: @@ -1097,7 +1140,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-unicode-property-regex@npm:^7.16.7, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": +"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": version: 7.18.6 resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" dependencies: @@ -1164,6 +1207,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-assertions@npm:^7.18.6": + version: 7.20.0 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" + dependencies: + "@babel/helper-plugin-utils": ^7.19.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6a86220e0aae40164cd3ffaf80e7c076a1be02a8f3480455dddbae05fda8140f429290027604df7a11b3f3f124866e8a6d69dbfa1dda61ee7377b920ad144d5b + languageName: node + linkType: hard + "@babel/plugin-syntax-json-strings@npm:^7.8.3": version: 7.8.3 resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" @@ -1263,7 +1317,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.16.7": +"@babel/plugin-transform-arrow-functions@npm:^7.18.6": version: 7.20.7 resolution: "@babel/plugin-transform-arrow-functions@npm:7.20.7" dependencies: @@ -1274,20 +1328,33 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.16.8, @babel/plugin-transform-async-to-generator@npm:^7.16.8": - version: 7.16.8 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.8" +"@babel/plugin-transform-async-to-generator@npm:7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.18.6" dependencies: - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-remap-async-to-generator": ^7.16.8 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-remap-async-to-generator": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3a2e781800e3dea1f526324ed259d1f9064c5ea3c9909c0c22b445d4c648ad489c579f358ae20ada11f7725ba67e0ddeb1e0241efadc734771e87dabd4c6820a + checksum: c2cca47468cf1aeefdc7ec35d670e195c86cee4de28a1970648c46a88ce6bd1806ef0bab27251b9e7fb791bb28a64dcd543770efd899f28ee5f7854e64e873d3 languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.16.7": +"@babel/plugin-transform-async-to-generator@npm:^7.18.6": + version: 7.20.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" + dependencies: + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" dependencies: @@ -1298,7 +1365,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.16.7": +"@babel/plugin-transform-block-scoping@npm:^7.18.9": version: 7.21.0 resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" dependencies: @@ -1309,7 +1376,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.16.7": +"@babel/plugin-transform-classes@npm:^7.18.9": version: 7.21.0 resolution: "@babel/plugin-transform-classes@npm:7.21.0" dependencies: @@ -1328,7 +1395,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.16.7": +"@babel/plugin-transform-computed-properties@npm:^7.18.9": version: 7.20.7 resolution: "@babel/plugin-transform-computed-properties@npm:7.20.7" dependencies: @@ -1340,18 +1407,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.16.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-destructuring@npm:7.20.7" +"@babel/plugin-transform-destructuring@npm:^7.18.9": + version: 7.21.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" dependencies: "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bd8affdb142c77662037215e37128b2110a786c92a67e1f00b38223c438c1610bd84cbc0386e9cd3479245ea811c5ca6c9838f49be4729b592159a30ce79add2 + checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.16.7, @babel/plugin-transform-dotall-regex@npm:^7.4.4": +"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.4.4": version: 7.18.6 resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" dependencies: @@ -1363,7 +1430,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.16.7": +"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": version: 7.18.9 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" dependencies: @@ -1374,7 +1441,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.16.7": +"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" dependencies: @@ -1386,7 +1453,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.16.7": +"@babel/plugin-transform-for-of@npm:^7.18.8": version: 7.21.0 resolution: "@babel/plugin-transform-for-of@npm:7.21.0" dependencies: @@ -1397,7 +1464,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.16.7": +"@babel/plugin-transform-function-name@npm:^7.18.9": version: 7.18.9 resolution: "@babel/plugin-transform-function-name@npm:7.18.9" dependencies: @@ -1410,7 +1477,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.16.7": +"@babel/plugin-transform-literals@npm:^7.18.9": version: 7.18.9 resolution: "@babel/plugin-transform-literals@npm:7.18.9" dependencies: @@ -1421,7 +1488,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.16.7": +"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" dependencies: @@ -1432,7 +1499,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.16.7": +"@babel/plugin-transform-modules-amd@npm:^7.18.6": version: 7.20.11 resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" dependencies: @@ -1444,7 +1511,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.16.8": +"@babel/plugin-transform-modules-commonjs@npm:^7.18.6": version: 7.21.2 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" dependencies: @@ -1457,7 +1524,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.16.7": +"@babel/plugin-transform-modules-systemjs@npm:^7.18.9": version: 7.20.11 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" dependencies: @@ -1471,7 +1538,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.16.7": +"@babel/plugin-transform-modules-umd@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" dependencies: @@ -1483,7 +1550,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.16.8": +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.18.6": version: 7.20.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" dependencies: @@ -1495,7 +1562,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.16.7": +"@babel/plugin-transform-new-target@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-new-target@npm:7.18.6" dependencies: @@ -1506,7 +1573,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.16.7": +"@babel/plugin-transform-object-super@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-object-super@npm:7.18.6" dependencies: @@ -1518,7 +1585,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.16.7, @babel/plugin-transform-parameters@npm:^7.20.7": +"@babel/plugin-transform-parameters@npm:^7.18.8": + version: 7.21.3 + resolution: "@babel/plugin-transform-parameters@npm:7.21.3" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c92128d7b1fcf54e2cab186c196bbbf55a9a6de11a83328dc2602649c9dc6d16ef73712beecd776cd49bfdc624b5f56740f4a53568d3deb9505ec666bc869da3 + languageName: node + linkType: hard + +"@babel/plugin-transform-parameters@npm:^7.20.7": version: 7.20.7 resolution: "@babel/plugin-transform-parameters@npm:7.20.7" dependencies: @@ -1529,7 +1607,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.16.7": +"@babel/plugin-transform-property-literals@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" dependencies: @@ -1540,7 +1618,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.16.7": +"@babel/plugin-transform-regenerator@npm:^7.18.6": version: 7.20.5 resolution: "@babel/plugin-transform-regenerator@npm:7.20.5" dependencies: @@ -1552,7 +1630,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.16.7": +"@babel/plugin-transform-reserved-words@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" dependencies: @@ -1563,23 +1641,23 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:7.16.10": - version: 7.16.10 - resolution: "@babel/plugin-transform-runtime@npm:7.16.10" +"@babel/plugin-transform-runtime@npm:7.18.10": + version: 7.18.10 + resolution: "@babel/plugin-transform-runtime@npm:7.18.10" dependencies: - "@babel/helper-module-imports": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - babel-plugin-polyfill-corejs2: ^0.3.0 - babel-plugin-polyfill-corejs3: ^0.5.0 - babel-plugin-polyfill-regenerator: ^0.3.0 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.9 + babel-plugin-polyfill-corejs2: ^0.3.2 + babel-plugin-polyfill-corejs3: ^0.5.3 + babel-plugin-polyfill-regenerator: ^0.4.0 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 62ef5fad74d68f444ced382d77f9f123d250cb7758a2a89dc97e92faabd2cb7ff665759f09f99fe2e7ae01af10453e6cc20542f980772f64c768772996b9481b + checksum: 98c18680b4258b8bd3f04926b73c72ae77037d5ea5b50761ca35de15896bf0d04bedabde39a81be56dbd4859c96ffaa7103fbefb5d5b58a36e0a80381e4a146c languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.16.7": +"@babel/plugin-transform-shorthand-properties@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" dependencies: @@ -1590,7 +1668,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.16.7": +"@babel/plugin-transform-spread@npm:^7.18.9": version: 7.20.7 resolution: "@babel/plugin-transform-spread@npm:7.20.7" dependencies: @@ -1602,7 +1680,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.16.7": +"@babel/plugin-transform-sticky-regex@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" dependencies: @@ -1613,7 +1691,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.16.7": +"@babel/plugin-transform-template-literals@npm:^7.18.9": version: 7.18.9 resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" dependencies: @@ -1624,7 +1702,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.16.7": +"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": version: 7.18.9 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" dependencies: @@ -1635,7 +1713,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.16.7": +"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": version: 7.18.10 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.18.10" dependencies: @@ -1646,7 +1724,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.16.7": +"@babel/plugin-transform-unicode-regex@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" dependencies: @@ -1658,36 +1736,37 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.16.11": - version: 7.16.11 - resolution: "@babel/preset-env@npm:7.16.11" +"@babel/preset-env@npm:7.18.10": + version: 7.18.10 + resolution: "@babel/preset-env@npm:7.18.10" dependencies: - "@babel/compat-data": ^7.16.8 - "@babel/helper-compilation-targets": ^7.16.7 - "@babel/helper-plugin-utils": ^7.16.7 - "@babel/helper-validator-option": ^7.16.7 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.16.7 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.16.7 - "@babel/plugin-proposal-async-generator-functions": ^7.16.8 - "@babel/plugin-proposal-class-properties": ^7.16.7 - "@babel/plugin-proposal-class-static-block": ^7.16.7 - "@babel/plugin-proposal-dynamic-import": ^7.16.7 - "@babel/plugin-proposal-export-namespace-from": ^7.16.7 - "@babel/plugin-proposal-json-strings": ^7.16.7 - "@babel/plugin-proposal-logical-assignment-operators": ^7.16.7 - "@babel/plugin-proposal-nullish-coalescing-operator": ^7.16.7 - "@babel/plugin-proposal-numeric-separator": ^7.16.7 - "@babel/plugin-proposal-object-rest-spread": ^7.16.7 - "@babel/plugin-proposal-optional-catch-binding": ^7.16.7 - "@babel/plugin-proposal-optional-chaining": ^7.16.7 - "@babel/plugin-proposal-private-methods": ^7.16.11 - "@babel/plugin-proposal-private-property-in-object": ^7.16.7 - "@babel/plugin-proposal-unicode-property-regex": ^7.16.7 + "@babel/compat-data": ^7.18.8 + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.18.9 + "@babel/plugin-proposal-async-generator-functions": ^7.18.10 + "@babel/plugin-proposal-class-properties": ^7.18.6 + "@babel/plugin-proposal-class-static-block": ^7.18.6 + "@babel/plugin-proposal-dynamic-import": ^7.18.6 + "@babel/plugin-proposal-export-namespace-from": ^7.18.9 + "@babel/plugin-proposal-json-strings": ^7.18.6 + "@babel/plugin-proposal-logical-assignment-operators": ^7.18.9 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 + "@babel/plugin-proposal-numeric-separator": ^7.18.6 + "@babel/plugin-proposal-object-rest-spread": ^7.18.9 + "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 + "@babel/plugin-proposal-optional-chaining": ^7.18.9 + "@babel/plugin-proposal-private-methods": ^7.18.6 + "@babel/plugin-proposal-private-property-in-object": ^7.18.6 + "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 "@babel/plugin-syntax-async-generators": ^7.8.4 "@babel/plugin-syntax-class-properties": ^7.12.13 "@babel/plugin-syntax-class-static-block": ^7.14.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + "@babel/plugin-syntax-import-assertions": ^7.18.6 "@babel/plugin-syntax-json-strings": ^7.8.3 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 @@ -1697,48 +1776,48 @@ __metadata: "@babel/plugin-syntax-optional-chaining": ^7.8.3 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 "@babel/plugin-syntax-top-level-await": ^7.14.5 - "@babel/plugin-transform-arrow-functions": ^7.16.7 - "@babel/plugin-transform-async-to-generator": ^7.16.8 - "@babel/plugin-transform-block-scoped-functions": ^7.16.7 - "@babel/plugin-transform-block-scoping": ^7.16.7 - "@babel/plugin-transform-classes": ^7.16.7 - "@babel/plugin-transform-computed-properties": ^7.16.7 - "@babel/plugin-transform-destructuring": ^7.16.7 - "@babel/plugin-transform-dotall-regex": ^7.16.7 - "@babel/plugin-transform-duplicate-keys": ^7.16.7 - "@babel/plugin-transform-exponentiation-operator": ^7.16.7 - "@babel/plugin-transform-for-of": ^7.16.7 - "@babel/plugin-transform-function-name": ^7.16.7 - "@babel/plugin-transform-literals": ^7.16.7 - "@babel/plugin-transform-member-expression-literals": ^7.16.7 - "@babel/plugin-transform-modules-amd": ^7.16.7 - "@babel/plugin-transform-modules-commonjs": ^7.16.8 - "@babel/plugin-transform-modules-systemjs": ^7.16.7 - "@babel/plugin-transform-modules-umd": ^7.16.7 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.16.8 - "@babel/plugin-transform-new-target": ^7.16.7 - "@babel/plugin-transform-object-super": ^7.16.7 - "@babel/plugin-transform-parameters": ^7.16.7 - "@babel/plugin-transform-property-literals": ^7.16.7 - "@babel/plugin-transform-regenerator": ^7.16.7 - "@babel/plugin-transform-reserved-words": ^7.16.7 - "@babel/plugin-transform-shorthand-properties": ^7.16.7 - "@babel/plugin-transform-spread": ^7.16.7 - "@babel/plugin-transform-sticky-regex": ^7.16.7 - "@babel/plugin-transform-template-literals": ^7.16.7 - "@babel/plugin-transform-typeof-symbol": ^7.16.7 - "@babel/plugin-transform-unicode-escapes": ^7.16.7 - "@babel/plugin-transform-unicode-regex": ^7.16.7 + "@babel/plugin-transform-arrow-functions": ^7.18.6 + "@babel/plugin-transform-async-to-generator": ^7.18.6 + "@babel/plugin-transform-block-scoped-functions": ^7.18.6 + "@babel/plugin-transform-block-scoping": ^7.18.9 + "@babel/plugin-transform-classes": ^7.18.9 + "@babel/plugin-transform-computed-properties": ^7.18.9 + "@babel/plugin-transform-destructuring": ^7.18.9 + "@babel/plugin-transform-dotall-regex": ^7.18.6 + "@babel/plugin-transform-duplicate-keys": ^7.18.9 + "@babel/plugin-transform-exponentiation-operator": ^7.18.6 + "@babel/plugin-transform-for-of": ^7.18.8 + "@babel/plugin-transform-function-name": ^7.18.9 + "@babel/plugin-transform-literals": ^7.18.9 + "@babel/plugin-transform-member-expression-literals": ^7.18.6 + "@babel/plugin-transform-modules-amd": ^7.18.6 + "@babel/plugin-transform-modules-commonjs": ^7.18.6 + "@babel/plugin-transform-modules-systemjs": ^7.18.9 + "@babel/plugin-transform-modules-umd": ^7.18.6 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.18.6 + "@babel/plugin-transform-new-target": ^7.18.6 + "@babel/plugin-transform-object-super": ^7.18.6 + "@babel/plugin-transform-parameters": ^7.18.8 + "@babel/plugin-transform-property-literals": ^7.18.6 + "@babel/plugin-transform-regenerator": ^7.18.6 + "@babel/plugin-transform-reserved-words": ^7.18.6 + "@babel/plugin-transform-shorthand-properties": ^7.18.6 + "@babel/plugin-transform-spread": ^7.18.9 + "@babel/plugin-transform-sticky-regex": ^7.18.6 + "@babel/plugin-transform-template-literals": ^7.18.9 + "@babel/plugin-transform-typeof-symbol": ^7.18.9 + "@babel/plugin-transform-unicode-escapes": ^7.18.10 + "@babel/plugin-transform-unicode-regex": ^7.18.6 "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.16.8 - babel-plugin-polyfill-corejs2: ^0.3.0 - babel-plugin-polyfill-corejs3: ^0.5.0 - babel-plugin-polyfill-regenerator: ^0.3.0 - core-js-compat: ^3.20.2 + "@babel/types": ^7.18.10 + babel-plugin-polyfill-corejs2: ^0.3.2 + babel-plugin-polyfill-corejs3: ^0.5.3 + babel-plugin-polyfill-regenerator: ^0.4.0 + core-js-compat: ^3.22.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c8029c272073df787309d983ae458dd094b57f87152b8ccad95c7c8b1e82b042c1077e169538aae5f98b7659de0632d10708d9c85acf21a5e9406d7dd3656d8c + checksum: 36eeb7157021091c8047703833b7a28e4963865d16968a5b9dbffe1eb05e44307a8d29ad45d81fd23817f68290b52921c42f513a93996c7083d23d5e2cea0c6b languageName: node linkType: hard @@ -1774,7 +1853,16 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.16.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:7.18.9": + version: 7.18.9 + resolution: "@babel/runtime@npm:7.18.9" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: 36dd736baba7164e82b3cc9d43e081f0cb2d05ff867ad39cac515d99546cee75b7f782018b02a3dcf5f2ef3d27f319faa68965fdfec49d4912c60c6002353a2e + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.8.4": version: 7.16.7 resolution: "@babel/runtime@npm:7.16.7" dependencies: @@ -1783,7 +1871,18 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:7.16.7, @babel/template@npm:^7.16.7": +"@babel/template@npm:7.18.10": + version: 7.18.10 + resolution: "@babel/template@npm:7.18.10" + dependencies: + "@babel/code-frame": ^7.18.6 + "@babel/parser": ^7.18.10 + "@babel/types": ^7.18.10 + checksum: 93a6aa094af5f355a72bd55f67fa1828a046c70e46f01b1606e6118fa1802b6df535ca06be83cc5a5e834022be95c7b714f0a268b5f20af984465a71e28f1473 + languageName: node + linkType: hard + +"@babel/template@npm:^7.16.7": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" dependencies: @@ -1823,6 +1922,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.18.10": + version: 7.21.4 + resolution: "@babel/traverse@npm:7.21.4" + dependencies: + "@babel/code-frame": ^7.21.4 + "@babel/generator": ^7.21.4 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/parser": ^7.21.4 + "@babel/types": ^7.21.4 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: f22f067c2d9b6497abf3d4e53ea71f3aa82a21f2ed434dd69b8c5767f11f2a4c24c8d2f517d2312c9e5248e5c69395fdca1c95a2b3286122c75f5783ddb6f53c + languageName: node + linkType: hard + "@babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.21.2 resolution: "@babel/types@npm:7.21.2" @@ -1834,6 +1951,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.18.10, @babel/types@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/types@npm:7.21.4" + dependencies: + "@babel/helper-string-parser": ^7.19.4 + "@babel/helper-validator-identifier": ^7.19.1 + to-fast-properties: ^2.0.0 + checksum: 587bc55a91ce003b0f8aa10d70070f8006560d7dc0360dc0406d306a2cb2a10154e2f9080b9c37abec76907a90b330a536406cb75e6bdc905484f37b75c73219 + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -1855,7 +1983,111 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-progressive-custom-properties@npm:^1.1.0": +"@csstools/postcss-cascade-layers@npm:^1.0.5": + version: 1.1.1 + resolution: "@csstools/postcss-cascade-layers@npm:1.1.1" + dependencies: + "@csstools/selector-specificity": ^2.0.2 + postcss-selector-parser: ^6.0.10 + peerDependencies: + postcss: ^8.2 + checksum: 8ecd6a929e8ddee3ad0834ab5017f50a569817ba8490d152b11c705c13cf3d9701f74792f375cbd72d8f33a4eeaabb3f984f1514adf8c5a530eb91be70c14cf4 + languageName: node + linkType: hard + +"@csstools/postcss-color-function@npm:^1.1.1": + version: 1.1.1 + resolution: "@csstools/postcss-color-function@npm:1.1.1" + dependencies: + "@csstools/postcss-progressive-custom-properties": ^1.1.0 + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: 087595985ebcc2fc42013d6305185d4cdc842d87fb261185db905dc31eaa24fc23a7cc068fa3da814b3c8b98164107ddaf1b4ab24f4ff5b2a7b5fbcd4c6ceec9 + languageName: node + linkType: hard + +"@csstools/postcss-font-format-keywords@npm:^1.0.1": + version: 1.0.1 + resolution: "@csstools/postcss-font-format-keywords@npm:1.0.1" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: ed8d9eab9793f0184e000709bcb155d4eb96c49a312e3ea9e549e006b74fd4aafac63cb9f9f01bec5b717a833539ff085c3f1ef7d273b97d587769ef637d50c1 + languageName: node + linkType: hard + +"@csstools/postcss-hwb-function@npm:^1.0.2": + version: 1.0.2 + resolution: "@csstools/postcss-hwb-function@npm:1.0.2" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: 352ead754a692f7ed33a712c491012cab5c2f2946136a669a354237cfe8e6faca90c7389ee793cb329b9b0ddec984faa06d47e2f875933aaca417afff74ce6aa + languageName: node + linkType: hard + +"@csstools/postcss-ic-unit@npm:^1.0.1": + version: 1.0.1 + resolution: "@csstools/postcss-ic-unit@npm:1.0.1" + dependencies: + "@csstools/postcss-progressive-custom-properties": ^1.1.0 + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: 09c414c9b7762b5fbe837ff451d7a11e4890f1ed3c92edc3573f02f3d89747f6ac3f2270799b68a332bd7f5de05bb0dfffddb6323fc4020c2bea33ff58314533 + languageName: node + linkType: hard + +"@csstools/postcss-is-pseudo-class@npm:^2.0.7": + version: 2.0.7 + resolution: "@csstools/postcss-is-pseudo-class@npm:2.0.7" + dependencies: + "@csstools/selector-specificity": ^2.0.0 + postcss-selector-parser: ^6.0.10 + peerDependencies: + postcss: ^8.2 + checksum: a4494bb8e9a34826944ba6872c91c1e88268caab6d06968897f1a0cc75ca5cfc4989435961fc668a9c6842a6d17f4cda0055fa256d23e598b8bbc6f022956125 + languageName: node + linkType: hard + +"@csstools/postcss-nested-calc@npm:^1.0.0": + version: 1.0.0 + resolution: "@csstools/postcss-nested-calc@npm:1.0.0" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: 53bb783dd61621c11c1e6e352f079577e2eb908de67947ceef31a178e070c06c223baae87acd5c3bd51c664515d2adc16166a129159168626111aff548583790 + languageName: node + linkType: hard + +"@csstools/postcss-normalize-display-values@npm:^1.0.1": + version: 1.0.1 + resolution: "@csstools/postcss-normalize-display-values@npm:1.0.1" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: 75901daec3869ba15e0adfd50d8e2e754ec06d55ac44fbd540748476388d223d53710fb3a3cbfe6695a2bab015a489fb47d9e3914ff211736923f8deb818dc0b + languageName: node + linkType: hard + +"@csstools/postcss-oklab-function@npm:^1.1.1": + version: 1.1.1 + resolution: "@csstools/postcss-oklab-function@npm:1.1.1" + dependencies: + "@csstools/postcss-progressive-custom-properties": ^1.1.0 + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: d66b789060b37ed810450d9a7d8319a0ae14e913c091f3e0ee482b3471538762e801d5eae3d62fda2f1eb1e88c76786d2c2b06c1172166eba1cca5e2a0dc95f2 + languageName: node + linkType: hard + +"@csstools/postcss-progressive-custom-properties@npm:^1.1.0, @csstools/postcss-progressive-custom-properties@npm:^1.3.0": version: 1.3.0 resolution: "@csstools/postcss-progressive-custom-properties@npm:1.3.0" dependencies: @@ -1866,6 +2098,48 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-stepped-value-functions@npm:^1.0.1": + version: 1.0.1 + resolution: "@csstools/postcss-stepped-value-functions@npm:1.0.1" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: 2fc88713a0d49d142010652be8139b00719e407df1173e46047284f1befd0647e1fff67f259f9f55ac3b46bba6462b21f0aa192bd10a2989c51a8ce0d25fc495 + languageName: node + linkType: hard + +"@csstools/postcss-text-decoration-shorthand@npm:^1.0.0": + version: 1.0.0 + resolution: "@csstools/postcss-text-decoration-shorthand@npm:1.0.0" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: d27aaf97872c42bec9f6fde4d8bf924e89f7886f0aca8e4fc5aaf2f9083b09bb43dbbfa29124fa36fcdeb2d4d3e0459a095acf62188260cd1577e9811bb1276e + languageName: node + linkType: hard + +"@csstools/postcss-trigonometric-functions@npm:^1.0.2": + version: 1.0.2 + resolution: "@csstools/postcss-trigonometric-functions@npm:1.0.2" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.2 + checksum: f7f5b5f2492606b79a56f09e814ae8f10a2ae9e9c5fb8019f0e347a4a6c07953b2cc663fd4fa43a60e6994dfd958958f39df8ec760e2a646cfe71fe2bb119382 + languageName: node + linkType: hard + +"@csstools/postcss-unset-value@npm:^1.0.2": + version: 1.0.2 + resolution: "@csstools/postcss-unset-value@npm:1.0.2" + peerDependencies: + postcss: ^8.2 + checksum: 3facdae154d6516ffd964f7582696f406465f11cf8dead503e0afdfecc99ebc25638ab2830affce4516131aa2db004458a235e439f575b04e9ef72ad82f55835 + languageName: node + linkType: hard + "@csstools/selector-specificity@npm:^2.0.0, @csstools/selector-specificity@npm:^2.0.2": version: 2.1.1 resolution: "@csstools/selector-specificity@npm:2.1.1" @@ -1876,10 +2150,17 @@ __metadata: languageName: node linkType: hard -"@discoveryjs/json-ext@npm:0.5.6": - version: 0.5.6 - resolution: "@discoveryjs/json-ext@npm:0.5.6" - checksum: e97df618511fb202dffa2eb0d23e17dfb02943a70e5bc38f6b9603ad1cb1d6b525aa2b07ff9fb00b041abe425b341146ddd9e487f1e35ddadc8c6b8c56358ae0 +"@discoveryjs/json-ext@npm:0.5.7": + version: 0.5.7 + resolution: "@discoveryjs/json-ext@npm:0.5.7" + checksum: 2176d301cc258ea5c2324402997cf8134ebb212469c0d397591636cea8d3c02f2b3cf9fd58dcb748c7a0dade77ebdc1b10284fa63e608c033a1db52fddc69918 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.15.5": + version: 0.15.5 + resolution: "@esbuild/linux-loong64@npm:0.15.5" + conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -1907,7 +2188,7 @@ __metadata: languageName: node linkType: hard -"@gar/promisify@npm:^1.0.1, @gar/promisify@npm:^1.1.3": +"@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 @@ -2021,14 +2302,21 @@ __metadata: languageName: node linkType: hard -"@ngtools/webpack@npm:13.3.10": - version: 13.3.10 - resolution: "@ngtools/webpack@npm:13.3.10" +"@leichtgewicht/ip-codec@npm:^2.0.1": + version: 2.0.4 + resolution: "@leichtgewicht/ip-codec@npm:2.0.4" + checksum: 468de1f04d33de6d300892683d7c8aecbf96d1e2c5fe084f95f816e50a054d45b7c1ebfb141a1447d844b86a948733f6eebd92234da8581c84a1ad4de2946a2d + languageName: node + linkType: hard + +"@ngtools/webpack@npm:14.2.11": + version: 14.2.11 + resolution: "@ngtools/webpack@npm:14.2.11" peerDependencies: - "@angular/compiler-cli": ^13.0.0 - typescript: ">=4.4.3 <4.7" - webpack: ^5.30.0 - checksum: 4bb5bcf2aeff5e291f0b39eb0ce5b3338569610c240d492270c00406b228dbebb1e4266ca4b41ad0e6a443b62afcfcc504c75bbe0a7fa450eaac84d57efa8b39 + "@angular/compiler-cli": ^14.0.0 + typescript: ">=4.6.2 <4.9" + webpack: ^5.54.0 + checksum: 84c5b078a1e0327907acaf49075913347d6686dd07388eeaf63782e79615a2517758de60371a4c2410e26e1eb9ceb543c07f4c5f1a132bb13e25404e10e1d757 languageName: node linkType: hard @@ -2059,16 +2347,6 @@ __metadata: languageName: node linkType: hard -"@npmcli/fs@npm:^1.0.0": - version: 1.1.1 - resolution: "@npmcli/fs@npm:1.1.1" - dependencies: - "@gar/promisify": ^1.0.1 - semver: ^7.3.5 - checksum: f5ad92f157ed222e4e31c352333d0901df02c7c04311e42a81d8eb555d4ec4276ea9c635011757de20cc476755af33e91622838de573b17e52e2e7703f0a9965 - languageName: node - linkType: hard - "@npmcli/fs@npm:^2.1.0": version: 2.1.2 resolution: "@npmcli/fs@npm:2.1.2" @@ -2079,23 +2357,24 @@ __metadata: languageName: node linkType: hard -"@npmcli/git@npm:^2.1.0": - version: 2.1.0 - resolution: "@npmcli/git@npm:2.1.0" +"@npmcli/git@npm:^3.0.0": + version: 3.0.2 + resolution: "@npmcli/git@npm:3.0.2" dependencies: - "@npmcli/promise-spawn": ^1.3.2 - lru-cache: ^6.0.0 + "@npmcli/promise-spawn": ^3.0.0 + lru-cache: ^7.4.4 mkdirp: ^1.0.4 - npm-pick-manifest: ^6.1.1 + npm-pick-manifest: ^7.0.0 + proc-log: ^2.0.0 promise-inflight: ^1.0.1 promise-retry: ^2.0.1 semver: ^7.3.5 which: ^2.0.2 - checksum: 1f89752df7b836f378b8828423c6ae344fe59399915b9460acded19686e2d0626246251a3cd4cc411ed21c1be6fe7f0c2195c17f392e88748581262ee806dc33 + checksum: bdfd1229bb1113ad4883ef89b74b5dc442a2c96225d830491dd0dec4fa83d083b93cde92b6978d4956a8365521e61bc8dc1891fb905c7c693d5d6aa178f2ab44 languageName: node linkType: hard -"@npmcli/installed-package-contents@npm:^1.0.6": +"@npmcli/installed-package-contents@npm:^1.0.7": version: 1.0.7 resolution: "@npmcli/installed-package-contents@npm:1.0.7" dependencies: @@ -2107,16 +2386,6 @@ __metadata: languageName: node linkType: hard -"@npmcli/move-file@npm:^1.0.1": - version: 1.1.2 - resolution: "@npmcli/move-file@npm:1.1.2" - dependencies: - mkdirp: ^1.0.4 - rimraf: ^3.0.2 - checksum: c96381d4a37448ea280951e46233f7e541058cf57a57d4094dd4bdcaae43fa5872b5f2eb6bfb004591a68e29c5877abe3cdc210cb3588cbf20ab2877f31a7de7 - languageName: node - linkType: hard - "@npmcli/move-file@npm:^2.0.0": version: 2.0.1 resolution: "@npmcli/move-file@npm:2.0.1" @@ -2127,31 +2396,32 @@ __metadata: languageName: node linkType: hard -"@npmcli/node-gyp@npm:^1.0.2": - version: 1.0.3 - resolution: "@npmcli/node-gyp@npm:1.0.3" - checksum: 496d5eef2e90e34bb07e96adbcbbce3dba5370ae87e8c46ff5b28570848f35470c8e008b8f69e50863632783e0a9190e6f55b2e4b049c537142821153942d26a +"@npmcli/node-gyp@npm:^2.0.0": + version: 2.0.0 + resolution: "@npmcli/node-gyp@npm:2.0.0" + checksum: b6bbf0015000f9b64d31aefdc30f244b0348c57adb64017667e0304e96c38644d83da46a4581252652f5d606268df49118f9c9993b41d8020f62b7b15dd2c8d8 languageName: node linkType: hard -"@npmcli/promise-spawn@npm:^1.2.0, @npmcli/promise-spawn@npm:^1.3.2": - version: 1.3.2 - resolution: "@npmcli/promise-spawn@npm:1.3.2" +"@npmcli/promise-spawn@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/promise-spawn@npm:3.0.0" dependencies: infer-owner: ^1.0.4 - checksum: 543b7c1e26230499b4100b10d45efa35b1077e8f25595050f34930ca3310abe9524f7387279fe4330139e0f28a0207595245503439276fd4b686cca2b6503080 + checksum: 3454465a2731cea5875ba51f80873e2205e5bd878c31517286b0ede4ea931c7bf3de895382287e906d03710fff6f9e44186bd0eee068ce578901c5d3b58e7692 languageName: node linkType: hard -"@npmcli/run-script@npm:^2.0.0": - version: 2.0.0 - resolution: "@npmcli/run-script@npm:2.0.0" +"@npmcli/run-script@npm:^4.1.0": + version: 4.2.1 + resolution: "@npmcli/run-script@npm:4.2.1" dependencies: - "@npmcli/node-gyp": ^1.0.2 - "@npmcli/promise-spawn": ^1.3.2 - node-gyp: ^8.2.0 - read-package-json-fast: ^2.0.1 - checksum: c016ea9411e434d84e9bb9c30814c2868eee3ff32625f3e1af4671c3abfe0768739ffb2dba5520da926ae44315fc5f507b744f0626a80bc9461f2f19760e5fa0 + "@npmcli/node-gyp": ^2.0.0 + "@npmcli/promise-spawn": ^3.0.0 + node-gyp: ^9.0.0 + read-package-json-fast: ^2.0.3 + which: ^2.0.2 + checksum: 7b8d6676353f157e68b26baf848e01e5d887bcf90ce81a52f23fc9a5d93e6ffb60057532d664cfd7aeeb76d464d0c8b0d314ee6cccb56943acb3b6c570b756c8 languageName: node linkType: hard @@ -2284,14 +2554,14 @@ __metadata: languageName: node linkType: hard -"@schematics/angular@npm:13.2.6": - version: 13.2.6 - resolution: "@schematics/angular@npm:13.2.6" +"@schematics/angular@npm:14.2.11": + version: 14.2.11 + resolution: "@schematics/angular@npm:14.2.11" dependencies: - "@angular-devkit/core": 13.2.6 - "@angular-devkit/schematics": 13.2.6 - jsonc-parser: 3.0.0 - checksum: f4f74d1d3ceee65374f49419ef5240a8aa8fb8df2eadf89f2f6af3bfc38b7d2b688bedb65e69200cfafceffbce790dfbb0855f1e0d408b330284c6e88da5dbc7 + "@angular-devkit/core": 14.2.11 + "@angular-devkit/schematics": 14.2.11 + jsonc-parser: 3.1.0 + checksum: 4a03962e06d089684bde47d6025f91bd8858a39a93145d69c6b120e6965dfbaca46f94425a22b013a636a3b98f708ef2ccf6619a71d0c7fc9341e84709eb6e1a languageName: node linkType: hard @@ -2302,13 +2572,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:1": - version: 1.1.2 - resolution: "@tootallnate/once@npm:1.1.2" - checksum: e1fb1bbbc12089a0cb9433dc290f97bddd062deadb6178ce9bcb93bb7c1aecde5e60184bc7065aec42fe1663622a213493c48bbd4972d931aae48315f18e1be9 - languageName: node - linkType: hard - "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -2415,7 +2678,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*": +"@types/express@npm:*, @types/express@npm:^4.17.13": version: 4.17.17 resolution: "@types/express@npm:4.17.17" dependencies: @@ -2545,7 +2808,7 @@ __metadata: languageName: node linkType: hard -"@types/serve-static@npm:*": +"@types/serve-static@npm:*, @types/serve-static@npm:^1.13.10": version: 1.15.1 resolution: "@types/serve-static@npm:1.15.1" dependencies: @@ -2564,7 +2827,7 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.2.2": +"@types/ws@npm:^8.5.1": version: 8.5.4 resolution: "@types/ws@npm:8.5.4" dependencies: @@ -2894,14 +3157,14 @@ __metadata: languageName: node linkType: hard -"abab@npm:^2.0.5": +"abab@npm:^2.0.6": version: 2.0.6 resolution: "abab@npm:2.0.6" checksum: 6ffc1af4ff315066c62600123990d87551ceb0aafa01e6539da77b0f5987ac7019466780bf480f1787576d4385e3690c81ccc37cfda12819bf510b8ab47e5a3e languageName: node linkType: hard -"abbrev@npm:1, abbrev@npm:^1.0.0": +"abbrev@npm:^1.0.0": version: 1.1.1 resolution: "abbrev@npm:1.1.1" checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 @@ -2936,7 +3199,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.8.0": +"acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0": version: 8.8.2 resolution: "acorn@npm:8.8.2" bin: @@ -2980,7 +3243,7 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": +"agentkeepalive@npm:^4.2.1": version: 4.3.0 resolution: "agentkeepalive@npm:4.3.0" dependencies: @@ -3035,15 +3298,15 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.9.0, ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": - version: 8.9.0 - resolution: "ajv@npm:8.9.0" +"ajv@npm:8.11.0": + version: 8.11.0 + resolution: "ajv@npm:8.11.0" dependencies: fast-deep-equal: ^3.1.1 json-schema-traverse: ^1.0.0 require-from-string: ^2.0.2 uri-js: ^4.2.2 - checksum: 756c048bfa917b43bb84c8a0a53e6a489123203bc4bdec8cbeb8ec2d715674f5e61d49560a1a6ec83268af4f33bed324f5cb6d9c76d96849fd58ed7089b8e7f3 + checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef languageName: node linkType: hard @@ -3059,6 +3322,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.8.0": + version: 8.9.0 + resolution: "ajv@npm:8.9.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 756c048bfa917b43bb84c8a0a53e6a489123203bc4bdec8cbeb8ec2d715674f5e61d49560a1a6ec83268af4f33bed324f5cb6d9c76d96849fd58ed7089b8e7f3 + languageName: node + linkType: hard + "angularx-qrcode@npm:13.0.x": version: 13.0.15 resolution: "angularx-qrcode@npm:13.0.15" @@ -3071,7 +3346,14 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": +"ansi-colors@npm:4.1.3": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e + languageName: node + linkType: hard + +"ansi-colors@npm:^4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 @@ -3237,7 +3519,7 @@ __metadata: languageName: node linkType: hard -"array-flatten@npm:^2.1.0": +"array-flatten@npm:^2.1.2": version: 2.1.2 resolution: "array-flatten@npm:2.1.2" checksum: e8988aac1fbfcdaae343d08c9a06a6fddd2c6141721eeeea45c3cf523bf4431d29a46602929455ed548c7a3e0769928cdc630405427297e7081bd118fdec9262 @@ -3255,15 +3537,8 @@ __metadata: "array-union@npm:^2.1.0": version: 2.1.0 - resolution: "array-union@npm:2.1.0" - checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d - languageName: node - linkType: hard - -"array-union@npm:^3.0.1": - version: 3.0.1 - resolution: "array-union@npm:3.0.1" - checksum: 47b29f88258e8f37ffb93ddaa327d4308edd950b52943c172b73558afdd3fa74cfd68816ba5aa4b894242cf281fa3c6d0362ae057e4a18bddbaedbe46ebe7112 + resolution: "array-union@npm:2.1.0" + checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d languageName: node linkType: hard @@ -3304,15 +3579,6 @@ __metadata: languageName: node linkType: hard -"async@npm:^2.6.4": - version: 2.6.4 - resolution: "async@npm:2.6.4" - dependencies: - lodash: ^4.17.14 - checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 - languageName: node - linkType: hard - "async@npm:^3.2.3": version: 3.2.4 resolution: "async@npm:3.2.4" @@ -3334,21 +3600,12 @@ __metadata: languageName: node linkType: hard -"atob@npm:^2.1.2": - version: 2.1.2 - resolution: "atob@npm:2.1.2" - bin: - atob: bin/atob.js - checksum: dfeeeb70090c5ebea7be4b9f787f866686c645d9f39a0d184c817252d0cf08455ed25267d79c03254d3be1f03ac399992a792edcd5ffb9c91e097ab5ef42833a - languageName: node - linkType: hard - -"autoprefixer@npm:^10.4.2": - version: 10.4.13 - resolution: "autoprefixer@npm:10.4.13" +"autoprefixer@npm:^10.4.8": + version: 10.4.14 + resolution: "autoprefixer@npm:10.4.14" dependencies: - browserslist: ^4.21.4 - caniuse-lite: ^1.0.30001426 + browserslist: ^4.21.5 + caniuse-lite: ^1.0.30001464 fraction.js: ^4.2.0 normalize-range: ^0.1.2 picocolors: ^1.0.0 @@ -3357,7 +3614,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: dcb1cb7ae96a3363d65d82e52f9a0a7d8c982256f6fd032d7e1ec311f099c23acfebfd517ff8e96bf93f716a66c4ea2b80c60aa19efd2f474ce434bd75ef7b79 + checksum: e9f18e664a4e4a54a8f4ec5f6b49ed228ec45afaa76efcae361c93721795dc5ab644f36d2fdfc0dea446b02a8067b9372f91542ea431994399e972781ed46d95 languageName: node linkType: hard @@ -3421,7 +3678,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.0": +"babel-plugin-polyfill-corejs2@npm:^0.3.2": version: 0.3.3 resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" dependencies: @@ -3434,7 +3691,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.5.0": +"babel-plugin-polyfill-corejs3@npm:^0.5.3": version: 0.5.3 resolution: "babel-plugin-polyfill-corejs3@npm:0.5.3" dependencies: @@ -3446,14 +3703,14 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.3.0": - version: 0.3.1 - resolution: "babel-plugin-polyfill-regenerator@npm:0.3.1" +"babel-plugin-polyfill-regenerator@npm:^0.4.0": + version: 0.4.1 + resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.1 + "@babel/helper-define-polyfill-provider": ^0.3.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f1473df7b700d6795ca41301b1e65a0aff15ce6c1463fc0ce2cf0c821114b0330920f59d4cebf52976363ee817ba29ad2758544a4661a724b08191080b9fe1da + checksum: ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c languageName: node linkType: hard @@ -3577,17 +3834,15 @@ __metadata: languageName: node linkType: hard -"bonjour@npm:^3.5.0": - version: 3.5.0 - resolution: "bonjour@npm:3.5.0" +"bonjour-service@npm:^1.0.11": + version: 1.1.1 + resolution: "bonjour-service@npm:1.1.1" dependencies: - array-flatten: ^2.1.0 - deep-equal: ^1.0.1 + array-flatten: ^2.1.2 dns-equal: ^1.0.0 - dns-txt: ^2.0.2 - multicast-dns: ^6.0.1 - multicast-dns-service-types: ^1.1.0 - checksum: 2cfbe9fa861f4507b5ff3853eeae3ef03a231ede2b7363efedd80880ea3c0576f64416f98056c96e429ed68ff38dc4a70c0583d1eb4dab72e491ca44a0f03444 + fast-deep-equal: ^3.1.3 + multicast-dns: ^7.2.5 + checksum: 832d0cf78b91368fac8bb11fd7a714e46f4c4fb1bb14d7283bce614a6fb3aae2f3fe209aba5b4fa051811c1cab6921d073a83db8432fb23292f27dd4161fb0f1 languageName: node linkType: hard @@ -3626,7 +3881,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.19.1, browserslist@npm:^4.21.3, browserslist@npm:^4.21.4, browserslist@npm:^4.21.5, browserslist@npm:^4.9.1": +"browserslist@npm:^4.14.5, browserslist@npm:^4.21.3, browserslist@npm:^4.21.5, browserslist@npm:^4.9.1": version: 4.21.5 resolution: "browserslist@npm:4.21.5" dependencies: @@ -3656,13 +3911,6 @@ __metadata: languageName: node linkType: hard -"buffer-indexof@npm:^1.0.0": - version: 1.1.1 - resolution: "buffer-indexof@npm:1.1.1" - checksum: 0967abc2981a8e7d776324c6b84811e4d84a7ead89b54a3bb8791437f0c4751afd060406b06db90a436f1cf771867331b5ecf5c4aca95b4ccb9f6cb146c22ebc - languageName: node - linkType: hard - "buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" @@ -3673,10 +3921,12 @@ __metadata: languageName: node linkType: hard -"builtins@npm:^1.0.3": - version: 1.0.3 - resolution: "builtins@npm:1.0.3" - checksum: 47ce94f7eee0e644969da1f1a28e5f29bd2e48b25b2bbb61164c345881086e29464ccb1fb88dbc155ea26e8b1f5fc8a923b26c8c1ed0935b67b644d410674513 +"builtins@npm:^5.0.0": + version: 5.0.1 + resolution: "builtins@npm:5.0.1" + dependencies: + semver: ^7.0.0 + checksum: 66d204657fe36522822a95b288943ad11b58f5eaede235b11d8c4edaa28ce4800087d44a2681524c340494aadb120a0068011acabe99d30e8f11a7d826d83515 languageName: node linkType: hard @@ -3694,33 +3944,33 @@ __metadata: languageName: node linkType: hard -"cacache@npm:15.3.0, cacache@npm:^15.0.5, cacache@npm:^15.2.0": - version: 15.3.0 - resolution: "cacache@npm:15.3.0" +"cacache@npm:16.1.2": + version: 16.1.2 + resolution: "cacache@npm:16.1.2" dependencies: - "@npmcli/fs": ^1.0.0 - "@npmcli/move-file": ^1.0.1 + "@npmcli/fs": ^2.1.0 + "@npmcli/move-file": ^2.0.0 chownr: ^2.0.0 - fs-minipass: ^2.0.0 - glob: ^7.1.4 + fs-minipass: ^2.1.0 + glob: ^8.0.1 infer-owner: ^1.0.4 - lru-cache: ^6.0.0 - minipass: ^3.1.1 + lru-cache: ^7.7.1 + minipass: ^3.1.6 minipass-collect: ^1.0.2 minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.2 - mkdirp: ^1.0.3 + minipass-pipeline: ^1.2.4 + mkdirp: ^1.0.4 p-map: ^4.0.0 promise-inflight: ^1.0.1 rimraf: ^3.0.2 - ssri: ^8.0.1 - tar: ^6.0.2 + ssri: ^9.0.0 + tar: ^6.1.11 unique-filename: ^1.1.1 - checksum: a07327c27a4152c04eb0a831c63c00390d90f94d51bb80624a66f4e14a6b6360bbf02a84421267bd4d00ca73ac9773287d8d7169e8d2eafe378d2ce140579db8 + checksum: defe1d6f557ddda178204cac111990da27e8a60ed276fcd608dad7109cc1936e7dcd57d7263d22cdb06a80e7ceb76ab5eb05133c7c7f886abf1d870d722abd6c languageName: node linkType: hard -"cacache@npm:^16.1.0": +"cacache@npm:^16.0.0, cacache@npm:^16.1.0": version: 16.1.3 resolution: "cacache@npm:16.1.3" dependencies: @@ -3746,7 +3996,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": +"call-bind@npm:^1.0.0": version: 1.0.2 resolution: "call-bind@npm:1.0.2" dependencies: @@ -3781,13 +4031,20 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001299, caniuse-lite@npm:^1.0.30001426, caniuse-lite@npm:^1.0.30001449": +"caniuse-lite@npm:^1.0.30001449": version: 1.0.30001462 resolution: "caniuse-lite@npm:1.0.30001462" checksum: e4a57d7851eec65e7c9b6c11c4bbcecdc49d87b1b01bff3c15ea27efb05f959891b4c70ac169842067c134d6fa126d9ad5a91d0f85c7387c5bd912eaf41ea647 languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001464": + version: 1.0.30001474 + resolution: "caniuse-lite@npm:1.0.30001474" + checksum: c05faab958fae1bbf3c595203c96d3a2f6b4c7a0d122069addc6c386f208b4db66eed3f5e3d606b80e3b384603d353b27a306f6dcb6145642b5b97a330dba86a + languageName: node + linkType: hard + "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -3846,7 +4103,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.0.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2": +"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.0.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -3879,15 +4136,6 @@ __metadata: languageName: node linkType: hard -"circular-dependency-plugin@npm:5.2.2": - version: 5.2.2 - resolution: "circular-dependency-plugin@npm:5.2.2" - peerDependencies: - webpack: ">=4.0.1" - checksum: d1a51e7f86e72d9e7a08c47234511cc7a5c3050781c2d6dcc77c0b22214f94f272702488c952e59b2af589c67944160ad1c9c0b7b3e0d4f89222f2a27ebf085e - languageName: node - linkType: hard - "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -4126,10 +4374,10 @@ __metadata: languageName: node linkType: hard -"connect-history-api-fallback@npm:^1.6.0": - version: 1.6.0 - resolution: "connect-history-api-fallback@npm:1.6.0" - checksum: 804ca2be28c999032ecd37a9f71405e5d7b7a4b3defcebbe41077bb8c5a0a150d7b59f51dcc33b2de30bc7e217a31d10f8cfad27e8e74c2fc7655eeba82d6e7e +"connect-history-api-fallback@npm:^2.0.0": + version: 2.0.0 + resolution: "connect-history-api-fallback@npm:2.0.0" + checksum: dc5368690f4a5c413889792f8df70d5941ca9da44523cde3f87af0745faee5ee16afb8195434550f0504726642734f2683d6c07f8b460f828a12c45fbd4c9a68 languageName: node linkType: hard @@ -4205,23 +4453,23 @@ __metadata: languageName: node linkType: hard -"copy-webpack-plugin@npm:10.2.1": - version: 10.2.1 - resolution: "copy-webpack-plugin@npm:10.2.1" +"copy-webpack-plugin@npm:11.0.0": + version: 11.0.0 + resolution: "copy-webpack-plugin@npm:11.0.0" dependencies: - fast-glob: ^3.2.7 + fast-glob: ^3.2.11 glob-parent: ^6.0.1 - globby: ^12.0.2 + globby: ^13.1.1 normalize-path: ^3.0.0 schema-utils: ^4.0.0 serialize-javascript: ^6.0.0 peerDependencies: webpack: ^5.1.0 - checksum: 59b4fc10aaa81658fa1b6164fc9985ef0e4b54ba2734c07e0da5fdbc6828a5954fe822e2c383beac1e247dd26e669b2c86fa0d7d5495dfb1cbf562196d005ec2 + checksum: df4f8743f003a29ee7dd3d9b1789998a3a99051c92afb2ba2203d3dacfa696f4e757b275560fafb8f206e520a0aa78af34b990324a0e36c2326cefdeef3ca82e languageName: node linkType: hard -"core-js-compat@npm:^3.20.2, core-js-compat@npm:^3.21.0": +"core-js-compat@npm:^3.21.0": version: 3.29.0 resolution: "core-js-compat@npm:3.29.0" dependencies: @@ -4230,6 +4478,15 @@ __metadata: languageName: node linkType: hard +"core-js-compat@npm:^3.22.1": + version: 3.30.0 + resolution: "core-js-compat@npm:3.30.0" + dependencies: + browserslist: ^4.21.5 + checksum: 51a34d8a292de51f52ac2d72b18ee94743a905d4570a42214262426ebf8f026c853fee22cf4d6c61c2d95f861749421c4de48e9389f551745c5ac1477a5f929f + languageName: node + linkType: hard + "core-js-pure@npm:^3.25.1": version: 3.29.0 resolution: "core-js-pure@npm:3.29.0" @@ -4237,13 +4494,6 @@ __metadata: languageName: node linkType: hard -"core-js@npm:3.20.3": - version: 3.20.3 - resolution: "core-js@npm:3.20.3" - checksum: 2106cdfb1330abf9e27d577666fc0421feafe8c39bb5af90a63af16e9706c767a7e3a82edc21ce3ed6b9d806f3200d1cf6cc3d0597a8c0af12dbec287c781d65 - languageName: node - linkType: hard - "core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" @@ -4299,7 +4549,7 @@ __metadata: languageName: node linkType: hard -"css-blank-pseudo@npm:^3.0.2": +"css-blank-pseudo@npm:^3.0.3": version: 3.0.3 resolution: "css-blank-pseudo@npm:3.0.3" dependencies: @@ -4319,7 +4569,7 @@ __metadata: languageName: node linkType: hard -"css-has-pseudo@npm:^3.0.3": +"css-has-pseudo@npm:^3.0.4": version: 3.0.4 resolution: "css-has-pseudo@npm:3.0.4" dependencies: @@ -4332,25 +4582,25 @@ __metadata: languageName: node linkType: hard -"css-loader@npm:6.5.1": - version: 6.5.1 - resolution: "css-loader@npm:6.5.1" +"css-loader@npm:6.7.1": + version: 6.7.1 + resolution: "css-loader@npm:6.7.1" dependencies: icss-utils: ^5.1.0 - postcss: ^8.2.15 + postcss: ^8.4.7 postcss-modules-extract-imports: ^3.0.0 postcss-modules-local-by-default: ^4.0.0 postcss-modules-scope: ^3.0.0 postcss-modules-values: ^4.0.0 - postcss-value-parser: ^4.1.0 + postcss-value-parser: ^4.2.0 semver: ^7.3.5 peerDependencies: webpack: ^5.0.0 - checksum: 5a3bedecb468038f09673d25c32d8db5b0baa6c38820253c54ce4c56c27a2250d5d5b4bace77dd5e20ba0a569604eb759362bab4e3128e7db2229e40857d4aca + checksum: 170fdbc630a05a43679ef60fa97694766b568dbde37adccc0faafa964fc675f08b976bc68837bb73b61d60240e8d2cbcbf51540fe94ebc9dafc56e7c46ba5527 languageName: node linkType: hard -"css-prefers-color-scheme@npm:^6.0.2": +"css-prefers-color-scheme@npm:^6.0.3": version: 6.0.3 resolution: "css-prefers-color-scheme@npm:6.0.3" peerDependencies: @@ -4381,21 +4631,10 @@ __metadata: languageName: node linkType: hard -"css@npm:^3.0.0": - version: 3.0.0 - resolution: "css@npm:3.0.0" - dependencies: - inherits: ^2.0.4 - source-map: ^0.6.1 - source-map-resolve: ^0.6.0 - checksum: 4273ac816ddf99b99acb9c1d1a27d86d266a533cc01118369d941d8e8a78277a83cad3315e267a398c509d930fbb86504e193ea1ebc620a4a4212e06fe76e8be - languageName: node - linkType: hard - -"cssdb@npm:^5.0.0": - version: 5.1.0 - resolution: "cssdb@npm:5.1.0" - checksum: 34b4e57b375348850fbe312b0ceefbd060ff70b2e47966d6053a8316b066423809fe9099c0ab08f5f8580b4dfd32eb63bfe10a52bccc4fec1660009873d13003 +"cssdb@npm:^7.0.0": + version: 7.5.3 + resolution: "cssdb@npm:7.5.3" + checksum: 9376f1a49405dde2e3bb2e6e99d4389a17d95e94e89ead66cf52067493311b2a6222a257438cd14655087ee711c239efbebefa551e2b2ce8744709c71de3e5f6 languageName: node linkType: hard @@ -4440,7 +4679,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.3, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.3 resolution: "debug@npm:4.3.3" dependencies: @@ -4452,16 +4691,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.1.0, debug@npm:^3.2.6, debug@npm:^3.2.7": - version: 3.2.7 - resolution: "debug@npm:3.2.7" - dependencies: - ms: ^2.1.1 - checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c - languageName: node - linkType: hard - -"debug@npm:^4.3.4": +"debug@npm:4.3.4, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -4473,6 +4703,15 @@ __metadata: languageName: node linkType: hard +"debug@npm:^3.1.0, debug@npm:^3.2.6": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: ^2.1.1 + checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -4490,27 +4729,6 @@ __metadata: languageName: node linkType: hard -"decode-uri-component@npm:^0.2.0": - version: 0.2.2 - resolution: "decode-uri-component@npm:0.2.2" - checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 - languageName: node - linkType: hard - -"deep-equal@npm:^1.0.1": - version: 1.1.1 - resolution: "deep-equal@npm:1.1.1" - dependencies: - is-arguments: ^1.0.4 - is-date-object: ^1.0.1 - is-regex: ^1.0.4 - object-is: ^1.0.1 - object-keys: ^1.1.1 - regexp.prototype.flags: ^1.2.0 - checksum: f92686f2c5bcdf714a75a5fa7a9e47cb374a8ec9307e717b8d1ce61f56a75aaebf5619c2a12b8087a705b5a2f60d0292c35f8b58cb1f72e3268a3a15cab9f78d - languageName: node - linkType: hard - "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -4543,16 +4761,6 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.3": - version: 1.2.0 - resolution: "define-properties@npm:1.2.0" - dependencies: - has-property-descriptors: ^1.0.0 - object-keys: ^1.1.1 - checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 - languageName: node - linkType: hard - "del@npm:^2.2.0": version: 2.2.2 resolution: "del@npm:2.2.2" @@ -4568,22 +4776,6 @@ __metadata: languageName: node linkType: hard -"del@npm:^6.0.0": - version: 6.1.1 - resolution: "del@npm:6.1.1" - dependencies: - globby: ^11.0.1 - graceful-fs: ^4.2.4 - is-glob: ^4.0.1 - is-path-cwd: ^2.2.0 - is-path-inside: ^3.0.2 - p-map: ^4.0.0 - rimraf: ^3.0.2 - slash: ^3.0.0 - checksum: 563288b73b8b19a7261c47fd21a330eeab6e2acd7c6208c49790dfd369127120dd7836cdf0c1eca216b77c94782a81507eac6b4734252d3bef2795cb366996b6 - languageName: node - linkType: hard - "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -4626,13 +4818,6 @@ __metadata: languageName: node linkType: hard -"detect-it@npm:^4.0.1": - version: 4.0.1 - resolution: "detect-it@npm:4.0.1" - checksum: 013981eae87c0265169e11e09efcc1c78fb2fc3e956b0001eb8d741ce03784a3e8e6d60e1445105f510c52be2ccffbd1c31c1bc094d01ad77f11df9830822a9c - languageName: node - linkType: hard - "detect-node@npm:^2.0.4": version: 2.1.0 resolution: "detect-node@npm:2.1.0" @@ -4640,15 +4825,6 @@ __metadata: languageName: node linkType: hard -"detect-passive-events@npm:^2.0.3": - version: 2.0.3 - resolution: "detect-passive-events@npm:2.0.3" - dependencies: - detect-it: ^4.0.1 - checksum: 2fb48474e340a1add096735427433578c571e2fed0c44af86ac116c50b9b7d70662a2e3bf1539d9dff4e2a43459c874b20876270afe7beeb5d4bef08ff9d4a5c - languageName: node - linkType: hard - "di@npm:^0.0.1": version: 0.0.1 resolution: "di@npm:0.0.1" @@ -4693,22 +4869,12 @@ __metadata: languageName: node linkType: hard -"dns-packet@npm:^1.3.1": - version: 1.3.4 - resolution: "dns-packet@npm:1.3.4" - dependencies: - ip: ^1.1.0 - safe-buffer: ^5.0.1 - checksum: 7dd87f85cb4f9d1a99c03470730e3d9385e67dc94f6c13868c4034424a5378631e492f9f1fbc43d3c42f319fbbfe18b6488bb9527c32d34692c52bf1f5eedf69 - languageName: node - linkType: hard - -"dns-txt@npm:^2.0.2": - version: 2.0.2 - resolution: "dns-txt@npm:2.0.2" +"dns-packet@npm:^5.2.2": + version: 5.5.0 + resolution: "dns-packet@npm:5.5.0" dependencies: - buffer-indexof: ^1.0.0 - checksum: 80130b665379ecd991687ae079fbee25d091e03e4c4cef41e7643b977849ac48c2f56bfcb3727e53594d29029b833749811110d9f3fbee1b26a6e6f8096a5cef + "@leichtgewicht/ip-codec": ^2.0.1 + checksum: 3aa26bb03a613362937225f786d46b1a39b5002d0a68b40537326b090685d5c53d46e25cc7c610f2a29ea5029c8ce480c368a8b0492932c5fb88ebc377676e84 languageName: node linkType: hard @@ -4782,27 +4948,26 @@ __metadata: version: 0.0.0-use.local resolution: "dres-frontend@workspace:." dependencies: - "@angular-devkit/build-angular": 13.3.x + "@angular-devkit/build-angular": 14.2.11 "@angular-eslint/builder": 13.2.0 "@angular-eslint/eslint-plugin": 13.2.0 "@angular-eslint/eslint-plugin-template": 13.2.0 "@angular-eslint/schematics": 13.2.0 "@angular-eslint/template-parser": 13.2.0 - "@angular-slider/ngx-slider": 2.0.x - "@angular/animations": 13.2.x + "@angular/animations": 14.3.0 "@angular/cdk": 13.2.x - "@angular/cli": 13.2.x - "@angular/common": 13.2.x - "@angular/compiler": 13.2.x - "@angular/compiler-cli": 13.2.x - "@angular/core": 13.2.x + "@angular/cli": 14.2.11 + "@angular/common": 14.3.0 + "@angular/compiler": 14.3.0 + "@angular/compiler-cli": 14.3.0 + "@angular/core": 14.3.0 "@angular/flex-layout": ^13.0.0-beta.38 - "@angular/forms": 13.2.x - "@angular/language-service": 13.2.x + "@angular/forms": 14.3.0 + "@angular/language-service": 14.3.0 "@angular/material": 13.2.x - "@angular/platform-browser": 13.2.x - "@angular/platform-browser-dynamic": 13.2.x - "@angular/router": 13.2.x + "@angular/platform-browser": 14.3.0 + "@angular/platform-browser-dynamic": 14.3.0 + "@angular/router": 14.3.0 "@types/jasmine": 3.6.x "@types/jasminewd2": 2.0.x "@types/node": 12.11.x @@ -4837,7 +5002,7 @@ __metadata: stylelint-config-standard-scss: ^3.0.0 ts-node: 8.3.x tslib: 2.0.x - typescript: 4.5.x + typescript: 4.8.4 zone.js: ~0.11.4 languageName: unknown linkType: soft @@ -4926,7 +5091,7 @@ __metadata: languageName: node linkType: hard -"encoding@npm:^0.1.12, encoding@npm:^0.1.13": +"encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" dependencies: @@ -4969,7 +5134,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.9.2": +"enhanced-resolve@npm:^5.10.0": version: 5.12.0 resolution: "enhanced-resolve@npm:5.12.0" dependencies: @@ -5059,172 +5224,185 @@ __metadata: languageName: node linkType: hard -"esbuild-android-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-android-arm64@npm:0.14.22" +"esbuild-android-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-android-64@npm:0.15.5" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"esbuild-android-arm64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-android-arm64@npm:0.15.5" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-darwin-64@npm:0.14.22" +"esbuild-darwin-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-darwin-64@npm:0.15.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-darwin-arm64@npm:0.14.22" +"esbuild-darwin-arm64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-darwin-arm64@npm:0.15.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-freebsd-64@npm:0.14.22" +"esbuild-freebsd-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-freebsd-64@npm:0.15.5" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-freebsd-arm64@npm:0.14.22" +"esbuild-freebsd-arm64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-freebsd-arm64@npm:0.15.5" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-32@npm:0.14.22" +"esbuild-linux-32@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-32@npm:0.15.5" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-64@npm:0.14.22" +"esbuild-linux-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-64@npm:0.15.5" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-arm64@npm:0.14.22" +"esbuild-linux-arm64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-arm64@npm:0.15.5" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-arm@npm:0.14.22" +"esbuild-linux-arm@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-arm@npm:0.15.5" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-mips64le@npm:0.14.22" +"esbuild-linux-mips64le@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-mips64le@npm:0.15.5" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-ppc64le@npm:0.14.22" +"esbuild-linux-ppc64le@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-ppc64le@npm:0.15.5" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-riscv64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-riscv64@npm:0.14.22" +"esbuild-linux-riscv64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-riscv64@npm:0.15.5" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"esbuild-linux-s390x@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-linux-s390x@npm:0.14.22" +"esbuild-linux-s390x@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-linux-s390x@npm:0.15.5" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-netbsd-64@npm:0.14.22" +"esbuild-netbsd-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-netbsd-64@npm:0.15.5" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-openbsd-64@npm:0.14.22" +"esbuild-openbsd-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-openbsd-64@npm:0.15.5" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-sunos-64@npm:0.14.22" +"esbuild-sunos-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-sunos-64@npm:0.15.5" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-wasm@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-wasm@npm:0.14.22" +"esbuild-wasm@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-wasm@npm:0.15.5" bin: esbuild: bin/esbuild - checksum: 56a75d428e086440126132bb465f64ad33e40fd9ecf91dbdd4fe5b1e16ca65b30d8cec8f90eb538c99ad0c5f57dcb15df809cbe626ed6ea5106d723de985a53b + checksum: eed3f81dc017192a4a70dc0474f7c61dc9709994a3070533a8b773b11482a1bc3051e0a10529741b146ae75b0878dc9ccf6db97c1afb4e5447b9c43b8e1c6eed languageName: node linkType: hard -"esbuild-windows-32@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-windows-32@npm:0.14.22" +"esbuild-windows-32@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-windows-32@npm:0.15.5" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-windows-64@npm:0.14.22" +"esbuild-windows-64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-windows-64@npm:0.15.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild-windows-arm64@npm:0.14.22" +"esbuild-windows-arm64@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild-windows-arm64@npm:0.15.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"esbuild@npm:0.14.22": - version: 0.14.22 - resolution: "esbuild@npm:0.14.22" - dependencies: - esbuild-android-arm64: 0.14.22 - esbuild-darwin-64: 0.14.22 - esbuild-darwin-arm64: 0.14.22 - esbuild-freebsd-64: 0.14.22 - esbuild-freebsd-arm64: 0.14.22 - esbuild-linux-32: 0.14.22 - esbuild-linux-64: 0.14.22 - esbuild-linux-arm: 0.14.22 - esbuild-linux-arm64: 0.14.22 - esbuild-linux-mips64le: 0.14.22 - esbuild-linux-ppc64le: 0.14.22 - esbuild-linux-riscv64: 0.14.22 - esbuild-linux-s390x: 0.14.22 - esbuild-netbsd-64: 0.14.22 - esbuild-openbsd-64: 0.14.22 - esbuild-sunos-64: 0.14.22 - esbuild-windows-32: 0.14.22 - esbuild-windows-64: 0.14.22 - esbuild-windows-arm64: 0.14.22 +"esbuild@npm:0.15.5": + version: 0.15.5 + resolution: "esbuild@npm:0.15.5" + dependencies: + "@esbuild/linux-loong64": 0.15.5 + esbuild-android-64: 0.15.5 + esbuild-android-arm64: 0.15.5 + esbuild-darwin-64: 0.15.5 + esbuild-darwin-arm64: 0.15.5 + esbuild-freebsd-64: 0.15.5 + esbuild-freebsd-arm64: 0.15.5 + esbuild-linux-32: 0.15.5 + esbuild-linux-64: 0.15.5 + esbuild-linux-arm: 0.15.5 + esbuild-linux-arm64: 0.15.5 + esbuild-linux-mips64le: 0.15.5 + esbuild-linux-ppc64le: 0.15.5 + esbuild-linux-riscv64: 0.15.5 + esbuild-linux-s390x: 0.15.5 + esbuild-netbsd-64: 0.15.5 + esbuild-openbsd-64: 0.15.5 + esbuild-sunos-64: 0.15.5 + esbuild-windows-32: 0.15.5 + esbuild-windows-64: 0.15.5 + esbuild-windows-arm64: 0.15.5 dependenciesMeta: + "@esbuild/linux-loong64": + optional: true + esbuild-android-64: + optional: true esbuild-android-arm64: optional: true esbuild-darwin-64: @@ -5265,7 +5443,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 8b99a61203b289ff0ad2be1dfc78420d3f838384ddef6a302203e5a9d8e4913f2c152d58dd775be9ab15ce6c77ea9588934c0af27b25806be48d56472e661676 + checksum: fc7f5df6542bd18dee1e61c58049ddf3d6b508d8d67eabcba455f7dbb560552e012f169a8afa2f209de554a206935fedf30d576aacdc2b46cb4da229181968fb languageName: node linkType: hard @@ -5530,7 +5708,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.1": +"express@npm:^4.17.3": version: 4.18.2 resolution: "express@npm:4.18.2" dependencies: @@ -5621,7 +5799,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" dependencies: @@ -5634,7 +5812,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:2.1.0, fast-json-stable-stringify@npm:^2.0.0": +"fast-json-stable-stringify@npm:^2.0.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: b191531e36c607977e5b1c47811158733c34ccb3bfde92c44798929e9b4154884378536d26ad90dfecd32e1ffc09c545d23535ad91b3161a27ddbb8ebe0cbecb @@ -5953,13 +6131,6 @@ __metadata: languageName: node linkType: hard -"functions-have-names@npm:^1.2.2": - version: 1.2.3 - resolution: "functions-have-names@npm:1.2.3" - checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 - languageName: node - linkType: hard - "gauge@npm:^4.0.3": version: 4.0.4 resolution: "gauge@npm:4.0.4" @@ -5990,7 +6161,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1": +"get-intrinsic@npm:^1.0.2": version: 1.2.0 resolution: "get-intrinsic@npm:1.2.0" dependencies: @@ -6063,7 +6234,20 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": +"glob@npm:8.0.3": + version: 8.0.3 + resolution: "glob@npm:8.0.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^5.0.1 + once: ^1.3.0 + checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 + languageName: node + linkType: hard + +"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -6126,7 +6310,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.0.1, globby@npm:^11.0.4, globby@npm:^11.1.0": +"globby@npm:^11.0.4, globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -6140,17 +6324,16 @@ __metadata: languageName: node linkType: hard -"globby@npm:^12.0.2": - version: 12.2.0 - resolution: "globby@npm:12.2.0" +"globby@npm:^13.1.1": + version: 13.1.3 + resolution: "globby@npm:13.1.3" dependencies: - array-union: ^3.0.1 dir-glob: ^3.0.1 - fast-glob: ^3.2.7 - ignore: ^5.1.9 + fast-glob: ^3.2.11 + ignore: ^5.2.0 merge2: ^1.4.1 slash: ^4.0.0 - checksum: 2539379a7fff3473d3e7c68b4540ba38f36970f43f760e36e301515d5cb98a0c5736554957d90390906bee632327beb2f9518d1acd6911f61e436db11b0da5b5 + checksum: 93f06e02002cdf368f7e3d55bd59e7b00784c7cc8fe92c7ee5082cc7171ff6109fda45e1c97a80bb48bc811dedaf7843c7c9186f5f84bde4883ab630e13c43df languageName: node linkType: hard @@ -6243,31 +6426,13 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" - dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": +"has-symbols@npm:^1.0.3": version: 1.0.3 resolution: "has-symbols@npm:1.0.3" checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 languageName: node linkType: hard -"has-tostringtag@npm:^1.0.0": - version: 1.0.0 - resolution: "has-tostringtag@npm:1.0.0" - dependencies: - has-symbols: ^1.0.2 - checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c - languageName: node - linkType: hard - "has-unicode@npm:^2.0.1": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" @@ -6318,6 +6483,15 @@ __metadata: languageName: node linkType: hard +"hosted-git-info@npm:^5.0.0": + version: 5.2.1 + resolution: "hosted-git-info@npm:5.2.1" + dependencies: + lru-cache: ^7.5.1 + checksum: fa35df185224adfd69141f3b2f8cc31f50e705a5ebb415ccfbfd055c5b94bd08d3e658edf1edad9e2ac7d81831ac7cf261f5d219b3adc8d744fb8cdacaaf2ead + languageName: node + linkType: hard + "hpack.js@npm:^2.1.6": version: 2.1.6 resolution: "hpack.js@npm:2.1.6" @@ -6397,17 +6571,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^4.0.1": - version: 4.0.1 - resolution: "http-proxy-agent@npm:4.0.1" - dependencies: - "@tootallnate/once": 1 - agent-base: 6 - debug: 4 - checksum: c6a5da5a1929416b6bbdf77b1aca13888013fe7eb9d59fc292e25d18e041bb154a8dfada58e223fc7b76b9b2d155a87e92e608235201f77d34aa258707963a82 - languageName: node - linkType: hard - "http-proxy-agent@npm:^5.0.0": version: 5.0.0 resolution: "http-proxy-agent@npm:5.0.0" @@ -6419,7 +6582,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-middleware@npm:^2.0.0": +"http-proxy-middleware@npm:^2.0.3": version: 2.0.6 resolution: "http-proxy-middleware@npm:2.0.6" dependencies: @@ -6459,13 +6622,13 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.0, https-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "https-proxy-agent@npm:5.0.0" +"https-proxy-agent@npm:5.0.1": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" dependencies: agent-base: 6 debug: 4 - checksum: 165bfb090bd26d47693597661298006841ab733d0c7383a8cb2f17373387a94c903a3ac687090aa739de05e379ab6f868bae84ab4eac288ad85c328cd1ec9e53 + checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 languageName: node linkType: hard @@ -6479,6 +6642,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "https-proxy-agent@npm:5.0.0" + dependencies: + agent-base: 6 + debug: 4 + checksum: 165bfb090bd26d47693597661298006841ab733d0c7383a8cb2f17373387a94c903a3ac687090aa739de05e379ab6f868bae84ab4eac288ad85c328cd1ec9e53 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -6504,7 +6677,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24, iconv-lite@npm:^0.4.4": +"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" dependencies: @@ -6538,16 +6711,16 @@ __metadata: languageName: node linkType: hard -"ignore-walk@npm:^4.0.1": - version: 4.0.1 - resolution: "ignore-walk@npm:4.0.1" +"ignore-walk@npm:^5.0.1": + version: 5.0.1 + resolution: "ignore-walk@npm:5.0.1" dependencies: - minimatch: ^3.0.4 - checksum: 903cd5cb68d57b2e70fddb83d885aea55f137a44636254a29b08037797376d8d3e09d1c58935778f3a271bf6a2b41ecc54fc22260ac07190e09e1ec7253b49f3 + minimatch: ^5.0.1 + checksum: 1a4ef35174653a1aa6faab3d9f8781269166536aee36a04946f6e2b319b2475c1903a75ed42f04219274128242f49d0a10e20c4354ee60d9548e97031451150b languageName: node linkType: hard -"ignore@npm:5.2.0, ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.1.9, ignore@npm:^5.2.0": +"ignore@npm:5.2.0, ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 @@ -6646,10 +6819,10 @@ __metadata: languageName: node linkType: hard -"ini@npm:2.0.0": - version: 2.0.0 - resolution: "ini@npm:2.0.0" - checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e +"ini@npm:3.0.0": + version: 3.0.0 + resolution: "ini@npm:3.0.0" + checksum: e92b6b0835ac369e58c677e7faa8db6019ac667d7404887978fb86b181d658e50f1742ecbba7d81eb5ff917b3ae4d63a48e1ef3a9f8a0527bd7605fe1a9995d4 languageName: node linkType: hard @@ -6660,9 +6833,9 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:8.2.0": - version: 8.2.0 - resolution: "inquirer@npm:8.2.0" +"inquirer@npm:8.2.4": + version: 8.2.4 + resolution: "inquirer@npm:8.2.4" dependencies: ansi-escapes: ^4.2.1 chalk: ^4.1.1 @@ -6674,18 +6847,12 @@ __metadata: mute-stream: 0.0.8 ora: ^5.4.1 run-async: ^2.4.0 - rxjs: ^7.2.0 + rxjs: ^7.5.5 string-width: ^4.1.0 strip-ansi: ^6.0.0 through: ^2.3.6 - checksum: 861d1a9324ae933b49126b3541d94e4d6a2f2a25411b3f3cc00c34bf1bdab34146362d702cf289efe6d8034900dc5905bcf2ea716092a02b6fc390e5986dd236 - languageName: node - linkType: hard - -"ip@npm:^1.1.0": - version: 1.1.8 - resolution: "ip@npm:1.1.8" - checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb + wrap-ansi: ^7.0.0 + checksum: dfcb6529d3af443dfea2241cb471508091b51f5121a088fdb8728b23ec9b349ef0a5e13a0ef2c8e19457b0bed22f7cbbcd561f7a4529d084c562a58c605e2655 languageName: node linkType: hard @@ -6710,16 +6877,6 @@ __metadata: languageName: node linkType: hard -"is-arguments@npm:^1.0.4": - version: 1.1.1 - resolution: "is-arguments@npm:1.1.1" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27 - languageName: node - linkType: hard - "is-arrayish@npm:^0.2.1": version: 0.2.1 resolution: "is-arrayish@npm:0.2.1" @@ -6736,7 +6893,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1": +"is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": version: 2.11.0 resolution: "is-core-module@npm:2.11.0" dependencies: @@ -6745,15 +6902,6 @@ __metadata: languageName: node linkType: hard -"is-date-object@npm:^1.0.1": - version: 1.0.5 - resolution: "is-date-object@npm:1.0.5" - dependencies: - has-tostringtag: ^1.0.0 - checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc - languageName: node - linkType: hard - "is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": version: 2.2.1 resolution: "is-docker@npm:2.2.1" @@ -6821,13 +6969,6 @@ __metadata: languageName: node linkType: hard -"is-path-cwd@npm:^2.2.0": - version: 2.2.0 - resolution: "is-path-cwd@npm:2.2.0" - checksum: 46a840921bb8cc0dc7b5b423a14220e7db338072a4495743a8230533ce78812dc152548c86f4b828411fe98c5451959f07cf841c6a19f611e46600bd699e8048 - languageName: node - linkType: hard - "is-path-in-cwd@npm:^1.0.0": version: 1.0.1 resolution: "is-path-in-cwd@npm:1.0.1" @@ -6846,7 +6987,7 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.2, is-path-inside@npm:^3.0.3": +"is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 @@ -6883,16 +7024,6 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.0.4": - version: 1.1.4 - resolution: "is-regex@npm:1.1.4" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652 - languageName: node - linkType: hard - "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -7156,14 +7287,7 @@ __metadata: languageName: node linkType: hard -"json-parse-better-errors@npm:^1.0.2": - version: 1.0.2 - resolution: "json-parse-better-errors@npm:1.0.2" - checksum: ff2b5ba2a70e88fd97a3cb28c1840144c5ce8fae9cbeeddba15afa333a5c407cf0e42300cd0a2885dbb055227fe68d405070faad941beeffbfde9cf3b2c78c5d - languageName: node - linkType: hard - -"json-parse-even-better-errors@npm:^2.3.0": +"json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1" checksum: 798ed4cf3354a2d9ccd78e86d2169515a0097a5c133337807cdf7f1fc32e1391d207ccfc276518cc1d7d8d4db93288b8a50ba4293d212ad1336e52a8ec0a941f @@ -7205,7 +7329,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.1.2, json5@npm:^2.2.2": +"json5@npm:^2.1.2, json5@npm:^2.2.1, json5@npm:^2.2.2": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -7221,6 +7345,13 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:3.1.0": + version: 3.1.0 + resolution: "jsonc-parser@npm:3.1.0" + checksum: 81b00c565c60cb1b400523a918d42ad9c7bb3d9cf34c708bf78d37c8c496ecd670c3ff8828f2f60aa6e6627ef4287982794ddf92261ea71e320973c54b29fb22 + languageName: node + linkType: hard + "jsonc-parser@npm:3.2.0": version: 3.2.0 resolution: "jsonc-parser@npm:3.2.0" @@ -7392,21 +7523,21 @@ __metadata: languageName: node linkType: hard -"less-loader@npm:10.2.0": - version: 10.2.0 - resolution: "less-loader@npm:10.2.0" +"less-loader@npm:11.0.0": + version: 11.0.0 + resolution: "less-loader@npm:11.0.0" dependencies: klona: ^2.0.4 peerDependencies: less: ^3.5.0 || ^4.0.0 webpack: ^5.0.0 - checksum: 42a2b56a9d474c066b22423e8ed82821ed6dbc5effad8d84b2a3d8651fb4c5027f32ac770a0fe4810e3e6d0595be576a2f7f2341f2603bdf795cff4e94d7340b + checksum: fe5f810549a04c3d1b7fdd838c598e1dd7e6ed35428bdc7ec0caa4e7f2c07abfd1519c477aca713ca1259f88905dae25dd5f0c27b61071d9ce0dfefded86be1a languageName: node linkType: hard -"less@npm:4.1.2": - version: 4.1.2 - resolution: "less@npm:4.1.2" +"less@npm:4.1.3": + version: 4.1.3 + resolution: "less@npm:4.1.3" dependencies: copy-anything: ^2.0.1 errno: ^0.1.1 @@ -7414,7 +7545,7 @@ __metadata: image-size: ~0.5.0 make-dir: ^2.1.0 mime: ^1.4.1 - needle: ^2.5.2 + needle: ^3.1.0 parse-node-version: ^1.0.1 source-map: ~0.6.0 tslib: ^2.3.0 @@ -7435,7 +7566,7 @@ __metadata: optional: true bin: lessc: bin/lessc - checksum: d3cbc3e18a8313e44f7feaf5fce1728b25911e116a8b9f7206e1f7bf5660902ddaf470b58e68a52288488c8a337433b7de686a3d2405ce49155c2280191983de + checksum: 1470fbec993a375eb28d729cd906805fd62b7a7f1b4f5b4d62d04e81eaba987a9373e74aa0b9fa9191149ebc0bfb42e2ea98a038555555b7b241c10a854067cc languageName: node linkType: hard @@ -7602,7 +7733,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.14, lodash@npm:^4.17.21": +"lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -7679,19 +7810,19 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.4.4, lru-cache@npm:^7.5.1, lru-cache@npm:^7.7.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 languageName: node linkType: hard -"magic-string@npm:0.25.7": - version: 0.25.7 - resolution: "magic-string@npm:0.25.7" +"magic-string@npm:0.26.2": + version: 0.26.2 + resolution: "magic-string@npm:0.26.2" dependencies: - sourcemap-codec: ^1.4.4 - checksum: 727a1fb70f9610304fe384f1df0251eb7d1d9dd779c07ef1225690361b71b216f26f5d934bfb11c919b5b0e7ba50f6240c823a6f2e44cfd33d4a07d7747ca829 + sourcemap-codec: ^1.4.8 + checksum: b4db4e2b370ac8d9ffc6443a2b591b75364bf1fc9121b5a4068d5b89804abff6709d1fa4a0e0c2d54f2e61e0e44db83efdfe219a5ab0ba6d25ee1f2b51fbed55 languageName: node linkType: hard @@ -7730,7 +7861,7 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.1, make-fetch-happen@npm:^10.0.3": +"make-fetch-happen@npm:^10.0.3, make-fetch-happen@npm:^10.0.6": version: 10.2.1 resolution: "make-fetch-happen@npm:10.2.1" dependencies: @@ -7754,30 +7885,6 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^9.1.0": - version: 9.1.0 - resolution: "make-fetch-happen@npm:9.1.0" - dependencies: - agentkeepalive: ^4.1.3 - cacache: ^15.2.0 - http-cache-semantics: ^4.1.0 - http-proxy-agent: ^4.0.1 - https-proxy-agent: ^5.0.0 - is-lambda: ^1.0.1 - lru-cache: ^6.0.0 - minipass: ^3.1.3 - minipass-collect: ^1.0.2 - minipass-fetch: ^1.3.2 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - negotiator: ^0.6.2 - promise-retry: ^2.0.1 - socks-proxy-agent: ^6.0.0 - ssri: ^8.0.0 - checksum: 0eb371c85fdd0b1584fcfdf3dc3c62395761b3c14658be02620c310305a9a7ecf1617a5e6fb30c1d081c5c8aaf177fa133ee225024313afabb7aa6a10f1e3d04 - languageName: node - linkType: hard - "map-obj@npm:^1.0.0": version: 1.0.1 resolution: "map-obj@npm:1.0.1" @@ -7806,7 +7913,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.2.2": +"memfs@npm:^3.4.3": version: 3.4.13 resolution: "memfs@npm:3.4.13" dependencies: @@ -7921,14 +8028,14 @@ __metadata: languageName: node linkType: hard -"mini-css-extract-plugin@npm:2.5.3": - version: 2.5.3 - resolution: "mini-css-extract-plugin@npm:2.5.3" +"mini-css-extract-plugin@npm:2.6.1": + version: 2.6.1 + resolution: "mini-css-extract-plugin@npm:2.6.1" dependencies: schema-utils: ^4.0.0 peerDependencies: webpack: ^5.0.0 - checksum: de53fbded09fd2ae81174b11754bc955fcf0e0a85b2c4df7e179fcc8a81533362498824395d43d50960b0bc93550eb2bd9cd1ded113eaa21bd84ab50ef29e65c + checksum: df60840404878c4832b4104799fd29c5a89b06b1e377956c8d4a5729efe0ef301a52e5087d6f383871df5e69a8445922a0ae635c11abf412d7645a7096d0e973 languageName: node linkType: hard @@ -7948,6 +8055,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:5.1.0": + version: 5.1.0 + resolution: "minimatch@npm:5.1.0" + dependencies: + brace-expansion: ^2.0.1 + checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90 + languageName: node + linkType: hard + "minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -7985,26 +8101,11 @@ __metadata: linkType: hard "minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" - dependencies: - minipass: ^3.0.0 - checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 - languageName: node - linkType: hard - -"minipass-fetch@npm:^1.3.2, minipass-fetch@npm:^1.4.1": - version: 1.4.1 - resolution: "minipass-fetch@npm:1.4.1" - dependencies: - encoding: ^0.1.12 - minipass: ^3.1.0 - minipass-sized: ^1.0.3 - minizlib: ^2.0.0 - dependenciesMeta: - encoding: - optional: true - checksum: ec93697bdb62129c4e6c0104138e681e30efef8c15d9429dd172f776f83898471bc76521b539ff913248cc2aa6d2b37b652c993504a51cc53282563640f29216 + version: 1.0.2 + resolution: "minipass-collect@npm:1.0.2" + dependencies: + minipass: ^3.0.0 + checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 languageName: node linkType: hard @@ -8042,7 +8143,7 @@ __metadata: languageName: node linkType: hard -"minipass-pipeline@npm:^1.2.2, minipass-pipeline@npm:^1.2.4": +"minipass-pipeline@npm:^1.2.4": version: 1.2.4 resolution: "minipass-pipeline@npm:1.2.4" dependencies: @@ -8060,7 +8161,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.0, minipass@npm:^3.1.1, minipass@npm:^3.1.3, minipass@npm:^3.1.6": +"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": version: 3.3.6 resolution: "minipass@npm:3.3.6" dependencies: @@ -8076,7 +8177,7 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^2.0.0, minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" dependencies: @@ -8086,7 +8187,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.5, mkdirp@npm:^0.5.6": +"mkdirp@npm:^0.5.5": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" dependencies: @@ -8127,22 +8228,15 @@ __metadata: languageName: node linkType: hard -"multicast-dns-service-types@npm:^1.1.0": - version: 1.1.0 - resolution: "multicast-dns-service-types@npm:1.1.0" - checksum: 0979fca1cce85484d256e4db3af591d941b41a61f134da3607213d2624c12ed5b8a246565cb19a9b3cb542819e8fbc71a90b07e77023ee6a9515540fe1d371f7 - languageName: node - linkType: hard - -"multicast-dns@npm:^6.0.1": - version: 6.2.3 - resolution: "multicast-dns@npm:6.2.3" +"multicast-dns@npm:^7.2.5": + version: 7.2.5 + resolution: "multicast-dns@npm:7.2.5" dependencies: - dns-packet: ^1.3.1 + dns-packet: ^5.2.2 thunky: ^1.0.2 bin: multicast-dns: cli.js - checksum: f515b49ca964429ab48a4ac8041fcf969c927aeb49ab65288bd982e52c849a870fc3b03565780b0d194a1a02da8821f28b6425e48e95b8107bc9fcc92f571a6f + checksum: 00b8a57df152d4cd0297946320a94b7c3cdf75a46a2247f32f958a8927dea42958177f9b7fdae69fab2e4e033fb3416881af1f5e9055a3e1542888767139e2fb languageName: node linkType: hard @@ -8169,20 +8263,20 @@ __metadata: languageName: node linkType: hard -"needle@npm:^2.5.2": - version: 2.9.1 - resolution: "needle@npm:2.9.1" +"needle@npm:^3.1.0": + version: 3.2.0 + resolution: "needle@npm:3.2.0" dependencies: debug: ^3.2.6 - iconv-lite: ^0.4.4 + iconv-lite: ^0.6.3 sax: ^1.2.4 bin: - needle: ./bin/needle - checksum: 746ae3a3782f0a057ff304a98843cc6f2009f978a0fad0c3e641a9d46d0b5702bb3e197ba08aecd48678067874a991c4f5fc320c7e51a4c041d9dd3441146cf0 + needle: bin/needle + checksum: d6f3e8668bbaf943d28ced0ad843eff793b56025e80152e511fd02313b8974e4dd9674bcbe3d8f9aa31882adb190dafe29ea5fce03a92b4724adf4850070bcfc languageName: node linkType: hard -"negotiator@npm:0.6.3, negotiator@npm:^0.6.2, negotiator@npm:^0.6.3": +"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 @@ -8261,27 +8355,7 @@ __metadata: languageName: node linkType: hard -"node-gyp@npm:^8.2.0": - version: 8.4.1 - resolution: "node-gyp@npm:8.4.1" - dependencies: - env-paths: ^2.2.0 - glob: ^7.1.4 - graceful-fs: ^4.2.6 - make-fetch-happen: ^9.1.0 - nopt: ^5.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 - semver: ^7.3.5 - tar: ^6.1.2 - which: ^2.0.2 - bin: - node-gyp: bin/node-gyp.js - checksum: 341710b5da39d3660e6a886b37e210d33f8282047405c2e62c277bcc744c7552c5b8b972ebc3a7d5c2813794e60cc48c3ebd142c46d6e0321db4db6c92dd0355 - languageName: node - linkType: hard - -"node-gyp@npm:latest": +"node-gyp@npm:^9.0.0, node-gyp@npm:latest": version: 9.3.1 resolution: "node-gyp@npm:9.3.1" dependencies: @@ -8308,17 +8382,6 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^5.0.0": - version: 5.0.0 - resolution: "nopt@npm:5.0.0" - dependencies: - abbrev: 1 - bin: - nopt: bin/nopt.js - checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f - languageName: node - linkType: hard - "nopt@npm:^6.0.0": version: 6.0.0 resolution: "nopt@npm:6.0.0" @@ -8354,6 +8417,18 @@ __metadata: languageName: node linkType: hard +"normalize-package-data@npm:^4.0.0": + version: 4.0.1 + resolution: "normalize-package-data@npm:4.0.1" + dependencies: + hosted-git-info: ^5.0.0 + is-core-module: ^2.8.1 + semver: ^7.3.5 + validate-npm-package-license: ^3.0.4 + checksum: 292e0aa740e73d62f84bbd9d55d4bfc078155f32d5d7572c32c9807f96d543af0f43ff7e5c80bfa6238667123fd68bd83cd412eae9b27b85b271fb041f624528 + languageName: node + linkType: hard + "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" @@ -8377,12 +8452,21 @@ __metadata: languageName: node linkType: hard -"npm-install-checks@npm:^4.0.0": - version: 4.0.0 - resolution: "npm-install-checks@npm:4.0.0" +"npm-bundled@npm:^2.0.0": + version: 2.0.1 + resolution: "npm-bundled@npm:2.0.1" + dependencies: + npm-normalize-package-bin: ^2.0.0 + checksum: 7747293985c48c5268871efe691545b03731cb80029692000cbdb0b3344b9617be5187aa36281cabbe6b938e3651b4e87236d1c31f9e645eef391a1a779413e6 + languageName: node + linkType: hard + +"npm-install-checks@npm:^5.0.0": + version: 5.0.0 + resolution: "npm-install-checks@npm:5.0.0" dependencies: semver: ^7.1.1 - checksum: 8308ff48e61e0863d7f148f62543e1f6c832525a7d8002ea742d5e478efa8b29bf65a87f9fb82786e15232e4b3d0362b126c45afdceed4c051c0d3c227dd0ace + checksum: 0e7d1aae52b1fe9d3a0fd4a008850c7047931722dd49ee908afd13fd0297ac5ddb10964d9c59afcdaaa2ca04b51d75af2788f668c729ae71fec0e4cdac590ffc languageName: node linkType: hard @@ -8393,54 +8477,87 @@ __metadata: languageName: node linkType: hard -"npm-package-arg@npm:8.1.5, npm-package-arg@npm:^8.0.1, npm-package-arg@npm:^8.1.2, npm-package-arg@npm:^8.1.5": - version: 8.1.5 - resolution: "npm-package-arg@npm:8.1.5" +"npm-normalize-package-bin@npm:^2.0.0": + version: 2.0.0 + resolution: "npm-normalize-package-bin@npm:2.0.0" + checksum: 7c5379f9b188b564c4332c97bdd9a5d6b7b15f02b5823b00989d6a0e6fb31eb0280f02b0a924f930e1fcaf00e60fae333aec8923d2a4c7747613c7d629d8aa25 + languageName: node + linkType: hard + +"npm-package-arg@npm:9.1.0": + version: 9.1.0 + resolution: "npm-package-arg@npm:9.1.0" dependencies: - hosted-git-info: ^4.0.1 - semver: ^7.3.4 - validate-npm-package-name: ^3.0.0 - checksum: ae76afbcebb4ea8d0b849b8b18ed1b0491030fb04a0af5d75f1b8390cc50bec186ced9fbe60f47d939eab630c7c0db0919d879ac56a87d3782267dfe8eec60d3 + hosted-git-info: ^5.0.0 + proc-log: ^2.0.1 + semver: ^7.3.5 + validate-npm-package-name: ^4.0.0 + checksum: 277c21477731a4f1e31bde36f0db5f5470deb2a008db2aaf1b015d588b23cb225c75f90291ea241235e86682a03de972bbe69fc805c921a786ea9616955990b9 languageName: node linkType: hard -"npm-packlist@npm:^3.0.0": - version: 3.0.0 - resolution: "npm-packlist@npm:3.0.0" +"npm-package-arg@npm:^9.0.0, npm-package-arg@npm:^9.0.1": + version: 9.1.2 + resolution: "npm-package-arg@npm:9.1.2" dependencies: - glob: ^7.1.6 - ignore-walk: ^4.0.1 - npm-bundled: ^1.1.1 - npm-normalize-package-bin: ^1.0.1 + hosted-git-info: ^5.0.0 + proc-log: ^2.0.1 + semver: ^7.3.5 + validate-npm-package-name: ^4.0.0 + checksum: 3793488843985ed71deb14fcba7c068d8ed03a18fd8f6b235c6a64465c9a25f60261598106d5cc8677c0bee9548e405c34c2e3c7a822e3113d3389351c745dfa + languageName: node + linkType: hard + +"npm-packlist@npm:^5.1.0": + version: 5.1.3 + resolution: "npm-packlist@npm:5.1.3" + dependencies: + glob: ^8.0.1 + ignore-walk: ^5.0.1 + npm-bundled: ^2.0.0 + npm-normalize-package-bin: ^2.0.0 bin: npm-packlist: bin/index.js - checksum: 8550ecdec5feb2708aa8289e71c3e9ed72dd792642dd3d2c871955504c0e460bc1c2106483a164eb405b3cdfcfddf311315d4a647fca1a511f710654c015a91e + checksum: 94cc9c66740e8f80243301de85eb0a2cec5bbd570c3f26b6ad7af1a3eca155f7e810580dc7ea4448f12a8fd82f6db307e7132a5fe69e157eb45b325acadeb22a languageName: node linkType: hard -"npm-pick-manifest@npm:6.1.1, npm-pick-manifest@npm:^6.0.0, npm-pick-manifest@npm:^6.1.1": - version: 6.1.1 - resolution: "npm-pick-manifest@npm:6.1.1" +"npm-pick-manifest@npm:7.0.1": + version: 7.0.1 + resolution: "npm-pick-manifest@npm:7.0.1" dependencies: - npm-install-checks: ^4.0.0 + npm-install-checks: ^5.0.0 npm-normalize-package-bin: ^1.0.1 - npm-package-arg: ^8.1.2 - semver: ^7.3.4 - checksum: 7a7b9475ae95cf903d37471229efbd12a829a9a7a1020ba36e75768aaa35da4c3a087fde3f06070baf81ec6b2ea2b660f022a1172644e6e7188199d7c1d2954b + npm-package-arg: ^9.0.0 + semver: ^7.3.5 + checksum: 9a4a8e64d2214783b2b74a361845000f5d91bb40c7858e2a30af2ac7876d9296efc37f8cacf60335e96a45effee2035b033d9bdefb4889757cc60d85959accbb + languageName: node + linkType: hard + +"npm-pick-manifest@npm:^7.0.0": + version: 7.0.2 + resolution: "npm-pick-manifest@npm:7.0.2" + dependencies: + npm-install-checks: ^5.0.0 + npm-normalize-package-bin: ^2.0.0 + npm-package-arg: ^9.0.0 + semver: ^7.3.5 + checksum: a93ec449c12219a2be8556837db9ac5332914f304a69469bb6f1f47717adc6e262aa318f79166f763512688abd9c4e4b6a2d83b2dd19753a7abe5f0360f2c8bc languageName: node linkType: hard -"npm-registry-fetch@npm:^12.0.0": - version: 12.0.2 - resolution: "npm-registry-fetch@npm:12.0.2" +"npm-registry-fetch@npm:^13.0.1": + version: 13.3.1 + resolution: "npm-registry-fetch@npm:13.3.1" dependencies: - make-fetch-happen: ^10.0.1 + make-fetch-happen: ^10.0.6 minipass: ^3.1.6 - minipass-fetch: ^1.4.1 + minipass-fetch: ^2.0.3 minipass-json-stream: ^1.0.1 minizlib: ^2.1.2 - npm-package-arg: ^8.1.5 - checksum: 88ef49b6fad104165f183ec804a65471a23cead40fa035ac57f2cbe084feffe9c10bed8c4234af3fa549d947108450d5359b41ae5dec9a1ffca4d8fa7c7f78b8 + npm-package-arg: ^9.0.1 + proc-log: ^2.0.0 + checksum: 5a941c2c799568e0dbccfc15f280444da398dadf2eede1b1921f08ddd5cb5f32c7cb4d16be96401f95a33073aeec13a3fd928c753790d3c412c2e64e7f7c6ee4 languageName: node linkType: hard @@ -8587,23 +8704,6 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.0.1": - version: 1.1.5 - resolution: "object-is@npm:1.1.5" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe - languageName: node - linkType: hard - -"object-keys@npm:^1.1.1": - version: 1.1.1 - resolution: "object-keys@npm:1.1.1" - checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a - languageName: node - linkType: hard - "obuf@npm:^1.0.0, obuf@npm:^1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" @@ -8765,32 +8865,34 @@ __metadata: languageName: node linkType: hard -"pacote@npm:12.0.3": - version: 12.0.3 - resolution: "pacote@npm:12.0.3" +"pacote@npm:13.6.2": + version: 13.6.2 + resolution: "pacote@npm:13.6.2" dependencies: - "@npmcli/git": ^2.1.0 - "@npmcli/installed-package-contents": ^1.0.6 - "@npmcli/promise-spawn": ^1.2.0 - "@npmcli/run-script": ^2.0.0 - cacache: ^15.0.5 + "@npmcli/git": ^3.0.0 + "@npmcli/installed-package-contents": ^1.0.7 + "@npmcli/promise-spawn": ^3.0.0 + "@npmcli/run-script": ^4.1.0 + cacache: ^16.0.0 chownr: ^2.0.0 fs-minipass: ^2.1.0 infer-owner: ^1.0.4 - minipass: ^3.1.3 - mkdirp: ^1.0.3 - npm-package-arg: ^8.0.1 - npm-packlist: ^3.0.0 - npm-pick-manifest: ^6.0.0 - npm-registry-fetch: ^12.0.0 + minipass: ^3.1.6 + mkdirp: ^1.0.4 + npm-package-arg: ^9.0.0 + npm-packlist: ^5.1.0 + npm-pick-manifest: ^7.0.0 + npm-registry-fetch: ^13.0.1 + proc-log: ^2.0.0 promise-retry: ^2.0.1 - read-package-json-fast: ^2.0.1 + read-package-json: ^5.0.0 + read-package-json-fast: ^2.0.3 rimraf: ^3.0.2 - ssri: ^8.0.1 - tar: ^6.1.0 + ssri: ^9.0.0 + tar: ^6.1.11 bin: pacote: lib/bin.js - checksum: 730e2b344619daff078b1f7c085c2da3b1417f1667204384cba981409098af2375b130a6470f75ea22f09b83c00fe227143b68e50d0dd7ff972e28a697b9c1d5 + checksum: a7b7f97094ab570a23e1c174537e9953a4d53176cc4b18bac77d7728bd89e2b9fa331d0f78fa463add03df79668a918bbdaa2750819504ee39242063abf53c6e languageName: node linkType: hard @@ -9018,18 +9120,7 @@ __metadata: languageName: node linkType: hard -"portfinder@npm:^1.0.28": - version: 1.0.32 - resolution: "portfinder@npm:1.0.32" - dependencies: - async: ^2.6.4 - debug: ^3.2.7 - mkdirp: ^0.5.6 - checksum: 116b4aed1b9e16f6d5503823d966d9ffd41b1c2339e27f54c06cd2f3015a9d8ef53e2a53b57bc0a25af0885977b692007353aa28f9a0a98a44335cb50487240d - languageName: node - linkType: hard - -"postcss-attribute-case-insensitive@npm:^5.0.0": +"postcss-attribute-case-insensitive@npm:^5.0.2": version: 5.0.2 resolution: "postcss-attribute-case-insensitive@npm:5.0.2" dependencies: @@ -9040,7 +9131,18 @@ __metadata: languageName: node linkType: hard -"postcss-color-functional-notation@npm:^4.2.1": +"postcss-clamp@npm:^4.1.0": + version: 4.1.0 + resolution: "postcss-clamp@npm:4.1.0" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.4.6 + checksum: 118eec936b3b035dc8d75c89973408f15c5a3de3d1ee210a2b3511e3e431d9c56e6f354b509a90540241e2225ffe3caaa2fdf25919c63348ce4583a28ada642c + languageName: node + linkType: hard + +"postcss-color-functional-notation@npm:^4.2.4": version: 4.2.4 resolution: "postcss-color-functional-notation@npm:4.2.4" dependencies: @@ -9051,7 +9153,7 @@ __metadata: languageName: node linkType: hard -"postcss-color-hex-alpha@npm:^8.0.2": +"postcss-color-hex-alpha@npm:^8.0.4": version: 8.0.4 resolution: "postcss-color-hex-alpha@npm:8.0.4" dependencies: @@ -9062,7 +9164,7 @@ __metadata: languageName: node linkType: hard -"postcss-color-rebeccapurple@npm:^7.0.2": +"postcss-color-rebeccapurple@npm:^7.1.1": version: 7.1.1 resolution: "postcss-color-rebeccapurple@npm:7.1.1" dependencies: @@ -9073,7 +9175,7 @@ __metadata: languageName: node linkType: hard -"postcss-custom-media@npm:^8.0.0": +"postcss-custom-media@npm:^8.0.2": version: 8.0.2 resolution: "postcss-custom-media@npm:8.0.2" dependencies: @@ -9084,7 +9186,7 @@ __metadata: languageName: node linkType: hard -"postcss-custom-properties@npm:^12.1.2": +"postcss-custom-properties@npm:^12.1.8": version: 12.1.11 resolution: "postcss-custom-properties@npm:12.1.11" dependencies: @@ -9095,7 +9197,7 @@ __metadata: languageName: node linkType: hard -"postcss-custom-selectors@npm:^6.0.0": +"postcss-custom-selectors@npm:^6.0.3": version: 6.0.3 resolution: "postcss-custom-selectors@npm:6.0.3" dependencies: @@ -9106,7 +9208,7 @@ __metadata: languageName: node linkType: hard -"postcss-dir-pseudo-class@npm:^6.0.3": +"postcss-dir-pseudo-class@npm:^6.0.5": version: 6.0.5 resolution: "postcss-dir-pseudo-class@npm:6.0.5" dependencies: @@ -9117,7 +9219,7 @@ __metadata: languageName: node linkType: hard -"postcss-double-position-gradients@npm:^3.0.4": +"postcss-double-position-gradients@npm:^3.1.2": version: 3.1.2 resolution: "postcss-double-position-gradients@npm:3.1.2" dependencies: @@ -9129,7 +9231,7 @@ __metadata: languageName: node linkType: hard -"postcss-env-function@npm:^4.0.4": +"postcss-env-function@npm:^4.0.6": version: 4.0.6 resolution: "postcss-env-function@npm:4.0.6" dependencies: @@ -9140,7 +9242,7 @@ __metadata: languageName: node linkType: hard -"postcss-focus-visible@npm:^6.0.3": +"postcss-focus-visible@npm:^6.0.4": version: 6.0.4 resolution: "postcss-focus-visible@npm:6.0.4" dependencies: @@ -9151,7 +9253,7 @@ __metadata: languageName: node linkType: hard -"postcss-focus-within@npm:^5.0.3": +"postcss-focus-within@npm:^5.0.4": version: 5.0.4 resolution: "postcss-focus-within@npm:5.0.4" dependencies: @@ -9171,7 +9273,7 @@ __metadata: languageName: node linkType: hard -"postcss-gap-properties@npm:^3.0.2": +"postcss-gap-properties@npm:^3.0.5": version: 3.0.5 resolution: "postcss-gap-properties@npm:3.0.5" peerDependencies: @@ -9180,7 +9282,7 @@ __metadata: languageName: node linkType: hard -"postcss-image-set-function@npm:^4.0.4": +"postcss-image-set-function@npm:^4.0.7": version: 4.0.7 resolution: "postcss-image-set-function@npm:4.0.7" dependencies: @@ -9191,16 +9293,16 @@ __metadata: languageName: node linkType: hard -"postcss-import@npm:14.0.2": - version: 14.0.2 - resolution: "postcss-import@npm:14.0.2" +"postcss-import@npm:15.0.0": + version: 15.0.0 + resolution: "postcss-import@npm:15.0.0" dependencies: postcss-value-parser: ^4.0.0 read-cache: ^1.0.0 resolve: ^1.1.7 peerDependencies: postcss: ^8.0.0 - checksum: e9f42b5956b1231f8516da11268021222d61969befc142bb7d0a65d9d3e53bdc574e51486b6ffdf72c0b395ce19060663eab5b5504b3d0855c173357425f9fb5 + checksum: e5048072514f33520348c0c5aaedb5db92da72188e72a7367c26a8d851b9cf92e56aeda30d9c7b4ed1a34f84a58959da1b4b27b4aa23d41fc0ce36efe00bf1d1 languageName: node linkType: hard @@ -9213,7 +9315,7 @@ __metadata: languageName: node linkType: hard -"postcss-lab-function@npm:^4.0.3": +"postcss-lab-function@npm:^4.2.1": version: 4.2.1 resolution: "postcss-lab-function@npm:4.2.1" dependencies: @@ -9225,21 +9327,21 @@ __metadata: languageName: node linkType: hard -"postcss-loader@npm:6.2.1": - version: 6.2.1 - resolution: "postcss-loader@npm:6.2.1" +"postcss-loader@npm:7.0.1": + version: 7.0.1 + resolution: "postcss-loader@npm:7.0.1" dependencies: cosmiconfig: ^7.0.0 klona: ^2.0.5 - semver: ^7.3.5 + semver: ^7.3.7 peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 - checksum: e40ae79c3e39df37014677a817b001bd115d8b10dedf53a07b97513d93b1533cd702d7a48831bdd77b9a9484b1ec84a5d4a723f80e83fb28682c75b5e65e8a90 + checksum: 2a3cbcaaade598d4919824d384ae34ffbfc14a9c8db6cc3b154582356f4f44a1c9af9e731b81cf1947b089accf7d0ab7a0c51c717946985f89aa1708d2b4304d languageName: node linkType: hard -"postcss-logical@npm:^5.0.3": +"postcss-logical@npm:^5.0.4": version: 5.0.4 resolution: "postcss-logical@npm:5.0.4" peerDependencies: @@ -9308,7 +9410,7 @@ __metadata: languageName: node linkType: hard -"postcss-nesting@npm:^10.1.2": +"postcss-nesting@npm:^10.1.10": version: 10.2.0 resolution: "postcss-nesting@npm:10.2.0" dependencies: @@ -9320,7 +9422,16 @@ __metadata: languageName: node linkType: hard -"postcss-overflow-shorthand@npm:^3.0.2": +"postcss-opacity-percentage@npm:^1.1.2": + version: 1.1.3 + resolution: "postcss-opacity-percentage@npm:1.1.3" + peerDependencies: + postcss: ^8.2 + checksum: 54d1b8ca68035bc1a5788aaabdbc3b66ffee34b5a2412cecf073627dad7e3f2bae07c01fac3bc7f46bbac5da3291ac9ddcf74bfee26dfd86f9f96c847a0afc13 + languageName: node + linkType: hard + +"postcss-overflow-shorthand@npm:^3.0.4": version: 3.0.4 resolution: "postcss-overflow-shorthand@npm:3.0.4" dependencies: @@ -9340,7 +9451,7 @@ __metadata: languageName: node linkType: hard -"postcss-place@npm:^7.0.3": +"postcss-place@npm:^7.0.5": version: 7.0.5 resolution: "postcss-place@npm:7.0.5" dependencies: @@ -9351,50 +9462,66 @@ __metadata: languageName: node linkType: hard -"postcss-preset-env@npm:7.2.3": - version: 7.2.3 - resolution: "postcss-preset-env@npm:7.2.3" - dependencies: - autoprefixer: ^10.4.2 - browserslist: ^4.19.1 - caniuse-lite: ^1.0.30001299 - css-blank-pseudo: ^3.0.2 - css-has-pseudo: ^3.0.3 - css-prefers-color-scheme: ^6.0.2 - cssdb: ^5.0.0 - postcss-attribute-case-insensitive: ^5.0.0 - postcss-color-functional-notation: ^4.2.1 - postcss-color-hex-alpha: ^8.0.2 - postcss-color-rebeccapurple: ^7.0.2 - postcss-custom-media: ^8.0.0 - postcss-custom-properties: ^12.1.2 - postcss-custom-selectors: ^6.0.0 - postcss-dir-pseudo-class: ^6.0.3 - postcss-double-position-gradients: ^3.0.4 - postcss-env-function: ^4.0.4 - postcss-focus-visible: ^6.0.3 - postcss-focus-within: ^5.0.3 +"postcss-preset-env@npm:7.8.0": + version: 7.8.0 + resolution: "postcss-preset-env@npm:7.8.0" + dependencies: + "@csstools/postcss-cascade-layers": ^1.0.5 + "@csstools/postcss-color-function": ^1.1.1 + "@csstools/postcss-font-format-keywords": ^1.0.1 + "@csstools/postcss-hwb-function": ^1.0.2 + "@csstools/postcss-ic-unit": ^1.0.1 + "@csstools/postcss-is-pseudo-class": ^2.0.7 + "@csstools/postcss-nested-calc": ^1.0.0 + "@csstools/postcss-normalize-display-values": ^1.0.1 + "@csstools/postcss-oklab-function": ^1.1.1 + "@csstools/postcss-progressive-custom-properties": ^1.3.0 + "@csstools/postcss-stepped-value-functions": ^1.0.1 + "@csstools/postcss-text-decoration-shorthand": ^1.0.0 + "@csstools/postcss-trigonometric-functions": ^1.0.2 + "@csstools/postcss-unset-value": ^1.0.2 + autoprefixer: ^10.4.8 + browserslist: ^4.21.3 + css-blank-pseudo: ^3.0.3 + css-has-pseudo: ^3.0.4 + css-prefers-color-scheme: ^6.0.3 + cssdb: ^7.0.0 + postcss-attribute-case-insensitive: ^5.0.2 + postcss-clamp: ^4.1.0 + postcss-color-functional-notation: ^4.2.4 + postcss-color-hex-alpha: ^8.0.4 + postcss-color-rebeccapurple: ^7.1.1 + postcss-custom-media: ^8.0.2 + postcss-custom-properties: ^12.1.8 + postcss-custom-selectors: ^6.0.3 + postcss-dir-pseudo-class: ^6.0.5 + postcss-double-position-gradients: ^3.1.2 + postcss-env-function: ^4.0.6 + postcss-focus-visible: ^6.0.4 + postcss-focus-within: ^5.0.4 postcss-font-variant: ^5.0.0 - postcss-gap-properties: ^3.0.2 - postcss-image-set-function: ^4.0.4 + postcss-gap-properties: ^3.0.5 + postcss-image-set-function: ^4.0.7 postcss-initial: ^4.0.1 - postcss-lab-function: ^4.0.3 - postcss-logical: ^5.0.3 + postcss-lab-function: ^4.2.1 + postcss-logical: ^5.0.4 postcss-media-minmax: ^5.0.0 - postcss-nesting: ^10.1.2 - postcss-overflow-shorthand: ^3.0.2 + postcss-nesting: ^10.1.10 + postcss-opacity-percentage: ^1.1.2 + postcss-overflow-shorthand: ^3.0.4 postcss-page-break: ^3.0.4 - postcss-place: ^7.0.3 - postcss-pseudo-class-any-link: ^7.0.2 + postcss-place: ^7.0.5 + postcss-pseudo-class-any-link: ^7.1.6 postcss-replace-overflow-wrap: ^4.0.0 - postcss-selector-not: ^5.0.0 + postcss-selector-not: ^6.0.1 + postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.4 - checksum: ee2f3a1e7855817b22e0aa44174d2efce0db8da92c6bfa40cfc97f9c27d77c843486879f7fc3ecbb700e9571b3cb9d8d37a3ff77d419ed34eddc90e64aef849c + postcss: ^8.2 + checksum: 7c07f6ecc776dc8063bfffbb8e44b88730cde0c8951c9960263c38bdb0c5103ace41f34b01eac0ff4861b00384e44ff450f7861f34072a50850afb862af4d6a8 languageName: node linkType: hard -"postcss-pseudo-class-any-link@npm:^7.0.2": +"postcss-pseudo-class-any-link@npm:^7.1.6": version: 7.1.6 resolution: "postcss-pseudo-class-any-link@npm:7.1.6" dependencies: @@ -9439,14 +9566,14 @@ __metadata: languageName: node linkType: hard -"postcss-selector-not@npm:^5.0.0": - version: 5.0.0 - resolution: "postcss-selector-not@npm:5.0.0" +"postcss-selector-not@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-selector-not@npm:6.0.1" dependencies: - balanced-match: ^1.0.0 + postcss-selector-parser: ^6.0.10 peerDependencies: - postcss: ^8.1.0 - checksum: eb7bdfdd665b2f0db660d4a2061f103b96d7c326a4b9d6241d55bf32bdcd1f5defaa4c8251123c73e1bcc75dad5a2ce77c520e42ce26ecd1e42f2f842baa155f + postcss: ^8.2 + checksum: fe523a0219e4bd34f04498534bb9e8aec3193f3585eafe4c388d086955b41201cae71fd20980ca465acade7f182029b43dbd5ca7e9d50bf34bbcaf1d19fe3ee6 languageName: node linkType: hard @@ -9467,7 +9594,18 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.5, postcss@npm:^8.2.14, postcss@npm:^8.2.15, postcss@npm:^8.3.7": +"postcss@npm:8.4.16": + version: 8.4.16 + resolution: "postcss@npm:8.4.16" + dependencies: + nanoid: ^3.3.4 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f + languageName: node + linkType: hard + +"postcss@npm:^8.2.14, postcss@npm:^8.3.7": version: 8.4.5 resolution: "postcss@npm:8.4.5" dependencies: @@ -9478,7 +9616,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.19": +"postcss@npm:^8.4.19, postcss@npm:^8.4.7": version: 8.4.21 resolution: "postcss@npm:8.4.21" dependencies: @@ -9553,6 +9691,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^2.0.0, proc-log@npm:^2.0.1": + version: 2.0.1 + resolution: "proc-log@npm:2.0.1" + checksum: f6f23564ff759097db37443e6e2765af84979a703d2c52c1b9df506ee9f87caa101ba49d8fdc115c1a313ec78e37e8134704e9069e6a870f3499d98bb24c436f + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -9734,7 +9879,7 @@ __metadata: languageName: node linkType: hard -"read-package-json-fast@npm:^2.0.1": +"read-package-json-fast@npm:^2.0.3": version: 2.0.3 resolution: "read-package-json-fast@npm:2.0.3" dependencies: @@ -9744,6 +9889,18 @@ __metadata: languageName: node linkType: hard +"read-package-json@npm:^5.0.0": + version: 5.0.2 + resolution: "read-package-json@npm:5.0.2" + dependencies: + glob: ^8.0.1 + json-parse-even-better-errors: ^2.3.1 + normalize-package-data: ^4.0.0 + npm-normalize-package-bin: ^2.0.0 + checksum: 0882ac9cec1bc92fb5515e9727611fb2909351e1e5c840dce3503cbb25b4cd48eb44b61071986e0fc51043208161f07d364a7336206c8609770186818753b51a + languageName: node + linkType: hard + "read-pkg-up@npm:^7.0.1": version: 7.0.1 resolution: "read-pkg-up@npm:7.0.1" @@ -9865,17 +10022,6 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.2.0": - version: 1.4.3 - resolution: "regexp.prototype.flags@npm:1.4.3" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - functions-have-names: ^1.2.2 - checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 - languageName: node - linkType: hard - "regexpp@npm:^3.2.0": version: 3.2.0 resolution: "regexpp@npm:3.2.0" @@ -9998,7 +10144,20 @@ __metadata: languageName: node linkType: hard -"resolve@npm:1.22.0, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": +"resolve@npm:1.22.1": + version: 1.22.1 + resolution: "resolve@npm:1.22.1" + dependencies: + is-core-module: ^2.9.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e + languageName: node + linkType: hard + +"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": version: 1.22.0 resolution: "resolve@npm:1.22.0" dependencies: @@ -10011,7 +10170,20 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@1.22.0#~builtin, resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": +"resolve@patch:resolve@1.22.1#~builtin": + version: 1.22.1 + resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" + dependencies: + is-core-module: ^2.9.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b + languageName: node + linkType: hard + +"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": version: 1.22.0 resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" dependencies: @@ -10109,7 +10281,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.5.x, rxjs@npm:^6.5.2, rxjs@npm:^6.5.4": +"rxjs@npm:6.5.x, rxjs@npm:^6.5.4": version: 6.5.5 resolution: "rxjs@npm:6.5.5" dependencies: @@ -10127,7 +10299,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": +"rxjs@npm:^7.5.5": version: 7.8.0 resolution: "rxjs@npm:7.8.0" dependencies: @@ -10150,16 +10322,16 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:^2.1.2, safer-buffer@npm:~2.1.0": +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 languageName: node linkType: hard -"sass-loader@npm:12.4.0": - version: 12.4.0 - resolution: "sass-loader@npm:12.4.0" +"sass-loader@npm:13.0.2": + version: 13.0.2 + resolution: "sass-loader@npm:13.0.2" dependencies: klona: ^2.0.4 neo-async: ^2.6.2 @@ -10167,6 +10339,7 @@ __metadata: fibers: ">= 3.1.0" node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 sass: ^1.3.0 + sass-embedded: "*" webpack: ^5.0.0 peerDependenciesMeta: fibers: @@ -10175,20 +10348,22 @@ __metadata: optional: true sass: optional: true - checksum: 0f7ca3633e7f61c412b0628766a9b57cb15f68def45e4303e68eb2f3a0722aec231956fbfd118489d93c997ab605470e89de8e3f7d6776830cc6366d9657d618 + sass-embedded: + optional: true + checksum: 6306712cc75bd4b4260ce67178778c92110089485f358b5956b6b12aba15a55e5c7287154daaf8b32678891df915b7bcda7356991afe8bf08ca7356ed30eb448 languageName: node linkType: hard -"sass@npm:1.49.9": - version: 1.49.9 - resolution: "sass@npm:1.49.9" +"sass@npm:1.54.4": + version: 1.54.4 + resolution: "sass@npm:1.54.4" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: e5653e3499274c5127dcb5c9e7c5f6930378fc61764d999a5d8965782e027181ed09714f94836dec74ef55e3a858107fe6c571954c0cab0ad0be5ab8e586829c + checksum: bb6aead09764de450a02b9a66e4ee538f0ba6bc8f2fc3905c71b2c302b5f47e089b510b86cfa7ef2d4139c210c8abf99fe157e7a5bd356c057f10d29e6c4b44c languageName: node linkType: hard @@ -10261,7 +10436,7 @@ __metadata: languageName: node linkType: hard -"selfsigned@npm:^2.0.0": +"selfsigned@npm:^2.0.1": version: 2.1.1 resolution: "selfsigned@npm:2.1.1" dependencies: @@ -10290,14 +10465,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.3.5, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": - version: 7.3.5 - resolution: "semver@npm:7.3.5" +"semver@npm:7.3.7": + version: 7.3.7 + resolution: "semver@npm:7.3.7" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 5eafe6102bea2a7439897c1856362e31cc348ccf96efd455c8b5bc2c61e6f7e7b8250dc26b8828c1d76a56f818a7ee907a36ae9fb37a599d3d24609207001d60 + checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 languageName: node linkType: hard @@ -10310,6 +10485,28 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5": + version: 7.3.5 + resolution: "semver@npm:7.3.5" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 5eafe6102bea2a7439897c1856362e31cc348ccf96efd455c8b5bc2c61e6f7e7b8250dc26b8828c1d76a56f818a7ee907a36ae9fb37a599d3d24609207001d60 + languageName: node + linkType: hard + +"semver@npm:^7.3.7": + version: 7.3.8 + resolution: "semver@npm:7.3.8" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -10524,7 +10721,7 @@ __metadata: languageName: node linkType: hard -"sockjs@npm:^0.3.21": +"sockjs@npm:^0.3.24": version: 0.3.24 resolution: "sockjs@npm:0.3.24" dependencies: @@ -10535,17 +10732,6 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^6.0.0": - version: 6.2.1 - resolution: "socks-proxy-agent@npm:6.2.1" - dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 9ca089d489e5ee84af06741135c4b0d2022977dad27ac8d649478a114cdce87849e8d82b7c22b51501a4116e231241592946fc7fae0afc93b65030ee57084f58 - languageName: node - linkType: hard - "socks-proxy-agent@npm:^7.0.0": version: 7.0.0 resolution: "socks-proxy-agent@npm:7.0.0" @@ -10574,26 +10760,16 @@ __metadata: languageName: node linkType: hard -"source-map-loader@npm:3.0.1": - version: 3.0.1 - resolution: "source-map-loader@npm:3.0.1" +"source-map-loader@npm:4.0.0": + version: 4.0.0 + resolution: "source-map-loader@npm:4.0.0" dependencies: - abab: ^2.0.5 + abab: ^2.0.6 iconv-lite: ^0.6.3 - source-map-js: ^1.0.1 + source-map-js: ^1.0.2 peerDependencies: - webpack: ^5.0.0 - checksum: 6ff27ba9335307e64edaab8fb8f87aa82a88d7efb12260732f7e3649c3fffe8bd3f77b6970c39c0bdd5e3a9b2a5ed8f11ac805bea90a6c99f186aa52033e53e0 - languageName: node - linkType: hard - -"source-map-resolve@npm:^0.6.0": - version: 0.6.0 - resolution: "source-map-resolve@npm:0.6.0" - dependencies: - atob: ^2.1.2 - decode-uri-component: ^0.2.0 - checksum: fe503b9e5dac1c54be835282fcfec10879434e7b3ee08a9774f230299c724a8d403484d9531276d1670c87390e0e4d1d3f92b14cca6e4a2445ea3016b786ecd4 + webpack: ^5.72.1 + checksum: 0b169701735bd6a32d66bff84b7475c31066972d717ebef5a24476cc8678cd19c2b8ebe03a7d62ade9586d4f7ba55c17772fdc8d8bc159dcb9315c757a01046e languageName: node linkType: hard @@ -10623,10 +10799,10 @@ __metadata: languageName: node linkType: hard -"source-map@npm:0.7.3, source-map@npm:^0.7.3": - version: 0.7.3 - resolution: "source-map@npm:0.7.3" - checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea +"source-map@npm:0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 languageName: node linkType: hard @@ -10637,7 +10813,14 @@ __metadata: languageName: node linkType: hard -"sourcemap-codec@npm:^1.4.4, sourcemap-codec@npm:^1.4.8": +"source-map@npm:^0.7.3": + version: 0.7.3 + resolution: "source-map@npm:0.7.3" + checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea + languageName: node + linkType: hard + +"sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" checksum: b57981c05611afef31605732b598ccf65124a9fcb03b833532659ac4d29ac0f7bfacbc0d6c5a28a03e84c7510e7e556d758d0bb57786e214660016fb94279316 @@ -10733,15 +10916,6 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^8.0.0, ssri@npm:^8.0.1": - version: 8.0.1 - resolution: "ssri@npm:8.0.1" - dependencies: - minipass: ^3.1.1 - checksum: bc447f5af814fa9713aa201ec2522208ae0f4d8f3bda7a1f445a797c7b929a02720436ff7c478fb5edc4045adb02b1b88d2341b436a80798734e2494f1067b36 - languageName: node - linkType: hard - "ssri@npm:^9.0.0": version: 9.0.1 resolution: "ssri@npm:9.0.1" @@ -10841,7 +11015,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.0, strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1": version: 7.0.1 resolution: "strip-ansi@npm:7.0.1" dependencies: @@ -11071,33 +11245,32 @@ __metadata: languageName: node linkType: hard -"stylus-loader@npm:6.2.0": - version: 6.2.0 - resolution: "stylus-loader@npm:6.2.0" +"stylus-loader@npm:7.0.0": + version: 7.0.0 + resolution: "stylus-loader@npm:7.0.0" dependencies: - fast-glob: ^3.2.7 - klona: ^2.0.4 + fast-glob: ^3.2.11 + klona: ^2.0.5 normalize-path: ^3.0.0 peerDependencies: stylus: ">=0.52.4" webpack: ^5.0.0 - checksum: 0a7bf31f1d8b0c0a0733509523ec542216e17088d01405d0e5e2dca6c5bacca3863cf5d40f4a2ffd8722c471e2e15db553dd8c65512602242533617b4a0fd70a + checksum: 3bb09d45901865c7414a33d80b36bc32054fef325cd307d3648a7be5f0eca8ad76b7687aacb31d5e36ad216c6af7fcd92d091c8d08542306db7f87821edec10f languageName: node linkType: hard -"stylus@npm:0.56.0": - version: 0.56.0 - resolution: "stylus@npm:0.56.0" +"stylus@npm:0.59.0": + version: 0.59.0 + resolution: "stylus@npm:0.59.0" dependencies: - css: ^3.0.0 + "@adobe/css-tools": ^4.0.1 debug: ^4.3.2 glob: ^7.1.6 - safer-buffer: ^2.1.2 sax: ~1.2.4 source-map: ^0.7.3 bin: stylus: bin/stylus - checksum: 0b5827f772bcfcdd194d20a980114011128f42815aa3bfc2bfd0967686301313516d7623179f6bc0bbd4fb45abf21097debe7861dc27c0bea877c98e5594f3dc + checksum: 2faf4a5618747c17b7d10854e40efdd43b438a6cf99d197b0231578f5091bfe9313663d846b5666bb37140140420c51606bb127fd877bbf2db46730c01403b01 languageName: node linkType: hard @@ -11277,7 +11450,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.2": +"tar@npm:^6.1.11, tar@npm:^6.1.2": version: 6.1.13 resolution: "tar@npm:6.1.13" dependencies: @@ -11484,10 +11657,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.3.1": - version: 2.3.1 - resolution: "tslib@npm:2.3.1" - checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 +"tslib@npm:2.4.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 languageName: node linkType: hard @@ -11593,7 +11766,17 @@ __metadata: languageName: node linkType: hard -"typescript@npm:4.5.x, typescript@npm:^4.5.4": +"typescript@npm:4.8.4": + version: 4.8.4 + resolution: "typescript@npm:4.8.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 3e4f061658e0c8f36c820802fa809e0fd812b85687a9a2f5430bc3d0368e37d1c9605c3ce9b39df9a05af2ece67b1d844f9f6ea8ff42819f13bcb80f85629af0 + languageName: node + linkType: hard + +"typescript@npm:^4.5.4": version: 4.5.5 resolution: "typescript@npm:4.5.5" bin: @@ -11603,7 +11786,17 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@4.5.x#~builtin, typescript@patch:typescript@^4.5.4#~builtin": +"typescript@patch:typescript@4.8.4#~builtin": + version: 4.8.4 + resolution: "typescript@patch:typescript@npm%3A4.8.4#~builtin::version=4.8.4&hash=bda367" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 563a0ef47abae6df27a9a3ab38f75fc681f633ccf1a3502b1108e252e187787893de689220f4544aaf95a371a4eb3141e4a337deb9895de5ac3c1ca76430e5f0 + languageName: node + linkType: hard + +"typescript@patch:typescript@^4.5.4#~builtin": version: 4.5.5 resolution: "typescript@patch:typescript@npm%3A4.5.5#~builtin::version=4.5.5&hash=bda367" bin: @@ -11770,7 +11963,7 @@ __metadata: languageName: node linkType: hard -"validate-npm-package-license@npm:^3.0.1": +"validate-npm-package-license@npm:^3.0.1, validate-npm-package-license@npm:^3.0.4": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" dependencies: @@ -11780,12 +11973,12 @@ __metadata: languageName: node linkType: hard -"validate-npm-package-name@npm:^3.0.0": - version: 3.0.0 - resolution: "validate-npm-package-name@npm:3.0.0" +"validate-npm-package-name@npm:^4.0.0": + version: 4.0.0 + resolution: "validate-npm-package-name@npm:4.0.0" dependencies: - builtins: ^1.0.3 - checksum: ce4c68207abfb22c05eedb09ff97adbcedc80304a235a0844f5344f1fd5086aa80e4dbec5684d6094e26e35065277b765c1caef68bcea66b9056761eddb22967 + builtins: ^5.0.0 + checksum: a32fd537bad17fcb59cfd58ae95a414d443866020d448ec3b22e8d40550cb585026582a57efbe1f132b882eea4da8ac38ee35f7be0dd72988a3cb55d305a20c1 languageName: node linkType: hard @@ -11831,7 +12024,7 @@ __metadata: languageName: node linkType: hard -"watchpack@npm:^2.3.1": +"watchpack@npm:^2.4.0": version: 2.4.0 resolution: "watchpack@npm:2.4.0" dependencies: @@ -11890,54 +12083,54 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:5.3.0, webpack-dev-middleware@npm:^5.3.0": - version: 5.3.0 - resolution: "webpack-dev-middleware@npm:5.3.0" +"webpack-dev-middleware@npm:5.3.3, webpack-dev-middleware@npm:^5.3.1": + version: 5.3.3 + resolution: "webpack-dev-middleware@npm:5.3.3" dependencies: colorette: ^2.0.10 - memfs: ^3.2.2 + memfs: ^3.4.3 mime-types: ^2.1.31 range-parser: ^1.2.1 schema-utils: ^4.0.0 peerDependencies: webpack: ^4.0.0 || ^5.0.0 - checksum: 01f9e11583bb682cd5ab5a1b9d6dc99545f777513c4c15aa67d10f5d057fc3d0c6f9365e02c07792d3c9b17bd47a16c8185e66eb66e9de74d8ccf561e75085e7 + checksum: dd332cc6da61222c43d25e5a2155e23147b777ff32fdf1f1a0a8777020c072fbcef7756360ce2a13939c3f534c06b4992a4d659318c4a7fe2c0530b52a8a6621 languageName: node linkType: hard -"webpack-dev-server@npm:4.7.3": - version: 4.7.3 - resolution: "webpack-dev-server@npm:4.7.3" +"webpack-dev-server@npm:4.11.0": + version: 4.11.0 + resolution: "webpack-dev-server@npm:4.11.0" dependencies: "@types/bonjour": ^3.5.9 "@types/connect-history-api-fallback": ^1.3.5 + "@types/express": ^4.17.13 "@types/serve-index": ^1.9.1 + "@types/serve-static": ^1.13.10 "@types/sockjs": ^0.3.33 - "@types/ws": ^8.2.2 + "@types/ws": ^8.5.1 ansi-html-community: ^0.0.8 - bonjour: ^3.5.0 - chokidar: ^3.5.2 + bonjour-service: ^1.0.11 + chokidar: ^3.5.3 colorette: ^2.0.10 compression: ^1.7.4 - connect-history-api-fallback: ^1.6.0 + connect-history-api-fallback: ^2.0.0 default-gateway: ^6.0.3 - del: ^6.0.0 - express: ^4.17.1 + express: ^4.17.3 graceful-fs: ^4.2.6 html-entities: ^2.3.2 - http-proxy-middleware: ^2.0.0 + http-proxy-middleware: ^2.0.3 ipaddr.js: ^2.0.1 open: ^8.0.9 p-retry: ^4.5.0 - portfinder: ^1.0.28 + rimraf: ^3.0.2 schema-utils: ^4.0.0 - selfsigned: ^2.0.0 + selfsigned: ^2.0.1 serve-index: ^1.9.1 - sockjs: ^0.3.21 + sockjs: ^0.3.24 spdy: ^4.0.2 - strip-ansi: ^7.0.0 - webpack-dev-middleware: ^5.3.0 - ws: ^8.1.0 + webpack-dev-middleware: ^5.3.1 + ws: ^8.4.2 peerDependencies: webpack: ^4.37.0 || ^5.0.0 peerDependenciesMeta: @@ -11945,7 +12138,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 6062db1ba62e372ab3bd127f0c1a575a0758ad15338bff56e65f344bfa495d566049752a3e0c18d44469123e2f0cd2ba26cc0cab8d4ca704e8b4ace596871b21 + checksum: b2c7e9810138d0c6fa670a890410a74b29d4dd23d428e92e61e0233c542689f57caf9b31c031fbcc772170b6710b0752e81fa9cbc41ac0deefdc32d5749ce08d languageName: node linkType: hard @@ -11981,40 +12174,40 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.70.0": - version: 5.70.0 - resolution: "webpack@npm:5.70.0" +"webpack@npm:5.76.1": + version: 5.76.1 + resolution: "webpack@npm:5.76.1" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^0.0.51 "@webassemblyjs/ast": 1.11.1 "@webassemblyjs/wasm-edit": 1.11.1 "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.4.1 + acorn: ^8.7.1 acorn-import-assertions: ^1.7.6 browserslist: ^4.14.5 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.9.2 + enhanced-resolve: ^5.10.0 es-module-lexer: ^0.9.0 eslint-scope: 5.1.1 events: ^3.2.0 glob-to-regexp: ^0.4.1 graceful-fs: ^4.2.9 - json-parse-better-errors: ^1.0.2 + json-parse-even-better-errors: ^2.3.1 loader-runner: ^4.2.0 mime-types: ^2.1.27 neo-async: ^2.6.2 schema-utils: ^3.1.0 tapable: ^2.1.1 terser-webpack-plugin: ^5.1.3 - watchpack: ^2.3.1 + watchpack: ^2.4.0 webpack-sources: ^3.2.3 peerDependenciesMeta: webpack-cli: optional: true bin: webpack: bin/webpack.js - checksum: 00439884a9cdd5305aed3ce93735635785a15c5464a6d2cfce87e17727a07585de02420913e82aa85ddd2ae7322175d2cfda6ac0878a17f061cb605e6a7db57a + checksum: b01fe0bc2dbca0e10d290ddb0bf81e807a031de48028176e2b21afd696b4d3f25ab9accdad888ef4a1f7c7f4d41f13d5bf2395b7653fdf3e5e3dafa54e56dab2 languageName: node linkType: hard @@ -12127,7 +12320,22 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.1.0, ws@npm:~8.11.0": +"ws@npm:^8.4.2": + version: 8.13.0 + resolution: "ws@npm:8.13.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + languageName: node + linkType: hard + +"ws@npm:~8.11.0": version: 8.11.0 resolution: "ws@npm:8.11.0" peerDependencies: @@ -12201,7 +12409,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.0.0, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c @@ -12225,6 +12433,21 @@ __metadata: languageName: node linkType: hard +"yargs@npm:17.5.1": + version: 17.5.1 + resolution: "yargs@npm:17.5.1" + dependencies: + cliui: ^7.0.2 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.0.0 + checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde + languageName: node + linkType: hard + "yargs@npm:^15.3.1": version: 15.4.1 resolution: "yargs@npm:15.4.1" From e0bdde45ff9864c0aa6d3ef5efefc0ccc18ef067 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 13 Apr 2023 14:49:13 +0200 Subject: [PATCH 265/498] Re-added ngx-slider as everything broke without it --- frontend/angular.json | 2 +- frontend/package.json | 1 + frontend/src/flex.scss | 115 +++++++++++++++++++++++++++++++++++++++ frontend/src/styles.scss | 1 + frontend/yarn.lock | 34 +++++++++++- 5 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 frontend/src/flex.scss diff --git a/frontend/angular.json b/frontend/angular.json index 53319d70f..d0a906313 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -24,7 +24,7 @@ "tsConfig": "tsconfig.app.json", "aot": true, "assets": ["src/immutable", "src/config.json"], - "styles": ["./node_modules/@angular/material/prebuilt-themes/purple-green.css", "src/styles.scss"], + "styles": ["./node_modules/@angular/material/prebuilt-themes/purple-green.css", "src/styles.scss", "src/flex.scss"], "scripts": [] }, "configurations": { diff --git a/frontend/package.json b/frontend/package.json index 95257e03d..f0a050a41 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ }, "private": true, "dependencies": { + "@angular-slider/ngx-slider": "^2.0.4", "@angular/animations": "14.3.0", "@angular/cdk": "13.2.x", "@angular/common": "14.3.0", diff --git a/frontend/src/flex.scss b/frontend/src/flex.scss new file mode 100644 index 000000000..8258fb333 --- /dev/null +++ b/frontend/src/flex.scss @@ -0,0 +1,115 @@ +/** +Reusable classed to mimic functionality formerly provided by fxFlex + */ + +/** +Flex containers + */ +.flex { + display: flex; +} + +.flex-inline{ + display: inline-flex; +} + +.flex-row { + flex-flow: row wrap; +} + +.flex-column { + flex-flow: column wrap; +} + +.flex-nowrap{ + flex-wrap: nowrap; +} + +.content-start{ + justify-content: flex-start; +} +.content-end{ + justify-content: flex-end; +} + +.content-center{ + justify-content: center; +} + +.content-between { + justify-content: space-between; +} + +.content-around { + justify-content: space-around; +} + +.content-evenly{ + justify-content: space-evenly; +} + +.align-start{ + align-items: flex-start; +} + +.align-end { + align-items: flex-end; +} + +.align-center{ + align-items: center; +} + +.align-stretch{ + align-items: stretch; +} + +.align-baseline{ + align-items: baseline; +} +/** +Flex items + */ +.flex-default { + flex: 0 1 auto; +} + +.flex-none { + flex: none; +} + +.flex-auto { + flex: 1 1 auto; +} + +.flex-initial { + flex: 0 1 auto; +} + +.flex-1 { + flex: 1 1 0; +} + +.flex-2 { + flex: 2 1 0; +} + +.flex-3 { + flex: 3 1 0; +} + +.flex-4 { + flex: 4 1 0; +} + +.self-flex-start { + align-self: flex-start; +} + +.self-flex-end{ + align-self: flex-end; +} + +.self-flex-center { + align-self: center; +} diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 71c93a6c1..3d4856bbc 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -113,3 +113,4 @@ $warn: mat-palette($mat-red, 500); .pb1e { padding-bottom: 1em; } + diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 39eed5fcd..d9c68a1c4 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -260,6 +260,21 @@ __metadata: languageName: node linkType: hard +"@angular-slider/ngx-slider@npm:^2.0.4": + version: 2.0.4 + resolution: "@angular-slider/ngx-slider@npm:2.0.4" + dependencies: + detect-passive-events: ^2.0.3 + rxjs: ^6.5.2 + tslib: ^1.9.0 + peerDependencies: + "@angular/common": ">=6.1.0" + "@angular/core": ">=6.1.0" + "@angular/forms": ">=6.1.0" + checksum: 5dc10802e2976edb9e45618c27646f57498014162b25f69231291abf126bf86de7511e77736f1fd2b48db95568fcfe94b56d60cd123269b51346e9c9eea27352 + languageName: node + linkType: hard + "@angular/animations@npm:14.3.0": version: 14.3.0 resolution: "@angular/animations@npm:14.3.0" @@ -4818,6 +4833,13 @@ __metadata: languageName: node linkType: hard +"detect-it@npm:^4.0.1": + version: 4.0.1 + resolution: "detect-it@npm:4.0.1" + checksum: 013981eae87c0265169e11e09efcc1c78fb2fc3e956b0001eb8d741ce03784a3e8e6d60e1445105f510c52be2ccffbd1c31c1bc094d01ad77f11df9830822a9c + languageName: node + linkType: hard + "detect-node@npm:^2.0.4": version: 2.1.0 resolution: "detect-node@npm:2.1.0" @@ -4825,6 +4847,15 @@ __metadata: languageName: node linkType: hard +"detect-passive-events@npm:^2.0.3": + version: 2.0.3 + resolution: "detect-passive-events@npm:2.0.3" + dependencies: + detect-it: ^4.0.1 + checksum: 2fb48474e340a1add096735427433578c571e2fed0c44af86ac116c50b9b7d70662a2e3bf1539d9dff4e2a43459c874b20876270afe7beeb5d4bef08ff9d4a5c + languageName: node + linkType: hard + "di@npm:^0.0.1": version: 0.0.1 resolution: "di@npm:0.0.1" @@ -4954,6 +4985,7 @@ __metadata: "@angular-eslint/eslint-plugin-template": 13.2.0 "@angular-eslint/schematics": 13.2.0 "@angular-eslint/template-parser": 13.2.0 + "@angular-slider/ngx-slider": ^2.0.4 "@angular/animations": 14.3.0 "@angular/cdk": 13.2.x "@angular/cli": 14.2.11 @@ -10290,7 +10322,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.6.7": +"rxjs@npm:6.6.7, rxjs@npm:^6.5.2": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: From 2d3fbe275f82b43afd8bf87196c1e0f52b9a1fc6 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 13 Apr 2023 16:04:37 +0200 Subject: [PATCH 266/498] Preparing for angular15 by replacing fxFlex with corresponding classes --- .../admin-auditlog-overview.component.html | 2 +- ...ollection-builder-dialog.component.spec.ts | 24 -------------- ...edia-item-builder-dialog.component.spec.ts | 24 -------------- .../collection-list.component.html | 2 +- .../collection-list.component.spec.ts | 24 -------------- .../collection-viewer.component.html | 4 +-- .../collection-viewer.component.spec.ts | 24 -------------- .../advanced-builder-dialog.component.spec.ts | 24 -------------- .../advanced-builder-dialog.component.ts | 4 +++ ...mpetition-builder-task-dialog.component.ts | 4 +++ .../competition-form.builder.ts | 1 + ...player-segment-builder-dialog.component.ts | 3 ++ ...ideo-player-segment-builder.component.html | 31 ++++++++----------- ...o-player-segment-builder.component.spec.ts | 24 -------------- .../video-player-segment-builder.component.ts | 15 --------- .../competition-builder.component.spec.ts | 24 -------------- .../competition-builder.component.ts | 3 ++ .../competition-builder.module.ts | 2 -- .../competition-list.component.html | 2 +- .../judgement-dialog.component.spec.ts | 24 -------------- .../judgement-media-viewer.component.html | 6 ++-- .../judgement/judgement-viewer.component.html | 16 +++++----- .../judgement-voting-viewer.component.html | 6 ++-- .../src/app/judgement/judgement.module.ts | 2 -- .../run-admin-submissions-list.component.html | 2 +- .../run-admin-toolbar.component.html | 10 +++--- .../run-async-admin-view.component.html | 6 ++-- frontend/src/app/run/run.module.ts | 4 --- .../submission-override-dialog.component.html | 1 - .../submission-override-dialog.component.scss | 0 .../submission-override-dialog.component.ts | 12 ------- .../app/run/viewer-run-list.component.html | 2 +- .../task-template-editor.component.scss | 4 +++ frontend/src/flex.scss | 31 ++++++++++++------- 34 files changed, 80 insertions(+), 287 deletions(-) delete mode 100644 frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.spec.ts delete mode 100644 frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.spec.ts delete mode 100644 frontend/src/app/collection/collection-list/collection-list.component.spec.ts delete mode 100644 frontend/src/app/collection/collection-viewer/collection-viewer.component.spec.ts delete mode 100644 frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.spec.ts delete mode 100644 frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.spec.ts delete mode 100644 frontend/src/app/competition/competition-builder/competition-builder.component.spec.ts delete mode 100644 frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.spec.ts delete mode 100644 frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.html delete mode 100644 frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.scss delete mode 100644 frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.ts diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html index e82e19e31..b82694984 100644 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html +++ b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html @@ -1,4 +1,4 @@ -
+

Audit logs

diff --git a/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.spec.ts b/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.spec.ts deleted file mode 100644 index f03b56769..000000000 --- a/frontend/src/app/collection/collection-builder/collection-builder-dialog/collection-builder-dialog.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { CollectionBuilderDialogComponent } from './collection-builder-dialog.component'; - -describe('CollectionBuilderDialogComponent', () => { - let component: CollectionBuilderDialogComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [CollectionBuilderDialogComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CollectionBuilderDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.spec.ts b/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.spec.ts deleted file mode 100644 index 7442ae127..000000000 --- a/frontend/src/app/collection/collection-builder/media-item-builder-dialog/media-item-builder-dialog.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { MediaItemBuilderDialogComponent } from './media-item-builder-dialog.component'; - -describe('MediaItemBuilderDialogComponent', () => { - let component: MediaItemBuilderDialogComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [MediaItemBuilderDialogComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(MediaItemBuilderDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/collection/collection-list/collection-list.component.html b/frontend/src/app/collection/collection-list/collection-list.component.html index 93ca47a46..231b2c11d 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.html +++ b/frontend/src/app/collection/collection-list/collection-list.component.html @@ -1,4 +1,4 @@ -
+

Media Collections

diff --git a/frontend/src/app/collection/collection-list/collection-list.component.spec.ts b/frontend/src/app/collection/collection-list/collection-list.component.spec.ts deleted file mode 100644 index 16cdb37a3..000000000 --- a/frontend/src/app/collection/collection-list/collection-list.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { CollectionListComponent } from './collection-list.component'; - -describe('CollectionListComponent', () => { - let component: CollectionListComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [CollectionListComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CollectionListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.html b/frontend/src/app/collection/collection-viewer/collection-viewer.component.html index 54a588bee..584af1d82 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.html +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.html @@ -1,4 +1,4 @@ -
+

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collectionId | async).substr(0, 8) }})

@@ -18,7 +18,7 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect

-
+
Description: {{ (collection | async)?.collection?.description }}
Base Path: {{ (collection | async)?.collection?.basePath }} diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.spec.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.spec.ts deleted file mode 100644 index 075ead881..000000000 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { CollectionViewerComponent } from './collection-viewer.component'; - -describe('CollectionViewerComponent', () => { - let component: CollectionViewerComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [CollectionViewerComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CollectionViewerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.spec.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.spec.ts deleted file mode 100644 index 8189ffbcf..000000000 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { AdvancedBuilderDialogComponent } from './advanced-builder-dialog.component'; - -describe('AdvancedBuilderDialogComponent', () => { - let component: AdvancedBuilderDialogComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [AdvancedBuilderDialogComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AdvancedBuilderDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.ts index 3113effdf..3beb6f4dc 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/advanced-builder-dialog/advanced-builder-dialog.component.ts @@ -8,6 +8,10 @@ export class AdvancedBuilderDialogData { builder: CompetitionFormBuilder; } +// TODO rewrite +/** + * @deprecated This has to be replaced by a proper, more sophisticated component. + */ @Component({ selector: 'app-advanced-builder-dialog', templateUrl: './advanced-builder-dialog.component.html', diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index 1b53e5719..5acb1caf7 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -26,6 +26,7 @@ import { TemplateBuilderService } from "../../../template/template-builder/templ /** * Its expected that the taskGroup and taskType properties are correctly given * even in the case this is 'edit'! + * @deprecated Got replaced since this isnt a dialog anymore */ export interface CompetitionBuilderTaskDialogData { taskGroup: ApiTaskGroup; @@ -33,6 +34,9 @@ export interface CompetitionBuilderTaskDialogData { task?: ApiTaskTemplate; } +/** + * @deprecated Got replaced by task-template-editor.component.ts + */ @Component({ selector: 'app-competition-builder-task-dialog', templateUrl: './competition-builder-task-dialog.component.html', diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 3e8ea5d00..5da45177a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -15,6 +15,7 @@ import { } from '../../../../../openapi'; import { TemplateBuilderService } from "../../../template/template-builder/template-builder.service"; +// TODO rename to TemplateFormBuilder export class CompetitionFormBuilder { /** The default duration of a query hint. This is currently a hard-coded constant. */ private static DEFAULT_HINT_DURATION = 30; diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts index 539e1785a..b5065bd2b 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component.ts @@ -6,6 +6,9 @@ import { } from '../video-player-segment-builder/video-player-segment-builder.component'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +/** + * @deprecated This component should not be used anymore, as there is no need for a dialog anymore + */ @Component({ selector: 'app-video-player-segment-builder-dialog', templateUrl: './video-player-segment-builder-dialog.component.html', diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html index 574df1a15..7d8b1f60a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html @@ -9,8 +9,8 @@

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

[loop]="doLoop" [muted]="(config.configAsObservable | async).effects.mute" > -
-
+
+
-
+
-
+
@@ -35,13 +35,13 @@

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

matTooltip="Time relative to the submission, inclusive some temporal context.">
-->
-
+
loop
-
-
{{ video.nativeElement.currentTime | formatTime }} / {{ durationInSeconds | formatTime }}
-
+
+
{{ video.nativeElement.currentTime | formatTime }} / {{ durationInSeconds | formatTime }}
+
Segment Editor for {{ data?.mediaItem?.name }} >
-
-
Start: {{ startInSeconds | formatTime }}
- +
+
Start: {{ startInSeconds | formatTime }}
+ Start [s] - + End [s] -
-
End: {{ endInSeconds | formatTime }}
+
End: {{ endInSeconds | formatTime }}
diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.spec.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.spec.ts deleted file mode 100644 index 0b7e245d8..000000000 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { VideoPlayerSegmentBuilderComponent } from './video-player-segment-builder.component'; - -describe('VideoPlayerSegmentBuilderComponent', () => { - let component: VideoPlayerSegmentBuilderComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [VideoPlayerSegmentBuilderComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(VideoPlayerSegmentBuilderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts index 6cd0d1b96..92e878bdf 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts @@ -2,7 +2,6 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, O import { Observable, of, Subscription } from 'rxjs'; import { ApiMediaItem, ApiTemporalPoint, ApiTemporalRange } from '../../../../../../openapi'; import { AppConfig } from '../../../../app.config'; -import { Options } from '@angular-slider/ngx-slider'; export interface VideoPlayerSegmentBuilderData { mediaItem: ApiMediaItem; @@ -27,12 +26,6 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest endInSeconds = 100; durationInSeconds = 100; - options = { - floor: 0, - ceil: 100, - showTicks: false, - } as Options; - doLoop = true; private requestSub: Subscription; @@ -53,8 +46,6 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest this.videoUrl = of( this.config.resolveApiUrl(`/media/${this.data?.mediaItem?.mediaItemId}`) ); - this.durationInSeconds = this.data.mediaItem.durationMs / 1000; - this.setNewRange(0, this.durationInSeconds); } if (this.data.segmentStart) { this.startInSeconds = this.data.segmentStart === -1 ? 0 : this.data.segmentStart; @@ -92,12 +83,6 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest }); } - setNewRange(start: number, end: number) { - const newOptions: Options = Object.assign({}, this.options); - newOptions.ceil = start; - newOptions.floor = end; - this.options = newOptions; - } stop() { this.videoUrl = undefined; diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.spec.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.spec.ts deleted file mode 100644 index 0bd317efb..000000000 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { CompetitionBuilderComponent } from './competition-builder.component'; - -describe('CompetitionBuilerComponent', () => { - let component: CompetitionBuilderComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [CompetitionBuilderComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CompetitionBuilderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index 63e7cedf0..fc0d3ba21 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -30,6 +30,9 @@ import { UserService } from '../../../../openapi'; +/** + * @deprecated Replaced with new template builder + */ @Component({ selector: 'app-competition-builer', templateUrl: './competition-builder.component.html', diff --git a/frontend/src/app/competition/competition-builder/competition-builder.module.ts b/frontend/src/app/competition/competition-builder/competition-builder.module.ts index bd8527e33..fd1381c08 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.module.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.module.ts @@ -33,7 +33,6 @@ import { AdvancedBuilderDialogComponent } from './competition-builder-task-dialo import { SharedModule } from '../../shared/shared.module'; import { VideoPlayerSegmentBuilderDialogComponent } from './competition-builder-task-dialog/video-player-segment-builder-dialog/video-player-segment-builder-dialog.component'; import { ColorPickerModule } from 'ngx-color-picker'; -import { NgxSliderModule } from '@angular-slider/ngx-slider'; @NgModule({ imports: [ @@ -57,7 +56,6 @@ import { NgxSliderModule } from '@angular-slider/ngx-slider'; MatSlideToggleModule, MatButtonToggleModule, MatGridListModule, - NgxSliderModule, ServicesModule, FlexModule, FormsModule, diff --git a/frontend/src/app/competition/competition-list/competition-list.component.html b/frontend/src/app/competition/competition-list/competition-list.component.html index 27891eaa4..6a892c5e2 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.html +++ b/frontend/src/app/competition/competition-list/competition-list.component.html @@ -1,4 +1,4 @@ -
+

Evaluation Templates

diff --git a/frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.spec.ts b/frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.spec.ts deleted file mode 100644 index cfe08d0a2..000000000 --- a/frontend/src/app/judgement/judgement-dialog/judgement-dialog.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { JudgementDialogComponent } from './judgement-dialog.component'; - -describe('JudgementWelcomeDialogComponent', () => { - let component: JudgementDialogComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [JudgementDialogComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(JudgementDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.html b/frontend/src/app/judgement/judgement-media-viewer.component.html index bf95097f4..9d53af84b 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.html +++ b/frontend/src/app/judgement/judgement-media-viewer.component.html @@ -9,8 +9,8 @@ loop [muted]="(config.configAsObservable | async).effects.mute" > -
-
+
+
-
+

Judgment for Submission

-
-
+
+

Description: {{ judgementRequest?.taskDescription }}

{{ judgePlayer?.videoUrlDebug | async }}

{{ noJudgementMessage }}
-
+
Awaiting Verdict: {{ pendingSubmissions ? (pendingSubmissions | async) : 0 }}
@@ -23,11 +23,9 @@

Judgment for Submission

-
+
-
+
-
+
-
+
-
+
+
- Tasks Overview @@ -55,7 +55,7 @@
- Tasks per Team diff --git a/frontend/src/app/run/run.module.ts b/frontend/src/app/run/run.module.ts index cbc7e0651..284a35830 100644 --- a/frontend/src/app/run/run.module.ts +++ b/frontend/src/app/run/run.module.ts @@ -20,7 +20,6 @@ import { RunAdminViewComponent } from './run-admin-view.component'; import { MatCardModule } from '@angular/material/card'; import { SharedModule } from '../shared/shared.module'; import { RunAdminSubmissionsListComponent } from './run-admin-submissions-list/run-admin-submissions-list.component'; -import { SubmissionOverrideDialogComponent } from './submission-override-dialog/submission-override-dialog.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; @@ -29,7 +28,6 @@ import { NgApexchartsModule } from 'ng-apexcharts'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatExpansionModule } from '@angular/material/expansion'; import { RunAsyncAdminViewComponent } from './run-async-admin-view/run-async-admin-view.component'; -import { FlexModule } from '@angular/flex-layout'; import { MatToolbarModule } from '@angular/material/toolbar'; import { RunAdminToolbarComponent } from './run-admin-toolbar/run-admin-toolbar.component'; @@ -58,7 +56,6 @@ import { RunAdminToolbarComponent } from './run-admin-toolbar/run-admin-toolbar. MatSlideToggleModule, NgApexchartsModule, MatExpansionModule, - FlexModule, MatToolbarModule, ], exports: [RunListComponent, RunAdminViewComponent, RunScoreHistoryComponent], @@ -69,7 +66,6 @@ import { RunAdminToolbarComponent } from './run-admin-toolbar/run-admin-toolbar. AdminRunListComponent, ViewerRunListComponent, RunAdminSubmissionsListComponent, - SubmissionOverrideDialogComponent, RunAsyncAdminViewComponent, RunAdminToolbarComponent, ], diff --git a/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.html b/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.html deleted file mode 100644 index 85b9c49cf..000000000 --- a/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.html +++ /dev/null @@ -1 +0,0 @@ -

submission-override-dialog works!

diff --git a/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.scss b/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.ts b/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.ts deleted file mode 100644 index 0e88d4aa6..000000000 --- a/frontend/src/app/run/submission-override-dialog/submission-override-dialog.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-submission-override-dialog', - templateUrl: './submission-override-dialog.component.html', - styleUrls: ['./submission-override-dialog.component.scss'], -}) -export class SubmissionOverrideDialogComponent implements OnInit { - constructor() {} - - ngOnInit(): void {} -} diff --git a/frontend/src/app/run/viewer-run-list.component.html b/frontend/src/app/run/viewer-run-list.component.html index 3c6ca4901..60be482da 100644 --- a/frontend/src/app/run/viewer-run-list.component.html +++ b/frontend/src/app/run/viewer-run-list.component.html @@ -1,4 +1,4 @@ -
+

Ongoing runs

diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss index e69de29bb..338d4541c 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.scss @@ -0,0 +1,4 @@ +.validation-error { + font-size: 10px; + color: darkred; +} diff --git a/frontend/src/flex.scss b/frontend/src/flex.scss index 8258fb333..509dc4814 100644 --- a/frontend/src/flex.scss +++ b/frontend/src/flex.scss @@ -14,11 +14,11 @@ Flex containers } .flex-row { - flex-flow: row wrap; + flex-flow: row; } .flex-column { - flex-flow: column wrap; + flex-flow: column; } .flex-nowrap{ @@ -86,22 +86,31 @@ Flex items flex: 0 1 auto; } -.flex-1 { - flex: 1 1 0; +.fill { + height: 100%; + min-height: 100%; + width: 100%; + min-width: 100%; } -.flex-2 { - flex: 2 1 0; -} +$growFlex: 1,2,3,4,5; -.flex-3 { - flex: 3 1 0; +@each $f in $growFlex { + .flex-#{$f} { + flex: $f 1 0; + } } -.flex-4 { - flex: 4 1 0; +$basisFlex: 5,10,15,20,25,30,33,35,40,45,48,50,55,60,65,66,70,75,80,85,90,95; + +@each $f in $basisFlex { + .flex-#{$f}{ + flex: 1 1 percentage($f); + } } + + .self-flex-start { align-self: flex-start; } From f02bd24797a8d930c65f153e719446b281f1b207 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 14 Apr 2023 12:06:54 +0200 Subject: [PATCH 267/498] Removed FlexLayoutModule and removed ngx-slider --- frontend/package.json | 1 - frontend/src/app/app-routing.module.ts | 12 +--- ...ideo-player-segment-builder.component.html | 6 +- .../services/pipes/format-time-pipe.pipe.ts | 17 +++-- .../services/pipes/space-to-newline.pipe.ts | 13 ++++ frontend/src/app/services/services.module.ts | 3 + .../actionable-dynamic-table.component.html | 2 +- .../server-info/server-info.component.html | 68 +++++++++---------- .../judges-list/judges-list.component.html | 2 +- .../task-template-editor.component.html | 21 +++--- .../task-templates-list.component.html | 2 +- .../teams-list/teams-list.component.html | 2 +- .../template-builder-components.module.ts | 2 - .../template-builder.component.html | 20 +++--- .../template-builder.module.ts | 2 - .../admin-user-list.component.html | 2 +- .../admin-user-list.component.spec.ts | 24 ------- .../app/user/login/login.component.spec.ts | 24 ------- .../user/profile/profile.component.spec.ts | 24 ------- frontend/src/app/user/user.module.ts | 2 - ...tition-scoreboard-viewer.component.spec.ts | 24 ------- frontend/src/flex.scss | 8 ++- frontend/yarn.lock | 34 +--------- 23 files changed, 97 insertions(+), 218 deletions(-) create mode 100644 frontend/src/app/services/pipes/space-to-newline.pipe.ts delete mode 100644 frontend/src/app/user/admin-user-list/admin-user-list.component.spec.ts delete mode 100644 frontend/src/app/user/login/login.component.spec.ts delete mode 100644 frontend/src/app/user/profile/profile.component.spec.ts delete mode 100644 frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.spec.ts diff --git a/frontend/package.json b/frontend/package.json index f0a050a41..95257e03d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,6 @@ }, "private": true, "dependencies": { - "@angular-slider/ngx-slider": "^2.0.4", "@angular/animations": "14.3.0", "@angular/cdk": "13.2.x", "@angular/common": "14.3.0", diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index d50d9fde7..2078ab058 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; -import { DefaultUrlSerializer, RouterModule, Routes, UrlSerializer } from '@angular/router'; -import { CompetitionBuilderComponent } from './competition/competition-builder/competition-builder.component'; +import { RouterModule, Routes, UrlSerializer } from '@angular/router'; import { CompetitionListComponent } from './competition/competition-list/competition-list.component'; import { LoginComponent } from './user/login/login.component'; import { AuthenticationGuard } from './services/session/authentication.guard'; @@ -41,14 +40,7 @@ const routes: Routes = [ data: { roles: [ApiRole.ADMIN] }, }, { - path: TEMPLATE_ROUTE+'/builder/:competitionId', - component: CompetitionBuilderComponent, - canActivate: [AuthenticationGuard], - canDeactivate: [CanDeactivateGuard], - data: { roles: [ApiRole.ADMIN] }, - }, - { - path: TEMPLATE_ROUTE+'/builder2/:templateId', + path: TEMPLATE_ROUTE+'/builder/:templateId', component: TemplateBuilderComponent, canActivate: [AuthenticationGuard], canDeactivate: [CanDeactivateGuard], diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html index 7d8b1f60a..4fe7eef4d 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html @@ -40,7 +40,7 @@

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

-
{{ video.nativeElement.currentTime | formatTime }} / {{ durationInSeconds | formatTime }}
+
{{ video.nativeElement.currentTime | formatTime: false }} / {{ durationInSeconds | formatTime: false }}
Segment Editor for {{ data?.mediaItem?.name }}

-
Start: {{ startInSeconds | formatTime }}
+
Start: {{ startInSeconds | formatTime:false }}
Start [s] @@ -61,6 +61,6 @@

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

-
End: {{ endInSeconds | formatTime }}
+
End: {{ endInSeconds | formatTime:false }}
diff --git a/frontend/src/app/services/pipes/format-time-pipe.pipe.ts b/frontend/src/app/services/pipes/format-time-pipe.pipe.ts index cbb14e85d..962b1e9c7 100644 --- a/frontend/src/app/services/pipes/format-time-pipe.pipe.ts +++ b/frontend/src/app/services/pipes/format-time-pipe.pipe.ts @@ -7,11 +7,12 @@ import { Pipe, PipeTransform } from '@angular/core'; name: 'formatTime', }) export class FormatTimePipePipe implements PipeTransform { - transform(value: number): string { - const hrs = Math.floor((value/1000) / 3600); - const mins = Math.floor(((value/1000) % 3600) / 60); - const secs = Math.floor((value/1000) % 60); - const ms = Math.floor(value % 1000); + transform(value: number, inMs = true): string { + const divisor = inMs ? 1000 : 1; + const hrs = Math.floor((value/divisor) / 3600); + const mins = Math.floor(((value/divisor) % 3600) / 60); + const secs = Math.floor((value/divisor) % 60); + const ms = inMs ? Math.floor(value % divisor) : ''; let out = ''; /* Hours if present */ if (hrs > 0) { @@ -20,9 +21,11 @@ export class FormatTimePipePipe implements PipeTransform { /* Minutes */ out += '' + (''+mins).padStart(2, '0') + ':'; /* Seconds */ - out += '' + (''+secs).padStart(2, '0') + '.'; + out += '' + (''+secs).padStart(2, '0') + (inMs ? '.' : ''); /* Milliseconds */ - out += '' + (''+ms).padStart(3,'0'); + if(inMs){ + out += '' + (''+ms).padStart(3,'0'); + } return out; } } diff --git a/frontend/src/app/services/pipes/space-to-newline.pipe.ts b/frontend/src/app/services/pipes/space-to-newline.pipe.ts new file mode 100644 index 000000000..8be8c567f --- /dev/null +++ b/frontend/src/app/services/pipes/space-to-newline.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'spaceToNewline' +}) +export class SpaceToNewlinePipe implements PipeTransform { + + transform(value: string, lineBreak= false): string { + const nl: string = lineBreak ? '
' : '\n'; + return value.split(' ').join(nl); + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index f8d6e345f..0ca115332 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -12,6 +12,7 @@ import { EnhanceTaskPastInfoPipe } from './pipes/enhance-task-past-info.pipe'; import { ResolveTeamPipe } from './pipes/resolve-team.pipe'; import { EnhanceTaskSubmissionInfoPipe } from './pipes/enhance-task-submission-info.pipe'; import {ApiModule, Configuration} from '../../../openapi'; +import { SpaceToNewlinePipe } from './pipes/space-to-newline.pipe'; /** * Provides the {@link AppConfig} reference. @@ -39,6 +40,7 @@ export function initializeApiConfig(appConfig: AppConfig) { EnhanceTaskPastInfoPipe, ResolveTeamPipe, EnhanceTaskSubmissionInfoPipe, + SpaceToNewlinePipe ], declarations: [ RoundPipePipe, @@ -49,6 +51,7 @@ export function initializeApiConfig(appConfig: AppConfig) { EnhanceTaskPastInfoPipe, ResolveTeamPipe, EnhanceTaskSubmissionInfoPipe, + SpaceToNewlinePipe, ], providers: [AuthenticationService, NavigationService, CanDeactivateGuard], }) diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index f63cbd1a5..49c4092a1 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -1,4 +1,4 @@ -
+

{{title}}

diff --git a/frontend/src/app/shared/server-info/server-info.component.html b/frontend/src/app/shared/server-info/server-info.component.html index c9cc397bb..efb7c9b46 100644 --- a/frontend/src/app/shared/server-info/server-info.component.html +++ b/frontend/src/app/shared/server-info/server-info.component.html @@ -1,51 +1,51 @@

DRES Server Information

-
-
- DRES version: - {{(info | async )?.version}} +
+
+ DRES version: + {{(info | async )?.version}}
-
- Start time: - {{(info | async )?.startTime}} +
+ Start time: + {{(info | async )?.startTime}}
-
- Uptime: - {{(info | async )?.uptime}} +
+ Uptime: + {{(info | async )?.uptime}}
-
- JVM: - {{(info | async )?.jvm}} +
+ JVM: + {{(info | async )?.jvm}}
-
- OS: - {{(info | async )?.os}} +
+ OS: + {{(info | async )?.os}}
-
- Cores: - {{(info | async )?.cores}} +
+ Cores: + {{(info | async )?.cores}}
-
- Server threads: - {{(info | async )?.availableSeverThreads}} +
+ Server threads: + {{(info | async )?.availableSeverThreads}}
-
- Total memory: - {{(info | async )?.totalMemory}} +
+ Total memory: + {{(info | async )?.totalMemory}}
-
- Free memory: - {{(info | async )?.freeMemory}} +
+ Free memory: + {{(info | async )?.freeMemory}}
-
- Load: - {{(info | async )?.load}} +
+ Load: + {{(info | async )?.load}}
-
- Start arguments: - {{(info | async )?.args}} +
+ Start arguments: +
{{(info | async )?.args | spaceToNewline}}
diff --git a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.html b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.html index bb912fcf0..e66719bf4 100644 --- a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.html +++ b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.html @@ -1,4 +1,4 @@ -
+

Judges

@@ -218,7 +218,6 @@

-
@@ -237,7 +236,7 @@

-
+

+

Tasks

diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html index d15003695..d38000469 100644 --- a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html @@ -1,4 +1,4 @@ -
+

Teams

- + - + The logo of the team

diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index eea6812ec..c4203fd45 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -44,6 +44,7 @@ export class CompetitionBuilderTeamDialogComponent { private config: AppConfig, @Inject(MAT_DIALOG_DATA) private team?: ApiTeam ) { + console.log("TEAM", team); this.form = new UntypedFormGroup({ id: new UntypedFormControl(team?.id), name: new UntypedFormControl(team?.name, [Validators.required, Validators.minLength(3)]), @@ -52,7 +53,6 @@ export class CompetitionBuilderTeamDialogComponent { Validators.minLength(7), Validators.maxLength(7), ]), - logoId: new UntypedFormControl(/*team?.logoId*/''), logoData: new UntypedFormControl(team?.logoData), users: new UntypedFormControl(team?.users != null ? team.users : []), userInput: new UntypedFormControl(''), @@ -128,7 +128,7 @@ export class CompetitionBuilderTeamDialogComponent { if (this.form.get('logoData').value != null) { return this.form.get('logoData').value; } else { - return this.config.resolveApiUrl(`/competition/logo/${this.form.get('logoId').value}`); + return this.config.resolveApiUrl(`/template/logo/${this.team.id}`); } } diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index 49c4092a1..3c452e0a1 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -1,5 +1,5 @@
-

{{title}}

+

{{tableTitle}}

diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts index 783fefcbb..271c01779 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts @@ -43,7 +43,7 @@ export class ActionableDynamicTable { public displayedColumns: string[]; @Input() - public title: string; + public tableTitle: string; @Input() public trackByProperty?: string; diff --git a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html index 19f0ffba4..36ab10b64 100644 --- a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html +++ b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.html @@ -4,7 +4,7 @@ [columnSchema]="columns" [displayedColumns]="displayedColumns" [onRemove]="remove" - title="Task groups" + tableTitle="Task groups" > +
@@ -8,8 +8,8 @@
diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html index 57303f6e0..06054dcf4 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html @@ -7,18 +7,17 @@

Add team

- - + {{ user.username }} cancel - + - + {{ user.username }} diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder.component.html index ecbd60f91..6b53eb321 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.html @@ -82,8 +82,8 @@

- - + > {{ taskType.name }} ({{ taskType.targetOption }} | {{ taskType.scoreOption }}) cancel - - + +

@@ -104,8 +104,8 @@

- - + > {{ group.name }} cancel - - + +

diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 479bab8cc..828c8482a 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -60,7 +60,7 @@
- + Active viewers @@ -85,7 +85,7 @@
- + Tasks @@ -169,7 +169,7 @@
- + Teams diff --git a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.html b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.html index abef5f735..ac170acca 100644 --- a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.html +++ b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.html @@ -1,7 +1,7 @@
- Tasks Overview @@ -55,7 +55,7 @@ - Tasks per Team diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss index 3dbfdd8c0..e6c542548 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.scss @@ -1,4 +1,4 @@ -.mat-row:hover { +.mat-mdc-row:hover { background: rgba(255,255,255,0.1); } diff --git a/frontend/src/app/user/login/login.component.html b/frontend/src/app/user/login/login.component.html index 9fe31c0d3..cd7d5f81d 100644 --- a/frontend/src/app/user/login/login.component.html +++ b/frontend/src/app/user/login/login.component.html @@ -1,16 +1,16 @@ - + Login

- + Username:

- + Password: diff --git a/frontend/src/app/user/login/login.component.scss b/frontend/src/app/user/login/login.component.scss index 2494b1876..84aee38dc 100644 --- a/frontend/src/app/user/login/login.component.scss +++ b/frontend/src/app/user/login/login.component.scss @@ -4,11 +4,12 @@ margin: 100px 0px; } -.mat-form-field { +.mat-mdc-form-field { width: 100%; min-width: 300px; } +/* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ mat-card-title, mat-card-content { display: flex; diff --git a/frontend/src/app/user/profile/profile.component.html b/frontend/src/app/user/profile/profile.component.html index 8661c02a7..fbb71329f 100644 --- a/frontend/src/app/user/profile/profile.component.html +++ b/frontend/src/app/user/profile/profile.component.html @@ -1,4 +1,4 @@ - +

diff --git a/frontend/src/app/user/profile/profile.component.scss b/frontend/src/app/user/profile/profile.component.scss index 2bd59f86e..0feb86ab7 100644 --- a/frontend/src/app/user/profile/profile.component.scss +++ b/frontend/src/app/user/profile/profile.component.scss @@ -4,11 +4,12 @@ margin: 100px 0px; } -.mat-form-field { +.mat-mdc-form-field { width: 100%; min-width: 300px; } +/* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version.*/ mat-card-title, mat-card-content { display: flex; @@ -32,7 +33,7 @@ mat-grid-list { } // See https://stackoverflow.com/a/52009315 -.mat-card-header { +.mat-mdc-card-header { width: 100% !important; } diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html index 0327114d7..599c41444 100644 --- a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html +++ b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html @@ -1,4 +1,4 @@ - + Normalized Competition Scores Scores of {{ currentTaskGroup | async }} diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index 4d371ecf9..4df16dac8 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -1,6 +1,6 @@
- + Viewer for '{{ (runInfo | async)?.name }}' diff --git a/frontend/src/app/viewer/task-viewer.component.html b/frontend/src/app/viewer/task-viewer.component.html index 180be1d91..5d8ba4103 100644 --- a/frontend/src/app/viewer/task-viewer.component.html +++ b/frontend/src/app/viewer/task-viewer.component.html @@ -1,4 +1,4 @@ - +
diff --git a/frontend/src/app/viewer/teams-viewer.component.html b/frontend/src/app/viewer/teams-viewer.component.html index f084929d1..fcda2bab2 100644 --- a/frontend/src/app/viewer/teams-viewer.component.html +++ b/frontend/src/app/viewer/teams-viewer.component.html @@ -1,4 +1,4 @@ - +
(id: string): T; - }; -}; - // First, initialize the Angular testing environment. getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 3a93e284b..98fcf832c 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -10,8 +10,12 @@ "module": "es2020", "moduleResolution": "node", "importHelpers": true, - "target": "es2020", - "lib": ["es2020", "dom"] + "target": "ES2022", + "lib": [ + "es2020", + "dom" + ], + "useDefineForClassFields": false }, "angularCompilerOptions": { "fullTemplateTypeCheck": true, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f06d148ba..f220dbf56 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5,14 +5,7 @@ __metadata: version: 6 cacheKey: 8 -"@adobe/css-tools@npm:^4.0.1": - version: 4.2.0 - resolution: "@adobe/css-tools@npm:4.2.0" - checksum: dc5cc92ba3d562e7ffddb79d6d222c7e00b65f255fd2725b3d71490ff268844be322f917415d8c4ab39eca646343b632058db8bd5b1d646193fcc94d1d3e420b - languageName: node - linkType: hard - -"@ampproject/remapping@npm:2.2.0, @ampproject/remapping@npm:^2.1.0, @ampproject/remapping@npm:^2.2.0": +"@ampproject/remapping@npm:2.2.0, @ampproject/remapping@npm:^2.1.0": version: 2.2.0 resolution: "@ampproject/remapping@npm:2.2.0" dependencies: @@ -22,98 +15,99 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1402.11": - version: 0.1402.11 - resolution: "@angular-devkit/architect@npm:0.1402.11" +"@angular-devkit/architect@npm:0.1502.6": + version: 0.1502.6 + resolution: "@angular-devkit/architect@npm:0.1502.6" dependencies: - "@angular-devkit/core": 14.2.11 + "@angular-devkit/core": 15.2.6 rxjs: 6.6.7 - checksum: c494678e47aa80662acdd5b1d68ecae892add773cca4690f0e43b344fbce3d4414d9f50fc642906b3ad4c89a6067b2c6672154114ed4f9e9b3d1d99f5f8e71ed + checksum: 7a27d8afd6c09cd275616b1e37fd3fb83ec6bdaa00f280ae60dc187f826fcdd99ab75c6b3d5d6492a8ca1db4f168163ec99c3ddfb9437cca540bb87dc271cf73 languageName: node linkType: hard -"@angular-devkit/build-angular@npm:14.2.11": - version: 14.2.11 - resolution: "@angular-devkit/build-angular@npm:14.2.11" +"@angular-devkit/build-angular@npm:15.2.6": + version: 15.2.6 + resolution: "@angular-devkit/build-angular@npm:15.2.6" dependencies: "@ampproject/remapping": 2.2.0 - "@angular-devkit/architect": 0.1402.11 - "@angular-devkit/build-webpack": 0.1402.11 - "@angular-devkit/core": 14.2.11 - "@babel/core": 7.18.10 - "@babel/generator": 7.18.12 + "@angular-devkit/architect": 0.1502.6 + "@angular-devkit/build-webpack": 0.1502.6 + "@angular-devkit/core": 15.2.6 + "@babel/core": 7.20.12 + "@babel/generator": 7.20.14 "@babel/helper-annotate-as-pure": 7.18.6 - "@babel/plugin-proposal-async-generator-functions": 7.18.10 - "@babel/plugin-transform-async-to-generator": 7.18.6 - "@babel/plugin-transform-runtime": 7.18.10 - "@babel/preset-env": 7.18.10 - "@babel/runtime": 7.18.9 - "@babel/template": 7.18.10 + "@babel/helper-split-export-declaration": 7.18.6 + "@babel/plugin-proposal-async-generator-functions": 7.20.7 + "@babel/plugin-transform-async-to-generator": 7.20.7 + "@babel/plugin-transform-runtime": 7.19.6 + "@babel/preset-env": 7.20.2 + "@babel/runtime": 7.20.13 + "@babel/template": 7.20.7 "@discoveryjs/json-ext": 0.5.7 - "@ngtools/webpack": 14.2.11 + "@ngtools/webpack": 15.2.6 ansi-colors: 4.1.3 - babel-loader: 8.2.5 + autoprefixer: 10.4.13 + babel-loader: 9.1.2 babel-plugin-istanbul: 6.1.1 - browserslist: ^4.9.1 - cacache: 16.1.2 + browserslist: 4.21.5 + cacache: 17.0.4 + chokidar: 3.5.3 copy-webpack-plugin: 11.0.0 critters: 0.0.16 - css-loader: 6.7.1 - esbuild: 0.15.5 - esbuild-wasm: 0.15.5 - glob: 8.0.3 + css-loader: 6.7.3 + esbuild: 0.17.8 + esbuild-wasm: 0.17.8 + glob: 8.1.0 https-proxy-agent: 5.0.1 inquirer: 8.2.4 - jsonc-parser: 3.1.0 + jsonc-parser: 3.2.0 karma-source-map-support: 1.4.0 less: 4.1.3 - less-loader: 11.0.0 + less-loader: 11.1.0 license-webpack-plugin: 4.0.2 loader-utils: 3.2.1 - mini-css-extract-plugin: 2.6.1 - minimatch: 5.1.0 - open: 8.4.0 + magic-string: 0.29.0 + mini-css-extract-plugin: 2.7.2 + open: 8.4.1 ora: 5.4.1 - parse5-html-rewriting-stream: 6.0.1 + parse5-html-rewriting-stream: 7.0.0 piscina: 3.2.0 - postcss: 8.4.16 - postcss-import: 15.0.0 - postcss-loader: 7.0.1 - postcss-preset-env: 7.8.0 - regenerator-runtime: 0.13.9 + postcss: 8.4.21 + postcss-loader: 7.0.2 resolve-url-loader: 5.0.0 rxjs: 6.6.7 - sass: 1.54.4 - sass-loader: 13.0.2 - semver: 7.3.7 - source-map-loader: 4.0.0 + sass: 1.58.1 + sass-loader: 13.2.0 + semver: 7.3.8 + source-map-loader: 4.0.1 source-map-support: 0.5.21 - stylus: 0.59.0 - stylus-loader: 7.0.0 - terser: 5.14.2 + terser: 5.16.3 text-table: 0.2.0 tree-kill: 1.2.2 - tslib: 2.4.0 + tslib: 2.5.0 webpack: 5.76.1 - webpack-dev-middleware: 5.3.3 - webpack-dev-server: 4.11.0 + webpack-dev-middleware: 6.0.1 + webpack-dev-server: 4.11.1 webpack-merge: 5.8.0 webpack-subresource-integrity: 5.1.0 peerDependencies: - "@angular/compiler-cli": ^14.0.0 - "@angular/localize": ^14.0.0 - "@angular/service-worker": ^14.0.0 + "@angular/compiler-cli": ^15.0.0 + "@angular/localize": ^15.0.0 + "@angular/platform-server": ^15.0.0 + "@angular/service-worker": ^15.0.0 karma: ^6.3.0 - ng-packagr: ^14.0.0 + ng-packagr: ^15.0.0 protractor: ^7.0.0 tailwindcss: ^2.0.0 || ^3.0.0 - typescript: ">=4.6.2 <4.9" + typescript: ">=4.8.2 <5.0" dependenciesMeta: esbuild: optional: true peerDependenciesMeta: "@angular/localize": optional: true + "@angular/platform-server": + optional: true "@angular/service-worker": optional: true karma: @@ -124,30 +118,30 @@ __metadata: optional: true tailwindcss: optional: true - checksum: b420ae49d561bf8c21203abc1eed2a9078cc3dd6ccbf6d18ab2662a31c6a84dd14a084b614cf18033fe0ca7bfb31ccad2e2e9b9595ee37f9c7435cfe6c36ab31 + checksum: 1afa83f113bff9386a5d9af0b5e63dbb1ff99181702f8a7ff4cbbb357b8b01ddda3f74da94c994f990b98887740837ad65939f24d0426173d9c4147dc669c674 languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1402.11": - version: 0.1402.11 - resolution: "@angular-devkit/build-webpack@npm:0.1402.11" +"@angular-devkit/build-webpack@npm:0.1502.6": + version: 0.1502.6 + resolution: "@angular-devkit/build-webpack@npm:0.1502.6" dependencies: - "@angular-devkit/architect": 0.1402.11 + "@angular-devkit/architect": 0.1502.6 rxjs: 6.6.7 peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^4.0.0 - checksum: 4619dfb2f958a9150b2e43e8d1a1e5e8905f3e8db5b8c8bc82e5c0cdbf4ce7ead35add634773fcb775a205c7d4469246b3b7d08dba0a346724c3482d9e1087e5 + checksum: bf22db0f25a9fe27cf7f5df513e7400ea733ac8374fe015025092f00ce8e11aafe75e23cc5f7690e783b248609f2449a71c24c108c184b82577244e8ba6403f3 languageName: node linkType: hard -"@angular-devkit/core@npm:14.2.11": - version: 14.2.11 - resolution: "@angular-devkit/core@npm:14.2.11" +"@angular-devkit/core@npm:15.2.6": + version: 15.2.6 + resolution: "@angular-devkit/core@npm:15.2.6" dependencies: - ajv: 8.11.0 + ajv: 8.12.0 ajv-formats: 2.1.1 - jsonc-parser: 3.1.0 + jsonc-parser: 3.2.0 rxjs: 6.6.7 source-map: 0.7.4 peerDependencies: @@ -155,20 +149,20 @@ __metadata: peerDependenciesMeta: chokidar: optional: true - checksum: 5672fe0f44b557e637d97a12c1aea9c23b154005430dda4e5c80e0f0f73ff465e115e726d398f2d9008b2cf81c152083402ac234f1454a76db3f748c057a1c72 + checksum: ec1456cd6ac94f35c73bbb91b3f7511c023286690259e37abc66fea2b26fafa86e75e7b4754e2c8a747bd196e7bb3b80622cd59b52fd394c124e7cbb01d70598 languageName: node linkType: hard -"@angular-devkit/schematics@npm:14.2.11": - version: 14.2.11 - resolution: "@angular-devkit/schematics@npm:14.2.11" +"@angular-devkit/schematics@npm:15.2.6": + version: 15.2.6 + resolution: "@angular-devkit/schematics@npm:15.2.6" dependencies: - "@angular-devkit/core": 14.2.11 - jsonc-parser: 3.1.0 - magic-string: 0.26.2 + "@angular-devkit/core": 15.2.6 + jsonc-parser: 3.2.0 + magic-string: 0.29.0 ora: 5.4.1 rxjs: 6.6.7 - checksum: 6162af280476fb2bfa2e752f9941addfd623bb88b1cf6adc1cd834b20376f3d4df3762e40a9959ca41b19e2a4062b5dcee9c9b9381ca5062cd42e459b34fa58b + checksum: 43475d230d8c56dc4905a57707af36521610e2b9420c3ee38606cbdb0fc7c280235c3e268b6aed855ed159f2c69f82d194ca565a5912b26a76e1ce3ffbb55f45 languageName: node linkType: hard @@ -260,206 +254,251 @@ __metadata: languageName: node linkType: hard -"@angular/animations@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/animations@npm:14.3.0" +"@angular/animations@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/animations@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 14.3.0 - checksum: fb609ea6ced8ba612938e24b47e1eb1d1c32ede7841b4829b4817629f9b2692d4b69e88ab10a0f04cec143f1ca68d0e8bbba0d863106f22e959515ed7a5172f3 + "@angular/core": 15.2.7 + checksum: 89b71779033c889e20f785bf6395bd798808792c541014937774b25c2ed2fa294031763bfb239b14e18f85e571365af3422359c699c7492b0a4575526381f285 languageName: node linkType: hard -"@angular/cdk@npm:14.2.7": - version: 14.2.7 - resolution: "@angular/cdk@npm:14.2.7" +"@angular/cdk@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/cdk@npm:15.2.7" dependencies: - parse5: ^5.0.0 + parse5: ^7.1.2 tslib: ^2.3.0 peerDependencies: - "@angular/common": ^14.0.0 || ^15.0.0 - "@angular/core": ^14.0.0 || ^15.0.0 + "@angular/common": ^15.0.0 || ^16.0.0 + "@angular/core": ^15.0.0 || ^16.0.0 rxjs: ^6.5.3 || ^7.4.0 dependenciesMeta: parse5: optional: true - checksum: 121cfbd556003e9e81115f43de3ff8597e2e2157dae91064d0e5c533e9c9e8378b5737d72cfd849dc93f9dcda0a8307aaaea1b2ae0605c6ddd6c1a11499da00a + checksum: f4c802334b4d74031ed8d7a7e268f3fecca672b8a33628f6c2ce94826eecbf2ed5b95d1b5b8669d08cdf4a039669be2352ffe207e7735033c71d745075038661 languageName: node linkType: hard -"@angular/cli@npm:14.2.11": - version: 14.2.11 - resolution: "@angular/cli@npm:14.2.11" +"@angular/cli@npm:15.2.6": + version: 15.2.6 + resolution: "@angular/cli@npm:15.2.6" dependencies: - "@angular-devkit/architect": 0.1402.11 - "@angular-devkit/core": 14.2.11 - "@angular-devkit/schematics": 14.2.11 - "@schematics/angular": 14.2.11 + "@angular-devkit/architect": 0.1502.6 + "@angular-devkit/core": 15.2.6 + "@angular-devkit/schematics": 15.2.6 + "@schematics/angular": 15.2.6 "@yarnpkg/lockfile": 1.1.0 ansi-colors: 4.1.3 - debug: 4.3.4 - ini: 3.0.0 + ini: 3.0.1 inquirer: 8.2.4 - jsonc-parser: 3.1.0 - npm-package-arg: 9.1.0 - npm-pick-manifest: 7.0.1 - open: 8.4.0 + jsonc-parser: 3.2.0 + npm-package-arg: 10.1.0 + npm-pick-manifest: 8.0.1 + open: 8.4.1 ora: 5.4.1 - pacote: 13.6.2 + pacote: 15.1.0 resolve: 1.22.1 - semver: 7.3.7 + semver: 7.3.8 symbol-observable: 4.0.0 - uuid: 8.3.2 - yargs: 17.5.1 + yargs: 17.6.2 bin: ng: bin/ng.js - checksum: 9754cb2cc97f63a4ef4f8ad7200156e3a47a404abbfa54346eb7b0e38fb7a58e104eeeb551240b4c7934ddb3e002550eaa90fc63d445bddec18bd2a3109f2eec + checksum: 13be6f34d68572de3949487a6daeba175d9bdae47e9198b25ef3ba52f0d490fb19e4dcb6d47a90c9af46727f3fe9172deb6aef03fbebf59d90a48b456fc10543 languageName: node linkType: hard -"@angular/common@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/common@npm:14.3.0" +"@angular/common@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/common@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 14.3.0 + "@angular/core": 15.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: 92e61f5b1425f6992d117f288909208ae5a2ac27e2b16ae758797d8f8eff6532e933f7292a21276877c532abf75aad21eae8132afc92ae84ddc4f1f0d5932287 + checksum: 5eac9700caff18e35ad36beb934b198a490722f1abcf52bf00f96731a049951ca932faa1f3b928110414dd75dfb40d29c3646e58553678953beacfe7c953df94 languageName: node linkType: hard -"@angular/compiler-cli@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/compiler-cli@npm:14.3.0" +"@angular/compiler-cli@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/compiler-cli@npm:15.2.7" dependencies: - "@babel/core": ^7.17.2 + "@babel/core": 7.19.3 + "@jridgewell/sourcemap-codec": ^1.4.14 chokidar: ^3.0.0 convert-source-map: ^1.5.1 dependency-graph: ^0.11.0 - magic-string: ^0.26.0 + magic-string: ^0.27.0 reflect-metadata: ^0.1.2 semver: ^7.0.0 - sourcemap-codec: ^1.4.8 tslib: ^2.3.0 yargs: ^17.2.1 peerDependencies: - "@angular/compiler": 14.3.0 - typescript: ">=4.6.2 <4.9" + "@angular/compiler": 15.2.7 + typescript: ">=4.8.2 <5.0" bin: ng-xi18n: bundles/src/bin/ng_xi18n.js ngc: bundles/src/bin/ngc.js ngcc: bundles/ngcc/main-ngcc.js - checksum: 4c67467680856b8a3fed3105ef178b851435fadb265e83cdc54374d0beda3d5cd4020179b9d2450dbf707f3b72d0e331d2f5a5045641b49d58c269b95c1acb58 + checksum: 610cc0bed7f108548db5ea2084585d58b4cb50cbdcb0861771a22df5805a2f497c59ec03802936b4eac5f72f5b40c3c28df80714a594bc2355556804dc1fd61d languageName: node linkType: hard -"@angular/compiler@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/compiler@npm:14.3.0" +"@angular/compiler@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/compiler@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/core": 14.3.0 + "@angular/core": 15.2.7 peerDependenciesMeta: "@angular/core": optional: true - checksum: 24667875b12e34b57f95b1ac1995284267aeeefb15bba0f2d2c7f242d3aba5b2be47824a5beb54d4b502ae4caf22850d0a040bd75db71bff0fc63fd69187d149 + checksum: 3e41ddd76475ddfce5af37a90ee4e5294a73df97891fed360072cc79b3fbde4be324e8b35384c66422e58443f3ab3c2390f61d3d1f5f852d3ddf812452847e8d languageName: node linkType: hard -"@angular/core@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/core@npm:14.3.0" +"@angular/core@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/core@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: rxjs: ^6.5.3 || ^7.4.0 - zone.js: ~0.11.4 || ~0.12.0 - checksum: 07c2adfee1ef53a93e5c08ad140f28248c5b4c118a716a5acfef4254e0595e3d52001df872341308200ba08600098f050ac0293bf89adb547c64b4aadfc9e8cd + zone.js: ~0.11.4 || ~0.12.0 || ~0.13.0 + checksum: f98459a67a20e64255f6829182d1609650ebca3754bdad21cb72689580a2c385f5f4f804411bcece760b6efe08f62127197fd7abd8e8d9f057a5ce6b8e509e94 languageName: node linkType: hard -"@angular/forms@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/forms@npm:14.3.0" +"@angular/forms@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/forms@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 14.3.0 - "@angular/core": 14.3.0 - "@angular/platform-browser": 14.3.0 + "@angular/common": 15.2.7 + "@angular/core": 15.2.7 + "@angular/platform-browser": 15.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: 2981590e5d71b18a71eb2828f303ee044068071aa2d0f473a89cc99cb1be7d031e5ce869608ef0494a29991212696133ac705aadaa8370344ce81d5bce9a60c2 - languageName: node - linkType: hard - -"@angular/language-service@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/language-service@npm:14.3.0" - checksum: cd0a0494dc51947fc51e261b9e12005c2d48b259dd4058da5c3a0ca02deeb9da5f77d5cbc135d187ed4ff65ce720085861a84d6c8d1d55c775293319d02e0a2e - languageName: node - linkType: hard - -"@angular/material@npm:14.2.7": - version: 14.2.7 - resolution: "@angular/material@npm:14.2.7" - dependencies: + checksum: 2ca9d5090020b6e368ff0ceda0408c166929b67025b17bb46522be257be5909907799cd2d482859fb16b2c3194475520132a590dc58627abd9efe22309e41ba9 + languageName: node + linkType: hard + +"@angular/language-service@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/language-service@npm:15.2.7" + checksum: 4bea599911dc37e14b35e91edcb0554495f06f8f2420ffe0db84c74ff9282a66a78329ba868ba67ef94e0aab288ef6e8802439ba10ecd5fb6144563944264727 + languageName: node + linkType: hard + +"@angular/material@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/material@npm:15.2.7" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/auto-init": 15.0.0-canary.684e33d25.0 + "@material/banner": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/button": 15.0.0-canary.684e33d25.0 + "@material/card": 15.0.0-canary.684e33d25.0 + "@material/checkbox": 15.0.0-canary.684e33d25.0 + "@material/chips": 15.0.0-canary.684e33d25.0 + "@material/circular-progress": 15.0.0-canary.684e33d25.0 + "@material/data-table": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dialog": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/drawer": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/fab": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/floating-label": 15.0.0-canary.684e33d25.0 + "@material/form-field": 15.0.0-canary.684e33d25.0 + "@material/icon-button": 15.0.0-canary.684e33d25.0 + "@material/image-list": 15.0.0-canary.684e33d25.0 + "@material/layout-grid": 15.0.0-canary.684e33d25.0 + "@material/line-ripple": 15.0.0-canary.684e33d25.0 + "@material/linear-progress": 15.0.0-canary.684e33d25.0 + "@material/list": 15.0.0-canary.684e33d25.0 + "@material/menu": 15.0.0-canary.684e33d25.0 + "@material/menu-surface": 15.0.0-canary.684e33d25.0 + "@material/notched-outline": 15.0.0-canary.684e33d25.0 + "@material/radio": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/segmented-button": 15.0.0-canary.684e33d25.0 + "@material/select": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/slider": 15.0.0-canary.684e33d25.0 + "@material/snackbar": 15.0.0-canary.684e33d25.0 + "@material/switch": 15.0.0-canary.684e33d25.0 + "@material/tab": 15.0.0-canary.684e33d25.0 + "@material/tab-bar": 15.0.0-canary.684e33d25.0 + "@material/tab-indicator": 15.0.0-canary.684e33d25.0 + "@material/tab-scroller": 15.0.0-canary.684e33d25.0 + "@material/textfield": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tooltip": 15.0.0-canary.684e33d25.0 + "@material/top-app-bar": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 tslib: ^2.3.0 peerDependencies: - "@angular/animations": ^14.0.0 || ^15.0.0 - "@angular/cdk": 14.2.7 - "@angular/common": ^14.0.0 || ^15.0.0 - "@angular/core": ^14.0.0 || ^15.0.0 - "@angular/forms": ^14.0.0 || ^15.0.0 - "@angular/platform-browser": ^14.0.0 || ^15.0.0 + "@angular/animations": ^15.0.0 || ^16.0.0 + "@angular/cdk": 15.2.7 + "@angular/common": ^15.0.0 || ^16.0.0 + "@angular/core": ^15.0.0 || ^16.0.0 + "@angular/forms": ^15.0.0 || ^16.0.0 + "@angular/platform-browser": ^15.0.0 || ^16.0.0 rxjs: ^6.5.3 || ^7.4.0 - checksum: 078e944fb852b7d727a879c7fb5a9914f12447dbb4686444405565ea4aa40b076a60624c9bf2b3d9bc568ac6a50026b8a5ca614494253328421f8d14adb4afde + checksum: e1012dc58f99d039f4c4987bcef583f8b881a88184215f2ef004122df5d6f3d16a850563fee84bc1eff69af6fc0594c5202ab8ccf603f68c40fad847e2bf9059 languageName: node linkType: hard -"@angular/platform-browser-dynamic@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/platform-browser-dynamic@npm:14.3.0" +"@angular/platform-browser-dynamic@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/platform-browser-dynamic@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 14.3.0 - "@angular/compiler": 14.3.0 - "@angular/core": 14.3.0 - "@angular/platform-browser": 14.3.0 - checksum: c18ad42030c8728c2123837f20e72cbbd44e6810ab62d9ee5ecd498136f30581a39112fe338583990587ab31ccc01d1cc5f15bfeea0544b294a7754aed31e76d + "@angular/common": 15.2.7 + "@angular/compiler": 15.2.7 + "@angular/core": 15.2.7 + "@angular/platform-browser": 15.2.7 + checksum: c6aa8c592f3cd145b9f2c4c2d7f5f327090c273743946a6e352f8ea76dc6a8e674f0b86e57fa8e705bdc569874cc9662f17a92e7a2a99ee0079bf05825557726 languageName: node linkType: hard -"@angular/platform-browser@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/platform-browser@npm:14.3.0" +"@angular/platform-browser@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/platform-browser@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/animations": 14.3.0 - "@angular/common": 14.3.0 - "@angular/core": 14.3.0 + "@angular/animations": 15.2.7 + "@angular/common": 15.2.7 + "@angular/core": 15.2.7 peerDependenciesMeta: "@angular/animations": optional: true - checksum: f46cf20606f7ab0e883df95d4a904b45972e9749668f5f80817f8a61f74ac07147a34f11d3542934e9b8c2055809d1488154b58876cb44a304e0da37c68f9152 + checksum: 2a89953d9925079eccfc6648d8dcc1a0b7971dbfea4d287388cbf281c53d03589a5c635728cf202c7d4454de795dcbad360b56e855ef2ec8c2c4d04884c5c648 languageName: node linkType: hard -"@angular/router@npm:14.3.0": - version: 14.3.0 - resolution: "@angular/router@npm:14.3.0" +"@angular/router@npm:15.2.7": + version: 15.2.7 + resolution: "@angular/router@npm:15.2.7" dependencies: tslib: ^2.3.0 peerDependencies: - "@angular/common": 14.3.0 - "@angular/core": 14.3.0 - "@angular/platform-browser": 14.3.0 + "@angular/common": 15.2.7 + "@angular/core": 15.2.7 + "@angular/platform-browser": 15.2.7 rxjs: ^6.5.3 || ^7.4.0 - checksum: 8c0a26916fc4ba5a4b0b466a6fda83372521886f471a719a0092451dbeac9c0f5d83244d82cf6de5dd00cfd29b53d34ddb206e3696a3a9ab45030a71d37b02fe + checksum: 9c9f5870e37ba46515a74a2db1da1de880a237c4c35db339d11fb6a7abb9062dd932b137c8f100708109a0684122d3a676451a69214373e481ce15aff99967b1 languageName: node linkType: hard @@ -495,33 +534,56 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.18.8": +"@babel/compat-data@npm:^7.20.1, @babel/compat-data@npm:^7.21.4": version: 7.21.4 resolution: "@babel/compat-data@npm:7.21.4" checksum: 5f8b98c66f2ffba9f3c3a82c0cf354c52a0ec5ad4797b370dc32bdcd6e136ac4febe5e93d76ce76e175632e2dbf6ce9f46319aa689fcfafa41b6e49834fa4b66 languageName: node linkType: hard -"@babel/core@npm:7.18.10": - version: 7.18.10 - resolution: "@babel/core@npm:7.18.10" +"@babel/core@npm:7.19.3": + version: 7.19.3 + resolution: "@babel/core@npm:7.19.3" dependencies: "@ampproject/remapping": ^2.1.0 "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.18.10 - "@babel/helper-compilation-targets": ^7.18.9 - "@babel/helper-module-transforms": ^7.18.9 - "@babel/helpers": ^7.18.9 - "@babel/parser": ^7.18.10 + "@babel/generator": ^7.19.3 + "@babel/helper-compilation-targets": ^7.19.3 + "@babel/helper-module-transforms": ^7.19.0 + "@babel/helpers": ^7.19.0 + "@babel/parser": ^7.19.3 "@babel/template": ^7.18.10 - "@babel/traverse": ^7.18.10 - "@babel/types": ^7.18.10 + "@babel/traverse": ^7.19.3 + "@babel/types": ^7.19.3 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.1 semver: ^6.3.0 - checksum: 3a3fcd878430a9e1cb165f755c89fff45acc4efe4dd3a2ba356e89af331cb1947886b9782d56902a49af19ba3c24f08cf638a632699b9c5a4d8305c57c6a150d + checksum: dd883311209ad5a2c65b227daeb7247d90a382c50f4c6ad60c5ee40927eb39c34f0690d93b775c0427794261b72fa8f9296589a2dbda0782366a9f1c6de00c08 + languageName: node + linkType: hard + +"@babel/core@npm:7.20.12": + version: 7.20.12 + resolution: "@babel/core@npm:7.20.12" + dependencies: + "@ampproject/remapping": ^2.1.0 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.20.7 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helpers": ^7.20.7 + "@babel/parser": ^7.20.7 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.20.12 + "@babel/types": ^7.20.7 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.2 + semver: ^6.3.0 + checksum: 62e6c3e2149a70b5c9729ef5f0d3e2e97e9dcde89fc039c8d8e3463d5d7ba9b29ee84d10faf79b61532ac1645aa62f2bd42338320617e6e3a8a4d8e2a27076e7 languageName: node linkType: hard @@ -548,37 +610,14 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.17.2": - version: 7.21.0 - resolution: "@babel/core@npm:7.21.0" - dependencies: - "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.21.0 - "@babel/helper-compilation-targets": ^7.20.7 - "@babel/helper-module-transforms": ^7.21.0 - "@babel/helpers": ^7.21.0 - "@babel/parser": ^7.21.0 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.0 - "@babel/types": ^7.21.0 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.2.2 - semver: ^6.3.0 - checksum: 357f4dd3638861ceebf6d95ff49ad8b902065ee8b7b352621deed5666c2a6d702a48ca7254dba23ecae2a0afb67d20f90db7dd645c3b75e35e72ad9776c671aa - languageName: node - linkType: hard - -"@babel/generator@npm:7.18.12": - version: 7.18.12 - resolution: "@babel/generator@npm:7.18.12" +"@babel/generator@npm:7.20.14": + version: 7.20.14 + resolution: "@babel/generator@npm:7.20.14" dependencies: - "@babel/types": ^7.18.10 + "@babel/types": ^7.20.7 "@jridgewell/gen-mapping": ^0.3.2 jsesc: ^2.5.1 - checksum: 07dd71d255144bb703a80ab0156c35d64172ce81ddfb70ff24e2be687b052080233840c9a28d92fa2c33f7ecb8a8b30aef03b807518afc53b74c7908bf8859b1 + checksum: 5f6aa2d86af26e76d276923a5c34191124a119b16ee9ccc34aef654a7dec84fbd7d2daed2e6458a6a06bf87f3661deb77c9fea59b8f67faff5c90793c96d76d6 languageName: node linkType: hard @@ -593,7 +632,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.18.10, @babel/generator@npm:^7.21.4": +"@babel/generator@npm:^7.19.3, @babel/generator@npm:^7.20.7, @babel/generator@npm:^7.21.4": version: 7.21.4 resolution: "@babel/generator@npm:7.21.4" dependencies: @@ -605,7 +644,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.21.0, @babel/generator@npm:^7.21.1": +"@babel/generator@npm:^7.21.1": version: 7.21.1 resolution: "@babel/generator@npm:7.21.1" dependencies: @@ -651,6 +690,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.19.3, @babel/helper-compilation-targets@npm:^7.20.0": + version: 7.21.4 + resolution: "@babel/helper-compilation-targets@npm:7.21.4" + dependencies: + "@babel/compat-data": ^7.21.4 + "@babel/helper-validator-option": ^7.21.0 + browserslist: ^4.21.3 + lru-cache: ^5.1.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: bf9c7d3e7e6adff9222c05d898724cd4ee91d7eb9d52222c7ad2a22955620c2872cc2d9bdf0e047df8efdb79f4e3af2a06b53f509286145feccc4d10ddc318be + languageName: node + linkType: hard + "@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helper-create-class-features-plugin@npm:7.21.0" @@ -681,7 +735,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.2, @babel/helper-define-polyfill-provider@npm:^0.3.3": +"@babel/helper-define-polyfill-provider@npm:^0.3.3": version: 0.3.3 resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" dependencies: @@ -750,7 +804,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.18.9, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0, @babel/helper-module-transforms@npm:^7.21.2": +"@babel/helper-module-transforms@npm:^7.16.7, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.19.0, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2": version: 7.21.2 resolution: "@babel/helper-module-transforms@npm:7.21.2" dependencies: @@ -782,7 +836,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.6, @babel/helper-remap-async-to-generator@npm:^7.18.9": +"@babel/helper-remap-async-to-generator@npm:^7.18.9": version: 7.18.9 resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" dependencies: @@ -828,7 +882,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.18.6": +"@babel/helper-split-export-declaration@npm:7.18.6, @babel/helper-split-export-declaration@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-split-export-declaration@npm:7.18.6" dependencies: @@ -851,7 +905,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.18.6": +"@babel/helper-validator-option@npm:^7.18.6, @babel/helper-validator-option@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helper-validator-option@npm:7.21.0" checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 @@ -870,7 +924,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.18.9, @babel/helpers@npm:^7.21.0": +"@babel/helpers@npm:^7.16.7, @babel/helpers@npm:^7.19.0, @babel/helpers@npm:^7.20.7": version: 7.21.0 resolution: "@babel/helpers@npm:7.21.0" dependencies: @@ -892,7 +946,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.0, @babel/parser@npm:^7.21.2": +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.12, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.2": version: 7.21.2 resolution: "@babel/parser@npm:7.21.2" bin: @@ -901,7 +955,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.18.10, @babel/parser@npm:^7.21.4": +"@babel/parser@npm:^7.19.3, @babel/parser@npm:^7.21.4": version: 7.21.4 resolution: "@babel/parser@npm:7.21.4" bin: @@ -934,21 +988,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:7.18.10": - version: 7.18.10 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.18.10" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-plugin-utils": ^7.18.9 - "@babel/helper-remap-async-to-generator": ^7.18.9 - "@babel/plugin-syntax-async-generators": ^7.8.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3a6c25085021053830f6c57780118d3337935ac3309eef7f09b11e413d189eed8119d50cbddeb4c8c02f42f8cc01e62a4667b869be6e158f40030bafb92a0629 - languageName: node - linkType: hard - -"@babel/plugin-proposal-async-generator-functions@npm:^7.18.10": +"@babel/plugin-proposal-async-generator-functions@npm:7.20.7, @babel/plugin-proposal-async-generator-functions@npm:^7.20.1": version: 7.20.7 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" dependencies: @@ -1059,7 +1099,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.18.9": +"@babel/plugin-proposal-object-rest-spread@npm:^7.20.2": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: @@ -1192,7 +1232,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.18.6": +"@babel/plugin-syntax-import-assertions@npm:^7.20.0": version: 7.20.0 resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" dependencies: @@ -1313,20 +1353,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.18.6" - dependencies: - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/helper-remap-async-to-generator": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c2cca47468cf1aeefdc7ec35d670e195c86cee4de28a1970648c46a88ce6bd1806ef0bab27251b9e7fb791bb28a64dcd543770efd899f28ee5f7854e64e873d3 - languageName: node - linkType: hard - -"@babel/plugin-transform-async-to-generator@npm:^7.18.6": +"@babel/plugin-transform-async-to-generator@npm:7.20.7, @babel/plugin-transform-async-to-generator@npm:^7.18.6": version: 7.20.7 resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" dependencies: @@ -1350,7 +1377,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.18.9": +"@babel/plugin-transform-block-scoping@npm:^7.20.2": version: 7.21.0 resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" dependencies: @@ -1361,7 +1388,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.18.9": +"@babel/plugin-transform-classes@npm:^7.20.2": version: 7.21.0 resolution: "@babel/plugin-transform-classes@npm:7.21.0" dependencies: @@ -1392,7 +1419,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.18.9": +"@babel/plugin-transform-destructuring@npm:^7.20.2": version: 7.21.3 resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" dependencies: @@ -1484,7 +1511,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.18.6": +"@babel/plugin-transform-modules-amd@npm:^7.19.6": version: 7.20.11 resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" dependencies: @@ -1496,7 +1523,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.18.6": +"@babel/plugin-transform-modules-commonjs@npm:^7.19.6": version: 7.21.2 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" dependencies: @@ -1509,7 +1536,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.18.9": +"@babel/plugin-transform-modules-systemjs@npm:^7.19.6": version: 7.20.11 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" dependencies: @@ -1535,7 +1562,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.18.6": +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.19.1": version: 7.20.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" dependencies: @@ -1570,7 +1597,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.18.8": +"@babel/plugin-transform-parameters@npm:^7.20.1": version: 7.21.3 resolution: "@babel/plugin-transform-parameters@npm:7.21.3" dependencies: @@ -1626,19 +1653,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:7.18.10": - version: 7.18.10 - resolution: "@babel/plugin-transform-runtime@npm:7.18.10" +"@babel/plugin-transform-runtime@npm:7.19.6": + version: 7.19.6 + resolution: "@babel/plugin-transform-runtime@npm:7.19.6" dependencies: "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.9 - babel-plugin-polyfill-corejs2: ^0.3.2 - babel-plugin-polyfill-corejs3: ^0.5.3 - babel-plugin-polyfill-regenerator: ^0.4.0 + "@babel/helper-plugin-utils": ^7.19.0 + babel-plugin-polyfill-corejs2: ^0.3.3 + babel-plugin-polyfill-corejs3: ^0.6.0 + babel-plugin-polyfill-regenerator: ^0.4.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 98c18680b4258b8bd3f04926b73c72ae77037d5ea5b50761ca35de15896bf0d04bedabde39a81be56dbd4859c96ffaa7103fbefb5d5b58a36e0a80381e4a146c + checksum: ef93efbcbb00dcf4da6dcc55bda698a2a57fca3fb05a6a13e932ecfdb7c1c5d2f0b5b245c1c4faca0318853937caba0d82442f58b7653249f64275d08052fbd8 languageName: node linkType: hard @@ -1653,7 +1680,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.18.9": +"@babel/plugin-transform-spread@npm:^7.19.0": version: 7.20.7 resolution: "@babel/plugin-transform-spread@npm:7.20.7" dependencies: @@ -1721,17 +1748,17 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.18.10": - version: 7.18.10 - resolution: "@babel/preset-env@npm:7.18.10" +"@babel/preset-env@npm:7.20.2": + version: 7.20.2 + resolution: "@babel/preset-env@npm:7.20.2" dependencies: - "@babel/compat-data": ^7.18.8 - "@babel/helper-compilation-targets": ^7.18.9 - "@babel/helper-plugin-utils": ^7.18.9 + "@babel/compat-data": ^7.20.1 + "@babel/helper-compilation-targets": ^7.20.0 + "@babel/helper-plugin-utils": ^7.20.2 "@babel/helper-validator-option": ^7.18.6 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.18.9 - "@babel/plugin-proposal-async-generator-functions": ^7.18.10 + "@babel/plugin-proposal-async-generator-functions": ^7.20.1 "@babel/plugin-proposal-class-properties": ^7.18.6 "@babel/plugin-proposal-class-static-block": ^7.18.6 "@babel/plugin-proposal-dynamic-import": ^7.18.6 @@ -1740,7 +1767,7 @@ __metadata: "@babel/plugin-proposal-logical-assignment-operators": ^7.18.9 "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 "@babel/plugin-proposal-numeric-separator": ^7.18.6 - "@babel/plugin-proposal-object-rest-spread": ^7.18.9 + "@babel/plugin-proposal-object-rest-spread": ^7.20.2 "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 "@babel/plugin-proposal-optional-chaining": ^7.18.9 "@babel/plugin-proposal-private-methods": ^7.18.6 @@ -1751,7 +1778,7 @@ __metadata: "@babel/plugin-syntax-class-static-block": ^7.14.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.18.6 + "@babel/plugin-syntax-import-assertions": ^7.20.0 "@babel/plugin-syntax-json-strings": ^7.8.3 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 @@ -1764,10 +1791,10 @@ __metadata: "@babel/plugin-transform-arrow-functions": ^7.18.6 "@babel/plugin-transform-async-to-generator": ^7.18.6 "@babel/plugin-transform-block-scoped-functions": ^7.18.6 - "@babel/plugin-transform-block-scoping": ^7.18.9 - "@babel/plugin-transform-classes": ^7.18.9 + "@babel/plugin-transform-block-scoping": ^7.20.2 + "@babel/plugin-transform-classes": ^7.20.2 "@babel/plugin-transform-computed-properties": ^7.18.9 - "@babel/plugin-transform-destructuring": ^7.18.9 + "@babel/plugin-transform-destructuring": ^7.20.2 "@babel/plugin-transform-dotall-regex": ^7.18.6 "@babel/plugin-transform-duplicate-keys": ^7.18.9 "@babel/plugin-transform-exponentiation-operator": ^7.18.6 @@ -1775,34 +1802,34 @@ __metadata: "@babel/plugin-transform-function-name": ^7.18.9 "@babel/plugin-transform-literals": ^7.18.9 "@babel/plugin-transform-member-expression-literals": ^7.18.6 - "@babel/plugin-transform-modules-amd": ^7.18.6 - "@babel/plugin-transform-modules-commonjs": ^7.18.6 - "@babel/plugin-transform-modules-systemjs": ^7.18.9 + "@babel/plugin-transform-modules-amd": ^7.19.6 + "@babel/plugin-transform-modules-commonjs": ^7.19.6 + "@babel/plugin-transform-modules-systemjs": ^7.19.6 "@babel/plugin-transform-modules-umd": ^7.18.6 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.18.6 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.19.1 "@babel/plugin-transform-new-target": ^7.18.6 "@babel/plugin-transform-object-super": ^7.18.6 - "@babel/plugin-transform-parameters": ^7.18.8 + "@babel/plugin-transform-parameters": ^7.20.1 "@babel/plugin-transform-property-literals": ^7.18.6 "@babel/plugin-transform-regenerator": ^7.18.6 "@babel/plugin-transform-reserved-words": ^7.18.6 "@babel/plugin-transform-shorthand-properties": ^7.18.6 - "@babel/plugin-transform-spread": ^7.18.9 + "@babel/plugin-transform-spread": ^7.19.0 "@babel/plugin-transform-sticky-regex": ^7.18.6 "@babel/plugin-transform-template-literals": ^7.18.9 "@babel/plugin-transform-typeof-symbol": ^7.18.9 "@babel/plugin-transform-unicode-escapes": ^7.18.10 "@babel/plugin-transform-unicode-regex": ^7.18.6 "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.18.10 - babel-plugin-polyfill-corejs2: ^0.3.2 - babel-plugin-polyfill-corejs3: ^0.5.3 - babel-plugin-polyfill-regenerator: ^0.4.0 - core-js-compat: ^3.22.1 + "@babel/types": ^7.20.2 + babel-plugin-polyfill-corejs2: ^0.3.3 + babel-plugin-polyfill-corejs3: ^0.6.0 + babel-plugin-polyfill-regenerator: ^0.4.1 + core-js-compat: ^3.25.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 36eeb7157021091c8047703833b7a28e4963865d16968a5b9dbffe1eb05e44307a8d29ad45d81fd23817f68290b52921c42f513a93996c7083d23d5e2cea0c6b + checksum: ece2d7e9c7789db6116e962b8e1a55eb55c110c44c217f0c8f6ffea4ca234954e66557f7bd019b7affadf7fbb3a53ccc807e93fc935aacd48146234b73b6947e languageName: node linkType: hard @@ -1828,12 +1855,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.18.9": - version: 7.18.9 - resolution: "@babel/runtime@npm:7.18.9" +"@babel/runtime@npm:7.20.13": + version: 7.20.13 + resolution: "@babel/runtime@npm:7.20.13" dependencies: - regenerator-runtime: ^0.13.4 - checksum: 36dd736baba7164e82b3cc9d43e081f0cb2d05ff867ad39cac515d99546cee75b7f782018b02a3dcf5f2ef3d27f319faa68965fdfec49d4912c60c6002353a2e + regenerator-runtime: ^0.13.11 + checksum: 09b7a97a05c80540db6c9e4ddf8c5d2ebb06cae5caf3a87e33c33f27f8c4d49d9c67a2d72f1570e796045288fad569f98a26ceba0c4f5fad2af84b6ad855c4fb languageName: node linkType: hard @@ -1846,14 +1873,14 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:7.18.10": - version: 7.18.10 - resolution: "@babel/template@npm:7.18.10" +"@babel/template@npm:7.20.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/template@npm:7.20.7" dependencies: "@babel/code-frame": ^7.18.6 - "@babel/parser": ^7.18.10 - "@babel/types": ^7.18.10 - checksum: 93a6aa094af5f355a72bd55f67fa1828a046c70e46f01b1606e6118fa1802b6df535ca06be83cc5a5e834022be95c7b714f0a268b5f20af984465a71e28f1473 + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e languageName: node linkType: hard @@ -1868,17 +1895,6 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/template@npm:7.20.7" - dependencies: - "@babel/code-frame": ^7.18.6 - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e - languageName: node - linkType: hard - "@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2": version: 7.21.2 resolution: "@babel/traverse@npm:7.21.2" @@ -1897,7 +1913,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.18.10": +"@babel/traverse@npm:^7.19.3, @babel/traverse@npm:^7.20.12": version: 7.21.4 resolution: "@babel/traverse@npm:7.21.4" dependencies: @@ -1926,7 +1942,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.18.10, @babel/types@npm:^7.21.4": +"@babel/types@npm:^7.19.3, @babel/types@npm:^7.21.4": version: 7.21.4 resolution: "@babel/types@npm:7.21.4" dependencies: @@ -1944,184 +1960,174 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-cascade-layers@npm:^1.0.5": - version: 1.1.1 - resolution: "@csstools/postcss-cascade-layers@npm:1.1.1" - dependencies: - "@csstools/selector-specificity": ^2.0.2 - postcss-selector-parser: ^6.0.10 +"@csstools/selector-specificity@npm:^2.0.2": + version: 2.1.1 + resolution: "@csstools/selector-specificity@npm:2.1.1" peerDependencies: - postcss: ^8.2 - checksum: 8ecd6a929e8ddee3ad0834ab5017f50a569817ba8490d152b11c705c13cf3d9701f74792f375cbd72d8f33a4eeaabb3f984f1514adf8c5a530eb91be70c14cf4 + postcss: ^8.4 + postcss-selector-parser: ^6.0.10 + checksum: 392ab62732e93aa8cbea445bf3485c1acbbecc8ec087b200e06c9ddd2acf740fd1fe46abdacf813e7a50a95a60346377ee3eecb4e1fe3709582e2851430b376a languageName: node linkType: hard -"@csstools/postcss-color-function@npm:^1.1.1": - version: 1.1.1 - resolution: "@csstools/postcss-color-function@npm:1.1.1" - dependencies: - "@csstools/postcss-progressive-custom-properties": ^1.1.0 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 087595985ebcc2fc42013d6305185d4cdc842d87fb261185db905dc31eaa24fc23a7cc068fa3da814b3c8b98164107ddaf1b4ab24f4ff5b2a7b5fbcd4c6ceec9 +"@discoveryjs/json-ext@npm:0.5.7": + version: 0.5.7 + resolution: "@discoveryjs/json-ext@npm:0.5.7" + checksum: 2176d301cc258ea5c2324402997cf8134ebb212469c0d397591636cea8d3c02f2b3cf9fd58dcb748c7a0dade77ebdc1b10284fa63e608c033a1db52fddc69918 languageName: node linkType: hard -"@csstools/postcss-font-format-keywords@npm:^1.0.1": - version: 1.0.1 - resolution: "@csstools/postcss-font-format-keywords@npm:1.0.1" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: ed8d9eab9793f0184e000709bcb155d4eb96c49a312e3ea9e549e006b74fd4aafac63cb9f9f01bec5b717a833539ff085c3f1ef7d273b97d587769ef637d50c1 +"@esbuild/android-arm64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/android-arm64@npm:0.17.8" + conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@csstools/postcss-hwb-function@npm:^1.0.2": - version: 1.0.2 - resolution: "@csstools/postcss-hwb-function@npm:1.0.2" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 352ead754a692f7ed33a712c491012cab5c2f2946136a669a354237cfe8e6faca90c7389ee793cb329b9b0ddec984faa06d47e2f875933aaca417afff74ce6aa +"@esbuild/android-arm@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/android-arm@npm:0.17.8" + conditions: os=android & cpu=arm languageName: node linkType: hard -"@csstools/postcss-ic-unit@npm:^1.0.1": - version: 1.0.1 - resolution: "@csstools/postcss-ic-unit@npm:1.0.1" - dependencies: - "@csstools/postcss-progressive-custom-properties": ^1.1.0 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 09c414c9b7762b5fbe837ff451d7a11e4890f1ed3c92edc3573f02f3d89747f6ac3f2270799b68a332bd7f5de05bb0dfffddb6323fc4020c2bea33ff58314533 +"@esbuild/android-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/android-x64@npm:0.17.8" + conditions: os=android & cpu=x64 languageName: node linkType: hard -"@csstools/postcss-is-pseudo-class@npm:^2.0.7": - version: 2.0.7 - resolution: "@csstools/postcss-is-pseudo-class@npm:2.0.7" - dependencies: - "@csstools/selector-specificity": ^2.0.0 - postcss-selector-parser: ^6.0.10 - peerDependencies: - postcss: ^8.2 - checksum: a4494bb8e9a34826944ba6872c91c1e88268caab6d06968897f1a0cc75ca5cfc4989435961fc668a9c6842a6d17f4cda0055fa256d23e598b8bbc6f022956125 +"@esbuild/darwin-arm64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/darwin-arm64@npm:0.17.8" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@csstools/postcss-nested-calc@npm:^1.0.0": - version: 1.0.0 - resolution: "@csstools/postcss-nested-calc@npm:1.0.0" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 53bb783dd61621c11c1e6e352f079577e2eb908de67947ceef31a178e070c06c223baae87acd5c3bd51c664515d2adc16166a129159168626111aff548583790 +"@esbuild/darwin-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/darwin-x64@npm:0.17.8" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@csstools/postcss-normalize-display-values@npm:^1.0.1": - version: 1.0.1 - resolution: "@csstools/postcss-normalize-display-values@npm:1.0.1" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 75901daec3869ba15e0adfd50d8e2e754ec06d55ac44fbd540748476388d223d53710fb3a3cbfe6695a2bab015a489fb47d9e3914ff211736923f8deb818dc0b +"@esbuild/freebsd-arm64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/freebsd-arm64@npm:0.17.8" + conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@csstools/postcss-oklab-function@npm:^1.1.1": - version: 1.1.1 - resolution: "@csstools/postcss-oklab-function@npm:1.1.1" - dependencies: - "@csstools/postcss-progressive-custom-properties": ^1.1.0 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: d66b789060b37ed810450d9a7d8319a0ae14e913c091f3e0ee482b3471538762e801d5eae3d62fda2f1eb1e88c76786d2c2b06c1172166eba1cca5e2a0dc95f2 +"@esbuild/freebsd-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/freebsd-x64@npm:0.17.8" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@csstools/postcss-progressive-custom-properties@npm:^1.1.0, @csstools/postcss-progressive-custom-properties@npm:^1.3.0": - version: 1.3.0 - resolution: "@csstools/postcss-progressive-custom-properties@npm:1.3.0" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.3 - checksum: e281845fde5b8a80d06ec20147bd74e96a9351bebbec5e5c3a6fb37ea30a597ff84172601786a8a270662f58f708b4a3bf8d822d6318023def9773d2f6589962 +"@esbuild/linux-arm64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-arm64@npm:0.17.8" + conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@csstools/postcss-stepped-value-functions@npm:^1.0.1": - version: 1.0.1 - resolution: "@csstools/postcss-stepped-value-functions@npm:1.0.1" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 2fc88713a0d49d142010652be8139b00719e407df1173e46047284f1befd0647e1fff67f259f9f55ac3b46bba6462b21f0aa192bd10a2989c51a8ce0d25fc495 +"@esbuild/linux-arm@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-arm@npm:0.17.8" + conditions: os=linux & cpu=arm languageName: node linkType: hard -"@csstools/postcss-text-decoration-shorthand@npm:^1.0.0": - version: 1.0.0 - resolution: "@csstools/postcss-text-decoration-shorthand@npm:1.0.0" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: d27aaf97872c42bec9f6fde4d8bf924e89f7886f0aca8e4fc5aaf2f9083b09bb43dbbfa29124fa36fcdeb2d4d3e0459a095acf62188260cd1577e9811bb1276e +"@esbuild/linux-ia32@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-ia32@npm:0.17.8" + conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@csstools/postcss-trigonometric-functions@npm:^1.0.2": - version: 1.0.2 - resolution: "@csstools/postcss-trigonometric-functions@npm:1.0.2" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: f7f5b5f2492606b79a56f09e814ae8f10a2ae9e9c5fb8019f0e347a4a6c07953b2cc663fd4fa43a60e6994dfd958958f39df8ec760e2a646cfe71fe2bb119382 +"@esbuild/linux-loong64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-loong64@npm:0.17.8" + conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@csstools/postcss-unset-value@npm:^1.0.2": - version: 1.0.2 - resolution: "@csstools/postcss-unset-value@npm:1.0.2" - peerDependencies: - postcss: ^8.2 - checksum: 3facdae154d6516ffd964f7582696f406465f11cf8dead503e0afdfecc99ebc25638ab2830affce4516131aa2db004458a235e439f575b04e9ef72ad82f55835 +"@esbuild/linux-mips64el@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-mips64el@npm:0.17.8" + conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@csstools/selector-specificity@npm:^2.0.0, @csstools/selector-specificity@npm:^2.0.2": - version: 2.1.1 - resolution: "@csstools/selector-specificity@npm:2.1.1" - peerDependencies: - postcss: ^8.4 - postcss-selector-parser: ^6.0.10 - checksum: 392ab62732e93aa8cbea445bf3485c1acbbecc8ec087b200e06c9ddd2acf740fd1fe46abdacf813e7a50a95a60346377ee3eecb4e1fe3709582e2851430b376a +"@esbuild/linux-ppc64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-ppc64@npm:0.17.8" + conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@discoveryjs/json-ext@npm:0.5.7": - version: 0.5.7 - resolution: "@discoveryjs/json-ext@npm:0.5.7" - checksum: 2176d301cc258ea5c2324402997cf8134ebb212469c0d397591636cea8d3c02f2b3cf9fd58dcb748c7a0dade77ebdc1b10284fa63e608c033a1db52fddc69918 +"@esbuild/linux-riscv64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-riscv64@npm:0.17.8" + conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.15.5": - version: 0.15.5 - resolution: "@esbuild/linux-loong64@npm:0.15.5" - conditions: os=linux & cpu=loong64 +"@esbuild/linux-s390x@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-s390x@npm:0.17.8" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/linux-x64@npm:0.17.8" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/netbsd-x64@npm:0.17.8" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/openbsd-x64@npm:0.17.8" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/sunos-x64@npm:0.17.8" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/win32-arm64@npm:0.17.8" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/win32-ia32@npm:0.17.8" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.17.8": + version: 0.17.8 + resolution: "@esbuild/win32-x64@npm:0.17.8" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2253,6 +2259,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.14, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.17 resolution: "@jridgewell/trace-mapping@npm:0.3.17" @@ -2270,80 +2283,889 @@ __metadata: languageName: node linkType: hard -"@ngtools/webpack@npm:14.2.11": - version: 14.2.11 - resolution: "@ngtools/webpack@npm:14.2.11" - peerDependencies: - "@angular/compiler-cli": ^14.0.0 - typescript: ">=4.6.2 <4.9" - webpack: ^5.54.0 - checksum: 84c5b078a1e0327907acaf49075913347d6686dd07388eeaf63782e79615a2517758de60371a4c2410e26e1eb9ceb543c07f4c5f1a132bb13e25404e10e1d757 +"@material/animation@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/animation@npm:15.0.0-canary.684e33d25.0" + dependencies: + tslib: ^2.1.0 + checksum: c56d0559b3ce323c98b6db797facd3619ef9094dd8a02c4d06ceaad78953679c67e3275dcf0df26266c1e07498534f22f16606ab6ba2143c9eb37cc6e595633e languageName: node linkType: hard -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" +"@material/auto-init@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/auto-init@npm:15.0.0-canary.684e33d25.0" dependencies: - "@nodelib/fs.stat": 2.0.5 - run-parallel: ^1.1.9 - checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59 + "@material/base": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 6c8f39e5471ffc6757d8443432a04ca19072ad328b484653efacf1bb70dee95fcf48f312a9856c72b4d04fac4af840e672553f84f34b3feb55a5f484e5e9dfd9 languageName: node linkType: hard -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 +"@material/banner@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/banner@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/button": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: e34480341eefc022708c73930838efe60ba7b2df0e59afaeafdd17c8cf2343cfbb83ab5f9321c751bd46ee67c0910e7c92b05e047f2161652e1a4bf31d03d166 languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" +"@material/base@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/base@npm:15.0.0-canary.684e33d25.0" dependencies: - "@nodelib/fs.scandir": 2.1.5 - fastq: ^1.6.0 - checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53 + tslib: ^2.1.0 + checksum: 8aa39ea96ab183df571e2f7604281289a2ffec7ec2efdd5d3a007dd59cae65b560927873f1e3f49dde7800faa4d9b95f2229bdfc7c703e63dc8cb91f21d816bd + languageName: node + linkType: hard + +"@material/button@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/button@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 9e298bc91ae32b120e99bb8e1bc8cf61316dc359cf7c3af95ac8be4656adab334ece66d3661c799739d9265183715e0e4f7c959bad2794d86527abc800e67809 languageName: node linkType: hard -"@npmcli/fs@npm:^2.1.0": - version: 2.1.2 - resolution: "@npmcli/fs@npm:2.1.2" +"@material/card@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/card@npm:15.0.0-canary.684e33d25.0" dependencies: - "@gar/promisify": ^1.1.3 - semver: ^7.3.5 - checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: cd7bd5724868feaca1a7d0f4a7224c7ad61362fd08a36000587523f463c0ea9eee7425f80f5b6325de5bdaa51859a48805298a91a7d28206407054eeb1253581 languageName: node linkType: hard -"@npmcli/git@npm:^3.0.0": - version: 3.0.2 - resolution: "@npmcli/git@npm:3.0.2" +"@material/checkbox@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/checkbox@npm:15.0.0-canary.684e33d25.0" dependencies: - "@npmcli/promise-spawn": ^3.0.0 - lru-cache: ^7.4.4 - mkdirp: ^1.0.4 - npm-pick-manifest: ^7.0.0 - proc-log: ^2.0.0 + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 66e1d94181f85a5415a34779729f8d35d492b10fbccbba9cfe4210a8f2e22b05d05433e5427a4456dfbbaa310ba466647b7168e2e1dd92e7a1c8b39dcb2b1d86 + languageName: node + linkType: hard + +"@material/chips@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/chips@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/checkbox": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + safevalues: ^0.3.4 + tslib: ^2.1.0 + checksum: fbdb1f36b945c74dc7a85683bbf82a21f6d92cb96100f24728ae8a06335104c1877c692f8f8670ba43638a9c7bd6d1a32a5f6ccda0b4aa7484677ef129f3e1c7 + languageName: node + linkType: hard + +"@material/circular-progress@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/circular-progress@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/progress-indicator": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 445813c773a101c40026e95cbd6dbc16dcd8baeae34d1643b66b9884aa32d95e8c4cf243cbaaa43718af3c4cd12e0abf834b90cf73382b855d17cb6ff7aa1e53 + languageName: node + linkType: hard + +"@material/data-table@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/data-table@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/checkbox": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/icon-button": 15.0.0-canary.684e33d25.0 + "@material/linear-progress": 15.0.0-canary.684e33d25.0 + "@material/list": 15.0.0-canary.684e33d25.0 + "@material/menu": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/select": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 914f8d378176f3ecebefc8e2c516b067b9440f074ded78e229b5e58305bd9a5e0027ee4e3852255549b43682f145d73ed211d49f1443b6d088224e40ccbccd2d + languageName: node + linkType: hard + +"@material/density@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/density@npm:15.0.0-canary.684e33d25.0" + dependencies: + tslib: ^2.1.0 + checksum: b9b990d64e2c36dea6a7fbceea21cf770e94760a7ed5e898c241da5a65fe87c1a63b57abcce601541465342b18089cf47c5bc02e2077723eab3663b5fad7a0bb + languageName: node + linkType: hard + +"@material/dialog@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/dialog@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/button": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/icon-button": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: ce9ef1822cc620e0787a3f6c968d0ca05c933dbcc2f4136365f875e5fdf956f5f150f65ba13b46cf87f63376354f72b101ffbe71b79c8f2eb1775dd36a32371f + languageName: node + linkType: hard + +"@material/dom@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/dom@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: d4d85e083822bf7f3511d3a7373311e411f7372d8e4aff84f9bd9ac4d5971e0b28be649c0016cdc50d6e892d13308eb07c28678b05f6ccff62a02a03ac197e20 + languageName: node + linkType: hard + +"@material/drawer@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/drawer@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/list": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 70d8a2f132a35df201044997e3c380a35420ec5c18864b122594092fefd9e3d85d117bf29c5e12aa217501d156e9e901cdbe5d616320f607f615df8682defe80 + languageName: node + linkType: hard + +"@material/elevation@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/elevation@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: ae9b126d824f2ce4a3aa79181244ede8d746b9519cf6cce06c6a7ed92d058622598052c8d53e03f3fea01ba9aa860242dfd1932b1bbe398425ad3fb9be3e6bd7 + languageName: node + linkType: hard + +"@material/fab@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/fab@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 3b0a9235c4dc696fbabe94f9a260a3d1b0f03a166086cf349f08648414747825ae158aa65c2ddbf0cf272fa64a10a3b3e34bf5a1451a196c0c44da76751d7ed6 + languageName: node + linkType: hard + +"@material/feature-targeting@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/feature-targeting@npm:15.0.0-canary.684e33d25.0" + dependencies: + tslib: ^2.1.0 + checksum: 46bad4d0bac3287660e976ff01f5ad0bf7a7265e407a234f66f476fb630edeb63ab9cb2f1a504872f95e521f9e51d35119783c52316f75970fb0e495983929a6 + languageName: node + linkType: hard + +"@material/floating-label@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/floating-label@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 1e85d92dd1d2848a21eaf93823abd9832a39c3f6f928a52d740c99ed138ebef551329696bc251207831cceac57cb3b57a8fad9e15921f3a5a0e99b72238acc2f + languageName: node + linkType: hard + +"@material/focus-ring@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/focus-ring@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + checksum: 90ceff6c037d9af1dc69431d7ddead100f8544d127f3f26e8059edff6ed63cc961a45532a4b9e2dbca8a442796ef4a3d5b02ca7e3708a6fac5de7d262f0447ad + languageName: node + linkType: hard + +"@material/form-field@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/form-field@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 1cdd890bf94d164217492f610492dc980c177e7966072de2711b867fa23af088f5c9a694bcacfac589a9c9b9dc48220db3240febc2129f0bc3fe5ae2ea90c765 + languageName: node + linkType: hard + +"@material/icon-button@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/icon-button@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 0f8d132d90adc6a9606d7e340acf5c64f1bea8fc3194a20785eeb943e2667d540abf9ef85dde467f223e1c19ac11339d9f5f520d6fcf2c71bab3936b02786b87 + languageName: node + linkType: hard + +"@material/image-list@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/image-list@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 77031ad7cde772f542573073d8d3407b747e6be63a21e81a4a410eff7e8840164d997e7e161380504c42ce6fda919238f32cd37723f601beea1ff93cecff5f3a + languageName: node + linkType: hard + +"@material/layout-grid@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/layout-grid@npm:15.0.0-canary.684e33d25.0" + dependencies: + tslib: ^2.1.0 + checksum: dd2ff438c550e168b2682cc871abe7ca6500bfc5725c03f1ec955461730347d1500e81d4a96d4b7576229542a88988d4084fd77ccd8e7a162bdc58d0bf7a2c2d + languageName: node + linkType: hard + +"@material/line-ripple@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/line-ripple@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 0ac959c09e9c0c583bdade9638aa7ce34371d702a1dfdf7da5d15a35cd46a7ad3f2a79f5280b161504c4b37c66fbf5bf5b2bb490deeda11a621794e9dbd53bac + languageName: node + linkType: hard + +"@material/linear-progress@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/linear-progress@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/progress-indicator": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 8c8fe5f110d9c28d8adc96adb253c906b18e97adab9761177661fd20be58d0ce82f99ebd5ce98658fc3991607bcb2b47cb4faaa74a44cb35adcb887419c1780f + languageName: node + linkType: hard + +"@material/list@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/list@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: c6dff9eb517411802b9fd188c8f8b5b3fa75bd28339f995e929421177a900c6e16693ec4676b15ed5009d7d7744bc2d4a9acc9e0210f45d221b47420732c2f45 + languageName: node + linkType: hard + +"@material/menu-surface@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/menu-surface@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 0644afb1adc6e4cffb66c24dab72c403103a2c2be4e3ec9588e7771593794157e033cd54d301f1ec4f63056e03a7ae7bbb95be2c8735bc5dd2a3204802f5bba2 + languageName: node + linkType: hard + +"@material/menu@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/menu@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/list": 15.0.0-canary.684e33d25.0 + "@material/menu-surface": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 67f36837c1d5b09b506ac37fcf0423e3da431d80b341fb6cabb851678dbfc6c6a622c33c0f2b452c7a9c2b4d0a2b15fd1b34f35fd9e3abf7a4687dfb2130c376 + languageName: node + linkType: hard + +"@material/notched-outline@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/notched-outline@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/floating-label": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 8757729f793a57e621a52f48b4509c7c7fa7de7de0d4c1431aa576123995389bd61422c524ca9a5fd7add05870fc7cb159dc526918339d7e812091db8b7a43ad + languageName: node + linkType: hard + +"@material/progress-indicator@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/progress-indicator@npm:15.0.0-canary.684e33d25.0" + dependencies: + tslib: ^2.1.0 + checksum: 67be90b6575aa0d143f7b65c64d86f077cd72745b0a3e0aa749709cccb184321d7cfc661f81ccd5f3eccf5ce4a3ded40e9ec321ef4a1677ce5f01f198d50497f + languageName: node + linkType: hard + +"@material/radio@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/radio@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: e2f0c0df5ca9a95ad3e7df15a32f5adfff79338e6ddcd0a46d02351de6249c183f9829d6645340e9407881c619b68aff543794106939ccb2b214f2dd5869abc3 + languageName: node + linkType: hard + +"@material/ripple@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/ripple@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 2ea9a6faa808828353318192eb6967d43d44a5b86bc141891837b2d999b479869317108be84db3a4148874765f0aaf47ea761cfa5b30fa37923fc57984b4689e + languageName: node + linkType: hard + +"@material/rtl@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/rtl@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: ead5b8cecc31bdd7bb1678361dc73b1a1c9f196c4f9195d7bd3f71a6f0307910dfaed3006ff0065eafe80257fa290bc56b46c1cf3ab8abc79c366a1ee2e552b1 + languageName: node + linkType: hard + +"@material/segmented-button@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/segmented-button@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/touch-target": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 5eaf2322e3ba2ace88aaf39e428d0f1274be0327de132cc4cd520d4a66ff1e769f94268452f4def9d443b6eb2aba10782f29bf1faeea7af98cb3abb9afc8357d + languageName: node + linkType: hard + +"@material/select@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/select@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/floating-label": 15.0.0-canary.684e33d25.0 + "@material/line-ripple": 15.0.0-canary.684e33d25.0 + "@material/list": 15.0.0-canary.684e33d25.0 + "@material/menu": 15.0.0-canary.684e33d25.0 + "@material/menu-surface": 15.0.0-canary.684e33d25.0 + "@material/notched-outline": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 73ef2fb9833bd43d9d74948774867060f12d92fa4ed9bd47ace9d9b85bffe228254af2f1c81b5536369378404064d1bd6296630f02fe3d9b0fb00d5af57d6e61 + languageName: node + linkType: hard + +"@material/shape@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/shape@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 33c03b9b3131a6f6ad9d54276415152d072efc56b3f4cb1f1a47d7c6987253f6cf2af631f2191d318f68433e389255e88ce5c5ac408cdc7b772f11f782923937 + languageName: node + linkType: hard + +"@material/slider@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/slider@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 578183dc2b5705b02cd489274086185220c74501f287f0bed672303d4cfb471f910d963d308cb16f62ac6981e66f39f409cabe8f71d25422205a4f055240bf46 + languageName: node + linkType: hard + +"@material/snackbar@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/snackbar@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/button": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/icon-button": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 8de53a01a75306a1eeb0e7707852692310333db865f31dd00521c06d667463aa6bee3e905c3ff9d15c161a8b19ed8a93fcc2467bf8999c95eda64ea35e54441e + languageName: node + linkType: hard + +"@material/switch@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/switch@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + safevalues: ^0.3.4 + tslib: ^2.1.0 + checksum: 6ad898cfe55ad01ba73555b2438e7d816f82f28d969b68649cfa18cce45d9387ee3e6c9c02d54dd757d57caadfb9c2d9346d128d3c97ee52aed0cfd2848bba91 + languageName: node + linkType: hard + +"@material/tab-bar@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/tab-bar@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/tab": 15.0.0-canary.684e33d25.0 + "@material/tab-indicator": 15.0.0-canary.684e33d25.0 + "@material/tab-scroller": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 56380f584c24376e54342a831231bec9d3ba9d74fa03e52eca2c2b231a64142f5aa1df5d18c6b7fdef52ce04951fa7465c40587bbeff1b8660d9211181c9d980 + languageName: node + linkType: hard + +"@material/tab-indicator@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/tab-indicator@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 9656be3d0576ab37162e7abdf16da09430f0ca8889b443eb8806d21373ec67bf35ae9c3c8d44af70a9c87d1ad9e6e3ec380b91edd756e40636a8d0dafecda6ff + languageName: node + linkType: hard + +"@material/tab-scroller@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/tab-scroller@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/tab": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 536353074bbf5e922d809a26f878c89bf3ef2142c723b9a742ba118314abe0459cf10ab30b28624f2c994eb5a81cde3125852ffdec8b1de4736add236eea47f2 + languageName: node + linkType: hard + +"@material/tab@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/tab@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/focus-ring": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/tab-indicator": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: d4617f18a55b59632cf662f82ad0bd351dbd28c3c177c5510bec5a47a741bae4da2a7cea272c2c1a1bc64adc0b5dacf3bfc6f45651820a5bbda2273841b1e702 + languageName: node + linkType: hard + +"@material/textfield@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/textfield@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/density": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/floating-label": 15.0.0-canary.684e33d25.0 + "@material/line-ripple": 15.0.0-canary.684e33d25.0 + "@material/notched-outline": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 71be9ec40cdc123d9dce2c26d019162b6ff3fc43559b1be3b186218dc38266149d6ee0efc80ea367d097b8ab1208f721513e84d657498e82ef6ae9744dca3522 + languageName: node + linkType: hard + +"@material/theme@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/theme@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 40048a9c350b613c3daa254223cca9fba03ddf3ffa3639c7fb6de601d71fe4f9939a4186692466bdbc69233bbdb5b8ac784e0a12940873165cfba1e71d8a6252 + languageName: node + linkType: hard + +"@material/tokens@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/tokens@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/elevation": 15.0.0-canary.684e33d25.0 + checksum: d76e9a9d7f616d7896a67b5e3cb7c713f214ab55b3ebc9b960ab47b705638640642e037de76462fbffdf63eff0b02df0c6e2314bf62549c9ca78ead696a63aea + languageName: node + linkType: hard + +"@material/tooltip@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/tooltip@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/button": 15.0.0-canary.684e33d25.0 + "@material/dom": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/tokens": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + safevalues: ^0.3.4 + tslib: ^2.1.0 + checksum: 59b207c4fec1ebc3f9f1ae95d1ee711a1ede067a2ae3fe7dfd59d39102e6e04dabfa1bf7dee59314e71352b0d50f7da8e524460a6258ad1ec87b5feecfabe4d7 + languageName: node + linkType: hard + +"@material/top-app-bar@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/top-app-bar@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/animation": 15.0.0-canary.684e33d25.0 + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/elevation": 15.0.0-canary.684e33d25.0 + "@material/ripple": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/shape": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + "@material/typography": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: 3a88cc4f49771ef937a44b31a2589856897a7242b01242bdee006497673a3bb0f7e1f3842f7378339bcad656f67526265a5ae3172c1de320a712990962386bf5 + languageName: node + linkType: hard + +"@material/touch-target@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/touch-target@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/base": 15.0.0-canary.684e33d25.0 + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/rtl": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: c6be1429af85011551fd3c2b2ec702f01c2777e0343b0d6e64e1700ec70bb2fd5014c40abcd21b8a54d78734c29476e82ca9c9f984e9660d8962a8b0b1d1a945 + languageName: node + linkType: hard + +"@material/typography@npm:15.0.0-canary.684e33d25.0": + version: 15.0.0-canary.684e33d25.0 + resolution: "@material/typography@npm:15.0.0-canary.684e33d25.0" + dependencies: + "@material/feature-targeting": 15.0.0-canary.684e33d25.0 + "@material/theme": 15.0.0-canary.684e33d25.0 + tslib: ^2.1.0 + checksum: a2ea1fada5a08902b1d0f8b6b0b3755765aff8ba0919d38649f3ebba3a680f33ce73d570133bc14f1af56cc106dc7fb0217e6887809a0a3264e92d0731a7eb96 + languageName: node + linkType: hard + +"@ngtools/webpack@npm:15.2.6": + version: 15.2.6 + resolution: "@ngtools/webpack@npm:15.2.6" + peerDependencies: + "@angular/compiler-cli": ^15.0.0 + typescript: ">=4.8.2 <5.0" + webpack: ^5.54.0 + checksum: fae38fe0c81ffc49503cf314cbb38e3256c323f6d6abc997dfee9524f32ed27e9062411186a1548a2f25d3cdf685d216ad8c3b360e4c1b6020b2cee0d4b945d1 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": 2.0.5 + run-parallel: ^1.1.9 + checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59 + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: ^1.6.0 + checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^2.1.0": + version: 2.1.2 + resolution: "@npmcli/fs@npm:2.1.2" + dependencies: + "@gar/promisify": ^1.1.3 + semver: ^7.3.5 + checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/fs@npm:3.1.0" + dependencies: + semver: ^7.3.5 + checksum: a50a6818de5fc557d0b0e6f50ec780a7a02ab8ad07e5ac8b16bf519e0ad60a144ac64f97d05c443c3367235d337182e1d012bbac0eb8dbae8dc7b40b193efd0e + languageName: node + linkType: hard + +"@npmcli/git@npm:^4.0.0": + version: 4.0.4 + resolution: "@npmcli/git@npm:4.0.4" + dependencies: + "@npmcli/promise-spawn": ^6.0.0 + lru-cache: ^7.4.4 + npm-pick-manifest: ^8.0.0 + proc-log: ^3.0.0 promise-inflight: ^1.0.1 promise-retry: ^2.0.1 semver: ^7.3.5 - which: ^2.0.2 - checksum: bdfd1229bb1113ad4883ef89b74b5dc442a2c96225d830491dd0dec4fa83d083b93cde92b6978d4956a8365521e61bc8dc1891fb905c7c693d5d6aa178f2ab44 + which: ^3.0.0 + checksum: fd8ad331138c906e090a0f0d3c1662be140fbb39f0dcf4259ee69e8dcb1a939385996dd003d7abb9ce61739e4119e2ea26b2be7ad396988ec1c1ed83179af032 languageName: node linkType: hard -"@npmcli/installed-package-contents@npm:^1.0.7": - version: 1.0.7 - resolution: "@npmcli/installed-package-contents@npm:1.0.7" +"@npmcli/installed-package-contents@npm:^2.0.1": + version: 2.0.2 + resolution: "@npmcli/installed-package-contents@npm:2.0.2" dependencies: - npm-bundled: ^1.1.1 - npm-normalize-package-bin: ^1.0.1 + npm-bundled: ^3.0.0 + npm-normalize-package-bin: ^3.0.0 bin: - installed-package-contents: index.js - checksum: a4a29b99d439827ce2e7817c1f61b56be160e640696e31dc513a2c8a37c792f75cdb6258ec15a1e22904f20df0a8a3019dd3766de5e6619f259834cf64233538 + installed-package-contents: lib/index.js + checksum: 60789d5ed209ee5df479232f62d9d38ecec36e95701cae88320b828b8651351b32d7b47d16d4c36cc7ce5000db4bf1f3e6981bed6381bdc5687ff4bc0795682d languageName: node linkType: hard @@ -2357,43 +3179,50 @@ __metadata: languageName: node linkType: hard -"@npmcli/node-gyp@npm:^2.0.0": - version: 2.0.0 - resolution: "@npmcli/node-gyp@npm:2.0.0" - checksum: b6bbf0015000f9b64d31aefdc30f244b0348c57adb64017667e0304e96c38644d83da46a4581252652f5d606268df49118f9c9993b41d8020f62b7b15dd2c8d8 +"@npmcli/node-gyp@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/node-gyp@npm:3.0.0" + checksum: fe3802b813eecb4ade7ad77c9396cb56721664275faab027e3bd8a5e15adfbbe39e2ecc19f7885feb3cfa009b96632741cc81caf7850ba74440c6a2eee7b4ffc languageName: node linkType: hard -"@npmcli/promise-spawn@npm:^3.0.0": - version: 3.0.0 - resolution: "@npmcli/promise-spawn@npm:3.0.0" +"@npmcli/promise-spawn@npm:^6.0.0, @npmcli/promise-spawn@npm:^6.0.1": + version: 6.0.2 + resolution: "@npmcli/promise-spawn@npm:6.0.2" dependencies: - infer-owner: ^1.0.4 - checksum: 3454465a2731cea5875ba51f80873e2205e5bd878c31517286b0ede4ea931c7bf3de895382287e906d03710fff6f9e44186bd0eee068ce578901c5d3b58e7692 + which: ^3.0.0 + checksum: aa725780c13e1f97ab32ed7bcb5a207a3fb988e1d7ecdc3d22a549a22c8034740366b351c4dde4b011bcffcd8c4a7be6083d9cf7bc7e897b88837150de018528 languageName: node linkType: hard -"@npmcli/run-script@npm:^4.1.0": - version: 4.2.1 - resolution: "@npmcli/run-script@npm:4.2.1" +"@npmcli/run-script@npm:^6.0.0": + version: 6.0.0 + resolution: "@npmcli/run-script@npm:6.0.0" dependencies: - "@npmcli/node-gyp": ^2.0.0 - "@npmcli/promise-spawn": ^3.0.0 + "@npmcli/node-gyp": ^3.0.0 + "@npmcli/promise-spawn": ^6.0.0 node-gyp: ^9.0.0 - read-package-json-fast: ^2.0.3 - which: ^2.0.2 - checksum: 7b8d6676353f157e68b26baf848e01e5d887bcf90ce81a52f23fc9a5d93e6ffb60057532d664cfd7aeeb76d464d0c8b0d314ee6cccb56943acb3b6c570b756c8 + read-package-json-fast: ^3.0.0 + which: ^3.0.0 + checksum: 9fc387f7c405ae4948921764b8b970c12ae07df22bacc242b0f68709c99a83b9d12f411ebd7e60c85a933e2d7be42c70e243ebd71a8d3f6e783e1aab5ccbb2f5 languageName: node linkType: hard -"@schematics/angular@npm:14.2.11": - version: 14.2.11 - resolution: "@schematics/angular@npm:14.2.11" +"@schematics/angular@npm:15.2.6": + version: 15.2.6 + resolution: "@schematics/angular@npm:15.2.6" dependencies: - "@angular-devkit/core": 14.2.11 - "@angular-devkit/schematics": 14.2.11 - jsonc-parser: 3.1.0 - checksum: 4a03962e06d089684bde47d6025f91bd8858a39a93145d69c6b120e6965dfbaca46f94425a22b013a636a3b98f708ef2ccf6619a71d0c7fc9341e84709eb6e1a + "@angular-devkit/core": 15.2.6 + "@angular-devkit/schematics": 15.2.6 + jsonc-parser: 3.2.0 + checksum: 2bee1779b250c4a36b77100bb70f7be9d481d1834955e362ca0268dfd5830dc272b08092072db6ea11e4a8b91a9252e38a67dc508ca4b6a6351efc1048b567f4 + languageName: node + linkType: hard + +"@sigstore/protobuf-specs@npm:^0.1.0": + version: 0.1.0 + resolution: "@sigstore/protobuf-specs@npm:0.1.0" + checksum: 9959bc5176906609dda6ad2a1f5226fac1e49fcb4d29f38969d2a2e3a05cba8e2479721ba78c46a507513abacb63f25a991e5e8856c300204cded455f34ba8c5 languageName: node linkType: hard @@ -2411,6 +3240,23 @@ __metadata: languageName: node linkType: hard +"@tufjs/canonical-json@npm:1.0.0": + version: 1.0.0 + resolution: "@tufjs/canonical-json@npm:1.0.0" + checksum: 9ff3bcd12988fb23643690da3e009f9130b7b10974f8e7af4bd8ad230a228119de8609aa76d75264fe80f152b50872dea6ea53def69534436a4c24b4fcf6a447 + languageName: node + linkType: hard + +"@tufjs/models@npm:1.0.2": + version: 1.0.2 + resolution: "@tufjs/models@npm:1.0.2" + dependencies: + "@tufjs/canonical-json": 1.0.0 + minimatch: ^8.0.3 + checksum: 40f234e1b168498340acf4eca51c16df118c56144d3a20c4d659045677ac93121c74b98419c0437f9f701a75cbf9b4d03290e6a7b07eb3218174cbb658044308 + languageName: node + linkType: hard + "@types/body-parser@npm:*": version: 1.19.2 resolution: "@types/body-parser@npm:1.19.2" @@ -2547,7 +3393,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": version: 7.0.11 resolution: "@types/json-schema@npm:7.0.11" checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d @@ -3194,15 +4040,15 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.11.0": - version: 8.11.0 - resolution: "ajv@npm:8.11.0" +"ajv@npm:8.12.0": + version: 8.12.0 + resolution: "ajv@npm:8.12.0" dependencies: fast-deep-equal: ^3.1.1 json-schema-traverse: ^1.0.0 require-from-string: ^2.0.2 uri-js: ^4.2.2 - checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 languageName: node linkType: hard @@ -3475,12 +4321,12 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.8": - version: 10.4.14 - resolution: "autoprefixer@npm:10.4.14" +"autoprefixer@npm:10.4.13": + version: 10.4.13 + resolution: "autoprefixer@npm:10.4.13" dependencies: - browserslist: ^4.21.5 - caniuse-lite: ^1.0.30001464 + browserslist: ^4.21.4 + caniuse-lite: ^1.0.30001426 fraction.js: ^4.2.0 normalize-range: ^0.1.2 picocolors: ^1.0.0 @@ -3489,7 +4335,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: e9f18e664a4e4a54a8f4ec5f6b49ed228ec45afaa76efcae361c93721795dc5ab644f36d2fdfc0dea446b02a8067b9372f91542ea431994399e972781ed46d95 + checksum: dcb1cb7ae96a3363d65d82e52f9a0a7d8c982256f6fd032d7e1ec311f099c23acfebfd517ff8e96bf93f716a66c4ea2b80c60aa19efd2f474ce434bd75ef7b79 languageName: node linkType: hard @@ -3523,18 +4369,16 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:8.2.5": - version: 8.2.5 - resolution: "babel-loader@npm:8.2.5" +"babel-loader@npm:9.1.2": + version: 9.1.2 + resolution: "babel-loader@npm:9.1.2" dependencies: - find-cache-dir: ^3.3.1 - loader-utils: ^2.0.0 - make-dir: ^3.1.0 - schema-utils: ^2.6.5 + find-cache-dir: ^3.3.2 + schema-utils: ^4.0.0 peerDependencies: - "@babel/core": ^7.0.0 - webpack: ">=2" - checksum: a6605557885eabbc3250412405f2c63ca87287a95a439c643fdb47d5ea3d5326f72e43ab97be070316998cb685d5dfbc70927ce1abe8be7a6a4f5919287773fb + "@babel/core": ^7.12.0 + webpack: ">=5" + checksum: f0edb8e157f9806b810ba3f2c8ca8fa489d377ae5c2b7b00c2ace900a6925641ce4ec520b9c12f70e37b94aa5366e7003e0f6271b26821643e109966ce741cb7 languageName: node linkType: hard @@ -3551,7 +4395,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.2": +"babel-plugin-polyfill-corejs2@npm:^0.3.3": version: 0.3.3 resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" dependencies: @@ -3564,19 +4408,19 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.5.3": - version: 0.5.3 - resolution: "babel-plugin-polyfill-corejs3@npm:0.5.3" +"babel-plugin-polyfill-corejs3@npm:^0.6.0": + version: 0.6.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.2 - core-js-compat: ^3.21.0 + "@babel/helper-define-polyfill-provider": ^0.3.3 + core-js-compat: ^3.25.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9c6644a1b0afbe59e402827fdafc6f44994ff92c5b2f258659cbbfd228f7075dea49e95114af10e66d70f36cbde12ff1d81263eb67be749b3ef0e2c18cf3c16d + checksum: 470bb8c59f7c0912bd77fe1b5a2e72f349b3f65bbdee1d60d6eb7e1f4a085c6f24b2dd5ab4ac6c2df6444a96b070ef6790eccc9edb6a2668c60d33133bfb62c6 languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.4.0": +"babel-plugin-polyfill-regenerator@npm:^0.4.1": version: 0.4.1 resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" dependencies: @@ -3754,7 +4598,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.21.3, browserslist@npm:^4.21.5, browserslist@npm:^4.9.1": +"browserslist@npm:4.21.5, browserslist@npm:^4.14.5, browserslist@npm:^4.21.3, browserslist@npm:^4.21.4, browserslist@npm:^4.21.5": version: 4.21.5 resolution: "browserslist@npm:4.21.5" dependencies: @@ -3817,33 +4661,28 @@ __metadata: languageName: node linkType: hard -"cacache@npm:16.1.2": - version: 16.1.2 - resolution: "cacache@npm:16.1.2" +"cacache@npm:17.0.4": + version: 17.0.4 + resolution: "cacache@npm:17.0.4" dependencies: - "@npmcli/fs": ^2.1.0 - "@npmcli/move-file": ^2.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 + "@npmcli/fs": ^3.1.0 + fs-minipass: ^3.0.0 glob: ^8.0.1 - infer-owner: ^1.0.4 lru-cache: ^7.7.1 - minipass: ^3.1.6 + minipass: ^4.0.0 minipass-collect: ^1.0.2 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 - mkdirp: ^1.0.4 p-map: ^4.0.0 promise-inflight: ^1.0.1 - rimraf: ^3.0.2 - ssri: ^9.0.0 + ssri: ^10.0.0 tar: ^6.1.11 - unique-filename: ^1.1.1 - checksum: defe1d6f557ddda178204cac111990da27e8a60ed276fcd608dad7109cc1936e7dcd57d7263d22cdb06a80e7ceb76ab5eb05133c7c7f886abf1d870d722abd6c + unique-filename: ^3.0.0 + checksum: fea0ed5ab9bb5a56a51c39714f0b93821155538d012b3699cd4e901f39498fdd6283353946625a8c47de20c01b18da4c73fb404552323c66b321d279d80ffa6a languageName: node linkType: hard -"cacache@npm:^16.0.0, cacache@npm:^16.1.0": +"cacache@npm:^16.1.0": version: 16.1.3 resolution: "cacache@npm:16.1.3" dependencies: @@ -3869,6 +4708,27 @@ __metadata: languageName: node linkType: hard +"cacache@npm:^17.0.0": + version: 17.0.5 + resolution: "cacache@npm:17.0.5" + dependencies: + "@npmcli/fs": ^3.1.0 + fs-minipass: ^3.0.0 + glob: ^9.3.1 + lru-cache: ^7.7.1 + minipass: ^4.0.0 + minipass-collect: ^1.0.2 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + p-map: ^4.0.0 + promise-inflight: ^1.0.1 + ssri: ^10.0.0 + tar: ^6.1.11 + unique-filename: ^3.0.0 + checksum: 83312d74acf4d17e378fc1f09ace1dedcb0a1b1033a0e9e22246052b8715cda7bdc8b7ab6dcadd3cb3f2965266def476835488cad5aea810159d452749757fbd + languageName: node + linkType: hard + "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": version: 1.0.2 resolution: "call-bind@npm:1.0.2" @@ -3904,6 +4764,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001426": + version: 1.0.30001478 + resolution: "caniuse-lite@npm:1.0.30001478" + checksum: 27a370dcb32a6a35e186307aabc570da1cd0fccc849913665e7df6822a87286de99509b163304e0586c23c539a991717fb68ed84b85bbd21b2cb86475ae5ffb2 + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30001449": version: 1.0.30001462 resolution: "caniuse-lite@npm:1.0.30001462" @@ -3911,13 +4778,6 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001464": - version: 1.0.30001474 - resolution: "caniuse-lite@npm:1.0.30001474" - checksum: c05faab958fae1bbf3c595203c96d3a2f6b4c7a0d122069addc6c386f208b4db66eed3f5e3d606b80e3b384603d353b27a306f6dcb6145642b5b97a330dba86a - languageName: node - linkType: hard - "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -3966,7 +4826,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.0.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.3": +"chokidar@npm:3.5.3, chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.0.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -4325,21 +5185,12 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.21.0": - version: 3.29.0 - resolution: "core-js-compat@npm:3.29.0" +"core-js-compat@npm:^3.25.1": + version: 3.30.1 + resolution: "core-js-compat@npm:3.30.1" dependencies: browserslist: ^4.21.5 - checksum: ca5d370296c15ebd5f961dae6b6a24a153a84937bff58543099b7f1c407e8d5bbafafa7ca27e65baad522ece762d6356e1d6ea9efa99815f6fefd150fac7e8a5 - languageName: node - linkType: hard - -"core-js-compat@npm:^3.22.1": - version: 3.30.0 - resolution: "core-js-compat@npm:3.30.0" - dependencies: - browserslist: ^4.21.5 - checksum: 51a34d8a292de51f52ac2d72b18ee94743a905d4570a42214262426ebf8f026c853fee22cf4d6c61c2d95f861749421c4de48e9389f551745c5ac1477a5f929f + checksum: e450a9771fc927ce982333929e1c4b32f180f641e4cfff9de6ed44b5930de19be7707cf74f45d1746ca69b8e8ac0698a555cb7244fbfbed6c38ca93844207bf7 languageName: node linkType: hard @@ -4398,19 +5249,6 @@ __metadata: languageName: node linkType: hard -"css-blank-pseudo@npm:^3.0.3": - version: 3.0.3 - resolution: "css-blank-pseudo@npm:3.0.3" - dependencies: - postcss-selector-parser: ^6.0.9 - peerDependencies: - postcss: ^8.4 - bin: - css-blank-pseudo: dist/cli.cjs - checksum: 9be0a13885a99d8ba9e1f45ea66e801d4da75b58c1c3c516a40772fa3a93ef9952b15dcac0418acbb6c89daaae0572819647701b8e553a02972826e33d4cd67f - languageName: node - linkType: hard - "css-functions-list@npm:^3.1.0": version: 3.1.0 resolution: "css-functions-list@npm:3.1.0" @@ -4418,45 +5256,21 @@ __metadata: languageName: node linkType: hard -"css-has-pseudo@npm:^3.0.4": - version: 3.0.4 - resolution: "css-has-pseudo@npm:3.0.4" - dependencies: - postcss-selector-parser: ^6.0.9 - peerDependencies: - postcss: ^8.4 - bin: - css-has-pseudo: dist/cli.cjs - checksum: 8f165d68f6621891d9fa1d874794916a52ed8847dfbec591523ad68774650cc1eae062ba08f59514666e04aeba27be72c9b211892f3a187c5ba6e287bd4260e7 - languageName: node - linkType: hard - -"css-loader@npm:6.7.1": - version: 6.7.1 - resolution: "css-loader@npm:6.7.1" +"css-loader@npm:6.7.3": + version: 6.7.3 + resolution: "css-loader@npm:6.7.3" dependencies: icss-utils: ^5.1.0 - postcss: ^8.4.7 + postcss: ^8.4.19 postcss-modules-extract-imports: ^3.0.0 postcss-modules-local-by-default: ^4.0.0 postcss-modules-scope: ^3.0.0 postcss-modules-values: ^4.0.0 postcss-value-parser: ^4.2.0 - semver: ^7.3.5 + semver: ^7.3.8 peerDependencies: webpack: ^5.0.0 - checksum: 170fdbc630a05a43679ef60fa97694766b568dbde37adccc0faafa964fc675f08b976bc68837bb73b61d60240e8d2cbcbf51540fe94ebc9dafc56e7c46ba5527 - languageName: node - linkType: hard - -"css-prefers-color-scheme@npm:^6.0.3": - version: 6.0.3 - resolution: "css-prefers-color-scheme@npm:6.0.3" - peerDependencies: - postcss: ^8.4 - bin: - css-prefers-color-scheme: dist/cli.cjs - checksum: 3a2b02f0454adda68861cdcaf6a0d11f462eadf165301cba61c5ec7c5f229ac261c5baa54c377d9b811ec5f21b30d72a02bc032cdad2415b3a566f08a0c47b3a + checksum: 473cc32b6c837c2848e2051ad1ba331c1457449f47442e75a8c480d9891451434ada241f7e3de2347e57de17fcd84610b3bcfc4a9da41102cdaedd1e17902d31 languageName: node linkType: hard @@ -4480,13 +5294,6 @@ __metadata: languageName: node linkType: hard -"cssdb@npm:^7.0.0": - version: 7.5.3 - resolution: "cssdb@npm:7.5.3" - checksum: 9376f1a49405dde2e3bb2e6e99d4389a17d95e94e89ead66cf52067493311b2a6222a257438cd14655087ee711c239efbebefa551e2b2ce8744709c71de3e5f6 - languageName: node - linkType: hard - "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -4540,7 +5347,16 @@ __metadata: languageName: node linkType: hard -"debug@npm:4.3.4, debug@npm:^4.3.4": +"debug@npm:^3.1.0, debug@npm:^3.2.6": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: ^2.1.1 + checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c + languageName: node + linkType: hard + +"debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -4552,15 +5368,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.1.0, debug@npm:^3.2.6": - version: 3.2.7 - resolution: "debug@npm:3.2.7" - dependencies: - ms: ^2.1.1 - checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c - languageName: node - linkType: hard - "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -4825,25 +5632,25 @@ __metadata: version: 0.0.0-use.local resolution: "dres-frontend@workspace:." dependencies: - "@angular-devkit/build-angular": 14.2.11 + "@angular-devkit/build-angular": 15.2.6 "@angular-eslint/builder": 15.2.1 "@angular-eslint/eslint-plugin": 15.2.1 "@angular-eslint/eslint-plugin-template": 15.2.1 "@angular-eslint/schematics": 15.2.1 "@angular-eslint/template-parser": 15.2.1 - "@angular/animations": 14.3.0 - "@angular/cdk": 14.2.7 - "@angular/cli": 14.2.11 - "@angular/common": 14.3.0 - "@angular/compiler": 14.3.0 - "@angular/compiler-cli": 14.3.0 - "@angular/core": 14.3.0 - "@angular/forms": 14.3.0 - "@angular/language-service": 14.3.0 - "@angular/material": 14.2.7 - "@angular/platform-browser": 14.3.0 - "@angular/platform-browser-dynamic": 14.3.0 - "@angular/router": 14.3.0 + "@angular/animations": 15.2.7 + "@angular/cdk": 15.2.7 + "@angular/cli": 15.2.6 + "@angular/common": 15.2.7 + "@angular/compiler": 15.2.7 + "@angular/compiler-cli": 15.2.7 + "@angular/core": 15.2.7 + "@angular/forms": 15.2.7 + "@angular/language-service": 15.2.7 + "@angular/material": 15.2.7 + "@angular/platform-browser": 15.2.7 + "@angular/platform-browser-dynamic": 15.2.7 + "@angular/router": 15.2.7 "@types/jasmine": 3.6.x "@types/jasminewd2": 2.0.x "@types/node": 12.11.x @@ -5007,6 +5814,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^4.3.0, entities@npm:^4.4.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -5081,226 +5895,89 @@ __metadata: languageName: node linkType: hard -"esbuild-android-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-android-64@npm:0.15.5" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - -"esbuild-android-arm64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-android-arm64@npm:0.15.5" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-darwin-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-darwin-64@npm:0.15.5" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"esbuild-darwin-arm64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-darwin-arm64@npm:0.15.5" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-freebsd-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-freebsd-64@npm:0.15.5" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"esbuild-freebsd-arm64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-freebsd-arm64@npm:0.15.5" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-linux-32@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-32@npm:0.15.5" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - -"esbuild-linux-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-64@npm:0.15.5" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"esbuild-linux-arm64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-arm64@npm:0.15.5" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-linux-arm@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-arm@npm:0.15.5" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"esbuild-linux-mips64le@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-mips64le@npm:0.15.5" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - -"esbuild-linux-ppc64le@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-ppc64le@npm:0.15.5" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - -"esbuild-linux-riscv64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-riscv64@npm:0.15.5" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - -"esbuild-linux-s390x@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-linux-s390x@npm:0.15.5" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - -"esbuild-netbsd-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-netbsd-64@npm:0.15.5" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - -"esbuild-openbsd-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-openbsd-64@npm:0.15.5" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - -"esbuild-sunos-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-sunos-64@npm:0.15.5" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - -"esbuild-wasm@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-wasm@npm:0.15.5" +"esbuild-wasm@npm:0.17.8": + version: 0.17.8 + resolution: "esbuild-wasm@npm:0.17.8" bin: esbuild: bin/esbuild - checksum: eed3f81dc017192a4a70dc0474f7c61dc9709994a3070533a8b773b11482a1bc3051e0a10529741b146ae75b0878dc9ccf6db97c1afb4e5447b9c43b8e1c6eed - languageName: node - linkType: hard - -"esbuild-windows-32@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-windows-32@npm:0.15.5" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"esbuild-windows-64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-windows-64@npm:0.15.5" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"esbuild-windows-arm64@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild-windows-arm64@npm:0.15.5" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"esbuild@npm:0.15.5": - version: 0.15.5 - resolution: "esbuild@npm:0.15.5" - dependencies: - "@esbuild/linux-loong64": 0.15.5 - esbuild-android-64: 0.15.5 - esbuild-android-arm64: 0.15.5 - esbuild-darwin-64: 0.15.5 - esbuild-darwin-arm64: 0.15.5 - esbuild-freebsd-64: 0.15.5 - esbuild-freebsd-arm64: 0.15.5 - esbuild-linux-32: 0.15.5 - esbuild-linux-64: 0.15.5 - esbuild-linux-arm: 0.15.5 - esbuild-linux-arm64: 0.15.5 - esbuild-linux-mips64le: 0.15.5 - esbuild-linux-ppc64le: 0.15.5 - esbuild-linux-riscv64: 0.15.5 - esbuild-linux-s390x: 0.15.5 - esbuild-netbsd-64: 0.15.5 - esbuild-openbsd-64: 0.15.5 - esbuild-sunos-64: 0.15.5 - esbuild-windows-32: 0.15.5 - esbuild-windows-64: 0.15.5 - esbuild-windows-arm64: 0.15.5 + checksum: 466a480806e1aa2439d6b24115e6cbe5125b80fd395099488d83cea4a9d23ad2b49b1a2f055cb96466df76d34b015384b311047fc1986791fbf91e96106160e6 + languageName: node + linkType: hard + +"esbuild@npm:0.17.8": + version: 0.17.8 + resolution: "esbuild@npm:0.17.8" + dependencies: + "@esbuild/android-arm": 0.17.8 + "@esbuild/android-arm64": 0.17.8 + "@esbuild/android-x64": 0.17.8 + "@esbuild/darwin-arm64": 0.17.8 + "@esbuild/darwin-x64": 0.17.8 + "@esbuild/freebsd-arm64": 0.17.8 + "@esbuild/freebsd-x64": 0.17.8 + "@esbuild/linux-arm": 0.17.8 + "@esbuild/linux-arm64": 0.17.8 + "@esbuild/linux-ia32": 0.17.8 + "@esbuild/linux-loong64": 0.17.8 + "@esbuild/linux-mips64el": 0.17.8 + "@esbuild/linux-ppc64": 0.17.8 + "@esbuild/linux-riscv64": 0.17.8 + "@esbuild/linux-s390x": 0.17.8 + "@esbuild/linux-x64": 0.17.8 + "@esbuild/netbsd-x64": 0.17.8 + "@esbuild/openbsd-x64": 0.17.8 + "@esbuild/sunos-x64": 0.17.8 + "@esbuild/win32-arm64": 0.17.8 + "@esbuild/win32-ia32": 0.17.8 + "@esbuild/win32-x64": 0.17.8 dependenciesMeta: - "@esbuild/linux-loong64": + "@esbuild/android-arm": optional: true - esbuild-android-64: + "@esbuild/android-arm64": optional: true - esbuild-android-arm64: + "@esbuild/android-x64": optional: true - esbuild-darwin-64: + "@esbuild/darwin-arm64": optional: true - esbuild-darwin-arm64: + "@esbuild/darwin-x64": optional: true - esbuild-freebsd-64: + "@esbuild/freebsd-arm64": optional: true - esbuild-freebsd-arm64: + "@esbuild/freebsd-x64": optional: true - esbuild-linux-32: + "@esbuild/linux-arm": optional: true - esbuild-linux-64: + "@esbuild/linux-arm64": optional: true - esbuild-linux-arm: + "@esbuild/linux-ia32": optional: true - esbuild-linux-arm64: + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": optional: true - esbuild-linux-mips64le: + "@esbuild/linux-ppc64": optional: true - esbuild-linux-ppc64le: + "@esbuild/linux-riscv64": optional: true - esbuild-linux-riscv64: + "@esbuild/linux-s390x": optional: true - esbuild-linux-s390x: + "@esbuild/linux-x64": optional: true - esbuild-netbsd-64: + "@esbuild/netbsd-x64": optional: true - esbuild-openbsd-64: + "@esbuild/openbsd-x64": optional: true - esbuild-sunos-64: + "@esbuild/sunos-x64": optional: true - esbuild-windows-32: + "@esbuild/win32-arm64": optional: true - esbuild-windows-64: + "@esbuild/win32-ia32": optional: true - esbuild-windows-arm64: + "@esbuild/win32-x64": optional: true bin: esbuild: bin/esbuild - checksum: fc7f5df6542bd18dee1e61c58049ddf3d6b508d8d67eabcba455f7dbb560552e012f169a8afa2f209de554a206935fedf30d576aacdc2b46cb4da229181968fb + checksum: d09776fcf3da2726975d0d3c11afe0a1db2a8fbb88cbcb25d97f269a1e58f17fa04cb881b9189195d8e76b8a1cfb165e990e27527ae0366f6dc2721fb0ed0af9 languageName: node linkType: hard @@ -5752,7 +6429,7 @@ __metadata: languageName: node linkType: hard -"find-cache-dir@npm:^3.3.1": +"find-cache-dir@npm:^3.3.2": version: 3.3.2 resolution: "find-cache-dir@npm:3.3.2" dependencies: @@ -5878,6 +6555,15 @@ __metadata: languageName: node linkType: hard +"fs-minipass@npm:^3.0.0": + version: 3.0.1 + resolution: "fs-minipass@npm:3.0.1" + dependencies: + minipass: ^4.0.0 + checksum: ce1fd3ccef7d64caa9ee5f566db1abe250b6e0067defe53003288537b310956e6f42c433c3ee6001e044f656ce8ba5a0b2e5b5589c513c67b57470d11c3d9b07 + languageName: node + linkType: hard + "fs-monkey@npm:^1.0.3": version: 1.0.3 resolution: "fs-monkey@npm:1.0.3" @@ -6021,20 +6707,20 @@ __metadata: languageName: node linkType: hard -"glob@npm:8.0.3": - version: 8.0.3 - resolution: "glob@npm:8.0.3" +"glob@npm:8.1.0, glob@npm:^8.0.1": + version: 8.1.0 + resolution: "glob@npm:8.1.0" dependencies: fs.realpath: ^1.0.0 inflight: ^1.0.4 inherits: 2 minimatch: ^5.0.1 once: ^1.3.0 - checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 + checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 languageName: node linkType: hard -"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": +"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.7": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -6048,16 +6734,15 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1": - version: 8.1.0 - resolution: "glob@npm:8.1.0" +"glob@npm:^9.3.0, glob@npm:^9.3.1": + version: 9.3.5 + resolution: "glob@npm:9.3.5" dependencies: fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^5.0.1 - once: ^1.3.0 - checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 + minimatch: ^8.0.2 + minipass: ^4.2.4 + path-scurry: ^1.6.1 + checksum: 94b093adbc591bc36b582f77927d1fb0dbf3ccc231828512b017601408be98d1fe798fc8c0b19c6f2d1a7660339c3502ce698de475e9d938ccbb69b47b647c84 languageName: node linkType: hard @@ -6304,12 +6989,12 @@ __metadata: languageName: node linkType: hard -"hosted-git-info@npm:^5.0.0": - version: 5.2.1 - resolution: "hosted-git-info@npm:5.2.1" +"hosted-git-info@npm:^6.0.0": + version: 6.1.1 + resolution: "hosted-git-info@npm:6.1.1" dependencies: lru-cache: ^7.5.1 - checksum: fa35df185224adfd69141f3b2f8cc31f50e705a5ebb415ccfbfd055c5b94bd08d3e658edf1edad9e2ac7d81831ac7cf261f5d219b3adc8d744fb8cdacaaf2ead + checksum: fcd3ca2eaa05f3201425ccbb8aa47f88cdda4a3a6d79453f8e269f7171356278bd1db08f059d8439eb5eaa91c6a8a20800fc49cca6e9e4e899b202a332d5ba6b languageName: node linkType: hard @@ -6346,7 +7031,7 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.1.0": +"http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -6532,12 +7217,12 @@ __metadata: languageName: node linkType: hard -"ignore-walk@npm:^5.0.1": - version: 5.0.1 - resolution: "ignore-walk@npm:5.0.1" +"ignore-walk@npm:^6.0.0": + version: 6.0.2 + resolution: "ignore-walk@npm:6.0.2" dependencies: - minimatch: ^5.0.1 - checksum: 1a4ef35174653a1aa6faab3d9f8781269166536aee36a04946f6e2b319b2475c1903a75ed42f04219274128242f49d0a10e20c4354ee60d9548e97031451150b + minimatch: ^7.4.2 + checksum: 99dda4d6977cf47b359ae17d62f4abfb9273a2507d14d38db7a29abcd8385ec45cc1d8cf00e73695f98ef4001e7439a4f5b619a3d4055a37bd953288be01b485 languageName: node linkType: hard @@ -6640,10 +7325,10 @@ __metadata: languageName: node linkType: hard -"ini@npm:3.0.0": - version: 3.0.0 - resolution: "ini@npm:3.0.0" - checksum: e92b6b0835ac369e58c677e7faa8db6019ac667d7404887978fb86b181d658e50f1742ecbba7d81eb5ff917b3ae4d63a48e1ef3a9f8a0527bd7605fe1a9995d4 +"ini@npm:3.0.1": + version: 3.0.1 + resolution: "ini@npm:3.0.1" + checksum: 947b582a822f06df3c22c75c90aec217d604ea11f7a20249530ee5c1cf8f508288439abe17b0e1d9b421bda5f4fae5e7aae0b18cb3ded5ac9d68f607df82f10f languageName: node linkType: hard @@ -7265,6 +7950,13 @@ __metadata: languageName: node linkType: hard +"json-parse-even-better-errors@npm:^3.0.0": + version: 3.0.0 + resolution: "json-parse-even-better-errors@npm:3.0.0" + checksum: f1970b5220c7fa23d888565510752c3d5e863f93668a202fcaa719739fa41485dfc6a1db212f702ebd3c873851cc067aebc2917e3f79763cae2fdb95046f38f3 + languageName: node + linkType: hard + "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -7309,10 +8001,10 @@ __metadata: languageName: node linkType: hard -"jsonc-parser@npm:3.1.0": - version: 3.1.0 - resolution: "jsonc-parser@npm:3.1.0" - checksum: 81b00c565c60cb1b400523a918d42ad9c7bb3d9cf34c708bf78d37c8c496ecd670c3ff8828f2f60aa6e6627ef4287982794ddf92261ea71e320973c54b29fb22 +"jsonc-parser@npm:3.2.0": + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 languageName: node linkType: hard @@ -7467,15 +8159,15 @@ __metadata: languageName: node linkType: hard -"less-loader@npm:11.0.0": - version: 11.0.0 - resolution: "less-loader@npm:11.0.0" +"less-loader@npm:11.1.0": + version: 11.1.0 + resolution: "less-loader@npm:11.1.0" dependencies: klona: ^2.0.4 peerDependencies: less: ^3.5.0 || ^4.0.0 webpack: ^5.0.0 - checksum: fe5f810549a04c3d1b7fdd838c598e1dd7e6ed35428bdc7ec0caa4e7f2c07abfd1519c477aca713ca1259f88905dae25dd5f0c27b61071d9ce0dfefded86be1a + checksum: 041216e0a6d95e24c9724f470719db3eb70b3888c45b1a1e9cd55edabe8ae79709522cd6c6713b466b3e9948544074e2a5b2be50ac3ced5ff41dfa9675f3b182 languageName: node linkType: hard @@ -7754,21 +8446,28 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:0.26.2": - version: 0.26.2 - resolution: "magic-string@npm:0.26.2" +"lru-cache@npm:^9.0.0": + version: 9.0.2 + resolution: "lru-cache@npm:9.0.2" + checksum: 83961e582f4458f56bb33e11a9c16b2a5b693f625650c465d92bf2e47c9869de36a5930ea7368106f3915f66ed05414ab89eaa8af9f66c86dd53234af8362e9a + languageName: node + linkType: hard + +"magic-string@npm:0.29.0": + version: 0.29.0 + resolution: "magic-string@npm:0.29.0" dependencies: - sourcemap-codec: ^1.4.8 - checksum: b4db4e2b370ac8d9ffc6443a2b591b75364bf1fc9121b5a4068d5b89804abff6709d1fa4a0e0c2d54f2e61e0e44db83efdfe219a5ab0ba6d25ee1f2b51fbed55 + "@jridgewell/sourcemap-codec": ^1.4.13 + checksum: 19e5398fcfc44804917127c72ad622c68a19a0a10cbdb8d4f9f9417584a087fe9e117140bfb2463d86743cf1ed9cf4182ae0b0ad1a7536f7fdda257ee4449ffb languageName: node linkType: hard -"magic-string@npm:^0.26.0": - version: 0.26.7 - resolution: "magic-string@npm:0.26.7" +"magic-string@npm:^0.27.0": + version: 0.27.0 + resolution: "magic-string@npm:0.27.0" dependencies: - sourcemap-codec: ^1.4.8 - checksum: 89b0d60cbb32bbf3d1e23c46ea93db082d18a8230b972027aecb10a40bba51be519ecce0674f995571e3affe917b76b09f59d8dbc9a1b2c9c4102a2b6e8a2b01 + "@jridgewell/sourcemap-codec": ^1.4.13 + checksum: 273faaa50baadb7a2df6e442eac34ad611304fc08fe16e24fe2e472fd944bfcb73ffb50d2dc972dc04e92784222002af46868cb9698b1be181c81830fd95a13e languageName: node linkType: hard @@ -7782,7 +8481,7 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2, make-dir@npm:^3.1.0": +"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2": version: 3.1.0 resolution: "make-dir@npm:3.1.0" dependencies: @@ -7798,27 +8497,50 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.3, make-fetch-happen@npm:^10.0.6": +"make-fetch-happen@npm:^10.0.3": version: 10.2.1 resolution: "make-fetch-happen@npm:10.2.1" dependencies: agentkeepalive: ^4.2.1 - cacache: ^16.1.0 - http-cache-semantics: ^4.1.0 + cacache: ^16.1.0 + http-cache-semantics: ^4.1.0 + http-proxy-agent: ^5.0.0 + https-proxy-agent: ^5.0.0 + is-lambda: ^1.0.1 + lru-cache: ^7.7.1 + minipass: ^3.1.6 + minipass-collect: ^1.0.2 + minipass-fetch: ^2.0.3 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.3 + promise-retry: ^2.0.1 + socks-proxy-agent: ^7.0.0 + ssri: ^9.0.0 + checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c + languageName: node + linkType: hard + +"make-fetch-happen@npm:^11.0.0, make-fetch-happen@npm:^11.0.1": + version: 11.1.0 + resolution: "make-fetch-happen@npm:11.1.0" + dependencies: + agentkeepalive: ^4.2.1 + cacache: ^17.0.0 + http-cache-semantics: ^4.1.1 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^2.0.3 + minipass: ^4.0.0 + minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 socks-proxy-agent: ^7.0.0 - ssri: ^9.0.0 - checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c + ssri: ^10.0.0 + checksum: bce5bdde6848f45c085bdb8b5f3a04deb284c0478bd8fac9ffc5bb611981f8b94c9496839513593f9a967db14d470452e72cbb3ffc1ddc054d8790ca33ed61eb languageName: node linkType: hard @@ -7850,6 +8572,15 @@ __metadata: languageName: node linkType: hard +"memfs@npm:^3.4.12": + version: 3.5.0 + resolution: "memfs@npm:3.5.0" + dependencies: + fs-monkey: ^1.0.3 + checksum: 8427db6c3644eeb9119b7a74b232d9a6178d018878acce6f05bd89d95e28b1073c9eeb00127131b0613b07a003e2e7b15b482f9004e548fe06a0aba7aa02515c + languageName: node + linkType: hard + "memfs@npm:^3.4.3": version: 3.4.13 resolution: "memfs@npm:3.4.13" @@ -7965,14 +8696,14 @@ __metadata: languageName: node linkType: hard -"mini-css-extract-plugin@npm:2.6.1": - version: 2.6.1 - resolution: "mini-css-extract-plugin@npm:2.6.1" +"mini-css-extract-plugin@npm:2.7.2": + version: 2.7.2 + resolution: "mini-css-extract-plugin@npm:2.7.2" dependencies: schema-utils: ^4.0.0 peerDependencies: webpack: ^5.0.0 - checksum: df60840404878c4832b4104799fd29c5a89b06b1e377956c8d4a5729efe0ef301a52e5087d6f383871df5e69a8445922a0ae635c11abf412d7645a7096d0e973 + checksum: cd65611d6dc452f230c6ebba8a47bc5f5146b813b13b0b402c6f4a69f6451242eeea781152bebd31cad8ca7c7e95dac91e7e464087f18fb65b2d1097b58cf4ae languageName: node linkType: hard @@ -7983,15 +8714,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:5.1.0": - version: 5.1.0 - resolution: "minimatch@npm:5.1.0" - dependencies: - brace-expansion: ^2.0.1 - checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90 - languageName: node - linkType: hard - "minimatch@npm:^3.0.4": version: 3.0.5 resolution: "minimatch@npm:3.0.5" @@ -8019,6 +8741,24 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^7.4.2": + version: 7.4.6 + resolution: "minimatch@npm:7.4.6" + dependencies: + brace-expansion: ^2.0.1 + checksum: 1a6c8d22618df9d2a88aabeef1de5622eb7b558e9f8010be791cb6b0fa6e102d39b11c28d75b855a1e377b12edc7db8ff12a99c20353441caa6a05e78deb5da9 + languageName: node + linkType: hard + +"minimatch@npm:^8.0.2, minimatch@npm:^8.0.3": + version: 8.0.4 + resolution: "minimatch@npm:8.0.4" + dependencies: + brace-expansion: ^2.0.1 + checksum: 2e46cffb86bacbc524ad45a6426f338920c529dd13f3a732cc2cf7618988ee1aae88df4ca28983285aca9e0f45222019ac2d14ebd17c1edadd2ee12221ab801a + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -8061,6 +8801,21 @@ __metadata: languageName: node linkType: hard +"minipass-fetch@npm:^3.0.0": + version: 3.0.2 + resolution: "minipass-fetch@npm:3.0.2" + dependencies: + encoding: ^0.1.13 + minipass: ^4.0.0 + minipass-sized: ^1.0.3 + minizlib: ^2.1.2 + dependenciesMeta: + encoding: + optional: true + checksum: f86eea7113d82d40a3527143d94b0f06da56d83642477d563a0c462cef1b1955429ffc78330dbc70fbc1bb53692408fdd11233de4b68727b41a3bb6e12b33ada + languageName: node + linkType: hard + "minipass-flush@npm:^1.0.5": version: 1.0.5 resolution: "minipass-flush@npm:1.0.5" @@ -8114,6 +8869,20 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^4.2.4": + version: 4.2.8 + resolution: "minipass@npm:4.2.8" + checksum: 7f4914d5295a9a30807cae5227a37a926e6d910c03f315930fde52332cf0575dfbc20295318f91f0baf0e6bb11a6f668e30cde8027dea7a11b9d159867a3c830 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 425dab288738853fded43da3314a0b5c035844d6f3097a8e3b5b29b328da8f3c1af6fc70618b32c29ff906284cf6406b6841376f21caaadd0793c1d5a6a620ea + languageName: node + linkType: hard + "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -8354,15 +9123,15 @@ __metadata: languageName: node linkType: hard -"normalize-package-data@npm:^4.0.0": - version: 4.0.1 - resolution: "normalize-package-data@npm:4.0.1" +"normalize-package-data@npm:^5.0.0": + version: 5.0.0 + resolution: "normalize-package-data@npm:5.0.0" dependencies: - hosted-git-info: ^5.0.0 + hosted-git-info: ^6.0.0 is-core-module: ^2.8.1 semver: ^7.3.5 validate-npm-package-license: ^3.0.4 - checksum: 292e0aa740e73d62f84bbd9d55d4bfc078155f32d5d7572c32c9807f96d543af0f43ff7e5c80bfa6238667123fd68bd83cd412eae9b27b85b271fb041f624528 + checksum: a459f05eaf7c2b643c61234177f08e28064fde97da15800e3d3ac0404e28450d43ac46fc95fbf6407a9bf20af4c58505ad73458a912dc1517f8c1687b1d68c27 languageName: node linkType: hard @@ -8380,121 +9149,76 @@ __metadata: languageName: node linkType: hard -"npm-bundled@npm:^1.1.1": - version: 1.1.2 - resolution: "npm-bundled@npm:1.1.2" - dependencies: - npm-normalize-package-bin: ^1.0.1 - checksum: 6e599155ef28d0b498622f47f1ba189dfbae05095a1ed17cb3a5babf961e965dd5eab621f0ec6f0a98de774e5836b8f5a5ee639010d64f42850a74acec3d4d09 - languageName: node - linkType: hard - -"npm-bundled@npm:^2.0.0": - version: 2.0.1 - resolution: "npm-bundled@npm:2.0.1" +"npm-bundled@npm:^3.0.0": + version: 3.0.0 + resolution: "npm-bundled@npm:3.0.0" dependencies: - npm-normalize-package-bin: ^2.0.0 - checksum: 7747293985c48c5268871efe691545b03731cb80029692000cbdb0b3344b9617be5187aa36281cabbe6b938e3651b4e87236d1c31f9e645eef391a1a779413e6 + npm-normalize-package-bin: ^3.0.0 + checksum: 110859c2d6dcd7941dac0932a29171cbde123060486a4b6e897aaf5e025abeb3d9ffcdfe9e9271992e6396b2986c2c534f1029a45a7c196f1257fa244305dbf8 languageName: node linkType: hard -"npm-install-checks@npm:^5.0.0": - version: 5.0.0 - resolution: "npm-install-checks@npm:5.0.0" +"npm-install-checks@npm:^6.0.0": + version: 6.1.1 + resolution: "npm-install-checks@npm:6.1.1" dependencies: semver: ^7.1.1 - checksum: 0e7d1aae52b1fe9d3a0fd4a008850c7047931722dd49ee908afd13fd0297ac5ddb10964d9c59afcdaaa2ca04b51d75af2788f668c729ae71fec0e4cdac590ffc - languageName: node - linkType: hard - -"npm-normalize-package-bin@npm:^1.0.1": - version: 1.0.1 - resolution: "npm-normalize-package-bin@npm:1.0.1" - checksum: ae7f15155a1e3ace2653f12ddd1ee8eaa3c84452fdfbf2f1943e1de264e4b079c86645e2c55931a51a0a498cba31f70022a5219d5665fbcb221e99e58bc70122 - languageName: node - linkType: hard - -"npm-normalize-package-bin@npm:^2.0.0": - version: 2.0.0 - resolution: "npm-normalize-package-bin@npm:2.0.0" - checksum: 7c5379f9b188b564c4332c97bdd9a5d6b7b15f02b5823b00989d6a0e6fb31eb0280f02b0a924f930e1fcaf00e60fae333aec8923d2a4c7747613c7d629d8aa25 + checksum: 8fb3ed05cfd3fdeb20d2fd22d45a89cd509afac3b05d188af7d9bcdf07ed745d1346943692782a4dca4c42b2c1fec34eb42fdf20e2ef8bb5b249fbb5a811ce3b languageName: node linkType: hard -"npm-package-arg@npm:9.1.0": - version: 9.1.0 - resolution: "npm-package-arg@npm:9.1.0" - dependencies: - hosted-git-info: ^5.0.0 - proc-log: ^2.0.1 - semver: ^7.3.5 - validate-npm-package-name: ^4.0.0 - checksum: 277c21477731a4f1e31bde36f0db5f5470deb2a008db2aaf1b015d588b23cb225c75f90291ea241235e86682a03de972bbe69fc805c921a786ea9616955990b9 +"npm-normalize-package-bin@npm:^3.0.0": + version: 3.0.0 + resolution: "npm-normalize-package-bin@npm:3.0.0" + checksum: 6a34886c150b0f5302aad52a9446e5c939aa14eeb462323e75681517b36c6b9eaef83e1f5bc2d7e5154b3b752cbce81bed05e290db3f1f7edf857cbb895e35c0 languageName: node linkType: hard -"npm-package-arg@npm:^9.0.0, npm-package-arg@npm:^9.0.1": - version: 9.1.2 - resolution: "npm-package-arg@npm:9.1.2" +"npm-package-arg@npm:10.1.0, npm-package-arg@npm:^10.0.0": + version: 10.1.0 + resolution: "npm-package-arg@npm:10.1.0" dependencies: - hosted-git-info: ^5.0.0 - proc-log: ^2.0.1 + hosted-git-info: ^6.0.0 + proc-log: ^3.0.0 semver: ^7.3.5 - validate-npm-package-name: ^4.0.0 - checksum: 3793488843985ed71deb14fcba7c068d8ed03a18fd8f6b235c6a64465c9a25f60261598106d5cc8677c0bee9548e405c34c2e3c7a822e3113d3389351c745dfa - languageName: node - linkType: hard - -"npm-packlist@npm:^5.1.0": - version: 5.1.3 - resolution: "npm-packlist@npm:5.1.3" - dependencies: - glob: ^8.0.1 - ignore-walk: ^5.0.1 - npm-bundled: ^2.0.0 - npm-normalize-package-bin: ^2.0.0 - bin: - npm-packlist: bin/index.js - checksum: 94cc9c66740e8f80243301de85eb0a2cec5bbd570c3f26b6ad7af1a3eca155f7e810580dc7ea4448f12a8fd82f6db307e7132a5fe69e157eb45b325acadeb22a + validate-npm-package-name: ^5.0.0 + checksum: 8fe4b6a742502345e4836ed42fdf26c544c9f75563c476c67044a481ada6e81f71b55462489c7e1899d516e4347150e58028036a90fa11d47e320bcc9365fd30 languageName: node linkType: hard -"npm-pick-manifest@npm:7.0.1": - version: 7.0.1 - resolution: "npm-pick-manifest@npm:7.0.1" +"npm-packlist@npm:^7.0.0": + version: 7.0.4 + resolution: "npm-packlist@npm:7.0.4" dependencies: - npm-install-checks: ^5.0.0 - npm-normalize-package-bin: ^1.0.1 - npm-package-arg: ^9.0.0 - semver: ^7.3.5 - checksum: 9a4a8e64d2214783b2b74a361845000f5d91bb40c7858e2a30af2ac7876d9296efc37f8cacf60335e96a45effee2035b033d9bdefb4889757cc60d85959accbb + ignore-walk: ^6.0.0 + checksum: 5ffa1f8f0b32141a60a66713fa3ed03b8ee4800b1ed6b59194d03c3c85da88f3fc21e1de29b665f322678bae85198732b16aa76c0a7cb0e283f9e0db50752233 languageName: node linkType: hard -"npm-pick-manifest@npm:^7.0.0": - version: 7.0.2 - resolution: "npm-pick-manifest@npm:7.0.2" +"npm-pick-manifest@npm:8.0.1, npm-pick-manifest@npm:^8.0.0": + version: 8.0.1 + resolution: "npm-pick-manifest@npm:8.0.1" dependencies: - npm-install-checks: ^5.0.0 - npm-normalize-package-bin: ^2.0.0 - npm-package-arg: ^9.0.0 + npm-install-checks: ^6.0.0 + npm-normalize-package-bin: ^3.0.0 + npm-package-arg: ^10.0.0 semver: ^7.3.5 - checksum: a93ec449c12219a2be8556837db9ac5332914f304a69469bb6f1f47717adc6e262aa318f79166f763512688abd9c4e4b6a2d83b2dd19753a7abe5f0360f2c8bc + checksum: b8e16f2fbcc40ba7d1405c9b566bcee32488c6709f883207f709b0715ed34e2f3f3bc5bf5cb9563d6aa23cb878102bf0011ba22cce9235caa9a0349784b48ecd languageName: node linkType: hard -"npm-registry-fetch@npm:^13.0.1": - version: 13.3.1 - resolution: "npm-registry-fetch@npm:13.3.1" +"npm-registry-fetch@npm:^14.0.0": + version: 14.0.4 + resolution: "npm-registry-fetch@npm:14.0.4" dependencies: - make-fetch-happen: ^10.0.6 - minipass: ^3.1.6 - minipass-fetch: ^2.0.3 + make-fetch-happen: ^11.0.0 + minipass: ^4.0.0 + minipass-fetch: ^3.0.0 minipass-json-stream: ^1.0.1 minizlib: ^2.1.2 - npm-package-arg: ^9.0.1 - proc-log: ^2.0.0 - checksum: 5a941c2c799568e0dbccfc15f280444da398dadf2eede1b1921f08ddd5cb5f32c7cb4d16be96401f95a33073aeec13a3fd928c753790d3c412c2e64e7f7c6ee4 + npm-package-arg: ^10.0.0 + proc-log: ^3.0.0 + checksum: 7d6e82f3fe8ce50b7e04490580fa7294e9934025db47e922c8d26c9a6c81374f91dd7e32e3c8fa34089dbd321adb128627f1c02d233714f77b5795140224af49 languageName: node linkType: hard @@ -8628,7 +9352,18 @@ __metadata: languageName: node linkType: hard -"open@npm:8.4.0, open@npm:^8.0.9": +"open@npm:8.4.1": + version: 8.4.1 + resolution: "open@npm:8.4.1" + dependencies: + define-lazy-prop: ^2.0.0 + is-docker: ^2.1.1 + is-wsl: ^2.2.0 + checksum: dbe8e1d98889df60b5179eab8b94b9591744d1f0033bce1a9a10738ba140bd9d625d6bcde7ff9f043e379aafb918975c2daa03b87cef13eb046ac18ed807f06d + languageName: node + linkType: hard + +"open@npm:^8.0.9": version: 8.4.0 resolution: "open@npm:8.4.0" dependencies: @@ -8739,34 +9474,31 @@ __metadata: languageName: node linkType: hard -"pacote@npm:13.6.2": - version: 13.6.2 - resolution: "pacote@npm:13.6.2" +"pacote@npm:15.1.0": + version: 15.1.0 + resolution: "pacote@npm:15.1.0" dependencies: - "@npmcli/git": ^3.0.0 - "@npmcli/installed-package-contents": ^1.0.7 - "@npmcli/promise-spawn": ^3.0.0 - "@npmcli/run-script": ^4.1.0 - cacache: ^16.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 - infer-owner: ^1.0.4 - minipass: ^3.1.6 - mkdirp: ^1.0.4 - npm-package-arg: ^9.0.0 - npm-packlist: ^5.1.0 - npm-pick-manifest: ^7.0.0 - npm-registry-fetch: ^13.0.1 - proc-log: ^2.0.0 + "@npmcli/git": ^4.0.0 + "@npmcli/installed-package-contents": ^2.0.1 + "@npmcli/promise-spawn": ^6.0.1 + "@npmcli/run-script": ^6.0.0 + cacache: ^17.0.0 + fs-minipass: ^3.0.0 + minipass: ^4.0.0 + npm-package-arg: ^10.0.0 + npm-packlist: ^7.0.0 + npm-pick-manifest: ^8.0.0 + npm-registry-fetch: ^14.0.0 + proc-log: ^3.0.0 promise-retry: ^2.0.1 - read-package-json: ^5.0.0 - read-package-json-fast: ^2.0.3 - rimraf: ^3.0.2 - ssri: ^9.0.0 + read-package-json: ^6.0.0 + read-package-json-fast: ^3.0.0 + sigstore: ^1.0.0 + ssri: ^10.0.0 tar: ^6.1.11 bin: pacote: lib/bin.js - checksum: a7b7f97094ab570a23e1c174537e9953a4d53176cc4b18bac77d7728bd89e2b9fa331d0f78fa463add03df79668a918bbdaa2750819504ee39242063abf53c6e + checksum: bf5c43d51350a2ed5d407caa6ddffa9bb8fd7098f3d36600bd1f2dd40a76f5e78b27dd5c2828ae8c312b12888f3c1846385ffbcd1f2e0bd0e2e549f5d124cca4 languageName: node linkType: hard @@ -8805,13 +9537,14 @@ __metadata: languageName: node linkType: hard -"parse5-html-rewriting-stream@npm:6.0.1": - version: 6.0.1 - resolution: "parse5-html-rewriting-stream@npm:6.0.1" +"parse5-html-rewriting-stream@npm:7.0.0": + version: 7.0.0 + resolution: "parse5-html-rewriting-stream@npm:7.0.0" dependencies: - parse5: ^6.0.1 - parse5-sax-parser: ^6.0.1 - checksum: 41b8c8ac611e2d478fe46fabb6b624b939bede68dc095beaf4e0a014cc095f0cb12cff62200f3cd39374b62ba91b8284f6c49d4bea8bc398085182408e2a2f5d + entities: ^4.3.0 + parse5: ^7.0.0 + parse5-sax-parser: ^7.0.0 + checksum: 5903351fbf481342a07db3664ce38e9100a22fba0c93050562ef09971fe9665ef0b0650ba934468330e1bb90d3df6a29b2b14e70052bee7815d089c57c349baa languageName: node linkType: hard @@ -8824,19 +9557,12 @@ __metadata: languageName: node linkType: hard -"parse5-sax-parser@npm:^6.0.1": - version: 6.0.1 - resolution: "parse5-sax-parser@npm:6.0.1" +"parse5-sax-parser@npm:^7.0.0": + version: 7.0.0 + resolution: "parse5-sax-parser@npm:7.0.0" dependencies: - parse5: ^6.0.1 - checksum: c62961aeac04a1fc4a6ce1053520da935694abb877d2b8284258361768b10c424e2c1a25aec22aa1e7841d36a74caddf79a0544020bdb43c7d57f90a5f7bacca - languageName: node - linkType: hard - -"parse5@npm:^5.0.0": - version: 5.1.1 - resolution: "parse5@npm:5.1.1" - checksum: 613a714af4c1101d1cb9f7cece2558e35b9ae8a0c03518223a4a1e35494624d9a9ad5fad4c13eab66a0e0adccd9aa3d522fc8f5f9cc19789e0579f3fa0bdfc65 + parse5: ^7.0.0 + checksum: 9826f9349c271d4c1a1cc402115f0888bcf1201fa4172d91dd14ae3db316355c02c949f8dfecee315332621c11ace8497dad47f3224dd5ae0eb3a41b0846d4cf languageName: node linkType: hard @@ -8847,6 +9573,15 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^7.0.0, parse5@npm:^7.1.2": + version: 7.1.2 + resolution: "parse5@npm:7.1.2" + dependencies: + entities: ^4.4.0 + checksum: 59465dd05eb4c5ec87b76173d1c596e152a10e290b7abcda1aecf0f33be49646ea74840c69af975d7887543ea45564801736356c568d6b5e71792fd0f4055713 + languageName: node + linkType: hard + "parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -8889,6 +9624,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.6.1": + version: 1.6.4 + resolution: "path-scurry@npm:1.6.4" + dependencies: + lru-cache: ^9.0.0 + minipass: ^5.0.0 + checksum: bd5262b51dc35b0d6f0b1d4fa4445789839982bd649904f18fe43717ecc3021d2313a80768b56cd0428f5ca50d740a6c609e747cd6a053efaa802e07eb5b7b18 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -8933,7 +9678,7 @@ __metadata: languageName: node linkType: hard -"pify@npm:^2.0.0, pify@npm:^2.3.0": +"pify@npm:^2.0.0": version: 2.3.0 resolution: "pify@npm:2.3.0" checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba @@ -8956,280 +9701,55 @@ __metadata: languageName: node linkType: hard -"pinkie@npm:^2.0.0": - version: 2.0.4 - resolution: "pinkie@npm:2.0.4" - checksum: b12b10afea1177595aab036fc220785488f67b4b0fc49e7a27979472592e971614fa1c728e63ad3e7eb748b4ec3c3dbd780819331dad6f7d635c77c10537b9db - languageName: node - linkType: hard - -"piscina@npm:3.2.0": - version: 3.2.0 - resolution: "piscina@npm:3.2.0" - dependencies: - eventemitter-asyncresource: ^1.0.0 - hdr-histogram-js: ^2.0.1 - hdr-histogram-percentiles-obj: ^3.0.0 - nice-napi: ^1.0.2 - dependenciesMeta: - nice-napi: - optional: true - checksum: c1980c7d45d85f53265652dd2fc62a2b9e9d2321f5bbb9fc1796edb9c1324bb77c153e823a0d6454c3c35098820efedff584737cc282207480afe478a3b8a166 - languageName: node - linkType: hard - -"pkg-dir@npm:^4.1.0": - version: 4.2.0 - resolution: "pkg-dir@npm:4.2.0" - dependencies: - find-up: ^4.0.0 - checksum: 9863e3f35132bf99ae1636d31ff1e1e3501251d480336edb1c211133c8d58906bed80f154a1d723652df1fda91e01c7442c2eeaf9dc83157c7ae89087e43c8d6 - languageName: node - linkType: hard - -"pngjs@npm:^5.0.0": - version: 5.0.0 - resolution: "pngjs@npm:5.0.0" - checksum: 04e912cc45fb9601564e2284efaf0c5d20d131d9b596244f8a6789fc6cdb6b18d2975a6bbf7a001858d7e159d5c5c5dd7b11592e97629b7137f7f5cef05904c8 - languageName: node - linkType: hard - -"postcss-attribute-case-insensitive@npm:^5.0.2": - version: 5.0.2 - resolution: "postcss-attribute-case-insensitive@npm:5.0.2" - dependencies: - postcss-selector-parser: ^6.0.10 - peerDependencies: - postcss: ^8.2 - checksum: c0b8139f37e68dba372724cba03a53c30716224f0085f98485cada99489beb7c3da9d598ffc1d81519b59d9899291712c9041c250205e6ec0b034bb2c144dcf9 - languageName: node - linkType: hard - -"postcss-clamp@npm:^4.1.0": - version: 4.1.0 - resolution: "postcss-clamp@npm:4.1.0" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.4.6 - checksum: 118eec936b3b035dc8d75c89973408f15c5a3de3d1ee210a2b3511e3e431d9c56e6f354b509a90540241e2225ffe3caaa2fdf25919c63348ce4583a28ada642c - languageName: node - linkType: hard - -"postcss-color-functional-notation@npm:^4.2.4": - version: 4.2.4 - resolution: "postcss-color-functional-notation@npm:4.2.4" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: b763e164fe3577a1de96f75e4bf451585c4f80b8ce60799763a51582cc9402d76faed57324a5d5e5556d90ca7ea0ebde565acb820c95e04bee6f36a91b019831 - languageName: node - linkType: hard - -"postcss-color-hex-alpha@npm:^8.0.4": - version: 8.0.4 - resolution: "postcss-color-hex-alpha@npm:8.0.4" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.4 - checksum: a2f3173a60176cf0aea3b7ebbc799b2cb08229127f0fff708fa31efa14e4ded47ca49aff549d8ed92e74ffe24adee32d5b9d557dbde0524fde5fe389bc520b4e - languageName: node - linkType: hard - -"postcss-color-rebeccapurple@npm:^7.1.1": - version: 7.1.1 - resolution: "postcss-color-rebeccapurple@npm:7.1.1" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 03482f9b8170da0fa014c41a5d88bce7b987471fb73fc456d397222a2455c89ac7f974dd6ddf40fd31907e768aad158057164b7c5f62cee63a6ecf29d47d7467 - languageName: node - linkType: hard - -"postcss-custom-media@npm:^8.0.2": - version: 8.0.2 - resolution: "postcss-custom-media@npm:8.0.2" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.3 - checksum: 887bbbacf6f8fab688123796e5dc1e8283b99f21e4c674235bd929dc8018c50df8634ea08932033ec93baaca32670ef2b87e6632863e0b4d84847375dbde9366 - languageName: node - linkType: hard - -"postcss-custom-properties@npm:^12.1.8": - version: 12.1.11 - resolution: "postcss-custom-properties@npm:12.1.11" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 421f9d8d6b9c9066919f39251859232efc4dc5dd406c01e62e08734319a6ccda6d03dd6b46063ba0971053ac6ad3f7abade56d67650b3e370851b2291e8e45e6 - languageName: node - linkType: hard - -"postcss-custom-selectors@npm:^6.0.3": - version: 6.0.3 - resolution: "postcss-custom-selectors@npm:6.0.3" - dependencies: - postcss-selector-parser: ^6.0.4 - peerDependencies: - postcss: ^8.3 - checksum: 18080d60a8a77a76d8ddff185104d65418fffd02bbf9824499f807ced7941509ba63828ab8fe3ec1d6b0d6c72a482bb90a79d79cdef58e5f4b30113cca16e69b - languageName: node - linkType: hard - -"postcss-dir-pseudo-class@npm:^6.0.5": - version: 6.0.5 - resolution: "postcss-dir-pseudo-class@npm:6.0.5" - dependencies: - postcss-selector-parser: ^6.0.10 - peerDependencies: - postcss: ^8.2 - checksum: 7810c439d8d1a9072c00f8ab39261a1492873ad170425745bd2819c59767db2f352f906588fc2a7d814e91117900563d7e569ecd640367c7332b26b9829927ef - languageName: node - linkType: hard - -"postcss-double-position-gradients@npm:^3.1.2": - version: 3.1.2 - resolution: "postcss-double-position-gradients@npm:3.1.2" - dependencies: - "@csstools/postcss-progressive-custom-properties": ^1.1.0 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: ca09bf2aefddc180f1c1413f379eef30d492b8147543413f7251216f23f413c394b2ed10b7cd255e87b18e0c8efe36087ea8b9bfb26a09813f9607a0b8e538b6 - languageName: node - linkType: hard - -"postcss-env-function@npm:^4.0.6": - version: 4.0.6 - resolution: "postcss-env-function@npm:4.0.6" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.4 - checksum: 645b2363cfa21be9dcce7fe4a0f172f0af70c00d6a4c1eb3d7ff7e9cfe26d569e291ec2533114d77b12d610023cd168a92d62c38f2fc969fa333b5ae2bff5ffe - languageName: node - linkType: hard - -"postcss-focus-visible@npm:^6.0.4": - version: 6.0.4 - resolution: "postcss-focus-visible@npm:6.0.4" - dependencies: - postcss-selector-parser: ^6.0.9 - peerDependencies: - postcss: ^8.4 - checksum: acd010b9ddef9b86ffb5fa604c13515ba83e18bc5118dad0a1281150f412aa0ece056c2c5ac56b55e2599f53ab0f740f5ebfdc51e1f5cfe43b8130bac0096fcc - languageName: node - linkType: hard - -"postcss-focus-within@npm:^5.0.4": - version: 5.0.4 - resolution: "postcss-focus-within@npm:5.0.4" - dependencies: - postcss-selector-parser: ^6.0.9 - peerDependencies: - postcss: ^8.4 - checksum: f23d8ab757345a6deaa807d76e10c88caf4b771c38b60e1593b24aee161c503b5823620e89302226a6ae5e7afdb6ac31809241291912e4176eb594a7ddcc9521 - languageName: node - linkType: hard - -"postcss-font-variant@npm:^5.0.0": - version: 5.0.0 - resolution: "postcss-font-variant@npm:5.0.0" - peerDependencies: - postcss: ^8.1.0 - checksum: a19286589261c2bc3e20470486e1ee3b4daf34271c5020167f30856c9b30c26f23264307cb97a184d503814e1b8c5d8a1f9f64a14fd4fd9551c173dca9424695 - languageName: node - linkType: hard - -"postcss-gap-properties@npm:^3.0.5": - version: 3.0.5 - resolution: "postcss-gap-properties@npm:3.0.5" - peerDependencies: - postcss: ^8.2 - checksum: aed559d6d375203a08a006c9ae8cf5ae90d9edaec5cadd20fe65c1b8ce63c2bc8dfe752d4331880a6e24a300541cde61058be790b7bd9b5d04d470c250fbcd39 +"pinkie@npm:^2.0.0": + version: 2.0.4 + resolution: "pinkie@npm:2.0.4" + checksum: b12b10afea1177595aab036fc220785488f67b4b0fc49e7a27979472592e971614fa1c728e63ad3e7eb748b4ec3c3dbd780819331dad6f7d635c77c10537b9db languageName: node linkType: hard -"postcss-image-set-function@npm:^4.0.7": - version: 4.0.7 - resolution: "postcss-image-set-function@npm:4.0.7" +"piscina@npm:3.2.0": + version: 3.2.0 + resolution: "piscina@npm:3.2.0" dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 7e509330986de14250ead1a557e8da8baaf66ebe8a40354a5dff60ab40d99a483d92aa57d52713251ca1adbf0055ef476c5702b0d0ba5f85a4f407367cdabac0 + eventemitter-asyncresource: ^1.0.0 + hdr-histogram-js: ^2.0.1 + hdr-histogram-percentiles-obj: ^3.0.0 + nice-napi: ^1.0.2 + dependenciesMeta: + nice-napi: + optional: true + checksum: c1980c7d45d85f53265652dd2fc62a2b9e9d2321f5bbb9fc1796edb9c1324bb77c153e823a0d6454c3c35098820efedff584737cc282207480afe478a3b8a166 languageName: node linkType: hard -"postcss-import@npm:15.0.0": - version: 15.0.0 - resolution: "postcss-import@npm:15.0.0" +"pkg-dir@npm:^4.1.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" dependencies: - postcss-value-parser: ^4.0.0 - read-cache: ^1.0.0 - resolve: ^1.1.7 - peerDependencies: - postcss: ^8.0.0 - checksum: e5048072514f33520348c0c5aaedb5db92da72188e72a7367c26a8d851b9cf92e56aeda30d9c7b4ed1a34f84a58959da1b4b27b4aa23d41fc0ce36efe00bf1d1 - languageName: node - linkType: hard - -"postcss-initial@npm:^4.0.1": - version: 4.0.1 - resolution: "postcss-initial@npm:4.0.1" - peerDependencies: - postcss: ^8.0.0 - checksum: 6956953853865de79c39d11533a2860e9f38b770bb284d0010d98a00b9469e22de344e4e5fd8208614d797030487e8918dd2f2c37d9e24d4dd59d565d4fc3e12 + find-up: ^4.0.0 + checksum: 9863e3f35132bf99ae1636d31ff1e1e3501251d480336edb1c211133c8d58906bed80f154a1d723652df1fda91e01c7442c2eeaf9dc83157c7ae89087e43c8d6 languageName: node linkType: hard -"postcss-lab-function@npm:^4.2.1": - version: 4.2.1 - resolution: "postcss-lab-function@npm:4.2.1" - dependencies: - "@csstools/postcss-progressive-custom-properties": ^1.1.0 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 26ac74b430011271b5581beba69b2cd788f56375fcb64c90f6ec1577379af85f6022dc38c410ff471dac520c7ddc289160a6a16cca3c7ff76f5af7e90d31eaa3 +"pngjs@npm:^5.0.0": + version: 5.0.0 + resolution: "pngjs@npm:5.0.0" + checksum: 04e912cc45fb9601564e2284efaf0c5d20d131d9b596244f8a6789fc6cdb6b18d2975a6bbf7a001858d7e159d5c5c5dd7b11592e97629b7137f7f5cef05904c8 languageName: node linkType: hard -"postcss-loader@npm:7.0.1": - version: 7.0.1 - resolution: "postcss-loader@npm:7.0.1" +"postcss-loader@npm:7.0.2": + version: 7.0.2 + resolution: "postcss-loader@npm:7.0.2" dependencies: cosmiconfig: ^7.0.0 klona: ^2.0.5 - semver: ^7.3.7 + semver: ^7.3.8 peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 - checksum: 2a3cbcaaade598d4919824d384ae34ffbfc14a9c8db6cc3b154582356f4f44a1c9af9e731b81cf1947b089accf7d0ab7a0c51c717946985f89aa1708d2b4304d - languageName: node - linkType: hard - -"postcss-logical@npm:^5.0.4": - version: 5.0.4 - resolution: "postcss-logical@npm:5.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 17c71291ed6a03883a5aa54b9923b874c32710707d041a0f0752e6febdb09dee5d2abf4ef271978d932e4a4c948f349bb23edf633c03e3427ba15e71bfc66ac7 - languageName: node - linkType: hard - -"postcss-media-minmax@npm:^5.0.0": - version: 5.0.0 - resolution: "postcss-media-minmax@npm:5.0.0" - peerDependencies: - postcss: ^8.1.0 - checksum: 2cd7283e07a1ac1acdcc3ecbaa0e9932f8d1e7647e7aeb14d91845fcb890d60d7257ec70c825cae8d48ae80a08cc77ebc4021a0dfa32360e0cd991e2bc021607 + checksum: 2d251537d482eb751f812c96c8b515f46d7c9905cad7afab33f0f34872670619b7440cefc9e2babbf89fb11b4708850d522d79fa5ff788227587645e78f16638 languageName: node linkType: hard @@ -9284,137 +9804,6 @@ __metadata: languageName: node linkType: hard -"postcss-nesting@npm:^10.1.10": - version: 10.2.0 - resolution: "postcss-nesting@npm:10.2.0" - dependencies: - "@csstools/selector-specificity": ^2.0.0 - postcss-selector-parser: ^6.0.10 - peerDependencies: - postcss: ^8.2 - checksum: 25e6e66186bd7f18bc4628cf0f43e02189268f28a449aa4a63b33b8f2c33745af99acfcd4ce2ac69319dc850e83b28dbaabcf517e3977dfe20e37fed0e032c7d - languageName: node - linkType: hard - -"postcss-opacity-percentage@npm:^1.1.2": - version: 1.1.3 - resolution: "postcss-opacity-percentage@npm:1.1.3" - peerDependencies: - postcss: ^8.2 - checksum: 54d1b8ca68035bc1a5788aaabdbc3b66ffee34b5a2412cecf073627dad7e3f2bae07c01fac3bc7f46bbac5da3291ac9ddcf74bfee26dfd86f9f96c847a0afc13 - languageName: node - linkType: hard - -"postcss-overflow-shorthand@npm:^3.0.4": - version: 3.0.4 - resolution: "postcss-overflow-shorthand@npm:3.0.4" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 74009022491e3901263f8f5811630393480323e51f5d23ef17f3fdc7e03bf9c2502a632f3ba8fe6a468b57590f13b2fa3b17a68ef19653589e76277607696743 - languageName: node - linkType: hard - -"postcss-page-break@npm:^3.0.4": - version: 3.0.4 - resolution: "postcss-page-break@npm:3.0.4" - peerDependencies: - postcss: ^8 - checksum: a7d08c945fc691f62c77ac701e64722218b14ec5c8fc1972b8af9c21553492d40808cf95e61b9697b1dacaf7e6180636876d7fee314f079e6c9e39ac1b1edc6f - languageName: node - linkType: hard - -"postcss-place@npm:^7.0.5": - version: 7.0.5 - resolution: "postcss-place@npm:7.0.5" - dependencies: - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 903fec0c313bb7ec20f2c8f0a125866fb7804aa3186b5b2c7c2d58cb9039ff301461677a060e9db643d1aaffaf80a0ff71e900a6da16705dad6b49c804cb3c73 - languageName: node - linkType: hard - -"postcss-preset-env@npm:7.8.0": - version: 7.8.0 - resolution: "postcss-preset-env@npm:7.8.0" - dependencies: - "@csstools/postcss-cascade-layers": ^1.0.5 - "@csstools/postcss-color-function": ^1.1.1 - "@csstools/postcss-font-format-keywords": ^1.0.1 - "@csstools/postcss-hwb-function": ^1.0.2 - "@csstools/postcss-ic-unit": ^1.0.1 - "@csstools/postcss-is-pseudo-class": ^2.0.7 - "@csstools/postcss-nested-calc": ^1.0.0 - "@csstools/postcss-normalize-display-values": ^1.0.1 - "@csstools/postcss-oklab-function": ^1.1.1 - "@csstools/postcss-progressive-custom-properties": ^1.3.0 - "@csstools/postcss-stepped-value-functions": ^1.0.1 - "@csstools/postcss-text-decoration-shorthand": ^1.0.0 - "@csstools/postcss-trigonometric-functions": ^1.0.2 - "@csstools/postcss-unset-value": ^1.0.2 - autoprefixer: ^10.4.8 - browserslist: ^4.21.3 - css-blank-pseudo: ^3.0.3 - css-has-pseudo: ^3.0.4 - css-prefers-color-scheme: ^6.0.3 - cssdb: ^7.0.0 - postcss-attribute-case-insensitive: ^5.0.2 - postcss-clamp: ^4.1.0 - postcss-color-functional-notation: ^4.2.4 - postcss-color-hex-alpha: ^8.0.4 - postcss-color-rebeccapurple: ^7.1.1 - postcss-custom-media: ^8.0.2 - postcss-custom-properties: ^12.1.8 - postcss-custom-selectors: ^6.0.3 - postcss-dir-pseudo-class: ^6.0.5 - postcss-double-position-gradients: ^3.1.2 - postcss-env-function: ^4.0.6 - postcss-focus-visible: ^6.0.4 - postcss-focus-within: ^5.0.4 - postcss-font-variant: ^5.0.0 - postcss-gap-properties: ^3.0.5 - postcss-image-set-function: ^4.0.7 - postcss-initial: ^4.0.1 - postcss-lab-function: ^4.2.1 - postcss-logical: ^5.0.4 - postcss-media-minmax: ^5.0.0 - postcss-nesting: ^10.1.10 - postcss-opacity-percentage: ^1.1.2 - postcss-overflow-shorthand: ^3.0.4 - postcss-page-break: ^3.0.4 - postcss-place: ^7.0.5 - postcss-pseudo-class-any-link: ^7.1.6 - postcss-replace-overflow-wrap: ^4.0.0 - postcss-selector-not: ^6.0.1 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2 - checksum: 7c07f6ecc776dc8063bfffbb8e44b88730cde0c8951c9960263c38bdb0c5103ace41f34b01eac0ff4861b00384e44ff450f7861f34072a50850afb862af4d6a8 - languageName: node - linkType: hard - -"postcss-pseudo-class-any-link@npm:^7.1.6": - version: 7.1.6 - resolution: "postcss-pseudo-class-any-link@npm:7.1.6" - dependencies: - postcss-selector-parser: ^6.0.10 - peerDependencies: - postcss: ^8.2 - checksum: 43aa18ea1ef1b168f61310856dd92f46ceb3dc60b6cf820e079ca1a849df5cc0f12a1511bdc1811a23f03d60ddcc959200c80c3f9a7b57feebe32bab226afb39 - languageName: node - linkType: hard - -"postcss-replace-overflow-wrap@npm:^4.0.0": - version: 4.0.0 - resolution: "postcss-replace-overflow-wrap@npm:4.0.0" - peerDependencies: - postcss: ^8.0.3 - checksum: 3ffe20b300a4c377a11c588b142740d8557e03c707474c45234c934190ac374750ddc92c7906c373471d273a20504a429c2062c21fdcaff830fb28e0a81ac1dc - languageName: node - linkType: hard - "postcss-resolve-nested-selector@npm:^0.1.1": version: 0.1.1 resolution: "postcss-resolve-nested-selector@npm:0.1.1" @@ -9440,18 +9829,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-not@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-selector-not@npm:6.0.1" - dependencies: - postcss-selector-parser: ^6.0.10 - peerDependencies: - postcss: ^8.2 - checksum: fe523a0219e4bd34f04498534bb9e8aec3193f3585eafe4c388d086955b41201cae71fd20980ca465acade7f182029b43dbd5ca7e9d50bf34bbcaf1d19fe3ee6 - languageName: node - linkType: hard - -"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6, postcss-selector-parser@npm:^6.0.9": +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6": version: 6.0.11 resolution: "postcss-selector-parser@npm:6.0.11" dependencies: @@ -9461,21 +9839,21 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f languageName: node linkType: hard -"postcss@npm:8.4.16": - version: 8.4.16 - resolution: "postcss@npm:8.4.16" +"postcss@npm:8.4.21, postcss@npm:^8.4.19": + version: 8.4.21 + resolution: "postcss@npm:8.4.21" dependencies: nanoid: ^3.3.4 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f + checksum: e39ac60ccd1542d4f9d93d894048aac0d686b3bb38e927d8386005718e6793dbbb46930f0a523fe382f1bbd843c6d980aaea791252bf5e176180e5a4336d9679 languageName: node linkType: hard @@ -9490,17 +9868,6 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.19, postcss@npm:^8.4.7": - version: 8.4.21 - resolution: "postcss@npm:8.4.21" - dependencies: - nanoid: ^3.3.4 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: e39ac60ccd1542d4f9d93d894048aac0d686b3bb38e927d8386005718e6793dbbb46930f0a523fe382f1bbd843c6d980aaea791252bf5e176180e5a4336d9679 - languageName: node - linkType: hard - "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -9565,10 +9932,10 @@ __metadata: languageName: node linkType: hard -"proc-log@npm:^2.0.0, proc-log@npm:^2.0.1": - version: 2.0.1 - resolution: "proc-log@npm:2.0.1" - checksum: f6f23564ff759097db37443e6e2765af84979a703d2c52c1b9df506ee9f87caa101ba49d8fdc115c1a313ec78e37e8134704e9069e6a870f3499d98bb24c436f +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 languageName: node linkType: hard @@ -9751,34 +10118,25 @@ __metadata: languageName: node linkType: hard -"read-cache@npm:^1.0.0": - version: 1.0.0 - resolution: "read-cache@npm:1.0.0" - dependencies: - pify: ^2.3.0 - checksum: cffc728b9ede1e0667399903f9ecaf3789888b041c46ca53382fa3a06303e5132774dc0a96d0c16aa702dbac1ea0833d5a868d414f5ab2af1e1438e19e6657c6 - languageName: node - linkType: hard - -"read-package-json-fast@npm:^2.0.3": - version: 2.0.3 - resolution: "read-package-json-fast@npm:2.0.3" +"read-package-json-fast@npm:^3.0.0": + version: 3.0.2 + resolution: "read-package-json-fast@npm:3.0.2" dependencies: - json-parse-even-better-errors: ^2.3.0 - npm-normalize-package-bin: ^1.0.1 - checksum: fca37b3b2160b9dda7c5588b767f6a2b8ce68d03a044000e568208e20bea0cf6dd2de17b90740ce8da8b42ea79c0b3859649dadf29510bbe77224ea65326a903 + json-parse-even-better-errors: ^3.0.0 + npm-normalize-package-bin: ^3.0.0 + checksum: 8d406869f045f1d76e2a99865a8fd1c1af9c1dc06200b94d2b07eef87ed734b22703a8d72e1cd36ea36cc48e22020bdd187f88243c7dd0563f72114d38c17072 languageName: node linkType: hard -"read-package-json@npm:^5.0.0": - version: 5.0.2 - resolution: "read-package-json@npm:5.0.2" +"read-package-json@npm:^6.0.0": + version: 6.0.1 + resolution: "read-package-json@npm:6.0.1" dependencies: - glob: ^8.0.1 - json-parse-even-better-errors: ^2.3.1 - normalize-package-data: ^4.0.0 - npm-normalize-package-bin: ^2.0.0 - checksum: 0882ac9cec1bc92fb5515e9727611fb2909351e1e5c840dce3503cbb25b4cd48eb44b61071986e0fc51043208161f07d364a7336206c8609770186818753b51a + glob: ^9.3.0 + json-parse-even-better-errors: ^3.0.0 + normalize-package-data: ^5.0.0 + npm-normalize-package-bin: ^3.0.0 + checksum: 2fb5c2248da02d5a7180c0538c5b9ebdf04920f4bbf5c19d336d656277d99f1559ba90f2afcdfd6f580c3182a46fe5fb1d3d8c01bc63ffdeae927c91a11a82c9 languageName: node linkType: hard @@ -9873,7 +10231,14 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:0.13.9, regenerator-runtime@npm:^0.13.4": +"regenerator-runtime@npm:^0.13.11": + version: 0.13.11 + resolution: "regenerator-runtime@npm:0.13.11" + checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 + languageName: node + linkType: hard + +"regenerator-runtime@npm:^0.13.4": version: 0.13.9 resolution: "regenerator-runtime@npm:0.13.9" checksum: 65ed455fe5afd799e2897baf691ca21c2772e1a969d19bb0c4695757c2d96249eb74ee3553ea34a91062b2a676beedf630b4c1551cc6299afb937be1426ec55e @@ -10042,7 +10407,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2": +"resolve@npm:^1.10.0, resolve@npm:^1.14.2": version: 1.22.0 resolution: "resolve@npm:1.22.0" dependencies: @@ -10068,7 +10433,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": +"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin": version: 1.22.0 resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" dependencies: @@ -10205,15 +10570,22 @@ __metadata: languageName: node linkType: hard -"sass-loader@npm:13.0.2": - version: 13.0.2 - resolution: "sass-loader@npm:13.0.2" +"safevalues@npm:^0.3.4": + version: 0.3.4 + resolution: "safevalues@npm:0.3.4" + checksum: 45f30fbbca4ce17157876e429857a28479a48898c92d7f97924623a32cf5996def99f40ba5e32b244b668576f7e3f908004d21f9ed9d47558aaad4003f51e85c + languageName: node + linkType: hard + +"sass-loader@npm:13.2.0": + version: 13.2.0 + resolution: "sass-loader@npm:13.2.0" dependencies: klona: ^2.0.4 neo-async: ^2.6.2 peerDependencies: fibers: ">= 3.1.0" - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 sass: ^1.3.0 sass-embedded: "*" webpack: ^5.0.0 @@ -10226,20 +10598,20 @@ __metadata: optional: true sass-embedded: optional: true - checksum: 6306712cc75bd4b4260ce67178778c92110089485f358b5956b6b12aba15a55e5c7287154daaf8b32678891df915b7bcda7356991afe8bf08ca7356ed30eb448 + checksum: ed6cdb5f5508e1a8a020d1451160a5e94805d0c2a97be5719c6a44ed28a258b5f37a1478d01b9d545f269367ae91ccb88adc93bd6202bfd609dbe3193228d51e languageName: node linkType: hard -"sass@npm:1.54.4": - version: 1.54.4 - resolution: "sass@npm:1.54.4" +"sass@npm:1.58.1": + version: 1.58.1 + resolution: "sass@npm:1.58.1" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: bb6aead09764de450a02b9a66e4ee538f0ba6bc8f2fc3905c71b2c302b5f47e089b510b86cfa7ef2d4139c210c8abf99fe157e7a5bd356c057f10d29e6c4b44c + checksum: ff079887d906b5c0dde99084d14ac36336d238c0c07935ff6381bad68f05de212c1ff12657ac1e8a0533523cd7a393126facdc2508d758e7d5700344a0e6ea51 languageName: node linkType: hard @@ -10252,24 +10624,13 @@ __metadata: languageName: node linkType: hard -"sax@npm:>=0.6.0, sax@npm:^1.2.4, sax@npm:~1.2.4": +"sax@npm:>=0.6.0, sax@npm:^1.2.4": version: 1.2.4 resolution: "sax@npm:1.2.4" checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe languageName: node linkType: hard -"schema-utils@npm:^2.6.5": - version: 2.7.1 - resolution: "schema-utils@npm:2.7.1" - dependencies: - "@types/json-schema": ^7.0.5 - ajv: ^6.12.4 - ajv-keywords: ^3.5.2 - checksum: 32c62fc9e28edd101e1bd83453a4216eb9bd875cc4d3775e4452b541908fa8f61a7bbac8ffde57484f01d7096279d3ba0337078e85a918ecbeb72872fb09fb2b - languageName: node - linkType: hard - "schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": version: 3.1.1 resolution: "schema-utils@npm:3.1.1" @@ -10312,7 +10673,7 @@ __metadata: languageName: node linkType: hard -"selfsigned@npm:^2.0.1": +"selfsigned@npm:^2.1.1": version: 2.1.1 resolution: "selfsigned@npm:2.1.1" dependencies: @@ -10330,14 +10691,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.3.7": - version: 7.3.7 - resolution: "semver@npm:7.3.7" +"semver@npm:7.3.8, semver@npm:^7.3.7": + version: 7.3.8 + resolution: "semver@npm:7.3.8" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 languageName: node linkType: hard @@ -10361,14 +10722,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.7": - version: 7.3.8 - resolution: "semver@npm:7.3.8" +"semver@npm:^7.3.8": + version: 7.4.0 + resolution: "semver@npm:7.4.0" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 + checksum: debf7f4d6fa36fdc5ef82bd7fc3603b6412165c8a3963a30be0c45a587be1a49e7681e80aa109da1875765741af24edc6e021cee1ba16ae96f649d06c5df296d languageName: node linkType: hard @@ -10500,6 +10861,19 @@ __metadata: languageName: node linkType: hard +"sigstore@npm:^1.0.0": + version: 1.2.0 + resolution: "sigstore@npm:1.2.0" + dependencies: + "@sigstore/protobuf-specs": ^0.1.0 + make-fetch-happen: ^11.0.1 + tuf-js: ^1.0.0 + bin: + sigstore: bin/sigstore.js + checksum: 8b06341a1bee97f363a8cab62102b27c88714c5ad9743fada5effb46cc3a5935c27c8149669384f0be7040c8f0c4e69bb7d533f138bdcf3aba91b803a69eac77 + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -10625,16 +10999,16 @@ __metadata: languageName: node linkType: hard -"source-map-loader@npm:4.0.0": - version: 4.0.0 - resolution: "source-map-loader@npm:4.0.0" +"source-map-loader@npm:4.0.1": + version: 4.0.1 + resolution: "source-map-loader@npm:4.0.1" dependencies: abab: ^2.0.6 iconv-lite: ^0.6.3 source-map-js: ^1.0.2 peerDependencies: webpack: ^5.72.1 - checksum: 0b169701735bd6a32d66bff84b7475c31066972d717ebef5a24476cc8678cd19c2b8ebe03a7d62ade9586d4f7ba55c17772fdc8d8bc159dcb9315c757a01046e + checksum: 4ddca8b03dc61f406effd4bffe70de4b87fef48bae6f737017b2dabcbc7d609133325be1e73838e9265331de28039111d729fcbb8bce88a6018a816bef510eb1 languageName: node linkType: hard @@ -10678,20 +11052,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.7.3": - version: 0.7.3 - resolution: "source-map@npm:0.7.3" - checksum: cd24efb3b8fa69b64bf28e3c1b1a500de77e84260c5b7f2b873f88284df17974157cc88d386ee9b6d081f08fdd8242f3fc05c953685a6ad81aad94c7393dedea - languageName: node - linkType: hard - -"sourcemap-codec@npm:^1.4.8": - version: 1.4.8 - resolution: "sourcemap-codec@npm:1.4.8" - checksum: b57981c05611afef31605732b598ccf65124a9fcb03b833532659ac4d29ac0f7bfacbc0d6c5a28a03e84c7510e7e556d758d0bb57786e214660016fb94279316 - languageName: node - linkType: hard - "spdx-correct@npm:^3.0.0": version: 3.2.0 resolution: "spdx-correct@npm:3.2.0" @@ -10781,6 +11141,15 @@ __metadata: languageName: node linkType: hard +"ssri@npm:^10.0.0": + version: 10.0.3 + resolution: "ssri@npm:10.0.3" + dependencies: + minipass: ^4.0.0 + checksum: 1a8d0ad28325a0146e67348e15d1455ab71b8356e5e95beac453ab5ec361555309496c1770d67010ee0c150d8abef7866b9001cbc36b6477e96772382914ec85 + languageName: node + linkType: hard + "ssri@npm:^9.0.0": version: 9.0.1 resolution: "ssri@npm:9.0.1" @@ -11099,35 +11468,6 @@ __metadata: languageName: node linkType: hard -"stylus-loader@npm:7.0.0": - version: 7.0.0 - resolution: "stylus-loader@npm:7.0.0" - dependencies: - fast-glob: ^3.2.11 - klona: ^2.0.5 - normalize-path: ^3.0.0 - peerDependencies: - stylus: ">=0.52.4" - webpack: ^5.0.0 - checksum: 3bb09d45901865c7414a33d80b36bc32054fef325cd307d3648a7be5f0eca8ad76b7687aacb31d5e36ad216c6af7fcd92d091c8d08542306db7f87821edec10f - languageName: node - linkType: hard - -"stylus@npm:0.59.0": - version: 0.59.0 - resolution: "stylus@npm:0.59.0" - dependencies: - "@adobe/css-tools": ^4.0.1 - debug: ^4.3.2 - glob: ^7.1.6 - sax: ~1.2.4 - source-map: ^0.7.3 - bin: - stylus: bin/stylus - checksum: 2faf4a5618747c17b7d10854e40efdd43b438a6cf99d197b0231578f5091bfe9313663d846b5666bb37140140420c51606bb127fd877bbf2db46730c01403b01 - languageName: node - linkType: hard - "supports-color@npm:^2.0.0": version: 2.0.0 resolution: "supports-color@npm:2.0.0" @@ -11327,9 +11667,9 @@ __metadata: languageName: node linkType: hard -"terser@npm:5.14.2": - version: 5.14.2 - resolution: "terser@npm:5.14.2" +"terser@npm:5.16.3": + version: 5.16.3 + resolution: "terser@npm:5.16.3" dependencies: "@jridgewell/source-map": ^0.3.2 acorn: ^8.5.0 @@ -11337,7 +11677,7 @@ __metadata: source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: cabb50a640d6c2cfb351e4f43dc7bf7436f649755bb83eb78b2cacda426d5e0979bd44e6f92d713f3ca0f0866e322739b9ced888ebbce6508ad872d08de74fcc + checksum: d3c2ac1c2723c37b698b25b68d76fd315a1277fddde113983d5783d1f2a01dd7b8ed83ba3f54e5e65f0b59dd971ed7be2fdf8d4be94ec694b2d27832d2e7561f languageName: node linkType: hard @@ -11487,10 +11827,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.4.0": - version: 2.4.0 - resolution: "tslib@npm:2.4.0" - checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 +"tslib@npm:2.5.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0": + version: 2.5.0 + resolution: "tslib@npm:2.5.0" + checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 languageName: node linkType: hard @@ -11501,13 +11841,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.1.0, tslib@npm:^2.3.0": - version: 2.5.0 - resolution: "tslib@npm:2.5.0" - checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 - languageName: node - linkType: hard - "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -11519,6 +11852,16 @@ __metadata: languageName: node linkType: hard +"tuf-js@npm:^1.0.0": + version: 1.1.3 + resolution: "tuf-js@npm:1.1.3" + dependencies: + "@tufjs/models": 1.0.2 + make-fetch-happen: ^11.0.1 + checksum: 87efe13069b681a131e5643f8bc8ee9ac07f8abeaa32096b896d2842416fcd0f8de29ad5e70be22f18d97a7b4723d72dd8b3962bb838312ff6e7d7465ed3489c + languageName: node + linkType: hard + "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -11674,15 +12017,6 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^1.1.1": - version: 1.1.1 - resolution: "unique-filename@npm:1.1.1" - dependencies: - unique-slug: ^2.0.0 - checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 - languageName: node - linkType: hard - "unique-filename@npm:^2.0.0": version: 2.0.1 resolution: "unique-filename@npm:2.0.1" @@ -11692,12 +12026,12 @@ __metadata: languageName: node linkType: hard -"unique-slug@npm:^2.0.0": - version: 2.0.2 - resolution: "unique-slug@npm:2.0.2" +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" dependencies: - imurmurhash: ^0.1.4 - checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a + unique-slug: ^4.0.0 + checksum: 8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df languageName: node linkType: hard @@ -11710,6 +12044,15 @@ __metadata: languageName: node linkType: hard +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" + dependencies: + imurmurhash: ^0.1.4 + checksum: 0884b58365af59f89739e6f71e3feacb5b1b41f2df2d842d0757933620e6de08eff347d27e9d499b43c40476cbaf7988638d3acb2ffbcb9d35fd035591adfd15 + languageName: node + linkType: hard + "universalify@npm:^0.1.0": version: 0.1.2 resolution: "universalify@npm:0.1.2" @@ -11761,15 +12104,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:8.3.2, uuid@npm:^8.3.2": - version: 8.3.2 - resolution: "uuid@npm:8.3.2" - bin: - uuid: dist/bin/uuid - checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df - languageName: node - linkType: hard - "uuid@npm:^3.3.2": version: 3.4.0 resolution: "uuid@npm:3.4.0" @@ -11779,6 +12113,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + "v8-compile-cache@npm:^2.3.0": version: 2.3.0 resolution: "v8-compile-cache@npm:2.3.0" @@ -11796,12 +12139,12 @@ __metadata: languageName: node linkType: hard -"validate-npm-package-name@npm:^4.0.0": - version: 4.0.0 - resolution: "validate-npm-package-name@npm:4.0.0" +"validate-npm-package-name@npm:^5.0.0": + version: 5.0.0 + resolution: "validate-npm-package-name@npm:5.0.0" dependencies: builtins: ^5.0.0 - checksum: a32fd537bad17fcb59cfd58ae95a414d443866020d448ec3b22e8d40550cb585026582a57efbe1f132b882eea4da8ac38ee35f7be0dd72988a3cb55d305a20c1 + checksum: 5342a994986199b3c28e53a8452a14b2bb5085727691ea7aa0d284a6606b127c371e0925ae99b3f1ef7cc7d2c9de75f52eb61a3d1cc45e39bca1e3a9444cbb4e languageName: node linkType: hard @@ -11906,7 +12249,22 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:5.3.3, webpack-dev-middleware@npm:^5.3.1": +"webpack-dev-middleware@npm:6.0.1": + version: 6.0.1 + resolution: "webpack-dev-middleware@npm:6.0.1" + dependencies: + colorette: ^2.0.10 + memfs: ^3.4.12 + mime-types: ^2.1.31 + range-parser: ^1.2.1 + schema-utils: ^4.0.0 + peerDependencies: + webpack: ^5.0.0 + checksum: eeda09cf4a1fdb09ee95f96ab657b0fb8d32c56f4d20c7f4499457a015c703f243711fda0b5bc0d103ff9e7c7b9b60568210a4c0fbd6d02361d09d2387cd723a + languageName: node + linkType: hard + +"webpack-dev-middleware@npm:^5.3.1": version: 5.3.3 resolution: "webpack-dev-middleware@npm:5.3.3" dependencies: @@ -11921,9 +12279,9 @@ __metadata: languageName: node linkType: hard -"webpack-dev-server@npm:4.11.0": - version: 4.11.0 - resolution: "webpack-dev-server@npm:4.11.0" +"webpack-dev-server@npm:4.11.1": + version: 4.11.1 + resolution: "webpack-dev-server@npm:4.11.1" dependencies: "@types/bonjour": ^3.5.9 "@types/connect-history-api-fallback": ^1.3.5 @@ -11948,7 +12306,7 @@ __metadata: p-retry: ^4.5.0 rimraf: ^3.0.2 schema-utils: ^4.0.0 - selfsigned: ^2.0.1 + selfsigned: ^2.1.1 serve-index: ^1.9.1 sockjs: ^0.3.24 spdy: ^4.0.2 @@ -11961,7 +12319,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: b2c7e9810138d0c6fa670a890410a74b29d4dd23d428e92e61e0233c542689f57caf9b31c031fbcc772170b6710b0752e81fa9cbc41ac0deefdc32d5749ce08d + checksum: b7601a39ee0f413988259e29a36835b0a68522cfaa161de5b7ec99b3399acdd99d44189add4aaf4a5191258bb130f9cf3e68919324a1955c7557f5fe6ab0d96c languageName: node linkType: hard @@ -12120,6 +12478,17 @@ __metadata: languageName: node linkType: hard +"which@npm:^3.0.0": + version: 3.0.0 + resolution: "which@npm:3.0.0" + dependencies: + isexe: ^2.0.0 + bin: + node-which: bin/which.js + checksum: fdcf3cadab414e60b86c6836e7ac9de9273561a8926f57cbc28641b602a771527239ee4d47f2689ed255666f035ba0a0d72390986cc0c4e45344491adc7d0eeb + languageName: node + linkType: hard + "wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" @@ -12281,25 +12650,25 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.0.0, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c languageName: node linkType: hard -"yargs@npm:17.5.1": - version: 17.5.1 - resolution: "yargs@npm:17.5.1" +"yargs@npm:17.6.2": + version: 17.6.2 + resolution: "yargs@npm:17.6.2" dependencies: - cliui: ^7.0.2 + cliui: ^8.0.1 escalade: ^3.1.1 get-caller-file: ^2.0.5 require-directory: ^2.1.1 string-width: ^4.2.3 y18n: ^5.0.5 - yargs-parser: ^21.0.0 - checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde + yargs-parser: ^21.1.1 + checksum: 47da1b0d854fa16d45a3ded57b716b013b2179022352a5f7467409da5a04a1eef5b3b3d97a2dfc13e8bbe5f2ffc0afe3bc6a4a72f8254e60f5a4bd7947138643 languageName: node linkType: hard From 7e4f0173173afb5d429251ff83d28d506464c3ef Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 17 Apr 2023 14:24:28 +0200 Subject: [PATCH 275/498] Angular 15 layout fix: User profile --- .../app/user/profile/profile.component.html | 18 +++++++++--------- .../app/user/profile/profile.component.scss | 16 ---------------- frontend/src/styles.scss | 3 +++ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/user/profile/profile.component.html b/frontend/src/app/user/profile/profile.component.html index fbb71329f..80de21f6c 100644 --- a/frontend/src/app/user/profile/profile.component.html +++ b/frontend/src/app/user/profile/profile.component.html @@ -1,13 +1,13 @@ - + -
+
person
{{(user | async)?.username}} - User Profile -
- @@ -15,25 +15,25 @@

- + User Name:

- + Password

- + Role:

- + SessionId: diff --git a/frontend/src/app/user/profile/profile.component.scss b/frontend/src/app/user/profile/profile.component.scss index 0feb86ab7..8efe50bfe 100644 --- a/frontend/src/app/user/profile/profile.component.scss +++ b/frontend/src/app/user/profile/profile.component.scss @@ -4,18 +4,6 @@ margin: 100px 0px; } -.mat-mdc-form-field { - width: 100%; - min-width: 300px; -} - -/* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version.*/ -mat-card-title, -mat-card-content { - display: flex; - justify-content: center; -} - mat-grid-list { min-width: 300px; } @@ -32,10 +20,6 @@ mat-grid-list { justify-content: flex-end; } -// See https://stackoverflow.com/a/52009315 -.mat-mdc-card-header { - width: 100% !important; -} .flush-right { margin-left: auto; diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 1a502a37a..7b18514fe 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -115,3 +115,6 @@ $warn: mat-palette($mat-red, 500); padding-bottom: 1em; } +.width-full { + width: 100%; +} From b79c98d3963b7ad2cd3c4e20d5c5baafdd9ee18f Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 21 Apr 2023 16:54:02 +0200 Subject: [PATCH 276/498] Angular 15: Styling upgrade --- frontend/src/app/app-routing.module.ts | 3 +- .../download-button.component.html | 2 +- .../evaluation-start-dialog.component.html | 52 +++++++ .../evaluation-start-dialog.component.scss | 0 .../evaluation-start-dialog.component.ts | 55 +++++++ .../template-create-dialog.component.html | 21 +++ .../template-create-dialog.component.scss | 0 .../template-create-dialog.component.ts | 35 +++++ .../template-list.component.html | 93 ++++++++++++ .../template-list.component.scss | 0 .../template-list/template-list.component.ts | 141 ++++++++++++++++++ frontend/src/app/template/template.module.ts | 39 ++++- 12 files changed, 432 insertions(+), 9 deletions(-) create mode 100644 frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html create mode 100644 frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.scss create mode 100644 frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts create mode 100644 frontend/src/app/template/template-create-dialog/template-create-dialog.component.html create mode 100644 frontend/src/app/template/template-create-dialog/template-create-dialog.component.scss create mode 100644 frontend/src/app/template/template-create-dialog/template-create-dialog.component.ts create mode 100644 frontend/src/app/template/template-list/template-list.component.html create mode 100644 frontend/src/app/template/template-list/template-list.component.scss create mode 100644 frontend/src/app/template/template-list/template-list.component.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index b8e21e650..1a44f61f4 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -20,6 +20,7 @@ import { RunAsyncAdminViewComponent } from './run/run-async-admin-view/run-async import { NonescapingUrlserializerClass } from './nonescaping-urlserializer.class'; import {ApiRole} from '../../openapi'; import {TemplateBuilderComponent} from './template/template-builder/template-builder.component'; +import { TemplateListComponent } from "./template/template-list/template-list.component"; /** * The ROUTE for evaluation templates. @@ -35,7 +36,7 @@ const EVALUATION_ROUTE = 'evaluation'; const routes: Routes = [ { path: TEMPLATE_ROUTE+'/list', - component: CompetitionListComponent, + component: TemplateListComponent, canActivate: [AuthenticationGuard], data: { roles: [ApiRole.ADMIN] }, }, diff --git a/frontend/src/app/shared/download-button/download-button.component.html b/frontend/src/app/shared/download-button/download-button.component.html index 4e3920424..3e4e17c0c 100644 --- a/frontend/src/app/shared/download-button/download-button.component.html +++ b/frontend/src/app/shared/download-button/download-button.component.html @@ -3,5 +3,5 @@ diff --git a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html new file mode 100644 index 000000000..a0df660e2 --- /dev/null +++ b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html @@ -0,0 +1,52 @@ +

Start competition run

+
+ +

+ + Name + + +

+ +

+ Participants Can View +

+ +

+ Allow repeated tasks +

+ +

+ + Shuffle Tasks (Async only) + +

+ +

+ + Submission Display Limit + + +

+ +

+ + Type + + + {{ type }} + + + +

+ +
+
+ + +
diff --git a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.scss b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts new file mode 100644 index 000000000..591ff5be7 --- /dev/null +++ b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts @@ -0,0 +1,55 @@ +import { Component } from '@angular/core'; +import { FormControl, FormGroup } from "@angular/forms"; +import { ApiEvaluationType } from "../../../../openapi"; +import { MatDialogRef } from "@angular/material/dialog"; + +export interface EvaluationStartDialogResult { + name: string; + type: ApiEvaluationType; + participantsCanView: boolean; + allowRepeatedTasks: boolean; + shuffleTasks: boolean; + limit: number; +} + +@Component({ + selector: 'app-evaluation-start-dialog', + templateUrl: './evaluation-start-dialog.component.html', + styleUrls: ['./evaluation-start-dialog.component.scss'] +}) +export class EvaluationStartDialogComponent { + + form: FormGroup = new FormGroup({ + name: new FormControl('', {nonNullable: true}), + type: new FormControl('', {nonNullable: true}), + participantsCanView: new FormControl(true), + shuffleTasks: new FormControl(false), + allowRepeatedTasks: new FormControl(false), + limit: new FormControl(0, {nonNullable: true}) + }) + + evaluationTypes: ApiEvaluationType[] = [ApiEvaluationType.SYNCHRONOUS, ApiEvaluationType.ASYNCHRONOUS]; + + typeObservable = this.form.get('type').valueChanges; + + constructor(public dialogRef: MatDialogRef) { + } + + public create(){ + if(this.form.valid){ + this.dialogRef.close({ + name: this.form.get('name').value, + type: this.form.get('type').value, + participantsCanView: this.form.get('participantsCanView').value, + allowRepeatedTasks: this.form.get('allowRepeatedTasks').value, + shuffleTasks: this.form.get('shuffleTasks').value, + limit: this.form.get('limit').value, + } as EvaluationStartDialogResult); + } + } + + public close(){ + this.dialogRef.close(null); + } + +} diff --git a/frontend/src/app/template/template-create-dialog/template-create-dialog.component.html b/frontend/src/app/template/template-create-dialog/template-create-dialog.component.html new file mode 100644 index 000000000..3b6821230 --- /dev/null +++ b/frontend/src/app/template/template-create-dialog/template-create-dialog.component.html @@ -0,0 +1,21 @@ +

New evaluation template

+
+
+

+ + Name + + +

+

+ + Description + + +

+
+
+
+ + +
diff --git a/frontend/src/app/template/template-create-dialog/template-create-dialog.component.scss b/frontend/src/app/template/template-create-dialog/template-create-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-create-dialog/template-create-dialog.component.ts b/frontend/src/app/template/template-create-dialog/template-create-dialog.component.ts new file mode 100644 index 000000000..260845848 --- /dev/null +++ b/frontend/src/app/template/template-create-dialog/template-create-dialog.component.ts @@ -0,0 +1,35 @@ +import { Component } from '@angular/core'; +import { FormControl, FormGroup, UntypedFormControl, UntypedFormGroup } from "@angular/forms"; +import { MatDialogRef } from "@angular/material/dialog"; +import { ApiCreateEvaluation } from "../../../../openapi"; + +@Component({ + selector: 'app-template-create-dialog', + templateUrl: './template-create-dialog.component.html', + styleUrls: ['./template-create-dialog.component.scss'] +}) +export class TemplateCreateDialogComponent { + form: FormGroup = new FormGroup({ + name: new FormControl('', {nonNullable: true}), + description: new FormControl('', {nonNullable: true}), +}); + + participantsCanView = true; + + constructor(public dialogRef: MatDialogRef) { + } + + public create(){ + if(this.form.valid){ + this.dialogRef.close({ + name: this.form.get('name').value, + description: this.form.get('description').value, + } as ApiCreateEvaluation); + } + } + + public close(){ + this.dialogRef.close(null); + } + +} diff --git a/frontend/src/app/template/template-list/template-list.component.html b/frontend/src/app/template/template-list/template-list.component.html new file mode 100644 index 000000000..47f435593 --- /dev/null +++ b/frontend/src/app/template/template-list/template-list.component.html @@ -0,0 +1,93 @@ +
+ +

Evaluation Templates

+
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Actions + + + + + + ID{{ row.id.substring(0, 8) }}Name{{ row.name }}Description{{ row.description }}# Tasks{{ row.taskCount }}# Teams{{ row.teamCount }}
+
diff --git a/frontend/src/app/template/template-list/template-list.component.scss b/frontend/src/app/template/template-list/template-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-list/template-list.component.ts b/frontend/src/app/template/template-list/template-list.component.ts new file mode 100644 index 000000000..6a9979354 --- /dev/null +++ b/frontend/src/app/template/template-list/template-list.component.ts @@ -0,0 +1,141 @@ +import { AfterViewInit, Component } from "@angular/core"; +import { + ApiCreateEvaluation, + ApiEvaluationOverview, ApiEvaluationStartMessage, + DownloadService, + EvaluationAdministratorService, RunProperties, SuccessStatus, + TemplateService +} from "../../../../openapi"; +import { Router } from "@angular/router"; +import { MatDialog } from "@angular/material/dialog"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { TemplateCreateDialogComponent } from "../template-create-dialog/template-create-dialog.component"; +import { filter, flatMap, take, tap } from "rxjs/operators"; +import { + ConfirmationDialogComponent, + ConfirmationDialogComponentData +} from "../../shared/confirmation-dialog/confirmation-dialog.component"; +import { EvaluationStartDialogComponent, EvaluationStartDialogResult } from "../evaluation-start-dialog/evaluation-start-dialog.component"; + +@Component({ + selector: 'app-template-list', + templateUrl: './template-list.component.html', + styleUrls: ['./template-list.component.scss'] +}) +export class TemplateListComponent implements AfterViewInit{ + + + displayedColumns = ['actions', 'id', 'name', 'description', 'nbTasks', 'nbTeams'] + templates: ApiEvaluationOverview[] = []; + waitingForRun = false; + + constructor( + private templateService: TemplateService, + private evaluationAdminService: EvaluationAdministratorService, + private downloadService: DownloadService, + private router: Router, + private dialog: MatDialog, + private snackBar: MatSnackBar + ) { } + + public create(){ + const dialogRef = this.dialog.open(TemplateCreateDialogComponent, {width: '500px'}); + dialogRef + .afterClosed() + .pipe( + filter((r) => r !=null), + flatMap((r: ApiCreateEvaluation) => { + return this.templateService.postApiV2Template(r); + }) + ).subscribe( + (r: SuccessStatus) => { + this.refresh(); + this.snackBar.open(`Success: ${r?.description}`, null, {duration: 5000}); + }, + (err) => { + this.snackBar.open(`Error: ${err?.description}`, null, {duration: 5000}); + } + ) + } + + public refresh(){ + this.templateService.getApiV2TemplateList().subscribe( + (results: ApiEvaluationOverview[]) => { + this.templates = results; + }, + (err) => { + this.templates = []; + this.snackBar.open(`Error: ${err?.error?.description}`, null, {duration: 5000}) + } + ); + } + + public createEvaluation(id: string){ + } + + ngAfterViewInit(): void { + this.refresh(); + } + + public edit(templateId: string){ + this.router.navigate(['/template/builder', templateId]); + } + + public delete(templateId: string){ + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + text: `Do you really want to delete competition with ID ${templateId}?`, + color: 'warn', + } as ConfirmationDialogComponentData, + }); + dialogRef.afterClosed().subscribe((result) => { + if(result){ + this.templateService.deleteApiV2TemplateByTemplateId(templateId).subscribe((r) => { + this.refresh(); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); + }, (r) => { + this.snackBar.open(`Error: ${r.error.description}`,null,{duration: 5000}); + }) + } + }) + } + + public createRun(templateId: string){ + const dialogRef = this.dialog.open(EvaluationStartDialogComponent, {width: '500px'}); + dialogRef.afterClosed().pipe( + filter((r) => r!= null), + tap((r) => (this.waitingForRun = true)), + flatMap((r: EvaluationStartDialogResult) => { + return this.evaluationAdminService.postApiV2EvaluationAdminCreate({ + templateId: templateId, + name: r.name, + type: r.type, + properties: { + participantCanView: r.participantsCanView, + shuffleTasks: r.shuffleTasks, + allowRepeatedTasks: r.allowRepeatedTasks, + limitSubmissionPreviews: r.limit + } as RunProperties // TODO Rename in BE to ApiEvaluationProperties + } as ApiEvaluationStartMessage) + }) + ).subscribe((r: SuccessStatus) => { + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); + this.waitingForRun = false; + },(r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + this.waitingForRun = false; + }); + } + + downloadProvider = (templateId) => { + return this.downloadService.getApiV2DownloadTemplateByTemplateId(templateId).pipe(take(1)); + } + + fileProvider = (name: string) => { + return () => name; + } + + resolveEvaluationOverviewById(_: number, item: ApiEvaluationOverview){ + return `${item}` + } +} diff --git a/frontend/src/app/template/template.module.ts b/frontend/src/app/template/template.module.ts index 41aed6a8d..2927fd088 100644 --- a/frontend/src/app/template/template.module.ts +++ b/frontend/src/app/template/template.module.ts @@ -3,16 +3,41 @@ import {CommonModule} from '@angular/common'; import {MatInputModule} from '@angular/material/input'; import {ReactiveFormsModule} from '@angular/forms'; import {TemplateBuilderModule} from './template-builder/template-builder.module'; +import { TemplateListComponent } from './template-list/template-list.component'; +import { TemplateCreateDialogComponent } from './template-create-dialog/template-create-dialog.component'; +import { MatButtonModule } from "@angular/material/button"; +import { MatDialogModule } from "@angular/material/dialog"; +import { EvaluationStartDialogComponent } from './evaluation-start-dialog/evaluation-start-dialog.component'; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatOptionModule } from "@angular/material/core"; +import { MatSelectModule } from "@angular/material/select"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { SharedModule } from "../shared/shared.module"; +import { MatTableModule } from "@angular/material/table"; +import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; @NgModule({ - declarations: [], - imports: [ - CommonModule, - MatInputModule, - ReactiveFormsModule, - TemplateBuilderModule - ] + declarations: [ + TemplateListComponent, + TemplateCreateDialogComponent, + EvaluationStartDialogComponent + ], + imports: [ + CommonModule, + MatInputModule, + ReactiveFormsModule, + TemplateBuilderModule, + MatButtonModule, + MatDialogModule, + MatCheckboxModule, + MatOptionModule, + MatSelectModule, + MatTooltipModule, + SharedModule, + MatTableModule, + MatProgressSpinnerModule + ] }) export class TemplateModule { } From 6ede5f1c9b2215455238cb02cb60823ffcd59ed5 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 29 Apr 2023 19:10:27 +0200 Subject: [PATCH 277/498] Fixed GetTaskHintHandler not returning external content Still not shown in viewer for some reason though --- .../evaluation/viewer/GetTaskHintHandler.kt | 26 ++++++++++++++----- .../dev/dres/data/model/config/Config.kt | 2 +- .../dres/data/model/template/task/DbHint.kt | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index a4813d332..c41b15c7d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -1,3 +1,4 @@ +import dev.dres.DRES import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.handler.evaluation.viewer.AbstractEvaluationViewerHandler @@ -22,6 +23,7 @@ import kotlinx.dnq.query.asSequence import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files +import java.nio.file.Path import java.nio.file.Paths import java.util.* @@ -124,19 +126,29 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa private fun DbHint.toContentElement(): ApiContentElement { val content = when (this.type) { DbHintType.IMAGE -> { - val item = this.item ?: throw IllegalStateException("DbHint of type IMAGE is expected to hold a valid media item but doesn't! This is a programmer's error!") - val path = this@GetTaskHintHandler.cache.asyncPreviewImage(item).get() /* This should return immediately, since the previews have been prepared. */ - if (Files.exists(path)) { + val path = if (this.item != null) { + this@GetTaskHintHandler.cache.asyncPreviewImage(this.item!!).get() /* This should return immediately, since the previews have been prepared. */ + } else { + DRES.EXTERNAL_ROOT.resolve( + this.path ?: throw IllegalStateException("DbHint of type IMAGE is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") + ) + } + if (Files.exists(path)) { Base64.getEncoder().encodeToString(Files.readAllBytes(path)) } else { null } } DbHintType.VIDEO -> { - val item = this.item ?: throw IllegalStateException("DbHint of type IMAGE is expected to hold a valid media item but doesn't! This is a programmer's error!") - val start = this.temporalRangeStart ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid start timestamp but doesn't! This is a programmer's error!") - val end = this.temporalRangeEnd ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid end timestamp but doesn't!! This is a programmer's error!") - val path = this@GetTaskHintHandler.cache.asyncPreviewVideo(item, start, end).get() /* This should return immediately, since the previews have been prepared. */ + val path = if (this.item != null) { + val start = this.temporalRangeStart ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid start timestamp but doesn't! This is a programmer's error!") + val end = this.temporalRangeEnd ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid end timestamp but doesn't!! This is a programmer's error!") + this@GetTaskHintHandler.cache.asyncPreviewVideo(this.item!!, start, end).get() /* This should return immediately, since the previews have been prepared. */ + } else { + DRES.EXTERNAL_ROOT.resolve( + this.path ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") + ) + } if (Files.exists(path)) { Base64.getEncoder().encodeToString(Files.readAllBytes(path)) } else { diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index de94ecfea..636a6f5e2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -13,7 +13,7 @@ data class Config( val enableSsl: Boolean = true, val keystorePath: String = "keystore.jks", val keystorePassword: String = "password", - val cache: CacheConfig= CacheConfig() + val cache: CacheConfig = CacheConfig() ) { companion object{ diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index f5e189c73..e629ae7c9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -36,7 +36,7 @@ class DbHint(entity: Entity) : XdEntity(entity) { /** The target text. Can be null. */ var text by xdStringProp { requireIf { type == DbHintType.TEXT }} - /** The target text. Can be null. */ + /** The target path. Can be null. */ var path by xdStringProp() { requireIf { (type == DbHintType.VIDEO || type == DbHintType.IMAGE) && item == null }} /** The start of a (potential) range. */ From dc2dda9b7e5098af41ab23039a7651d26aa3832f Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 1 May 2023 15:14:06 +0200 Subject: [PATCH 278/498] Added missing permitation logic to async evaluation --- .../dev/dres/api/cli/EvaluationCommand.kt | 4 +- .../admin/CreateEvaluationHandler.kt | 3 +- .../evaluation/viewer/GetTaskHintHandler.kt | 6 +- .../dev/dres/data/model/run/DbEvaluation.kt | 93 ++++++++++++++++++- .../run/InteractiveAsynchronousEvaluation.kt | 78 +--------------- .../run/InteractiveSynchronousEvaluation.kt | 14 --- .../run/InteractiveAsynchronousRunManager.kt | 87 +++++++++-------- .../main/kotlin/dev/dres/run/RunExecutor.kt | 2 +- 8 files changed, 154 insertions(+), 133 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index f82bda611..4cdd302e8 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -147,7 +147,7 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom paddingRight = 1 } header { - row("id", "name", "description", "lastTask", "status", "start", "end") + row("id", "name", "description", "type", "lastTask", "status", "start", "end") } body { query.asSequence().forEach { @@ -155,11 +155,13 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom it.id, it.name, it.template.description, + it.type.description, if (it.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { it.tasks.firstOrNull()?.template?.name ?: "N/A" } else { "N/A" }, + it.status.description, it.started?.toDateString() ?: "-", it.ended?.toDateString() ?: "-", ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 278b1a96c..d5079d942 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -105,11 +105,12 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca this.participantCanView = message.properties.participantCanView this.shuffleTasks = message.properties.shuffleTasks this.limitSubmissionPreviews = message.properties.limitSubmissionPreviews + initPermutation() } /* Try to flush change prior to scheduling it. */ RunExecutor.schedule(when (message.type) { - ApiEvaluationType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation, emptyMap()) /* TODO: Team map */ + ApiEvaluationType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation) ApiEvaluationType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) ApiEvaluationType.NON_INTERACTIVE -> TODO() }, this.store) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index c41b15c7d..2724b3dba 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -134,7 +134,11 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa ) } if (Files.exists(path)) { - Base64.getEncoder().encodeToString(Files.readAllBytes(path)) + if (path.toString().endsWith(".jpg", ignoreCase = true)) { + Base64.getEncoder().encodeToString(Files.readAllBytes(path)) + } else { //convert to jpg + null + } } else { null } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 4e72df2c6..905370ab4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -1,14 +1,17 @@ package dev.dres.data.model.run +import com.fasterxml.jackson.module.kotlin.* import dev.dres.api.rest.types.evaluation.ApiEvaluation import dev.dres.data.model.PersistentEntity import dev.dres.data.model.run.interfaces.Evaluation import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun +import dev.dres.data.model.template.team.TeamId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.size /** * Represents a [DbEvaluation], i.e., a concrete instance of a [DbEvaluationTemplate], as executed by DRES. @@ -28,7 +31,9 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { /** The [EvaluationId] of this [DbEvaluation]. */ override var evaluationId: EvaluationId get() = this.id - set(value) { this.id = value } + set(value) { + this.id = value + } /** The name held by this [DbEvaluation]. Must be unique!*/ var name by xdRequiredStringProp(trimmed = true) @@ -52,7 +57,7 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { var ended by xdNullableLongProp() /** The [DbTask]s that belong to this [DbEvaluation]. */ - val tasks by xdChildren0_N(DbTask::evaluation) + val tasks by xdChildren0_N(DbTask::evaluation) /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ var participantCanView by xdBooleanProp() @@ -66,6 +71,86 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { /** A fixed limit on submission previews. */ var limitSubmissionPreviews by xdIntProp() + /** A serialized representation of the task permutation map used for [InteractiveAsynchronousEvaluation]s */ + var taskPermutationString by xdStringProp() + + fun permutation() : Map>? { + val mapper = jacksonObjectMapper() + + return if (taskPermutationString != null) { + try { + mapper.readValue>>(taskPermutationString!!) + } catch (e: Exception) { //parsing failed for some reason TODO log + null + } + } else { + null + } + } + + fun initPermutation() { + val mapper = jacksonObjectMapper() + val permutation = generatePermutation() + taskPermutationString = mapper.writeValueAsString(permutation) + } + + private fun generatePermutation(): Map> = + if (shuffleTasks) { + template.teams.asSequence().associate { it.id to makeLoop(template.tasks.size()) } + } else { + template.teams.asSequence().associate { it.id to template.tasks.asSequence().toList().indices.toList() } + } + + /** + * generates a sequence of tasks that loops through all tasks exactly once + */ + private fun makeLoop(length: Int): List { + if (length <= 0) { + return emptyList() + } + val positions = (0 until length).shuffled() + val array = IntArray(length) { -1 } + + fun recursionStep(open: List, idx: Int): Boolean { + + //nothing left to do + if (open.isEmpty()) { + return true + } + + //invalid state, need to backtrack + if (array[idx] != -1) { + return false + } + + //for all remaining options... + for (nextPosition in open) { + //...assign the next one... + array[idx] = nextPosition + //...and continue recursively + if (recursionStep( + (open - nextPosition), //without the last assigned value + (nextPosition + 1) % array.size + ) //at the index after the last assigned position + ) { + //assignment succeeded + return true + } + } + + //there was no valid assignment in the given options, need to back track + array[idx] = -1 + return false + } + + if (!recursionStep(positions, 0)) { + error("Error during generation of task sequence") + } + + return array.toList() + } + + /** * Converts this [DbEvaluation] to a RESTful API representation [ApiEvaluation]. * @@ -89,9 +174,9 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { * * @return [EvaluationRun] */ - fun toRun(): EvaluationRun = when(this.type) { + fun toRun(): EvaluationRun = when (this.type) { DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) - DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this, emptyMap()) /* TODO: Not sure about semantics here. */ + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this) DbEvaluationType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) else -> throw IllegalArgumentException("Unsupported run type ${this.type.description}. This is a programmer's error!") } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index fa817b4ec..aa1cea152 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -32,82 +32,14 @@ import java.util.concurrent.ConcurrentHashMap * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [IATaskRun]s and access the current [IATaskRun]. * */ -class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val permutation: Map>) : AbstractEvaluation(evaluation) { +class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { - companion object { - fun generatePermutation(description: DbEvaluationTemplate, shuffle: Boolean): Map> = - if (shuffle) { - description.teams.asSequence().associate { it.id to makeLoop(description.tasks.size()) } - } else { - description.teams.asSequence().associate { it.id to description.tasks.asSequence().toList().indices.toList() } - } - - /** - * generates a sequence of tasks that loops through all tasks exactly once - */ - private fun makeLoop(length: Int): List { - if (length <= 0) { - return emptyList() - } - val positions = (0 until length).shuffled() - val array = IntArray(length) { -1 } + private val permutation: Map> - fun recursionStep(open: List, idx: Int): Boolean { - - //nothing left to do - if (open.isEmpty()) { - return true - } - - //invalid state, need to backtrack - if (array[idx] != -1) { - return false - } - - //for all remaining options... - for (nextPosition in open) { - //...assign the next one... - array[idx] = nextPosition - //...and continue recursively - if (recursionStep( - (open - nextPosition), //without the last assigned value - (nextPosition + 1) % array.size - ) //at the index after the last assigned position - ) { - //assignment succeeded - return true - } - } - - //there was no valid assignment in the given options, need to back track - array[idx] = -1 - return false - } - - if (!recursionStep(positions, 0)) { - error("Error during generation of task sequence") - } - - return array.toList() - } + init { + this.permutation = evaluation.permutation()!! } - /** - * Internal constructor to create an [InteractiveAsynchronousEvaluation] from an [DbEvaluationTemplate]. - * Requires a transaction context! - * - * @param name The name of the new [InteractiveSynchronousEvaluation] - * @param shuffle Flag indicating if [IATaskRun]s should be shuffled. - * @param template The [DbEvaluationTemplate] - */ - constructor(name: String, shuffle: Boolean, template: DbEvaluationTemplate) : this(DbEvaluation.new { - this.type = DbEvaluationType.INTERACTIVE_ASYNCHRONOUS - this.name = name - this.template = template - this.shuffleTasks = shuffle - this.started = System.currentTimeMillis() - }, generatePermutation(template, shuffle)) - /** A [List] of all active [IATaskRun]s.*/ override val tasks: List get() = this.tasksMap.values.flatten() @@ -135,7 +67,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation, private val pe } fun goTo(teamId: TeamId, index: Int) { - this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).single() + this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).first() } fun currentTaskDescription(teamId: TeamId): DbTaskTemplate = diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 98c92e1dc..2de8aac27 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -45,20 +45,6 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } - /** - * Internal constructor to create an [InteractiveSynchronousEvaluation] from an [DbEvaluationTemplate]. - * - * Requires a transaction context! - * - * @param name The name of the new [InteractiveSynchronousEvaluation] - * @param template The [DbEvaluationTemplate] - */ - constructor(name: String, template: DbEvaluationTemplate) : this(DbEvaluation.new { - this.type = DbEvaluationType.INTERACTIVE_SYNCHRONOUS - this.template = template - this.name = name - }) - /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ override val tasks = LinkedList() diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 65f78e44e..313820f42 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -109,34 +109,33 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) - this.store.transactional(true) { - this.template.teams.asSequence().forEach { - val teamContext = RunActionContext("", it.teamId, setOf(DbRole.ADMIN)) - this.updatables.add(EndTaskUpdatable(this, teamContext)) - - /* Initialize map and set all tasks pointers to the first task. */ - this.statusMap[it.teamId] = if (this.evaluation.hasStarted) { - RunManagerStatus.ACTIVE - } else { - RunManagerStatus.CREATED - } + this.template.teams.asSequence().forEach { + val teamContext = RunActionContext("", it.teamId, setOf(DbRole.ADMIN)) + this.updatables.add(EndTaskUpdatable(this, teamContext)) + + /* Initialize map and set all tasks pointers to the first task. */ + this.statusMap[it.teamId] = if (this.evaluation.hasStarted) { + RunManagerStatus.ACTIVE + } else { + RunManagerStatus.CREATED + } - /** End ongoing runs upon initialization (in case server crashed during task execution). */ - if (this.evaluation.tasksForTeam(it.teamId).lastOrNull()?.isRunning == true) { - this.evaluation.tasksForTeam(it.teamId).last().end() - } + /** End ongoing runs upon initialization (in case server crashed during task execution). */ + if (this.evaluation.tasksForTeam(it.teamId).lastOrNull()?.isRunning == true) { + this.evaluation.tasksForTeam(it.teamId).last().end() } + } - /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ - this.evaluation.tasks.forEach { task -> - task.getSubmissions().forEach { sub -> - this.scoresUpdatable.enqueue(task) - sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.asSequence().forEach { - task.validator.validate(it) - } + /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ + this.evaluation.tasks.forEach { task -> + task.getSubmissions().forEach { sub -> + this.scoresUpdatable.enqueue(task) + sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.forEach { + task.validator.validate(it) } } } + } /** @@ -673,29 +672,41 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * i.e., status updates that are not triggered by an outside interaction. */ private fun internalStateUpdate() = this.stateLock.read { - for (team in this.evaluation.description.teams.asSequence()) { - if (teamHasRunningTask(team.teamId)) { - this.stateLock.write { - this.store.transactional { - val task = this.evaluation.currentTaskForTeam(team.teamId) ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") - val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) + this.store.transactional { + for (team in this.evaluation.description.teams.asSequence()) { //FIXME template was removed?! + if (teamHasRunningTask(team.teamId)) { + this.stateLock.write { + + val task = this.evaluation.currentTaskForTeam(team.teamId) + ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") + val timeLeft = max( + 0L, + task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION + ) if (timeLeft <= 0) { task.end() DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(team.teamId, ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId)) + RunExecutor.broadcastWsMessage( + team.teamId, + ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId) + ) } + } - } - } else if (teamHasPreparingTask(team.teamId)) { - this.stateLock.write { - this.store.transactional { - val task = this.evaluation.currentTaskForTeam(team.teamId) - ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") - task.start() - DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) - RunExecutor.broadcastWsMessage(team.teamId, ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId)) + } else if (teamHasPreparingTask(team.teamId)) { + this.stateLock.write { + this.store.transactional { + val task = this.evaluation.currentTaskForTeam(team.teamId) + ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") + task.start() + DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + RunExecutor.broadcastWsMessage( + team.teamId, + ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) + ) + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 7c32d20f1..3e256c5f3 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -78,7 +78,7 @@ object RunExecutor : Consumer { this.schedule(when (e.type) { DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(e) - DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(e, emptyMap()) /* TODO: Team map. */ + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(e) DbEvaluationType.NON_INTERACTIVE -> TODO() else -> throw IllegalStateException("Unsupported evaluation type ${e.type}.") }, store) From c5fcf37282d8c525da0e0451c0e2444914b696e6 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 1 May 2023 15:56:12 +0200 Subject: [PATCH 279/498] Keeping list of teamIds in InteractiveAsynchronousRunManager rather than re-querying all the time --- .../run/InteractiveAsynchronousRunManager.kt | 137 ++++++++++++------ 1 file changed, 89 insertions(+), 48 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 313820f42..78cac0c66 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -42,7 +42,10 @@ import kotlin.math.max * @version 1.0.0 * @author Ralph Gasser */ -class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsynchronousEvaluation, override val store: TransientEntityStore): InteractiveRunManager { +class InteractiveAsynchronousRunManager( + override val evaluation: InteractiveAsynchronousEvaluation, + override val store: TransientEntityStore +) : InteractiveRunManager { companion object { private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) @@ -52,7 +55,14 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ override val runProperties: RunProperties - get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) + get() = RunProperties( + this.evaluation.participantCanView, + false, + this.evaluation.allowRepeatedTasks, + this.evaluation.limitSubmissionPreviews + ) + + private val teamIds = this.evaluation.description.teams.asSequence().map { it.teamId }.toList() /** Tracks the current [DbTaskTemplate] per [TeamId]. */ private val statusMap: MutableMap = HashMap() @@ -109,20 +119,20 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) - this.template.teams.asSequence().forEach { - val teamContext = RunActionContext("", it.teamId, setOf(DbRole.ADMIN)) + this.teamIds.forEach { + val teamContext = RunActionContext("", it, setOf(DbRole.ADMIN)) this.updatables.add(EndTaskUpdatable(this, teamContext)) /* Initialize map and set all tasks pointers to the first task. */ - this.statusMap[it.teamId] = if (this.evaluation.hasStarted) { + this.statusMap[it] = if (this.evaluation.hasStarted) { RunManagerStatus.ACTIVE } else { RunManagerStatus.CREATED } /** End ongoing runs upon initialization (in case server crashed during task execution). */ - if (this.evaluation.tasksForTeam(it.teamId).lastOrNull()?.isRunning == true) { - this.evaluation.tasksForTeam(it.teamId).last().end() + if (this.evaluation.tasksForTeam(it).lastOrNull()?.isRunning == true) { + this.evaluation.tasksForTeam(it).last().end() } } @@ -283,7 +293,14 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE, this.evaluation.currentTaskForTeam(context.teamId)?.taskId)) + RunExecutor.broadcastWsMessage( + context.teamId, + ServerMessage( + this.id, + ServerMessageType.COMPETITION_UPDATE, + this.evaluation.currentTaskForTeam(context.teamId)?.taskId + ) + ) LOGGER.info("SynchronousRunManager ${this.id} set to task $idx") @@ -309,7 +326,9 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn val currentTaskTemplate = this.evaluation.currentTaskDescription(context.teamId) /* Check for duplicate task runs */ - if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasksForTeam(context.teamId).any { it.template.id == currentTaskTemplate.id }) { + if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasksForTeam(context.teamId) + .any { it.template.id == currentTaskTemplate.id } + ) { throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used") } @@ -320,7 +339,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId)) + RunExecutor.broadcastWsMessage( + context.teamId, + ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId) + ) LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") } @@ -341,14 +363,18 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } /* End TaskRun and update status. */ - val currentTask = this.currentTask(context) ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") + val currentTask = this.currentTask(context) + ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") currentTask.end() /* Mark scoreboards and DAO for update. */ this.scoreboardsUpdatable.dirty = true /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId)) + RunExecutor.broadcastWsMessage( + context.teamId, + ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId) + ) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } @@ -433,10 +459,11 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * @param context The [RunActionContext] used for the invocation. * @return [InteractiveAsynchronousEvaluation.IATaskRun] that is currently active or null, if no such task is active. */ - override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = this.stateLock.read { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.evaluation.currentTaskForTeam(context.teamId) - } + override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = + this.stateLock.read { + require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } + return this.evaluation.currentTaskForTeam(context.teamId) + } /** * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. @@ -502,10 +529,13 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn override fun postSubmission(context: RunActionContext, submission: ApiSubmission) = this.stateLock.read { require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } - require(submission.answerSets().count() == 1) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ + require( + submission.answerSets().count() == 1 + ) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ /* Register submission. */ - val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") + val task = this.currentTask(context) + ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") /* Sanity check. */ check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } @@ -535,7 +565,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(context.teamId, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) + RunExecutor.broadcastWsMessage( + context.teamId, + ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId) + ) } @@ -560,7 +593,11 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * * @return Whether the update was successful or not */ - override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: DbVerdictStatus): Boolean = this.stateLock.read { + override fun updateSubmission( + context: RunActionContext, + submissionId: SubmissionId, + submissionStatus: DbVerdictStatus + ): Boolean = this.stateLock.read { val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false val task = this.taskForId(context, answerSet.task.id) ?: return false @@ -572,7 +609,10 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(context.teamId!!, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) + RunExecutor.broadcastWsMessage( + context.teamId!!, + ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId) + ) return true } @@ -654,17 +694,21 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ private fun invokeUpdatables() = this.stateLock.read { - this.statusMap.values.toSet().forEach { status -> //call update once for every possible status which is currently set for any team - this.updatables.forEach { - if (it.shouldBeUpdated(status)) { - try { - it.update(status) - } catch (e: Throwable) { - LOGGER.error("Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) + this.statusMap.values.toSet() + .forEach { status -> //call update once for every possible status which is currently set for any team + this.updatables.forEach { + if (it.shouldBeUpdated(status)) { + try { + it.update(status) + } catch (e: Throwable) { + LOGGER.error( + "Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", + e + ) + } } } } - } } /** @@ -672,13 +716,13 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn * i.e., status updates that are not triggered by an outside interaction. */ private fun internalStateUpdate() = this.stateLock.read { - this.store.transactional { - for (team in this.evaluation.description.teams.asSequence()) { //FIXME template was removed?! - if (teamHasRunningTask(team.teamId)) { - this.stateLock.write { - val task = this.evaluation.currentTaskForTeam(team.teamId) - ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") + for (teamId in teamIds) { + this.store.transactional { + if (teamHasRunningTask(teamId)) { //FIXME DbTaskTemplate[...] was removed. + this.stateLock.write { + val task = this.evaluation.currentTaskForTeam(teamId) + ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") val timeLeft = max( 0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION @@ -689,24 +733,21 @@ class InteractiveAsynchronousRunManager(override val evaluation: InteractiveAsyn /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( - team.teamId, + teamId, ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId) ) } - } - } else if (teamHasPreparingTask(team.teamId)) { + } else if (teamHasPreparingTask(teamId)) { this.stateLock.write { - this.store.transactional { - val task = this.evaluation.currentTaskForTeam(team.teamId) - ?: throw IllegalStateException("Could not find active task for team ${team.teamId} despite status of the team being ${this.statusMap[team.teamId]}. This is a programmer's error!") - task.start() - DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) - RunExecutor.broadcastWsMessage( - team.teamId, - ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) - ) - } + val task = this.evaluation.currentTaskForTeam(teamId) + ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") + task.start() + DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + RunExecutor.broadcastWsMessage( + teamId, + ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) + ) } } } From c2325e5b39ae977f588a1f4880644c35ac900718 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 2 May 2023 11:15:12 +0200 Subject: [PATCH 280/498] Limiting file endings considered by ListExternalItemHandler --- .../handler/collection/ListExternalItemHandler.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index e7f1f2190..00327d7be 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -7,6 +7,7 @@ import io.javalin.http.Context import io.javalin.openapi.* import java.nio.file.FileVisitOption import java.nio.file.Files +import kotlin.io.path.name import kotlin.streams.toList /** @@ -17,7 +18,7 @@ import kotlin.streams.toList /** * Lists and returns the media items in the external media item directory. */ -class ListExternalItemHandler: GetRestHandler> { +class ListExternalItemHandler : GetRestHandler> { override val apiVersion = "v2" @@ -37,14 +38,12 @@ class ListExternalItemHandler: GetRestHandler> { OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): Array { + override fun doGet(ctx: Context): List { // TODO https://github.com/javalin/javalin-openapi/issues/178 Apparently, we cannot use the slash-included notation here (https://javalin.io/documentation#endpoint-handlers) val startsWith = ctx.pathParamMap()["startsWith"] ?: "" - val list = Files.walk(DRES.EXTERNAL_ROOT, 1, FileVisitOption.FOLLOW_LINKS).filter { - Files.isRegularFile(it) && it.toFile().name.startsWith(startsWith) - - }.limit(50).map { it.toFile().name }.toList() - return list.toTypedArray() + return Files.walk(DRES.EXTERNAL_ROOT, 1, FileVisitOption.FOLLOW_LINKS).filter { + Files.isRegularFile(it) && it.name.startsWith(startsWith) && (it.name.endsWith(".jpg", ignoreCase = true) || it.name.endsWith(".mkv", ignoreCase = true) || it.name.endsWith(".mp4", ignoreCase = true)) + }.sorted { o1, o2 -> o1.name.length - o2.name.length }.limit(50).map { it.toFile().name }.toList() } override val route: String = "external/{startsWith}" From 617f7cf9a5f3e91b426456ed76b162911854b98f Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 4 May 2023 10:40:04 +0200 Subject: [PATCH 281/498] Bumped log4j slf4j adapter Bumped log4j version --- backend/build.gradle | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 27f86eb11..39d253cdf 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -98,7 +98,7 @@ dependencies { ////// Log4J implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: version_log4j implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: version_log4j - implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: version_log4j + implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j2-impl', version: version_log4j implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: version_log4j ////// FastUtil diff --git a/gradle.properties b/gradle.properties index f2ddb277b..74aebd7a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ version_jline3=3.21.0 version_junit=5.9.1 version_kotlin=1.8.10 version_kotlin_csv=1.6.0 -version_log4j=2.18.0 +version_log4j=2.20.0 version_picnic=0.6.0 version_xodus=2.0.1 version_xodus_dnq=2.0.0 From 3bcd05f9678d6adbaba3cc056fc5617c19d2014f Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 4 May 2023 10:45:51 +0200 Subject: [PATCH 282/498] Adjusted layout in task template editor --- .../task-template-editor.component.html | 80 ++++++++++--------- .../task-template-editor.component.ts | 6 +- frontend/src/flex.scss | 9 ++- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 4bce6c074..cbd036d44 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -17,19 +17,19 @@
-
-

- +

+ Name - + - + Duration [s] @@ -50,16 +50,14 @@

Target - + - - + + -->

-
-
-

+

+ +
+

-

- +

+ + [formControl]="target.get('segment_start')" /> - + [formControl]="target.get('segment_end')" /> - + - + +

>

- -
+
+
- @@ -516,14 +520,14 @@

- - + + - + Date: Sun, 7 May 2023 18:28:45 +0200 Subject: [PATCH 283/498] Implemented some more logic for non-interactive evaluations --- .../data/model/run/AbstractInteractiveTask.kt | 13 ----- .../dev/dres/data/model/run/AbstractTask.kt | 17 ++++++ .../model/run/NonInteractiveEvaluation.kt | 30 +++++++++- .../dev/dres/run/NonInteractiveRunManager.kt | 57 ++++++++++++++++++- .../dres/run/updatables/ScoresUpdatable.kt | 5 +- 5 files changed, 104 insertions(+), 18 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index da89e5526..c0e0ffc76 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -28,11 +28,6 @@ abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override abstract var duration: Long - /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ - val teamGroupAggregators: Map by lazy { - this.competition.description.teamGroups.asSequence().associate { it.id to it.newAggregator() } - } - /** The [AnswerSetValidator] used to validate [DbSubmission]s. */ final override val validator: AnswerSetValidator @@ -61,12 +56,4 @@ abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { } } - /** - * Updates the per-team aggregation for this [AbstractInteractiveTask]. - * - * @param teamScores Map of team scores. - */ - internal fun updateTeamAggregation(teamScores: Map) { - this.teamGroupAggregators.values.forEach { it.aggregate(teamScores) } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 0a5323fa1..34caceb10 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -6,6 +6,9 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.team.TeamAggregatorImpl +import dev.dres.data.model.template.team.TeamGroupId +import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.SubmissionFilter import dev.dres.run.transformer.SubmissionTransformer import dev.dres.run.validation.interfaces.AnswerSetValidator @@ -146,4 +149,18 @@ abstract class AbstractTask(task: DbTask): TaskRun { }.mapDistinct { it.submission }.asSequence() + + /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ + val teamGroupAggregators: Map by lazy { + this.competition.description.teamGroups.asSequence().associate { it.id to it.newAggregator() } + } + + /** + * Updates the per-team aggregation for this [AbstractInteractiveTask]. + * + * @param teamScores Map of team scores. + */ + internal fun updateTeamAggregation(teamScores: Map) { + this.teamGroupAggregators.values.forEach { it.aggregate(teamScores) } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 3ad39ce6d..6f231f24a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -4,13 +4,19 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun +import dev.dres.data.model.template.task.options.DbConfiguredOption +import dev.dres.data.model.template.task.options.DbScoreOption import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.TeamId +import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter +import dev.dres.run.filter.SubmissionFilterAggregator import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard +import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.CachingTaskScorer +import dev.dres.run.score.scorer.KisTaskScorer import dev.dres.run.score.scorer.TaskScorer import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchFilter @@ -65,7 +71,13 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev get() = this@NonInteractiveEvaluation.tasks.indexOf(this) /** The [CachingTaskScorer] instance used by this [NITaskRun]. */ - override val scorer: CachingTaskScorer = TODO("Will we have the same scorers for non-interactive tasks.") + override val scorer: CachingTaskScorer = CachingTaskScorer( + when(val scoreOption = this.template.taskGroup.type.score) { + DbScoreOption.KIS -> throw IllegalStateException("KIS task scorer is not applicable to non-interactive evaluations") + DbScoreOption.AVS -> AvsTaskScorer(this) + else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") + } + ) override val transformer: SubmissionTransformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { SubmissionTransformerAggregator( @@ -79,7 +91,21 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev } /** The [SubmissionFilter] instance used by this [NITaskRun]. */ - override val filter: SubmissionFilter = TODO("Can there be submission filters for non-interactive tasks?") + override val filter: SubmissionFilter; + + init{ + if (this.template.taskGroup.type.submission.isEmpty) { + this.filter = AllSubmissionFilter + } else { + this.filter = SubmissionFilterAggregator( + this.template.taskGroup.type.submission.asSequence().map { option -> + val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() + ) + } + } /** List of [TeamId]s that work on this [NITaskRun]. */ override val teams: List = this@NonInteractiveEvaluation.description.teams.asSequence().map { it.teamId }.toList() diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 8e5fbcdac..2301522ba 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -9,8 +9,10 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.* import dev.dres.data.model.template.team.TeamId +import dev.dres.run.filter.SubmissionRejectedException import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable +import dev.dres.run.updatables.ScoresUpdatable import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore import org.slf4j.LoggerFactory @@ -18,6 +20,7 @@ import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read +import kotlin.concurrent.write class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation, override val store: TransientEntityStore) : RunManager { @@ -44,6 +47,9 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation override val template: DbEvaluationTemplate get() = this.evaluation.description + /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ + private val scoresUpdatable = ScoresUpdatable(this) + /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) //TODO requires some changes @@ -152,7 +158,7 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation } - Thread.sleep(100) + Thread.sleep(1000) } @@ -167,7 +173,56 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation */ override fun tasks(context: RunActionContext): List = this.evaluation.tasks + private val taskMap = this.evaluation.tasks.associateBy { it.taskId } + override fun postSubmission(context: RunActionContext, submission: ApiSubmission) { + + val submissionByTask = submission.answers.groupBy { it.taskId }.mapValues { submission.copy(answers = it.value) } + + if (submissionByTask.keys.any { !taskMap.containsKey(it) }) { + throw IllegalStateException("Unknown task") + } + + this.stateLock.write { + + submissionByTask.forEach { (taskId, submission) -> + + val task = taskMap[taskId] ?: throw IllegalStateException("Unknown task $taskId") + + try{ + + /* Check if ApiSubmission meets formal requirements. */ + task.filter.acceptOrThrow(submission) + + /* Apply transformations to submissions */ + val transformedSubmission = task.transformer.transform(submission) + + /* Check if there are answers left after transformation */ + if (transformedSubmission.answers.isEmpty()) { + return@forEach + } + + /* At this point, the submission is considered valid and is persisted */ + /* Validator is applied to each answer set */ + transformedSubmission.answerSets().forEach { + task.validator.validate(it) + } + + /* Persist the submission. */ + transformedSubmission.toNewDb() + + /* Enqueue submission for post-processing. */ + this.scoresUpdatable.enqueue(task) + + } catch (e: SubmissionRejectedException) { + //TODO give feedback about parts of submissions that have been rejected somehow + } + + + } + + } + TODO("Not yet implemented") } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index c948b5429..be2daf6a2 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -3,6 +3,7 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask +import dev.dres.data.model.run.AbstractTask import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet @@ -31,7 +32,7 @@ class ScoresUpdatable(private val manager: RunManager): Updatable { } /** Internal list of [DbAnswerSet] that pend processing. */ - private val set = LinkedHashSet() + private val set = LinkedHashSet() /** The [Phase] this [ScoresUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN @@ -42,7 +43,7 @@ class ScoresUpdatable(private val manager: RunManager): Updatable { * @param task The [AbstractInteractiveTask] tha requires score re-calculation. */ @Synchronized - fun enqueue(task: AbstractInteractiveTask) = this.set.add(task) + fun enqueue(task: AbstractTask) = this.set.add(task) @Synchronized override fun update(status: RunManagerStatus) { From b5696d8007da5c502ba2e6a6f9e6282804f182fa Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 7 May 2023 20:19:25 +0200 Subject: [PATCH 284/498] Added client swagger UI back --- .../dev/dres/api/rest/ClientOpenApiPlugin.kt | 9 ++++++- .../dev/dres/api/rest/ClientSwaggerPlugin.kt | 26 ++++++++++++++----- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 4 +++ .../main/kotlin/dev/dres/run/RunExecutor.kt | 2 +- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt index 9f54fc324..dad3b4199 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt @@ -20,7 +20,14 @@ class ClientOpenApiPlugin : OpenApiPlugin(OpenApiConfiguration().apply { "/mediaItem", "/score", "/user/list", - "/user/session/" + "/user/session/", + "/evaluation/admin", + "/evaluation/template", + "/evaluation/{evaluationId}/judge", + "/evaluation/{evaluationId}/vote", + "/download", + "/mediaitem", + "/template" ) val relevantRoutes = doc["paths"].fields().asSequence().filter { blacklist.none { b -> it.key.contains(b) } }.map { it.key }.toList() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt index 583fdee3f..e6bf97a24 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt @@ -1,11 +1,25 @@ package dev.dres.api.rest +import io.javalin.Javalin import io.javalin.openapi.plugin.swagger.SwaggerConfiguration +import io.javalin.openapi.plugin.swagger.SwaggerHandler import io.javalin.openapi.plugin.swagger.SwaggerPlugin +import io.javalin.plugin.Plugin -class ClientSwaggerPlugin : SwaggerPlugin( - SwaggerConfiguration().apply { - this.documentationPath = "/client-oas" - this.uiPath = "/swagger-client" - this.version = "4.10.3" - }) +class ClientSwaggerPlugin : Plugin { + override fun apply(app: Javalin) { + val swaggerHandler = SwaggerHandler( + title = "DRES Client API", + documentationPath = "/client-oas", + swaggerVersion = "4.10.3", + validatorUrl = "https://validator.swagger.io/validator", + routingPath = app.cfg.routing.contextPath, + basePath = null, + tagsSorter = "'alpha'", + operationsSorter = "'alpha'" + ) + + app.get("/swagger-client", swaggerHandler) + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index a406f6faa..502731a3a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -239,6 +239,10 @@ object RestApi { ) ) + it.plugins.register( + ClientSwaggerPlugin() + ) + it.http.defaultContentType = "application/json" it.http.prefer405over404 = true it.jetty.server { setupHttpServer() } diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 3e256c5f3..be2a5529d 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -276,7 +276,7 @@ object RunExecutor : Consumer { * * @param competition [EvaluationRun] that should be dumped. */ - fun dump(competition: EvaluationRun) { + fun dump(competition: EvaluationRun) { //FIXME can no longer properly dump things, since they refer to dnq objects try { val file = File("run_dump_${competition.id}.json") jacksonObjectMapper().writeValue(file, competition) From 332e36c165f0a327b638aa064ea8c47daffc45bc Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 8 May 2023 08:46:15 +0200 Subject: [PATCH 285/498] Removed shadow for viewer list --- .../competition-list/competition-create-dialog.component.ts | 3 +++ .../competition-list/competition-list.component.ts | 3 +++ .../competition-list/competition-start-dialog.component.ts | 3 +++ frontend/src/app/run/run-admin-view.component.html | 5 ++--- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts index 27dfff45e..3cdc2db5c 100644 --- a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts +++ b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts @@ -3,6 +3,9 @@ import { MatDialogRef } from '@angular/material/dialog'; import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import {ApiCreateEvaluation} from '../../../../openapi'; +/** + * @deprecated Replaced by TemplateCreateDialog + */ @Component({ selector: 'app-competition-create-dialog', templateUrl: 'competition-create-dialog.component.html', diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts index 79a274ff3..a99235770 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ b/frontend/src/app/competition/competition-list/competition-list.component.ts @@ -17,6 +17,9 @@ import { TemplateService } from '../../../../openapi'; +/** + * @deprecated Replaced by TemplateList + */ @Component({ selector: 'app-competition-list', templateUrl: './competition-list.component.html', diff --git a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts index e7b836110..15c01d6d1 100644 --- a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts +++ b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts @@ -12,6 +12,9 @@ export interface CompetitionStartDialogResult { limit: Number; } +/** + * @deprecated Replaced by TemplateStart + */ @Component({ selector: 'app-competition-start-dialog', templateUrl: 'competition-start-dialog.component.html', diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 828c8482a..24d2fe002 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -70,13 +70,12 @@ *ngFor="let viewer of viewers | async; let i = index; trackBy: resolveViewerById" [style.color]="viewer.ready ? 'darkgreen' : 'darkred'" (click)="forceViewer(viewer.viewersId)" - class="outline-white" >
- {{ viewer.viewersId }} + {{ viewer.viewersId }}
- User: {{ viewer.username }}, Address: {{ viewer.host }} + User: {{ viewer.username }}, Address: {{ viewer.host }}
From d75b2b11c1edd904511659ab1ac3ee854dabbc34 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 10:43:52 +0200 Subject: [PATCH 286/498] Adjusted Updatable interface to be more generally applicable to the different types of Runs & implemented prolongation and ending of tasks upon submission as Updatables. --- .../run/InteractiveAsynchronousRunManager.kt | 43 +++++++---- .../run/InteractiveSynchronousRunManager.kt | 61 ++++++--------- .../run/updatables/EndOnSubmitUpdatable.kt | 77 +++++++++++++++++++ .../dres/run/updatables/EndTaskUpdatable.kt | 61 --------------- .../updatables/ProlongOnSubmitUpdatable.kt | 73 ++++++++++++++++++ .../run/updatables/ScoreboardsUpdatable.kt | 23 ++++-- .../dres/run/updatables/ScoresUpdatable.kt | 25 +++--- .../dev/dres/run/updatables/Updatable.kt | 15 ++-- build.gradle | 2 +- 9 files changed, 240 insertions(+), 140 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 78cac0c66..9ee4f2cf2 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -13,6 +13,10 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* +import dev.dres.data.model.template.task.options.DbSubmissionOption +import dev.dres.data.model.template.task.options.DbTaskOption +import dev.dres.data.model.template.task.options.Defaults +import dev.dres.data.model.template.task.options.Parameters import dev.dres.data.model.template.team.TeamId import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -23,6 +27,7 @@ import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* +import kotlinx.dnq.query.FilteringContext.eq import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -119,10 +124,10 @@ class InteractiveAsynchronousRunManager( this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) - this.teamIds.forEach { - val teamContext = RunActionContext("", it, setOf(DbRole.ADMIN)) - this.updatables.add(EndTaskUpdatable(this, teamContext)) + /* Loads optional updatable. */ + this.registerOptionalUpdatables() + this.teamIds.forEach { /* Initialize map and set all tasks pointers to the first task. */ this.statusMap[it] = if (this.evaluation.hasStarted) { RunManagerStatus.ACTIVE @@ -694,21 +699,15 @@ class InteractiveAsynchronousRunManager( * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ private fun invokeUpdatables() = this.stateLock.read { - this.statusMap.values.toSet() - .forEach { status -> //call update once for every possible status which is currently set for any team - this.updatables.forEach { - if (it.shouldBeUpdated(status)) { - try { - it.update(status) - } catch (e: Throwable) { - LOGGER.error( - "Uncaught exception while updating ${it.javaClass.simpleName} for competition run ${this.id}. Loop will continue to work but this error should be handled!", - e - ) - } - } + for (teamId in this.teamIds) { + val runStatus = this.statusMap[teamId] ?: throw IllegalStateException("Run status for team $teamId is not set. This is a programmers error!") + val taskStatus = this.evaluation.currentTaskForTeam(teamId)?.status?.toApi() + for (updatable in this.updatables) { + if (updatable.shouldBeUpdated(runStatus, taskStatus)) { + updatable.update(runStatus, taskStatus, RunActionContext(null, teamId, setOf(DbRole.ADMIN))) } } + } } /** @@ -716,7 +715,6 @@ class InteractiveAsynchronousRunManager( * i.e., status updates that are not triggered by an outside interaction. */ private fun internalStateUpdate() = this.stateLock.read { - for (teamId in teamIds) { this.store.transactional { if (teamHasRunningTask(teamId)) { //FIXME DbTaskTemplate[...] was removed. @@ -754,6 +752,17 @@ class InteractiveAsynchronousRunManager( } } + /** + * Applies the [DbSubmissionOption.LIMIT_CORRECT_PER_TEAM]. + */ + private fun registerOptionalUpdatables() { + /* Determine if task should be ended once submission threshold per team is reached. */ + val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options }.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + if (endOnSubmit) { + this.updatables.add(EndOnSubmitUpdatable(this)) + } + } + /** * Checks if the [InteractiveAsynchronousRunManager] is in one of the given [RunManagerStatus] and throws an exception, if not. * diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index b9dd0f29e..6db94a280 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -13,9 +13,8 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.task.options.DbTaskOption -import dev.dres.data.model.template.task.options.Defaults -import dev.dres.data.model.template.task.options.Parameters import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent @@ -97,13 +96,9 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this) - /** The internal [EndTaskUpdatable] used to end a task once no more submissions are possible */ - private val endTaskUpdatable = EndTaskUpdatable(this, RunActionContext.INTERNAL) - /** List of [Updatable] held by this [InteractiveSynchronousRunManager]. */ private val updatables = mutableListOf() - /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() @@ -113,12 +108,14 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } init { - /* Register relevant Updatables. */ + /* Register relevant updatable (these are always required). */ this.updatables.add(this.scoresUpdatable) this.updatables.add(this.scoreboardsUpdatable) - this.updatables.add(this.endTaskUpdatable) - /** End ongoing tasks upon initialization (in case server crashed during task execution). */ + /* Loads optional updatable. */ + this.registerOptionalUpdatables() + + /* End ongoing tasks upon initialization (in case server crashed during task execution). */ for (task in this.evaluation.tasks) { if (task.isRunning || task.status == DbTaskStatus.RUNNING) { task.end() @@ -480,11 +477,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Persist the submission. */ transformedSubmission.toNewDb() - /** Checks for the presence of the [DbTaskOption.PROLONG_ON_SUBMISSION] and applies it. */ - if (task.template.taskGroup.type.options.contains(DbTaskOption.PROLONG_ON_SUBMISSION)) { - this.prolongOnSubmit(context, transformedSubmission) - } - /* Enqueue submission for post-processing. */ this.scoresUpdatable.enqueue(task) @@ -588,9 +580,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ private fun invokeUpdatables() { + val runStatus = this.status + val taskStatus = this.evaluation.currentTask?.status?.toApi() this.updatables.forEach { - if (it.shouldBeUpdated(this.status)) { - it.update(this.status) + if (it.shouldBeUpdated(runStatus, taskStatus)) { + it.update(runStatus, taskStatus) } } } @@ -629,30 +623,19 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Applies the [DbTaskOption.PROLONG_ON_SUBMISSION]. - * - * @param context [RunActionContext] used for invocation. - * @param sub The [Submission] to apply the [DbTaskOption] for. + * Applies the [DbTaskOption.PROLONG_ON_SUBMISSION] and [DbSubmissionOption.LIMIT_CORRECT_PER_TEAM] options. */ - private fun prolongOnSubmit(context: RunActionContext, sub: Submission) { - val option = this.evaluation.currentTask?.template?.taskGroup?.type?.options?.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }?.any() - if (option == true) { - val limit = this.evaluation.currentTask?.template?.taskGroup?.type?.configurations?.filter { - it.key eq Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM - }?.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT - val prolongBy = this.evaluation.currentTask?.template?.taskGroup?.type?.configurations?.filter { - it.key eq Parameters.PROLONG_ON_SUBMISSION_BY_PARAM - }?.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_BY_DEFAULT - val correctOnly = this.evaluation.currentTask?.template?.taskGroup?.type?.configurations?.filter { - it.key eq Parameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM - }?.firstOrNull()?.value?.toBooleanStrictOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT - if (correctOnly && sub.answerSets().all { it.status() != VerdictStatus.CORRECT }) { - return - } - val timeLeft = Math.floorDiv(this.timeLeft(context), 1000) - if (timeLeft in 0 until limit) { - this.adjustDuration(context, prolongBy) - } + private fun registerOptionalUpdatables() { + /* Determine if any task should be prolonged upon submission. */ + val prolongOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options }.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }.any() + if (prolongOnSubmit) { + this.updatables.add(ProlongOnSubmitUpdatable(this)) + } + + /* Determine if any task should be ended once submission threshold per team is reached. */ + val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission }.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + if (endOnSubmit) { + this.updatables.add(EndOnSubmitUpdatable(this)) } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt new file mode 100644 index 000000000..d03b60fad --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt @@ -0,0 +1,77 @@ +package dev.dres.run.updatables + +import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.options.DbSubmissionOption +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunManagerStatus +import kotlinx.dnq.query.* + +/** + * An [Updatable] that takes care of ending a task if enough submissions have been received. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 1.2.0 + */ +class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private val context: RunActionContext = RunActionContext.INTERNAL) : Updatable { + + /** The [EndOnSubmitUpdatable] always belongs to the [Phase.MAIN]. */ + override val phase: Phase = Phase.MAIN + + /** + * Determines if the current task should be aborted based how many correct submissions have been received per team. + * + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null! + * @param context The [RunActionContext] used to invoke this [Updatable]. + */ + override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { + if (runStatus == RunManagerStatus.ACTIVE && taskStatus == ApiTaskStatus.RUNNING) { + /* Get list of teams and list of submissions. */ + val currentTask = this.manager.currentTask(this.context) ?: return + + /* Determine of endOnSubmit is true for given task. */ + val endOnSubmit = currentTask.template.taskGroup.type.submission.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + if (endOnSubmit) { + val teams = currentTask.teams.associateWith { 0 }.toMutableMap() + val submissions = currentTask.getSubmissions().toList() + + /* If there is no submission, we can abort here. */ + if (submissions.isNotEmpty()) { + val limit = currentTask.template.taskGroup.type.configurations.filter { + it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description + }.firstOrNull()?.value?.toIntOrNull() ?: 1 + + /* Count number of correct submissions per team. */ + if (this.manager.timeLeft(this.context) > 0) { + for (s in submissions) { + for (a in s.answerSets.toList()) { + if (a.status == DbVerdictStatus.CORRECT) { + teams[s.teamId] = teams[s.teamId]!! + 1 + } + } + } + + /* If all teams have reached the limit, end the task. */ + if (teams.all { it.value >= limit }) { + this.manager.abortTask(this.context) + } + } + } + } + } + } + + /** + * Returns true, if the [EndOnSubmitUpdatable] should be updated given the [RunManagerStatus] and the [ApiEvaluationState]. + * + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null + * @return True if update is required, which is while a task is running. + */ + override fun shouldBeUpdated(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?): Boolean = + (runStatus == RunManagerStatus.ACTIVE && taskStatus == ApiTaskStatus.RUNNING) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt deleted file mode 100644 index 0811a6b8a..000000000 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndTaskUpdatable.kt +++ /dev/null @@ -1,61 +0,0 @@ -package dev.dres.run.updatables - -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.template.task.options.DbSubmissionOption -import dev.dres.run.InteractiveAsynchronousRunManager -import dev.dres.run.InteractiveRunManager -import dev.dres.run.RunManagerStatus -import kotlinx.dnq.query.* -import java.util.concurrent.atomic.AtomicInteger - -/** - * An [Updatable] that takes care of ending a task if enough submissions have been received. - * - * @author Luca Rossetto - * @version 1.1.0 - */ -class EndTaskUpdatable(private val manager: InteractiveRunManager, private val context: RunActionContext) : Updatable { - - /** The [EndTaskUpdatable] always belongs to the [Phase.MAIN]. */ - override val phase: Phase = Phase.MAIN - - /** Number of submissions seen during the last update. */ - private var submissions = AtomicInteger(0) - - /** Internal flag indicating whether the provided [InteractiveRunManager] is asynchronous. */ - private val isAsync = this.manager is InteractiveAsynchronousRunManager - - override fun update(status: RunManagerStatus) { - val taskRun = this.manager.currentTask(this.context) - if (taskRun != null) { - this.manager.store.transactional(true) { - if (taskRun.template.taskGroup.type.submission.contains(DbSubmissionOption.LIMIT_CORRECT_PER_TEAM)) { - val limit = taskRun.template.taskGroup.type.configurations.filter { it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.firstOrNull()?.key?.toIntOrNull() ?: 1 - if (this.manager.timeLeft(this.context) > 0) { - val submissionCount = taskRun.getSubmissions().count() - if (this.submissions.getAndSet(submissionCount) < submissionCount) { - val allDone = if (this.isAsync) { - val numberOfSubmissions = this.manager.currentSubmissions(this.context).count { it.team.id == context.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } - numberOfSubmissions >= limit - } else { - /* Determine of all teams have submitted . */ - this.manager.template.teams.asSequence().all { team -> - val numberOfSubmissions = this.manager.currentSubmissions(this.context).count { it.team.id == team.teamId && it.answerSets.first().status == DbVerdictStatus.CORRECT } - numberOfSubmissions >= limit - } - } - if (allDone) { - this.manager.abortTask(this.context) - this.submissions.set(0) - } - } - } - } - } - } - } - - override fun shouldBeUpdated(status: RunManagerStatus): Boolean = - (status == RunManagerStatus.ACTIVE) //FIXME needs to also check status of the task run -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt new file mode 100644 index 000000000..63d53e09b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt @@ -0,0 +1,73 @@ +package dev.dres.run.updatables + +import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.template.task.options.DbTaskOption +import dev.dres.data.model.template.task.options.Defaults +import dev.dres.data.model.template.task.options.Parameters +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunManagerStatus +import kotlinx.dnq.query.any +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.firstOrNull + +/** + * An [Updatable] that takes care of prolonging a task if a last-minute [DbSubmission] was received. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class ProlongOnSubmitUpdatable(private val manager: InteractiveRunManager): Updatable { + + /** The [ProlongOnSubmitUpdatable] belongs to the [Phase.MAIN]. */ + override val phase: Phase = Phase.MAIN + + /** + * Determines if the current task should be prolonged based on the last [DbSubmission] and does so if necessary. + * + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null! + * @param context The [RunActionContext] used to invoke this [Updatable]. + */ + override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { + if (runStatus == RunManagerStatus.ACTIVE && taskStatus == ApiTaskStatus.RUNNING) { + val currentTask = this.manager.currentTask(context) ?: return + val prolongOnSubmit = currentTask.template.taskGroup.type.options.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }.any() + if (prolongOnSubmit) { + /* Retrieve relevant parameters. */ + val limit = currentTask.template.taskGroup.type.configurations.filter { + it.key eq Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM + }.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT + val prolongBy = currentTask.template.taskGroup.type.configurations.filter { + it.key eq Parameters.PROLONG_ON_SUBMISSION_BY_PARAM + }.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_BY_DEFAULT + val correctOnly = currentTask.template.taskGroup.type.configurations.filter { + it.key eq Parameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM + }.firstOrNull()?.value?.toBooleanStrictOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT + + /* Apply prolongation if necessary. */ + val submission: DbSubmission = this.manager.currentSubmissions(context).last() + if (correctOnly && submission.answerSets().all { it.status() != VerdictStatus.CORRECT }) { + return + } + val timeLeft = Math.floorDiv(this.manager.timeLeft(context), 1000) + if (timeLeft in 0 until limit) { + this.manager.adjustDuration(context, prolongBy) + } + } + } + } + + /** + * Returns true, if the [EndOnSubmitUpdatable] should be updated given the [RunManagerStatus] and the [ApiEvaluationState]. + * + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null + * @return True if update is required, which is while a task is running. + */ + override fun shouldBeUpdated(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?): Boolean = + (runStatus == RunManagerStatus.ACTIVE && taskStatus == ApiTaskStatus.RUNNING) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index b3b2e8888..b84e95d57 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -1,5 +1,8 @@ package dev.dres.run.updatables +import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.data.model.run.RunActionContext import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus import dev.dres.run.score.ScoreTimePoint @@ -11,17 +14,14 @@ import java.util.* * Implements the [Updatable] interface. * * @author Ralph Gasser & Luca Rossetto - * @version 1.1.0 + * @version 1.1.1 */ class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs: Long): StatefulUpdatable { - companion object { - private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) - } - /** The [Phase] this [ScoreboardsUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN + /** Indicates, that this [ScoreboardsUpdatable] has unprocessed changes. */ @Volatile override var dirty: Boolean = true @@ -34,7 +34,7 @@ class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs val timeSeries: List get() = this._timeSeries - override fun update(status: RunManagerStatus) { + override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { val now = System.currentTimeMillis() if (this.dirty && (now - lastUpdate) > this.updateIntervalMs) { this.dirty = false @@ -48,5 +48,14 @@ class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs } } - override fun shouldBeUpdated(status: RunManagerStatus): Boolean = ELIGIBLE_STATUS.contains(status) + /** + * Returns true, if the [ScoreboardsUpdatable] should be updated given the [RunManagerStatus] + * and the [ApiEvaluationState]. The [ScoreboardsUpdatable] is always triggered if the run is ongoing. + * + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null + * @return True if update is required, which is while a task is running. + */ + override fun shouldBeUpdated(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?): Boolean + = runStatus == RunManagerStatus.ACTIVE } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt index be2daf6a2..33719dc35 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoresUpdatable.kt @@ -1,36 +1,32 @@ package dev.dres.run.updatables +import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.evaluation.ApiTaskStatus import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.run.AbstractInteractiveTask import dev.dres.data.model.run.AbstractTask +import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.run.RunExecutor import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus -import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.Scoreable -import dev.dres.run.score.scoreboard.Scoreboard import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.mapDistinct -import java.util.* import kotlin.collections.LinkedHashSet /** * This is a [Updatable] that runs necessary post-processing after a [DbSubmission] has been validated and updates the scores for the respective [Scoreable]. * * @author Ralph Gasser - * @version 1.3.0 + * @version 1.3.1 */ class ScoresUpdatable(private val manager: RunManager): Updatable { - companion object { - private val ELIGIBLE_STATUS = arrayOf(RunManagerStatus.ACTIVE) - } - /** Internal list of [DbAnswerSet] that pend processing. */ private val set = LinkedHashSet() @@ -46,7 +42,7 @@ class ScoresUpdatable(private val manager: RunManager): Updatable { fun enqueue(task: AbstractTask) = this.set.add(task) @Synchronized - override fun update(status: RunManagerStatus) { + override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { val removed = mutableListOf() if (this.set.isNotEmpty()) { this.manager.store.transactional(true) { @@ -65,5 +61,14 @@ class ScoresUpdatable(private val manager: RunManager): Updatable { } } - override fun shouldBeUpdated(status: RunManagerStatus): Boolean = ELIGIBLE_STATUS.contains(status) + /** + * Returns true, if the [ScoresUpdatable] should be updated given the [RunManagerStatus] + * and the [ApiEvaluationState]. The [ScoresUpdatable] is always triggered if the run is ongoing. + * + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null + * @return True if update is required, which is while a task is running. + */ + override fun shouldBeUpdated(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?): Boolean + = (runStatus == RunManagerStatus.ACTIVE) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt index 888b8682e..bcbd506ac 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/Updatable.kt @@ -1,13 +1,15 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.ApiEvaluationState +import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.data.model.run.RunActionContext import dev.dres.run.RunManagerStatus /** * Interface implemented by classes that are updated during the lifecycle of a [RunManager]. * * @author Ralph Gasser - * @version 1.0.0 + * @version 1.1.0 */ interface Updatable { @@ -17,15 +19,18 @@ interface Updatable { /** * Triggers an update of this [Updatable]. * - * @param state The [RunManagerStatus] for which to provide an update. + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null + * @param context The [RunActionContext] used to invoke this [Updatable]. */ - fun update(status: RunManagerStatus) + fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus? = null, context: RunActionContext = RunActionContext.INTERNAL) /** * Returns true, if this [Updatable] should be updated given the [ApiEvaluationState] and false otherwise. * - * @param status The [RunManagerStatus] to check + * @param runStatus The [RunManagerStatus] to check. + * @param taskStatus The [ApiTaskStatus] to check. Can be null * @return True if update is required, false otherwise. */ - fun shouldBeUpdated(status: RunManagerStatus): Boolean + fun shouldBeUpdated(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus? = null): Boolean } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 830b028c4..506f2e854 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ openApiGenerate { ] } -task generateOAS(type: Download) { +tasks.register('generateOAS', Download) { /* Requires DRES running on default port */ def f = new File(oasFile) src fullOAS From 6a9cb420fce2b76db0ce999c98fb5d4c16fb97a6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 11:51:08 +0200 Subject: [PATCH 287/498] Fixed issue #393 that could occur when asynchronous run was started and the underlying scheduling problem when creating new evaluations. --- .../dev/dres/api/cli/EvaluationCommand.kt | 42 +++++--- .../admin/CreateEvaluationHandler.kt | 22 ++--- .../dev/dres/data/model/run/DbEvaluation.kt | 15 ++- .../run/InteractiveAsynchronousRunManager.kt | 99 +++++++++++-------- .../run/InteractiveSynchronousRunManager.kt | 26 +++-- .../main/kotlin/dev/dres/run/RunExecutor.kt | 38 +------ 6 files changed, 127 insertions(+), 115 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index 4cdd302e8..014cdffe7 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -275,27 +275,37 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom /** [EvaluationId] of the [DbEvaluation] that should be reactivated. .*/ private val id: EvaluationId by option("-i", "--id").required() - override fun run() = this@EvaluationCommand.store.transactional(true) { - val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() - if (evaluation == null) { - println("Evaluation ${this.id} does not seem to exist.") - return@transactional - } + override fun run() { + val run = this@EvaluationCommand.store.transactional { + val evaluation = DbEvaluation.query(DbEvaluation::id eq this.id).firstOrNull() + if (evaluation == null) { + println("Evaluation ${this.id} does not seem to exist.") + return@transactional null + } - if (evaluation.ended == null) { - println("Evaluation has not ended yet.") - return@transactional + if (evaluation.ended == null) { + println("Evaluation has not ended yet.") + return@transactional null + } + + if (RunExecutor.managers().any { it.id == evaluation.id }) { + println("Evaluation is already active.") + return@transactional null + } + + /* Create run and reactivate. */ + val manager = evaluation.toRunManager(this@EvaluationCommand.store) + manager.evaluation.reactivate() + manager } - if (RunExecutor.managers().any { it.id == evaluation.id }) { - println("Evaluation is already active.") - return@transactional + if (run != null) { + this@EvaluationCommand.store.transactional { + RunExecutor.schedule(run) + } } - /* Create run and reactivate. */ - val run = evaluation.toRun() - run.reactivate() - RunExecutor.schedule(run, this@EvaluationCommand.store) + println("Evaluation ${this.id} was reactivated.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index d5079d942..841c91eef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -11,6 +11,7 @@ import dev.dres.data.model.run.InteractiveAsynchronousEvaluation import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.mgmt.cache.CacheManager +import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManagerStatus @@ -62,7 +63,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca } /* Prepare run manager. */ - val evaluationId = this.store.transactional { + val manager = this.store.transactional { val template = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq message.templateId) and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Evaluation template with ID ${message.templateId} could not be found.'", ctx) @@ -76,12 +77,13 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca /* Check and prepare videos */ val segmentTasks = template.getAllVideos() - val await = segmentTasks.map { + val await = segmentTasks.mapNotNull { val item = it.first val path = item.pathToOriginal() if (!Files.exists(path)) { logger.error("Required media file $path not found for item ${item.name}.") - throw ErrorStatusException(500, "Required media file $path not found for item ${item.name}.", ctx) + //throw ErrorStatusException(500, "Required media file $path not found for item ${item.name}.", ctx) + return@mapNotNull null } /** Schedule request for preparing required preview. */ @@ -108,16 +110,12 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca initPermutation() } - /* Try to flush change prior to scheduling it. */ - RunExecutor.schedule(when (message.type) { - ApiEvaluationType.ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(evaluation) - ApiEvaluationType.SYNCHRONOUS -> InteractiveSynchronousEvaluation(evaluation) - ApiEvaluationType.NON_INTERACTIVE -> TODO() - }, this.store) - evaluation.id + /* Create evaluation + run manager and end transaction. */ + evaluation.toRunManager(this.store) } - /* Schedule newly created run manager. */ - return SuccessStatus("Evaluation '${message.name}' was started and is running with ID $evaluationId.") + /* Schedule newly created run manager. IMPORTANT: This MUST take place outside the previous transaction context. */ + RunExecutor.schedule(manager) + return SuccessStatus("Evaluation '${message.name}' was started and is running with ID ${manager.id}.") } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 905370ab4..0188602d5 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -8,6 +8,11 @@ import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.team.TeamId +import dev.dres.run.InteractiveAsynchronousRunManager +import dev.dres.run.InteractiveSynchronousRunManager +import dev.dres.run.NonInteractiveRunManager +import dev.dres.run.RunManager +import jetbrains.exodus.database.TransientEntityStore import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence @@ -170,14 +175,14 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { ) /** - * Generates and returns an [EvaluationRun] instance for this [DbEvaluation]. + * Generates and returns an [RunManager] instance for this [DbEvaluation]. * * @return [EvaluationRun] */ - fun toRun(): EvaluationRun = when (this.type) { - DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(this) - DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(this) - DbEvaluationType.NON_INTERACTIVE -> NonInteractiveEvaluation(this) + fun toRunManager(store: TransientEntityStore): RunManager = when (this.type) { + DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousRunManager(InteractiveSynchronousEvaluation(this), store) + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousRunManager(InteractiveAsynchronousEvaluation(this), store) + DbEvaluationType.NON_INTERACTIVE -> NonInteractiveRunManager(NonInteractiveEvaluation(this), store) else -> throw IllegalArgumentException("Unsupported run type ${this.type.description}. This is a programmer's error!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 9ee4f2cf2..e8ab964a1 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -1,5 +1,6 @@ package dev.dres.run +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage @@ -106,8 +107,9 @@ class InteractiveAsynchronousRunManager( } else { RunManagerStatus.CREATED } - private set + private set + /** Returns list [JudgementValidator]s associated with this [InteractiveAsynchronousRunManager]. May be empty! */ override val judgementValidators: List get() = this.evaluation.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } @@ -649,27 +651,40 @@ class InteractiveAsynchronousRunManager( } /** + * Method that orchestrates the internal progression of the [InteractiveSynchronousEvaluation]. * + * Implements the main run-loop. */ override fun run() { - /** Sort list of by [Phase] in ascending order. */ - this.updatables.sortBy { it.phase } + /* Preparation / Phase: PREPARE. */ + this.stateLock.read { + this.store.transactional { + this.updatables.sortBy { it.phase } /* Sort list of by [Phase] in ascending order. */ + AccessManager.registerRunManager(this) /* Register the run manager with the access manager. */ + } + } - /** Start [InteractiveSynchronousRunManager] . */ + /* Initialize error counter. */ var errorCounter = 0 + + /* Start [InteractiveSynchronousRunManager]; main run-loop. */ while (this.status != RunManagerStatus.TERMINATED) { try { this.stateLock.read { - /* 1) Invoke all relevant [Updatable]s. */ - this.invokeUpdatables() + this.store.transactional { + /* 1) Invoke all relevant [Updatable]s. */ + this.invokeUpdatables() - /* 2) Process internal state updates (if necessary). */ - this.internalStateUpdate() - - /* 3) Reset error counter and yield to other threads. */ - errorCounter = 0 + /* 2) Process internal state updates (if necessary). */ + this.internalStateUpdate() + } } + + /* 3) Yield to other threads. */ Thread.sleep(250) + + /* 4) Reset error counter and yield to other threads. */ + errorCounter = 0 } catch (ie: InterruptedException) { LOGGER.info("Interrupted run manager thread; exiting...") return @@ -690,8 +705,14 @@ class InteractiveAsynchronousRunManager( } } - /** Invoke [Updatable]s one last time. */ - this.invokeUpdatables() + /* Finalization. */ + this.stateLock.read { + this.store.transactional { + this.invokeUpdatables() /* Invoke [Updatable]s one last time. */ + AccessManager.deregisterRunManager(this) /* De-register this run manager with the access manager. */ + } + } + LOGGER.info("Run manager ${this.id} has reached end of run logic.") } @@ -716,38 +737,36 @@ class InteractiveAsynchronousRunManager( */ private fun internalStateUpdate() = this.stateLock.read { for (teamId in teamIds) { - this.store.transactional { - if (teamHasRunningTask(teamId)) { //FIXME DbTaskTemplate[...] was removed. - this.stateLock.write { - val task = this.evaluation.currentTaskForTeam(teamId) - ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") - val timeLeft = max( - 0L, - task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION - ) - if (timeLeft <= 0) { - task.end() - DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) - - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - teamId, - ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId) - ) - } - } - } else if (teamHasPreparingTask(teamId)) { - this.stateLock.write { - val task = this.evaluation.currentTaskForTeam(teamId) - ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") - task.start() - DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + if (teamHasRunningTask(teamId)) { //FIXME DbTaskTemplate[...] was removed. + this.stateLock.write { + val task = this.evaluation.currentTaskForTeam(teamId) + ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") + val timeLeft = max( + 0L, + task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION + ) + if (timeLeft <= 0) { + task.end() + DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) + + /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( teamId, - ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) + ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId) ) } } + } else if (teamHasPreparingTask(teamId)) { + this.stateLock.write { + val task = this.evaluation.currentTaskForTeam(teamId) + ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") + task.start() + DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + RunExecutor.broadcastWsMessage( + teamId, + ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) + ) + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 6db94a280..83654c87e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -1,5 +1,6 @@ package dev.dres.run +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage @@ -525,15 +526,23 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Internal method that orchestrates the internal progression of the [InteractiveSynchronousEvaluation]. + * Method that orchestrates the internal progression of the [InteractiveSynchronousEvaluation]. + * + * Implements the main run-loop. */ override fun run() { - /** Sort list of by [Phase] in ascending order. */ - this.updatables.sortBy { it.phase } + /* Preparation . */ + this.stateLock.read { + this.store.transactional { + this.updatables.sortBy { it.phase } /* Sort list of by [Phase] in ascending order. */ + AccessManager.registerRunManager(this) /* Register the run manager with the access manager. */ + } + } + /* Initialize error counter. */ var errorCounter = 0 - /** Start [InteractiveSynchronousRunManager] . */ + /* Start [InteractiveSynchronousRunManager]; main run-loop. */ while (this.status != RunManagerStatus.TERMINATED) { try { /* Obtain lock on current state. */ @@ -550,7 +559,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* 3) Yield to other threads. */ Thread.sleep(500) - /* Reset error counter. */ + /* 4) Reset error counter and yield to other threads. */ errorCounter = 0 } catch (ie: InterruptedException) { LOGGER.info("Interrupted SynchronousRunManager, exiting") @@ -568,9 +577,12 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } } - /** Invoke [Updatable]s one last time. */ + /* Finalization. */ this.stateLock.read { - this.invokeUpdatables() + this.store.transactional { + this.invokeUpdatables() /* Invoke [Updatable]s one last time. */ + AccessManager.deregisterRunManager(this) /* De-register this run manager with the access manager. */ + } } LOGGER.info("SynchronousRunManager ${this.id} reached end of run logic.") diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index be2a5529d..36b106b51 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -32,7 +32,7 @@ import java.util.function.Consumer * The execution environment for [RunManager]s * * @author Ralph Gasser - * @version 1.2.0 + * @version 1.3.0 */ object RunExecutor : Consumer { @@ -71,50 +71,21 @@ object RunExecutor : Consumer { fun init(config: Config, store: TransientEntityStore, cache: CacheManager) { store.transactional { DbEvaluation.filter { (it.started) ne null and (it.ended eq null) }.asSequence().forEach {e -> - /* Force-end tasks that are still running. */ - e.tasks.filter { t -> (t.ended eq null) }.asSequence().forEach { t -> - t.ended = System.currentTimeMillis() - } - - this.schedule(when (e.type) { - DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousEvaluation(e) - DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousEvaluation(e) - DbEvaluationType.NON_INTERACTIVE -> TODO() - else -> throw IllegalStateException("Unsupported evaluation type ${e.type}.") - }, store) + this.schedule(e.toRunManager(store)) /* Re-schedule evaluations. */ } } } - /** - * Schedules a new [EvaluationRun] with this [RunExecutor]. - * - * @param evaluation [EvaluationRun] to execute. - * @param store [TransientEntityStore] instanced used to interact with database. - */ - fun schedule(evaluation: EvaluationRun, store: TransientEntityStore) { - val run = when(evaluation) { - is InteractiveSynchronousEvaluation -> InteractiveSynchronousRunManager(evaluation, store) - is InteractiveAsynchronousEvaluation -> InteractiveAsynchronousRunManager(evaluation, store) - is NonInteractiveEvaluation -> NonInteractiveRunManager(evaluation, store) - else -> throw NotImplementedError("No matching run manager found for $evaluation") - } - this.schedule(run) - } - /** * Schedules a new [RunManager] with this [RunExecutor]. * * @param manager [RunManager] to execute. */ - private fun schedule(manager: RunManager) = this.runManagerLock.write { + fun schedule(manager: RunManager) = this.runManagerLock.write { if (this.runManagers.containsKey(manager.id)) { throw IllegalArgumentException("This RunExecutor already runs a RunManager with the given ID ${manager.id}. The same RunManager cannot be executed twice!") } - /* Register [RunManager] with AccessManager. */ - AccessManager.registerRunManager(manager) - /* Setup all the required data structures. */ this.runManagers[manager.id] = manager this.observingClients[manager.id] = HashSet() @@ -133,9 +104,6 @@ object RunExecutor : Consumer { logger.info("RunManager $v (done = ${k.isDone}, cancelled = ${k.isCancelled}) will be removed!") stamp = this@RunExecutor.runManagerLock.tryConvertToWriteLock(stamp) if (stamp > -1L) { - /* Deregister the RunManager. */ - AccessManager.deregisterRunManager(this@RunExecutor.runManagers[v]!!) - /* Cleanup. */ this@RunExecutor.runManagers.remove(v) this@RunExecutor.observingClients.remove(v) From e8f43e584213f4c6e83ac55245917756ae830ce5 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 8 May 2023 12:44:14 +0200 Subject: [PATCH 288/498] Resolved #364 --- .../task-template-editor.component.ts | 16 +++- .../template-information.component.html | 9 +- .../template-information.component.ts | 90 ++++++++++++++----- .../template-builder.service.ts | 8 ++ 4 files changed, 95 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index cc6018059..37d2e3e9b 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -218,9 +218,19 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { public pickRandomSegment(item: ApiMediaItem, startControl: UntypedFormControl, endControl: UntypedFormControl, unitControl: UntypedFormControl) { const start = TaskTemplateEditorComponent.randInt(1, item.durationMs / 1000 / 2); // always in first half let end = 1; - do { - end = start + TaskTemplateEditorComponent.randInt(5, item.durationMs / 1000); // Arbitrary 5 seconds minimal length - } while (end > item.durationMs / 1000); + if(this.builderService.defaultSegmentLength === 0){ + console.log("Using random length for random segment") + do { + end = start + TaskTemplateEditorComponent.randInt(5, item.durationMs / 1000); // Arbitrary 5 seconds minimal length + } while (end > item.durationMs / 1000); + }else{ + console.log("Using default length for random segment (start, defaultLength)", start, this.builderService.defaultSegmentLength) + end = start + this.builderService.defaultSegmentLength; + if(end > item.durationMs / 1000){ + end = (item.durationMs / 1000) - start; + } + } + startControl.setValue(start); endControl.setValue(end); unitControl.setValue('SECONDS'); diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.html b/frontend/src/app/template/template-builder/components/template-information/template-information.component.html index 33f9575bd..86461d955 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.html +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.html @@ -13,7 +13,7 @@

- + Default collection for tasks within this evaluation template + + Default length of a random segment in seconds. If set to zero this causes random length. + +

diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts index 20931dcea..1efce8cb8 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts @@ -1,21 +1,22 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; -import {UntypedFormControl, UntypedFormGroup} from '@angular/forms'; +import { AfterViewInit, Component, OnDestroy, OnInit } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { FormControl, UntypedFormControl, UntypedFormGroup, Validators } from "@angular/forms"; import { Observable, Subscription } from "rxjs"; import { ApiMediaCollection, CollectionService, TemplateService } from "../../../../../../openapi"; -import {TemplateBuilderService} from '../../template-builder.service'; +import { TemplateBuilderService } from "../../template-builder.service"; @Component({ - selector: 'app-template-information', - templateUrl: './template-information.component.html', - styleUrls: ['./template-information.component.scss'] + selector: "app-template-information", + templateUrl: "./template-information.component.html", + styleUrls: ["./template-information.component.scss"] }) -export class TemplateInformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { +export class TemplateInformationComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy, AfterViewInit { form: UntypedFormGroup = new UntypedFormGroup({ - name: new UntypedFormControl(''), - description: new UntypedFormControl(''), - collection: new UntypedFormControl('') + name: new UntypedFormControl(""), + description: new UntypedFormControl(""), + collection: new UntypedFormControl(""), + defaultRandomLength: new FormControl(20, [Validators.min(0)]) }); private changeSub: Subscription; @@ -25,48 +26,89 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone mediaCollectionSource: Observable; constructor( - private templateService: TemplateService, - private collectionService: CollectionService, - builder: TemplateBuilderService + private templateService: TemplateService, + private collectionService: CollectionService, + builder: TemplateBuilderService ) { super(builder); } + ngAfterViewInit(): void { + // builderService is not yet initialised ?!? + this.form.get('collection').setValue(this.getMostUsedCollection(), {emitEvent: false}); + } + ngOnInit(): void { this.onInit(); this.changeSub = this.form.valueChanges.subscribe((value) => { let isDirty = false; - if(value.name !== this.builderService.getTemplate().name){ + if (value.name !== this.builderService.getTemplate().name) { isDirty = true; } - if(value.description !== this.builderService.getTemplate().description){ + if (value.description !== this.builderService.getTemplate().description) { isDirty = true; } - if(value.collection && value.collection?.length > 0){ + if (value.collection && value.collection?.length > 0) { this.builderService.defaultCollection = value.collection; + } else if (this.builderService.getTemplate().tasks?.length > 0) { + /* Simply take majority of used target collection as default collection */ + const buckets = new Map(); + this.builderService.getTemplate().tasks.forEach(t => { + if (buckets.has(t.collectionId)) { + buckets[t.collectionId]++; + } else { + buckets.set(t.collectionId, 1); + } + }); + const highestUsedCollection = [...buckets.entries()].sort((a, b) => b[1] - a[1])[0]; + this.builderService.defaultCollection = highestUsedCollection[0]; + } + if (value.defaultRandomLength) { + this.builderService.defaultSegmentLength = value.defaultRandomLength; } - if(isDirty){ + if (isDirty) { this.builderService.markDirty(); } }); this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); } + private getMostUsedCollection() { + console.log("nb Tasks: ", this.builderService?.getTemplate()?.tasks?.length) + if (this.builderService?.getTemplate()?.tasks?.length > 0) { + const buckets = new Map(); + this.builderService.getTemplate().tasks.forEach(t => { + if (buckets.has(t.collectionId)) { + buckets[t.collectionId]++; + } else { + buckets.set(t.collectionId, 1); + } + }); + const highestUsedCollection = [...buckets.entries()].sort((a, b) => b[1] - a[1])[0]; + this.builderService.defaultCollection = highestUsedCollection[0]; + console.log("Most used collection: ", highestUsedCollection[0]) + return highestUsedCollection[0]; + } else { + console.log('No most used collection') + return ""; + } + } + ngOnDestroy() { this.onDestroy(); this.changeSub.unsubscribe(); } onChange() { - if(this.builderService.getTemplate()){ - if(this.form.get('name').value !== this.builderService.getTemplate().name){ - this.form.get('name').setValue(this.builderService.getTemplate().name, {emitEvent: !this.initOngoing}); + if (this.builderService.getTemplate()) { + if (this.form.get("name").value !== this.builderService.getTemplate().name) { + this.form.get("name").setValue(this.builderService.getTemplate().name, { emitEvent: !this.initOngoing }); } - if(this.form.get('description').value !== this.builderService.getTemplate().description){ - this.form.get('description').setValue(this.builderService.getTemplate().description, {emitEvent: !this.initOngoing}); + if (this.form.get("description").value !== this.builderService.getTemplate().description) { + this.form.get("description").setValue(this.builderService.getTemplate().description, { emitEvent: !this.initOngoing }); } // We need to check the end of init here - if(this.initOngoing){ + if (this.initOngoing) { this.initOngoing = false; } } diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 8637d282a..a876cfe4e 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -19,6 +19,14 @@ export class TemplateBuilderService { return this._defaultCollection; } private _defaultCollection: string = ''; + + set defaultSegmentLength(value: number){ + this._defaultSegmentLength = value; + } + get defaultSegmentLength():number{ + return this._defaultSegmentLength; + } + private _defaultSegmentLength: number = 0; get selectedTaskGroup(): ApiTaskGroup { return this._selectedTaskGroup; } From 1d1ae4802738ee52f0bf1c0847fdce5a803a8cad Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 15:05:47 +0200 Subject: [PATCH 289/498] Removed workaround for testing. --- .../rest/handler/evaluation/admin/CreateEvaluationHandler.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 841c91eef..54004474e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -82,8 +82,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca val path = item.pathToOriginal() if (!Files.exists(path)) { logger.error("Required media file $path not found for item ${item.name}.") - //throw ErrorStatusException(500, "Required media file $path not found for item ${item.name}.", ctx) - return@mapNotNull null + throw ErrorStatusException(500, "Required media file $path not found for item ${item.name}.", ctx) } /** Schedule request for preparing required preview. */ From 1f9f808d7c53e468e927897255102970e4632d0f Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 8 May 2023 15:34:14 +0200 Subject: [PATCH 290/498] Cleanup in ClientOpenApiPlugin --- .../dev/dres/api/rest/ClientOpenApiPlugin.kt | 72 +++++++++++-------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt index dad3b4199..9ce1fdab7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt @@ -1,41 +1,53 @@ package dev.dres.api.rest import com.fasterxml.jackson.databind.node.ObjectNode +import dev.dres.DRES +import io.javalin.openapi.CookieAuth import io.javalin.openapi.plugin.OpenApiConfiguration import io.javalin.openapi.plugin.OpenApiPlugin +import io.javalin.openapi.plugin.OpenApiPluginConfiguration +import io.javalin.openapi.plugin.SecurityComponentConfiguration -class ClientOpenApiPlugin : OpenApiPlugin(OpenApiConfiguration().apply { - this.info.title = "DRES API (client)" - this.info.version = "1.0" - this.info.description = "API for DRES (Distributed Retrieval Evaluation Server), Version 1.0" - this.documentationPath = "/client-oas" - this.documentProcessor = {doc -> - - val blacklist = setOf( - "/external/", - "/collection", - "/competition", - "/run", - "/audit", - "/mediaItem", - "/score", - "/user/list", - "/user/session/", - "/evaluation/admin", - "/evaluation/template", - "/evaluation/{evaluationId}/judge", - "/evaluation/{evaluationId}/vote", - "/download", - "/mediaitem", - "/template" +class ClientOpenApiPlugin : OpenApiPlugin(OpenApiPluginConfiguration() + .withDocumentationPath("/client-oas") + .withDefinitionConfiguration { _, u -> + u.withOpenApiInfo { t -> + t.title = "DRES Client API" + t.version = DRES.VERSION + t.description = "Client API for DRES (Distributed Retrieval Evaluation Server), Version ${DRES.VERSION}" + } + u.withSecurity( + SecurityComponentConfiguration() + .withSecurityScheme("CookieAuth", CookieAuth(AccessManager.SESSION_COOKIE_NAME)) ) + u.withDefinitionProcessor { doc -> + + val blacklist = setOf( + "/external/", + "/collection", + "/competition", + "/run", + "/audit", + "/mediaItem", + "/score", + "/user/list", + "/user/session/", + "/evaluation/admin", + "/evaluation/template", + "/evaluation/{evaluationId}/judge", + "/evaluation/{evaluationId}/vote", + "/download", + "/mediaitem", + "/template" + ) - val relevantRoutes = doc["paths"].fields().asSequence().filter { blacklist.none { b -> it.key.contains(b) } }.map { it.key }.toList() + val relevantRoutes = + doc["paths"].fields().asSequence().filter { blacklist.none { b -> it.key.contains(b) } }.map { it.key } + .toList() - (doc["paths"] as ObjectNode).retain(relevantRoutes) + (doc["paths"] as ObjectNode).retain(relevantRoutes) - doc.toPrettyString() + doc.toPrettyString() - } -}) { -} \ No newline at end of file + } + }) \ No newline at end of file From 4851db7a9b686560ca4d63ca5dd37ee9198b801a Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 15:51:45 +0200 Subject: [PATCH 291/498] RunExecutor should now also resume tasks that have not been started but created. --- backend/src/main/kotlin/dev/dres/run/RunExecutor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 36b106b51..d0fa7e81f 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -70,7 +70,7 @@ object RunExecutor : Consumer { */ fun init(config: Config, store: TransientEntityStore, cache: CacheManager) { store.transactional { - DbEvaluation.filter { (it.started) ne null and (it.ended eq null) }.asSequence().forEach {e -> + DbEvaluation.filter { (it.ended eq null) }.asSequence().forEach {e -> this.schedule(e.toRunManager(store)) /* Re-schedule evaluations. */ } } From c5e26551eb8001a7370570341d0cc476b48b1507 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 15:52:40 +0200 Subject: [PATCH 292/498] Fixed use of Context.eligibleManagerForId(), which requires a transaction context. --- .../handler/download/ScoreDownloadHandler.kt | 24 ++++----- .../client/ClientTaskInfoHandler.kt | 4 +- .../scores/CurrentTaskScoreHandler.kt | 6 +-- .../scores/HistoryTaskScoreHandler.kt | 3 +- .../scores/ListEvaluationScoreHandler.kt | 9 ++-- .../scores/ListScoreSeriesHandler.kt | 2 +- .../scores/ListScoreboardsHandler.kt | 7 +-- .../scores/TeamGroupScoreHandler.kt | 2 +- .../viewer/GetCurrentTaskHandler.kt | 14 +++-- .../viewer/GetEvaluationInfoHandler.kt | 15 +++--- .../viewer/GetEvaluationStateHandler.kt | 14 +++-- .../viewer/GetSubmissionHistoryInfoHandler.kt | 32 ++++++----- .../viewer/GetSubmissionInfoHandler.kt | 30 +++++------ .../evaluation/viewer/GetTaskHintHandler.kt | 2 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 2 +- .../judgement/DequeueJudgementHandler.kt | 7 ++- .../handler/judgement/DequeueVoteHandler.kt | 54 +++++++++---------- .../judgement/JudgementStatusHandler.kt | 11 ++-- .../handler/judgement/PostJudgementHandler.kt | 3 +- .../rest/handler/judgement/PostVoteHandler.kt | 3 +- .../handler/template/GetTeamLogoHandler.kt | 1 - .../utilities/extensions/ContextExtensions.kt | 28 +++++----- 22 files changed, 127 insertions(+), 146 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index b3adf5496..316c64407 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.handler.download import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.run.RunManager import dev.dres.utilities.extensions.eligibleManagerForId import io.javalin.http.Context import io.javalin.openapi.* @@ -36,24 +37,23 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): String { - val manager = ctx.eligibleManagerForId() + override fun doGet(ctx: Context): String = this.store.transactional(true) { + val manager = ctx.eligibleManagerForId() + val rac = RunActionContext.runActionContext(ctx, manager) /* Update response header. */ ctx.contentType("text/csv") ctx.header("Content-Disposition", "attachment; filename=\"scores-${manager.id}.csv\"") - return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, manager) - "startTime,task,group,team,score\n" + manager.tasks(rac).filter { - it.started != null - }.sortedBy { - it.started - } - .flatMap { task -> - task.scorer.scoreListFromCache().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.filter { t -> t.id eq it.first }.firstOrNull()?.name ?: "???"}\",${it.third}" } - }.joinToString(separator = "\n") + /* Prepare and return response. */ + "startTime,task,group,team,score\n" + manager.tasks(rac).filter { + it.started != null + }.sortedBy { + it.started } + .flatMap { task -> + task.scorer.scoreListFromCache().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.filter { t -> t.id eq it.first }.firstOrNull()?.name ?: "???"}\",${it.third}" } + }.joinToString(separator = "\n") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index a6069ac31..38b30e1c6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -37,8 +37,8 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional(true) { tx -> - val run = ctx.eligibleManagerForId() + override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional(true) { + val run = ctx.eligibleManagerForId() val rac = RunActionContext.runActionContext(ctx, run) if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index afbb6d612..b98f0db35 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -46,11 +46,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle ) override fun doGet(ctx: Context): ApiScoreOverview = this.store.transactional(true) { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException( - 400, - "Specified evaluation ${ctx.evaluationId()} does not scores for a current task.", - ctx - ) + val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index f6dc42ed9..0767ad850 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -48,11 +48,12 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiScoreOverview { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a score history.'", ctx) val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' is missing!'", ctx) if (!ctx.isAdmin()) throw ErrorStatusException(403, "Access denied.", ctx) return this.store.transactional(true) { + val manager = ctx.eligibleManagerForId() + val rac = RunActionContext.runActionContext(ctx, manager) val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run ${manager.id}.", ctx) val scores = scorer.scoreMapFromCache() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt index 48c2a68fa..51b261983 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt @@ -5,6 +5,7 @@ import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.handler.evaluation.scores.AbstractScoreHandler import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.run.RunManager import dev.dres.run.score.scoreboard.ScoreOverview import io.javalin.http.Context import io.javalin.openapi.* @@ -34,10 +35,8 @@ class ListEvaluationScoreHandler(store: TransientEntityStore) : AbstractScoreHan ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { - val manager = ctx.eligibleManagerForId() - return this.store.transactional (true) { - manager.scoreboards.map { it.overview().toApi() } - } + override fun doGet(ctx: Context): List = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + manager.scoreboards.map { it.overview().toApi() } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt index 57ab699cc..c119a45d1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt @@ -37,8 +37,8 @@ class ListScoreSeriesHandler(store: TransientEntityStore) : AbstractScoreHandler ) override fun doGet(ctx: Context): List { val scoreboard = ctx.pathParamMap().getOrElse("scoreboard") { throw ErrorStatusException(400, "Parameter 'scoreboard' is missing!'", ctx) } - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a score history.'", ctx) return this.store.transactional(true) { + val manager = ctx.eligibleManagerForId() manager.scoreHistory .filter { it.name == scoreboard } .groupBy { it.team } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt index c26ffdb34..042898974 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.handler.evaluation.scores import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.run.RunManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -29,8 +30,8 @@ class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { - val manager = ctx.eligibleManagerForId() - return manager.scoreboards.map { it.name } + override fun doGet(ctx: Context): List = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + manager.scoreboards.map { it.name } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index 2b886dc9d..544431bf7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -37,7 +37,7 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a score history.'", ctx) + val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index 8edbfdd79..71e852c36 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -46,14 +46,12 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiTaskTemplateInfo { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have a current task.'", ctx) - return this.store.transactional (true) { - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - val rac = RunActionContext.runActionContext(ctx, manager) - ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) + override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) } + val rac = RunActionContext.runActionContext(ctx, manager) + ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt index 1f878562d..27a8be29d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -6,6 +6,9 @@ import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.ApiEvaluationInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunManager +import dev.dres.utilities.extensions.evaluationId import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -31,13 +34,11 @@ class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluation ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiEvaluationInfo { - val manager = ctx.eligibleManagerForId() - return this.store.transactional (true) { - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access Denied", ctx) - } - ApiEvaluationInfo(manager) + override fun doGet(ctx: Context): ApiEvaluationInfo = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) } + ApiEvaluationInfo(manager) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index 1f06b118d..98ccf04a6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -34,14 +34,12 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiEvaluationState { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) - return this.store.transactional (true) { - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access Denied", ctx) - } - val rac = RunActionContext.runActionContext(ctx, manager) - ApiEvaluationState(manager, rac) + override fun doGet(ctx: Context): ApiEvaluationState = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) } + val rac = RunActionContext.runActionContext(ctx, manager) + ApiEvaluationState(manager, rac) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index c769c28d4..2aab1e60a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -37,25 +37,23 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) - return this.store.transactional (true) { - val rac = RunActionContext.runActionContext(ctx, manager) - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access denied.", ctx) - } - val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(404, "Missing task id", ctx) - val task = manager.currentTask(rac) - if (task?.template?.id == taskId && task.isRunning) { - if (task.isRunning) { - val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq DbTaskOption.HIDDEN_RESULTS }.any() - manager.currentSubmissions(rac).map { it.toApi(hidden) } - } else { - manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() }?.toList() ?: emptyList() - } + override fun doGet(ctx: Context): List = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + val rac = RunActionContext.runActionContext(ctx, manager) + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(404, "Missing task id", ctx) + val task = manager.currentTask(rac) + if (task?.template?.id == taskId && task.isRunning) { + if (task.isRunning) { + val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq DbTaskOption.HIDDEN_RESULTS }.any() + manager.currentSubmissions(rac).map { it.toApi(hidden) } + } else { + manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() }?.toList() ?: emptyList() } - emptyList() } + emptyList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index 9d7e9c3ac..08744cf9a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -38,25 +38,23 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) - return this.store.transactional (true) { - val rac = RunActionContext.runActionContext(ctx, manager) + override fun doGet(ctx: Context): List = this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() + val rac = RunActionContext.runActionContext(ctx, manager) - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access denied.", ctx) - } + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) + } - val limit = manager.runProperties.limitSubmissionPreviews - val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) - val blind = currentTask.template.taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS) && currentTask.isRunning + val limit = manager.runProperties.limitSubmissionPreviews + val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) + val blind = currentTask.template.taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS) && currentTask.isRunning - /* Obtain current task run and check status. */ - if (limit > 0) { - limitSubmissions(manager.currentSubmissions(rac), limit, blind) - } else { - manager.currentSubmissions(rac).map { it.toApi(blind) } - } + /* Obtain current task run and check status. */ + if (limit > 0) { + limitSubmissions(manager.currentSubmissions(rac), limit, blind) + } else { + manager.currentSubmissions(rac).map { it.toApi(blind) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 2724b3dba..7dd1ef363 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -63,8 +63,8 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa ) override fun doGet(ctx: Context): ApiHintContent { val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) return this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 0c7acf9cb..ec766b3e3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -58,8 +58,8 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val cache: Cache ) override fun doGet(ctx: Context): ApiTargetContent { val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) return this.store.transactional (true) { + val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index a0abc846b..2c0ba8840 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.run.RunManager import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -41,11 +42,9 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiJudgementRequest { - /* Obtain manager and check if any submissions are waiting for judgement. */ - val evaluationManager = ctx.eligibleManagerForId() - /* Start transaction. */ - val request = this.store.transactional { + val request = this.store.transactional(true) { + val evaluationManager = ctx.eligibleManagerForId() checkEligibility(ctx, evaluationManager) do { val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: break diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 207850e6e..588ebfbda 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -9,6 +9,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.run.RunManager import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.Context import io.javalin.openapi.* @@ -39,36 +40,31 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiJudgementRequest { - /* Obtain manager and check if any submissions are waiting for judgement. */ - val evaluationManager = ctx.eligibleManagerForId() - - /* Start transaction. */ - this.store.transactional {//TODO needs adjustment to deal with answerSets - do { - val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break - val next = validator.nextSubmissionToVoteOn() ?: break - val taskDescription = next.task().template.textualDescription() - when (next.answers().firstOrNull()?.type()) { - AnswerType.TEXT -> { - val text = next.answers().firstOrNull()?.text ?: continue - return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) - } - AnswerType.ITEM -> { - val item = next.answers().firstOrNull()?.item ?: continue - return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.dbCollection().id, item.mediaItemId, taskDescription, null, null) - } - AnswerType.TEMPORAL -> { - val answer = next.answers().firstOrNull() ?: continue - val item = answer.item ?: continue - val start = answer.start ?: continue - val end = answer.end ?: continue - return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.dbCollection().id, item.mediaItemId, taskDescription, start, end) - } - else -> continue + override fun doGet(ctx: Context): ApiJudgementRequest = this.store.transactional(true) {//TODO needs adjustment to deal with answerSets + val evaluationManager = ctx.eligibleManagerForId() + do { + val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: break + val next = validator.nextSubmissionToVoteOn() ?: break + val taskDescription = next.task().template.textualDescription() + when (next.answers().firstOrNull()?.type()) { + AnswerType.TEXT -> { + val text = next.answers().firstOrNull()?.text ?: continue + return@transactional ApiJudgementRequest(null, ApiMediaType.TEXT, validator.id, "text", text, taskDescription, null, null) + } + AnswerType.ITEM -> { + val item = next.answers().firstOrNull()?.item ?: continue + return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.dbCollection().id, item.mediaItemId, taskDescription, null, null) + } + AnswerType.TEMPORAL -> { + val answer = next.answers().firstOrNull() ?: continue + val item = answer.item ?: continue + val start = answer.start ?: continue + val end = answer.end ?: continue + return@transactional ApiJudgementRequest(null, item.type().toApi(), validator.id, item.dbCollection().id, item.mediaItemId, taskDescription, start, end) } - } while (true) - } + else -> continue + } + } while (true) throw ErrorStatusException(202, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx, true) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt index cf6e9c066..2a233249e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -5,6 +5,7 @@ import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.types.judgement.ApiJudgementValidatorStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.run.RunManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -35,12 +36,10 @@ class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHand ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List { - val evaluationManager = ctx.eligibleManagerForId() - return this.store.transactional(true) { - checkEligibility(ctx, evaluationManager) - evaluationManager.judgementValidators.map { ApiJudgementValidatorStatus(it.id, it.pending, it.open) } - } + override fun doGet(ctx: Context): List = this.store.transactional(true) { + val evaluationManager = ctx.eligibleManagerForId() + checkEligibility(ctx, evaluationManager) + evaluationManager.judgementValidators.map { ApiJudgementValidatorStatus(it.id, it.pending, it.open) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index e25d82550..94f7575e0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.run.RunManager import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.utilities.extensions.eligibleManagerForId @@ -44,7 +45,6 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle ) override fun doPost(ctx: Context): SuccessStatus { /* Obtain manager and check if any submissions are waiting for judgement. */ - val evaluationManager = ctx.eligibleManagerForId() val judgement = try { ctx.bodyAsClass(ApiJudgement::class.java) } catch (e: BadRequestResponse) { @@ -53,6 +53,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle /* Start transaction. */ this.store.transactional { + val evaluationManager = ctx.eligibleManagerForId() checkEligibility(ctx, evaluationManager) val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index cfb8181b7..0ca3ab6f2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.run.RunManager import dev.dres.run.validation.interfaces.VoteValidator import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -40,7 +41,6 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto ] ) override fun doPost(ctx: Context): SuccessStatus { - val evaluationManager = ctx.eligibleManagerForId() val vote = try { ctx.bodyAsClass(ApiVote::class.java) } catch (e: BadRequestResponse) { @@ -48,6 +48,7 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto } this.store.transactional { + val evaluationManager = ctx.eligibleManagerForId() val validator = evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } // Get first active vote validator ?: throw ErrorStatusException(404, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx) validator as VoteValidator diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index 79ee72144..bc1aa2b86 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -39,7 +39,6 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla override fun get(ctx: Context) { /* Extract logoId. */ val teamId = ctx.pathParamMap()["teamId"] ?: throw ErrorStatusException(400, "Parameter 'teamId' is missing!'", ctx) - this.store.transactional(true) { val logo = DbTeam.query(DbTeam::id eq teamId).firstOrNull()?.logo if (logo != null) { diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 0fbe791b1..986269962 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.util.MimeTypeHelper import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus @@ -14,6 +15,7 @@ import io.javalin.http.Context import kotlinx.dnq.query.filter import kotlinx.dnq.query.flatMapDistinct import kotlinx.dnq.query.isEmpty +import kotlinx.dnq.query.isNotEmpty import java.io.File import java.nio.file.Files import java.nio.file.Path @@ -124,27 +126,21 @@ fun Context.activeManagerForUser(): RunManager { * * @return [RunManager] */ -fun Context.eligibleManagerForId(): RunManager { +inline fun Context.eligibleManagerForId(): T { val userId = this.userId() val evaluationId = this.evaluationId() - val manager = RunExecutor.managerForId(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) - if (this.isJudge()) { - if (manager.template.judges.filter { it.userId eq userId }.isEmpty) { - throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as judge.", this) - } - } - if (this.isParticipant()) { - if (manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isEmpty) { - throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) - } - } - - if (this.isAdmin()) { + val manager = RunExecutor.managerForId(evaluationId) as? T ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) + if (this.isJudge() && manager.template.judges.filter { it.userId eq userId }.isNotEmpty) { + return manager + } else if (this.isParticipant() && manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isNotEmpty) { return manager + } else if (this.isAdmin()) { + return manager + } else { + throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) } - - throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) } + /** * Checks uf user associated with current [Context] has [ApiRole.PARTICIPANT]. * From af6c64b75568ff528f15ccadae253a52a9a35c5e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 15:55:07 +0200 Subject: [PATCH 293/498] Fixed authorization issue with GetTeamLogoHandler. --- .../dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index bc1aa2b86..e73ad7504 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -2,9 +2,11 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.template.team.DbTeam import io.javalin.http.Context import io.javalin.openapi.* +import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.eq import kotlinx.dnq.query.firstOrNull @@ -26,6 +28,9 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla //not used override fun doGet(ctx: Context): Any = "" + /** All authorised users can access the team logo. */ + override val permittedRoles: Set = setOf(ApiRole.PARTICIPANT, ApiRole.VIEWER, ApiRole.JUDGE, ApiRole.ADMIN) + @OpenApi( summary = "Returns the logo for the given team ID.", path = "/api/v2/template/logo/{teamId}", From d73e2b05a60216449fdc7db52b247bd9b5c77592 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 8 May 2023 16:15:51 +0200 Subject: [PATCH 294/498] Fixed issue #325. --- frontend/src/app/app-routing.module.ts | 2 +- .../competition-list.component.html | 45 +++++++------- .../competition-list.component.ts | 8 +-- .../template-list.component.html | 60 ++++++++++--------- .../template-list/template-list.component.ts | 10 ++-- 5 files changed, 65 insertions(+), 60 deletions(-) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 1a44f61f4..c0c5ea0ca 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -35,7 +35,7 @@ const EVALUATION_ROUTE = 'evaluation'; const routes: Routes = [ { - path: TEMPLATE_ROUTE+'/list', + path: TEMPLATE_ROUTE + '/list', component: TemplateListComponent, canActivate: [AuthenticationGuard], data: { roles: [ApiRole.ADMIN] }, diff --git a/frontend/src/app/competition/competition-list/competition-list.component.html b/frontend/src/app/competition/competition-list/competition-list.component.html index 6a892c5e2..de33eeee9 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.html +++ b/frontend/src/app/competition/competition-list/competition-list.component.html @@ -27,28 +27,29 @@ Actions - - - - - + + + + + + + diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts index a99235770..9cf4de8ec 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ b/frontend/src/app/competition/competition-list/competition-list.component.ts @@ -29,7 +29,7 @@ export class CompetitionListComponent implements AfterViewInit { /** */ displayedColumns = ['actions', 'id', 'name', 'description', 'taskCount', 'teamCount']; competitions: ApiEvaluationOverview[] = []; - waitingForRun = false; + waitingForRun = new Map() constructor( private evaluationService: TemplateService, @@ -67,7 +67,7 @@ export class CompetitionListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - tap((r) => (this.waitingForRun = true)), + tap((r) => (this.waitingForRun[id] = true)), flatMap((r: CompetitionStartDialogResult) => { const properties = { participantCanView: r.participantCanView, @@ -86,11 +86,11 @@ export class CompetitionListComponent implements AfterViewInit { .subscribe( (r: SuccessStatus) => { this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - this.waitingForRun = false; + this.waitingForRun[id] = false; }, (r) => { this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - this.waitingForRun = false; + this.waitingForRun[id] = false; } ); } diff --git a/frontend/src/app/template/template-list/template-list.component.html b/frontend/src/app/template/template-list/template-list.component.html index 47f435593..bd44e535f 100644 --- a/frontend/src/app/template/template-list/template-list.component.html +++ b/frontend/src/app/template/template-list/template-list.component.html @@ -27,35 +27,37 @@

Evaluation Templates

Actions - - - - - + + + + + + + diff --git a/frontend/src/app/template/template-list/template-list.component.ts b/frontend/src/app/template/template-list/template-list.component.ts index 6a9979354..929144bb9 100644 --- a/frontend/src/app/template/template-list/template-list.component.ts +++ b/frontend/src/app/template/template-list/template-list.component.ts @@ -27,7 +27,9 @@ export class TemplateListComponent implements AfterViewInit{ displayedColumns = ['actions', 'id', 'name', 'description', 'nbTasks', 'nbTeams'] templates: ApiEvaluationOverview[] = []; - waitingForRun = false; + + /** Map of runs that are currently being generated. */ + public waitingForRun = new Map() constructor( private templateService: TemplateService, @@ -104,7 +106,7 @@ export class TemplateListComponent implements AfterViewInit{ const dialogRef = this.dialog.open(EvaluationStartDialogComponent, {width: '500px'}); dialogRef.afterClosed().pipe( filter((r) => r!= null), - tap((r) => (this.waitingForRun = true)), + tap((r) => (this.waitingForRun[templateId] = true)), flatMap((r: EvaluationStartDialogResult) => { return this.evaluationAdminService.postApiV2EvaluationAdminCreate({ templateId: templateId, @@ -120,10 +122,10 @@ export class TemplateListComponent implements AfterViewInit{ }) ).subscribe((r: SuccessStatus) => { this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); - this.waitingForRun = false; + this.waitingForRun[templateId] = false; },(r) => { this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - this.waitingForRun = false; + this.waitingForRun[templateId] = false; }); } From 9e37afa73210bd570db8298c239397397eb45dc8 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 9 May 2023 16:49:22 +0200 Subject: [PATCH 295/498] Working on submission view #395 --- frontend/src/app/app-routing.module.ts | 3 +- frontend/src/app/app.module.ts | 4 +- ...mpetition-builder-team-dialog.component.ts | 4 +- .../answer-set/answer-set.component.html | 43 ++++++++++ .../answer-set/answer-set.component.scss | 3 + .../answer-set/answer-set.component.ts | 20 +++++ .../submission/answer/answer.component.html | 26 ++++++ .../submission/answer/answer.component.scss | 3 + .../submission/answer/answer.component.ts | 21 +++++ .../submissions-details.component.html | 44 ++++++++++ .../submissions-details.component.scss | 19 +++++ .../submissions-details.component.ts | 29 +++++++ .../submissions-list.component.html | 24 ++++++ .../submissions-list.component.scss | 0 .../submissions-list.component.ts | 83 +++++++++++++++++++ .../src/app/evaluation/evaluation.module.ts | 37 +++++++++ .../run-admin-submissions-list.component.ts | 3 + .../src/app/run/run-admin-view.component.html | 3 +- .../src/app/run/run-admin-view.component.ts | 3 +- 19 files changed, 367 insertions(+), 5 deletions(-) create mode 100644 frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.html create mode 100644 frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.scss create mode 100644 frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.ts create mode 100644 frontend/src/app/evaluation/admin/submission/answer/answer.component.html create mode 100644 frontend/src/app/evaluation/admin/submission/answer/answer.component.scss create mode 100644 frontend/src/app/evaluation/admin/submission/answer/answer.component.ts create mode 100644 frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html create mode 100644 frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.scss create mode 100644 frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts create mode 100644 frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html create mode 100644 frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.scss create mode 100644 frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts create mode 100644 frontend/src/app/evaluation/evaluation.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index c0c5ea0ca..23e051ba8 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -21,6 +21,7 @@ import { NonescapingUrlserializerClass } from './nonescaping-urlserializer.class import {ApiRole} from '../../openapi'; import {TemplateBuilderComponent} from './template/template-builder/template-builder.component'; import { TemplateListComponent } from "./template/template-list/template-list.component"; +import { SubmissionsListComponent } from "./evaluation/admin/submission/submissions-list/submissions-list.component"; /** * The ROUTE for evaluation templates. @@ -73,7 +74,7 @@ const routes: Routes = [ }, { path: EVALUATION_ROUTE+'/admin/submissions/:runId/:taskId', - component: RunAdminSubmissionsListComponent, + component: SubmissionsListComponent, canActivate: [AuthenticationGuard], data: { roles: [ApiRole.ADMIN] }, }, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index aa47ad7d8..8a6c046cb 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -24,6 +24,7 @@ import { SharedModule } from './shared/shared.module'; import { CollectionModule } from './collection/collection.module'; import { CompetitionBuilderModule } from './competition/competition-builder/competition-builder.module'; import {TemplateModule} from './template/template.module'; +import { EvaluationModule } from './evaluation/evaluation.module'; /** * Method used to load application config. @@ -60,7 +61,8 @@ export function initializeApp(appConfig: AppConfig) { RunModule, CollectionModule, JudgementModule, - TemplateModule + TemplateModule, + EvaluationModule ], providers: [ AppConfig, diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index c4203fd45..210d3e4cf 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -127,8 +127,10 @@ export class CompetitionBuilderTeamDialogComponent { public teamLogo(): string { if (this.form.get('logoData').value != null) { return this.form.get('logoData').value; - } else { + } else if(this.team){ return this.config.resolveApiUrl(`/template/logo/${this.team.id}`); + } else { + return ""; } } diff --git a/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.html b/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.html new file mode 100644 index 000000000..7ddfaf8ee --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + +
ID{{row.id}}Answers + + There are {{row.answers.length}} individual answers here! + Status{{row.status}}Actions + + + check + + + close + + + help_outline + + + + +
diff --git a/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.scss b/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.scss new file mode 100644 index 000000000..e894f5771 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.scss @@ -0,0 +1,3 @@ +table { + width: 100%; +} diff --git a/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.ts b/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.ts new file mode 100644 index 000000000..0208596d9 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/answer-set/answer-set.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from "@angular/core"; +import { ApiAnswerSet } from "../../../../../../openapi"; + +@Component({ + selector: 'app-answer-set', + templateUrl: './answer-set.component.html', + styleUrls: ['./answer-set.component.scss'] +}) +export class AnswerSetComponent { + + @Input() + public answerSets: ApiAnswerSet[]; + + public displayedColumns: ['id', 'answers', 'verdict', 'actions']; + + update(submission, value: any) { + console.log("Update answerset for ", submission, value) + // TODO actually update + } +} diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html new file mode 100644 index 000000000..119773fb2 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
Type{{row.type}}Text{{row.text}}Media Item{{row.item ? row.item.name + ' ('+ row.item.type+')': 'N/A'}}Start{{row.start ? (row.start | formatTime: true) : "N/A"}}End{{row.end ? (row.end | formatTime: true): "N/A"}}
diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.scss b/frontend/src/app/evaluation/admin/submission/answer/answer.component.scss new file mode 100644 index 000000000..e894f5771 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.scss @@ -0,0 +1,3 @@ +table { + width: 100%; +} diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts new file mode 100644 index 000000000..4d496ea43 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from "@angular/core"; +import { ApiAnswer, ApiAnswerType } from "../../../../../../openapi"; + +@Component({ + selector: 'app-answer', + templateUrl: './answer.component.html', + styleUrls: ['./answer.component.scss'] +}) +export class AnswerComponent { + + @Input() + public answers: ApiAnswer[]; + + public displayedColumnsWithoutText = ['type', 'item', 'start', 'end'] + public displayedColumnsText = ['type', 'text'] + public displayedHeaders = ['type', 'text', 'item', 'start', 'end'] + + public hasTextType = (index: number, rowData: ApiAnswer) => rowData.type === ApiAnswerType.TEXT; + + public hasNotTextType = (index: number, rowData: ApiAnswer) => rowData.type !== ApiAnswerType.TEXT; +} diff --git a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html new file mode 100644 index 000000000..04c1f3e78 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html @@ -0,0 +1,44 @@ +
+

Submissions of {{submission.taskId.substring(0,8)}}

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{{row.submissionId}}Timestamp{{row.timestamp | epoch2date | date: 'dd.MM.yyyy HH:mm:ss' }}Submitter{{row.memberId}} / {{row.teamId}}# Answers{{row.answers.length ?? 0}}  + + +
+ +
+
diff --git a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.scss b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.scss new file mode 100644 index 000000000..e00259e12 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.scss @@ -0,0 +1,19 @@ +table { + width: 100%; +} + +tr.submissions-detail-row { + height: 0; +} +tr.submissions-element-row:not(.submissions-expanded-row):hover { + background: rgba(255,255,255,0.1); +} +tr.submissions-element-row:not(.submissions-expanded-row):active { + background: rgba(255,255,255,0.2); +} + +.submissions-element-row td { + border-bottom-width: 0; +} + + diff --git a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts new file mode 100644 index 000000000..081d91ac3 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts @@ -0,0 +1,29 @@ +import { Component, ComponentFactoryResolver, Input } from "@angular/core"; +import { ApiSubmission, ApiSubmissionInfo } from "../../../../../../openapi"; +import { animate, state, style, transition, trigger } from "@angular/animations"; + +@Component({ + selector: 'app-submissions-details', + templateUrl: './submissions-details.component.html', + styleUrls: ['./submissions-details.component.scss'], + animations: [ + trigger('detailExpand', [ + state('collapsed', style({height: '0px', minHeight: '0'})), + state('expanded', style({height: '*'})), + transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4,0.0,0.2,1)')), + ]), + ], +}) +export class SubmissionsDetailsComponent { + + @Input() + public submission: ApiSubmissionInfo; + + public columnsToDisplay = ['id', 'timestamp', 'author', 'nbAnswers']; + public columnsToDisplayWithExpand = [...this.columnsToDisplay, 'expand'] + public expandedElement: ApiSubmission | null; + + trackById(_:number, item: ApiSubmission){ + return item.submissionId; + } +} diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html new file mode 100644 index 000000000..e16c544e4 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html @@ -0,0 +1,24 @@ +
+ +
+

Submission list for task {{(taskId | async).substring(0,8)}}

+
+
+ +
+ Poll for updates (every {{pollingFrequencyInSeconds}}s +
+ Anonymize +
+
+ + + + Submissions of task run {{id.substring(0,8)}} + + + + +
diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.scss b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts new file mode 100644 index 000000000..957efb4c7 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts @@ -0,0 +1,83 @@ +import { AfterViewInit, Component, OnDestroy, ViewChild } from "@angular/core"; +import { merge, Observable, of, Subject, Subscription, timer } from "rxjs"; +import { MatButtonToggleGroup } from "@angular/material/button-toggle"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { MatDialog } from "@angular/material/dialog"; +import { ActivatedRoute } from "@angular/router"; +import { ApiSubmission, ApiSubmissionInfo, EvaluationAdministratorService } from "../../../../../../openapi"; +import { AppConfig } from "../../../../app.config"; +import { catchError, filter, map, switchMap, withLatestFrom } from "rxjs/operators"; + +@Component({ + selector: 'app-submissions-list', + templateUrl: './submissions-list.component.html', + styleUrls: ['./submissions-list.component.scss'] +}) +export class SubmissionsListComponent implements AfterViewInit, OnDestroy{ + + @ViewChild('toggleGroup', {static: true}) toggleGroup: MatButtonToggleGroup; + + public runId: Observable; + public taskId: Observable; + + public pollingFrequencyInSeconds = 30; + + public polling = true; + + public anonymize = true; + + public refreshSubject: Subject = new Subject(); + + public taskRunIds: string[] = []; + public submissionInfosByRunId: Map = new Map(); + + private subscription: Subscription; + + constructor( + private snackBar: MatSnackBar, + private dialog: MatDialog, + private activeRoute: ActivatedRoute, + private evaluationService: EvaluationAdministratorService, + public config: AppConfig, + ) { + this.runId = this.activeRoute.paramMap.pipe(map((params) => params.get('runId'))); + this.taskId = this.activeRoute.paramMap.pipe(map((params) => params.get('taskId'))); + } + ngAfterViewInit(): void { + this.subscription = merge( + timer(0, this.pollingFrequencyInSeconds * 1000) + .pipe(filter((_) => this.polling)), + this.refreshSubject) + .pipe( + withLatestFrom(this.runId, this.taskId), + switchMap(([_,r,t]) => this.evaluationService.getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId(r,t)), + catchError((err, o) => { + console.error(`[SubmissionList] Error occurred while laoding submissions: ${err?.message}`); + this.snackBar.open(`Error: Couldn't load submissions for reason: ${err?.message}`, null, {duration: 5000}); + return of([]); + }) + ) + .subscribe((s: ApiSubmissionInfo[]) => { + this.taskRunIds = []; + this.submissionInfosByRunId.clear(); + s.forEach((si) => { + this.taskRunIds.push(si.taskId); + this.submissionInfosByRunId.set(si.taskId, si); + }) + }) + } + + ngOnDestroy(): void { + this.subscription?.unsubscribe(); + this.subscription = null; + } + + trackById(_: number, item: ApiSubmissionInfo){ + return item.taskId; + } + + trackBySelf(_: number, item: string){ + return item; + } + +} diff --git a/frontend/src/app/evaluation/evaluation.module.ts b/frontend/src/app/evaluation/evaluation.module.ts new file mode 100644 index 000000000..78f2b808f --- /dev/null +++ b/frontend/src/app/evaluation/evaluation.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AnswerComponent } from './admin/submission/answer/answer.component'; +import { MatTableModule } from "@angular/material/table"; +import { SharedModule } from "../shared/shared.module"; +import { AnswerSetComponent } from './admin/submission/answer-set/answer-set.component'; +import { MatButtonToggleModule } from "@angular/material/button-toggle"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { SubmissionsDetailsComponent } from './admin/submission/submissions-details/submissions-details.component'; +import { SubmissionsListComponent } from './admin/submission/submissions-list/submissions-list.component'; +import { MatSlideToggleModule } from "@angular/material/slide-toggle"; +import { MatExpansionModule } from "@angular/material/expansion"; +import { FormsModule } from "@angular/forms"; +import { MatCardModule } from "@angular/material/card"; + + + +@NgModule({ + declarations: [ + AnswerComponent, + AnswerSetComponent, + SubmissionsDetailsComponent, + SubmissionsListComponent, + ], + imports: [ + CommonModule, + MatTableModule, + SharedModule, + MatButtonToggleModule, + MatTooltipModule, + MatSlideToggleModule, + MatExpansionModule, + FormsModule, + MatCardModule + ] +}) +export class EvaluationModule { } diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts index 6cfc016a1..f4595cb98 100644 --- a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts +++ b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts @@ -10,6 +10,9 @@ import { MatPaginator } from '@angular/material/paginator'; import { AppConfig } from '../../app.config'; import { ApiSubmission, ApiSubmissionInfo, ApiVerdictStatus, EvaluationAdministratorService} from 'openapi'; +/** + * @deprecated Replaced by evaluation/submission/submission-list-component + */ @Component({ selector: 'app-run-admin-submissions-list', templateUrl: './run-admin-submissions-list.component.html', diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 24d2fe002..92a65a5d3 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -145,7 +145,8 @@ > directions -
- - - + + + @@ -30,9 +30,9 @@

Submissions of {{submission.taskId.substring(0,8)}}

- diff --git a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts index 081d91ac3..b27884ae6 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts +++ b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts @@ -19,11 +19,15 @@ export class SubmissionsDetailsComponent { @Input() public submission: ApiSubmissionInfo; - public columnsToDisplay = ['id', 'timestamp', 'author', 'nbAnswers']; + public columnsToDisplay = ['submission-id', 'timestamp', 'author', 'nbAnswers']; public columnsToDisplayWithExpand = [...this.columnsToDisplay, 'expand'] public expandedElement: ApiSubmission | null; trackById(_:number, item: ApiSubmission){ return item.submissionId; } + + update(element: any, value: any){ + console.log("UPDATE", element, value); + } } From bdfd9e2d537ec1ab6cad5d941ceed037c9a69c0c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 10 May 2023 13:56:45 +0200 Subject: [PATCH 297/498] Addressing #357 by adding target information into submission overview for admins --- frontend/src/app/app.config.ts | 4 ++ .../submissions-list.component.html | 5 +- .../submissions-list.component.ts | 30 ++++++++++- .../template-info.component.html | 53 +++++++++++++++++++ .../template-info.component.scss | 0 .../template-info/template-info.component.ts | 19 +++++++ .../src/app/evaluation/evaluation.module.ts | 2 + .../judgement-media-viewer.component.ts | 2 +- .../pipes/format-temporal-point.pipe.ts | 28 ++++++++++ .../pipes/format-temporal-unit.pipe.ts | 22 ++++++++ .../pipes/resolve-media-item-url.pipe.ts | 40 ++++++++++++++ .../services/pipes/resolve-mediaitem.pipe.ts | 27 ++++++++++ frontend/src/app/services/services.module.ts | 15 +++++- .../media-item-viewer.component.html | 14 +++++ .../media-item-viewer.component.scss | 0 .../media-item-viewer.component.ts | 20 +++++++ frontend/src/app/shared/shared.module.ts | 7 ++- .../target-media-viewer.component.html | 9 ++++ .../target-media-viewer.component.scss | 0 .../target-media-viewer.component.ts | 14 +++++ 20 files changed, 305 insertions(+), 6 deletions(-) create mode 100644 frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html create mode 100644 frontend/src/app/evaluation/admin/submission/template-info/template-info.component.scss create mode 100644 frontend/src/app/evaluation/admin/submission/template-info/template-info.component.ts create mode 100644 frontend/src/app/services/pipes/format-temporal-point.pipe.ts create mode 100644 frontend/src/app/services/pipes/format-temporal-unit.pipe.ts create mode 100644 frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts create mode 100644 frontend/src/app/services/pipes/resolve-mediaitem.pipe.ts create mode 100644 frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html create mode 100644 frontend/src/app/shared/media-item-viewer/media-item-viewer.component.scss create mode 100644 frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts create mode 100644 frontend/src/app/shared/target-media-viewer/target-media-viewer.component.html create mode 100644 frontend/src/app/shared/target-media-viewer/target-media-viewer.component.scss create mode 100644 frontend/src/app/shared/target-media-viewer/target-media-viewer.component.ts diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index ef563c50a..8f4b267ac 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -66,6 +66,10 @@ export class AppConfig { return `${this.baseUrl}/api/${version}${path.startsWith('/') ? '' : '/'}${path}`; } + public resolveMediaItemUrl(mediaItemId: string, version: string = "v2"): string{ + return this.resolveApiUrl(`/media/${mediaItemId}`, version); + } + /** * (Re-)loads the default configuration from a JSON file. */ diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html index e16c544e4..82c739833 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html +++ b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html @@ -1,7 +1,7 @@
-

Submission list for task {{(taskId | async).substring(0,8)}}

+

Submission list for task {{(taskId | async).substring(0,8)}} | {{taskTemplate?.name ?? "N/A"}}

+
+ +
diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts index 957efb4c7..ee0eea279 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts +++ b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts @@ -4,7 +4,13 @@ import { MatButtonToggleGroup } from "@angular/material/button-toggle"; import { MatSnackBar } from "@angular/material/snack-bar"; import { MatDialog } from "@angular/material/dialog"; import { ActivatedRoute } from "@angular/router"; -import { ApiSubmission, ApiSubmissionInfo, EvaluationAdministratorService } from "../../../../../../openapi"; +import { + ApiSubmission, + ApiSubmissionInfo, ApiTaskTemplate, + EvaluationAdministratorService, + EvaluationService, + TemplateService +} from "../../../../../../openapi"; import { AppConfig } from "../../../../app.config"; import { catchError, filter, map, switchMap, withLatestFrom } from "rxjs/operators"; @@ -31,13 +37,20 @@ export class SubmissionsListComponent implements AfterViewInit, OnDestroy{ public taskRunIds: string[] = []; public submissionInfosByRunId: Map = new Map(); + public taskTemplate: ApiTaskTemplate; + private subscription: Subscription; + private sub: Subscription; + + constructor( private snackBar: MatSnackBar, private dialog: MatDialog, private activeRoute: ActivatedRoute, + private evalService: EvaluationService, private evaluationService: EvaluationAdministratorService, + private templateService: TemplateService, public config: AppConfig, ) { this.runId = this.activeRoute.paramMap.pipe(map((params) => params.get('runId'))); @@ -65,11 +78,26 @@ export class SubmissionsListComponent implements AfterViewInit, OnDestroy{ this.submissionInfosByRunId.set(si.taskId, si); }) }) + this.sub = this.runId.pipe( + switchMap((r) => this.evalService.getApiV2EvaluationByEvaluationIdInfo(r)), + catchError((error, o) => { + console.log(`[SubmissionList] Error occurred while loading template information: ${error?.message}`); + this.snackBar.open(`Error: Couldn't load template information: ${error?.message}`, null, {duration: 5000}); + return of(null); + }), + filter((r) => r != null), + switchMap((evalInfo) => this.templateService.getApiV2TemplateByTemplateIdTaskList(evalInfo.templateId)), + withLatestFrom(this.taskId) + ).subscribe(([taskList, taskId]) => { + this.taskTemplate = taskList.find((t) => t.id === taskId) + }); } ngOnDestroy(): void { this.subscription?.unsubscribe(); this.subscription = null; + this.sub?.unsubscribe(); + this.sub = null; } trackById(_: number, item: ApiSubmissionInfo){ diff --git a/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html new file mode 100644 index 000000000..d27c71f2b --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html @@ -0,0 +1,53 @@ +

Target Information

+
+
+
+ Task Type: {{template.taskType}}, Task Group: {{template.taskGroup}}
+
ID{{row.submissionId}}Submission ID{{row.submissionId.substring(0,8)}} Timestamp -
- +
+
+
+ + + + + + + + + + + + + + + + + + +
Type{{element.type}}Target + + + {{(element.target | resolveMediaItem | async)?.name}} ({{element.target.substring(0,8)}}) + + + {{(element.target | resolveMediaItem | async)?.name}} ({{element.target.substring(0,8)}}) + + + {{element.target}} + + + {{element?.target ?? 'N/A'}} + + + Auxiliary Information + + {{element?.range?.start | formatTemporalPoint}} – {{element?.range?.end | formatTemporalPoint}} + + Target + +
+
+
+ +
+
diff --git a/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.scss b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.ts b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.ts new file mode 100644 index 000000000..310322bf7 --- /dev/null +++ b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.ts @@ -0,0 +1,19 @@ +import { AfterViewInit, Component, Input } from "@angular/core"; +import { ApiTarget, ApiTaskTemplate } from "../../../../../../openapi"; + +@Component({ + selector: 'app-template-info', + templateUrl: './template-info.component.html', + styleUrls: ['./template-info.component.scss'] +}) +export class TemplateInfoComponent implements AfterViewInit{ + + public shownElement: ApiTarget; + + @Input() + public template: ApiTaskTemplate; + + ngAfterViewInit(): void { + } + +} diff --git a/frontend/src/app/evaluation/evaluation.module.ts b/frontend/src/app/evaluation/evaluation.module.ts index 78f2b808f..79ef1c990 100644 --- a/frontend/src/app/evaluation/evaluation.module.ts +++ b/frontend/src/app/evaluation/evaluation.module.ts @@ -12,6 +12,7 @@ import { MatSlideToggleModule } from "@angular/material/slide-toggle"; import { MatExpansionModule } from "@angular/material/expansion"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; +import { TemplateInfoComponent } from './admin/submission/template-info/template-info.component'; @@ -21,6 +22,7 @@ import { MatCardModule } from "@angular/material/card"; AnswerSetComponent, SubmissionsDetailsComponent, SubmissionsListComponent, + TemplateInfoComponent, ], imports: [ CommonModule, diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.ts b/frontend/src/app/judgement/judgement-media-viewer.component.ts index 9c08e5f33..2b4ee48b4 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-media-viewer.component.ts @@ -234,7 +234,7 @@ export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, private resolvePath(req: ApiJudgementRequest, time = true): string { const timeSuffix = time ? `#t=${this.startInSeconds},${this.endInSeconds}` : ''; - return this.config.resolveApiUrl(`/media/${req.collection}/${req.item}${timeSuffix}`); + return this.config.resolveApiUrl(`/media/${req.item}${timeSuffix}`); } ngAfterViewChecked(): void { diff --git a/frontend/src/app/services/pipes/format-temporal-point.pipe.ts b/frontend/src/app/services/pipes/format-temporal-point.pipe.ts new file mode 100644 index 000000000..a4a5cf6a6 --- /dev/null +++ b/frontend/src/app/services/pipes/format-temporal-point.pipe.ts @@ -0,0 +1,28 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ApiTemporalPoint } from "../../../../openapi"; +import { FormatTemporalUnitPipe } from "./format-temporal-unit.pipe"; +import { FormatTimePipePipe } from "./format-time-pipe.pipe"; + +@Pipe({ + name: 'formatTemporalPoint' +}) +export class FormatTemporalPointPipe implements PipeTransform { + + constructor( + private unitPipe: FormatTemporalUnitPipe, + private timePipe: FormatTimePipePipe, + ) {} + transform(value: ApiTemporalPoint, ...args: unknown[]): string { + switch(value.unit){ + case "FRAME_NUMBER": + case "SECONDS": + return `${value.value}${this.unitPipe.transform(value.unit)}`; + case "MILLISECONDS": + return `${Number(value.value) / 1000}${this.unitPipe.transform(value.unit)}`; + case "TIMECODE": + return this.timePipe.transform(Number(value.value)); + + } + } + +} diff --git a/frontend/src/app/services/pipes/format-temporal-unit.pipe.ts b/frontend/src/app/services/pipes/format-temporal-unit.pipe.ts new file mode 100644 index 000000000..06d877ca2 --- /dev/null +++ b/frontend/src/app/services/pipes/format-temporal-unit.pipe.ts @@ -0,0 +1,22 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ApiTemporalUnit } from "../../../../openapi"; + +@Pipe({ + name: 'formatTemporalUnit' +}) +export class FormatTemporalUnitPipe implements PipeTransform { + + transform(value: ApiTemporalUnit, ...args: unknown[]): unknown { + switch(value){ + case "FRAME_NUMBER": + return "f"; + case "SECONDS": + return "s"; + case "MILLISECONDS": + return "ms"; + case "TIMECODE": + return ""; + } + } + +} diff --git a/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts b/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts new file mode 100644 index 000000000..95f0758b2 --- /dev/null +++ b/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts @@ -0,0 +1,40 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ApiMediaItem, ApiTemporalRange } from "../../../../openapi"; +import { AppConfig } from "../../app.config"; +import { TimeUtilities } from "../../utilities/time.utilities"; + +export interface MediaItemUrlOptions { + range: ApiTemporalRange +} + +function isOptions(obj: any): obj is MediaItemUrlOptions{ + return 'range' in obj; +} + +@Pipe({ + name: 'resolveMediaItemUrl' +}) +export class ResolveMediaItemUrlPipe implements PipeTransform { + + constructor(private config: AppConfig){ + + } + + transform(value: ApiMediaItem, ...args: unknown[]): string { + console.log('[ResolveMediaItemUrlPipe] item and args', value, args) + let suffix = ''; + if(args && args.length > 0){ + console.log("Args given", typeof(args[0])) + if(args[0] && isOptions(args[0])){ + console.log("args 0 is options") + const range = args[0].range; + const startInSeconds = TimeUtilities.point2Milliseconds(range.start, value.fps) / 1000; + const endInSeconds = TimeUtilities.point2Milliseconds(range.end, value.fps) / 1000; + + suffix = `#t=${startInSeconds},${endInSeconds}`; + } + } + return this.config.resolveMediaItemUrl(`${value.mediaItemId}${suffix}`); + } + +} diff --git a/frontend/src/app/services/pipes/resolve-mediaitem.pipe.ts b/frontend/src/app/services/pipes/resolve-mediaitem.pipe.ts new file mode 100644 index 000000000..1a6eb2e4b --- /dev/null +++ b/frontend/src/app/services/pipes/resolve-mediaitem.pipe.ts @@ -0,0 +1,27 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ApiMediaItem, CollectionService } from "../../../../openapi"; +import { Observable } from "rxjs"; + +@Pipe({ + name: 'resolveMediaItem' +}) +export class ResolveMediaItemPipe implements PipeTransform { + + private cachedItem: Observable | null = null + private cachedId: string = '' + + constructor( + private mediaService: CollectionService, + ){} + + transform(value: string, ...args: unknown[]): Observable { + if(value !== this.cachedId){ + this.cachedItem = null; + this.cachedId = value; + this.cachedItem = this.mediaService.getApiV2MediaItemByMediaItemId(value); + } + console.log(this.cachedItem) + return this.cachedItem; + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 0ca115332..617037e92 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -13,6 +13,10 @@ import { ResolveTeamPipe } from './pipes/resolve-team.pipe'; import { EnhanceTaskSubmissionInfoPipe } from './pipes/enhance-task-submission-info.pipe'; import {ApiModule, Configuration} from '../../../openapi'; import { SpaceToNewlinePipe } from './pipes/space-to-newline.pipe'; +import { FormatTemporalUnitPipe } from './pipes/format-temporal-unit.pipe'; +import { FormatTemporalPointPipe } from './pipes/format-temporal-point.pipe'; +import { ResolveMediaItemPipe } from './pipes/resolve-mediaitem.pipe'; +import { ResolveMediaItemUrlPipe } from './pipes/resolve-media-item-url.pipe'; /** * Provides the {@link AppConfig} reference. @@ -40,7 +44,10 @@ export function initializeApiConfig(appConfig: AppConfig) { EnhanceTaskPastInfoPipe, ResolveTeamPipe, EnhanceTaskSubmissionInfoPipe, - SpaceToNewlinePipe + SpaceToNewlinePipe, + FormatTemporalPointPipe, + ResolveMediaItemPipe, + ResolveMediaItemUrlPipe ], declarations: [ RoundPipePipe, @@ -52,7 +59,11 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveTeamPipe, EnhanceTaskSubmissionInfoPipe, SpaceToNewlinePipe, + FormatTemporalUnitPipe, + FormatTemporalPointPipe, + ResolveMediaItemPipe, + ResolveMediaItemUrlPipe, ], - providers: [AuthenticationService, NavigationService, CanDeactivateGuard], + providers: [AuthenticationService, NavigationService, CanDeactivateGuard, FormatTemporalPointPipe, FormatTemporalUnitPipe, FormatTimePipePipe, Epoch2DatePipePipe], }) export class ServicesModule {} diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html new file mode 100644 index 000000000..76f72c877 --- /dev/null +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html @@ -0,0 +1,14 @@ + +

Name{{item.name}} ID{{item.mediaItemId.substring(0,8)}}

+ + + The image media item + + + + + + (Text:) {{item.name}} + + +
diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.scss b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts new file mode 100644 index 000000000..806cdc4e8 --- /dev/null +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from "@angular/core"; +import { ApiMediaItem, ApiTemporalRange } from "../../../../openapi"; +import { AppConfig } from "../../app.config"; + +@Component({ + selector: 'app-media-item-viewer', + templateUrl: './media-item-viewer.component.html', + styleUrls: ['./media-item-viewer.component.scss'] +}) +export class MediaItemViewerComponent { + + + @Input() + public item: ApiMediaItem; + + @Input() + public range?: ApiTemporalRange; + + +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index bc92f6794..6307c411d 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -14,6 +14,8 @@ import { DynamicTableComponent } from './dynamic-table/dynamic-table.component'; import {MatTableModule} from '@angular/material/table'; import { ActionableDynamicTable } from './actionable-dynamic-table/actionable-dynamic-table.component'; import { ServerInfoComponent } from './server-info/server-info.component'; +import { TargetMediaViewerComponent } from './target-media-viewer/target-media-viewer.component'; +import { MediaItemViewerComponent } from './media-item-viewer/media-item-viewer.component'; @NgModule({ declarations: [ @@ -25,6 +27,8 @@ import { ServerInfoComponent } from './server-info/server-info.component'; DynamicTableComponent, ActionableDynamicTable, ServerInfoComponent, + TargetMediaViewerComponent, + MediaItemViewerComponent, ], exports: [ BackButtonComponent, @@ -34,7 +38,8 @@ import { ServerInfoComponent } from './server-info/server-info.component'; ApiStatusComponent, DownloadButtonComponent, UploadJsonButtonComponent, - ActionableDynamicTable + ActionableDynamicTable, + TargetMediaViewerComponent ], imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule] }) diff --git a/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.html b/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.html new file mode 100644 index 000000000..ac363e300 --- /dev/null +++ b/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.html @@ -0,0 +1,9 @@ + + +
{{target.target}}
+
Judgement targets do not have a preview
+
Judgement targets do not have a preview
+ + +
+
diff --git a/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.scss b/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.ts b/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.ts new file mode 100644 index 000000000..0c1d3a7d7 --- /dev/null +++ b/frontend/src/app/shared/target-media-viewer/target-media-viewer.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from "@angular/core"; +import { ApiTarget } from "../../../../openapi"; + +@Component({ + selector: 'app-target-media-viewer', + templateUrl: './target-media-viewer.component.html', + styleUrls: ['./target-media-viewer.component.scss'] +}) +export class TargetMediaViewerComponent { + + + @Input() + public target: ApiTarget +} From 1f7893cc18e0893a3ceda3afd9fdebcf5114196d Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 10 May 2023 15:25:39 +0200 Subject: [PATCH 298/498] Resolved #357. By displaying submission content --- .../admin/submission/answer/answer.component.html | 13 +++++++++---- .../admin/submission/answer/answer.component.ts | 4 ++-- .../submissions-details.component.html | 13 ++++++------- .../submissions-details.component.ts | 14 ++++++++++++++ .../submissions-list.component.html | 2 +- .../submissions-list.component.ts | 15 +++++++++------ .../template-info/template-info.component.html | 4 ++-- .../services/pipes/resolve-media-item-url.pipe.ts | 4 ---- .../media-item-viewer.component.html | 2 +- frontend/src/app/shared/shared.module.ts | 3 ++- frontend/src/styles.scss | 4 ++++ 11 files changed, 50 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html index 119773fb2..1ccff96f8 100644 --- a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html @@ -1,15 +1,15 @@ - + - + - + @@ -19,7 +19,12 @@ - + + + + diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts index 4d496ea43..43ac526bf 100644 --- a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts @@ -11,9 +11,9 @@ export class AnswerComponent { @Input() public answers: ApiAnswer[]; - public displayedColumnsWithoutText = ['type', 'item', 'start', 'end'] + public displayedColumnsWithoutText = ['type', 'item', 'start', 'end', 'preview'] public displayedColumnsText = ['type', 'text'] - public displayedHeaders = ['type', 'text', 'item', 'start', 'end'] + public displayedHeaders = ['type', 'text', 'item', 'start', 'end', 'preview'] public hasTextType = (index: number, rowData: ApiAnswer) => rowData.type === ApiAnswerType.TEXT; diff --git a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html index 710e28511..538c0b8b4 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html +++ b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.html @@ -21,24 +21,23 @@

Submissions of {{submission.taskId.substring(0,8)}}

- + [class.submissions-expanded-row]="isExpanded(element)" + (click)="toggleExpanded(element)">
Type{{row.type}}{{row.type}} Text{{row.text}}{{row.text}} Media Item{{row.item ? row.item.name + ' ('+ row.item.type+')': 'N/A'}}{{row.item ? row.item.name + ' ('+ row.item.type+')': 'N/A'}} StartEnd {{row.end ? (row.end | formatTime: true): "N/A"}} Media Item + +
  - -
+
+
diff --git a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts index b27884ae6..c3e4d68e4 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts +++ b/frontend/src/app/evaluation/admin/submission/submissions-details/submissions-details.component.ts @@ -23,6 +23,20 @@ export class SubmissionsDetailsComponent { public columnsToDisplayWithExpand = [...this.columnsToDisplay, 'expand'] public expandedElement: ApiSubmission | null; + public expandedElements: ApiSubmission[] = []; + + public isExpanded(element: ApiSubmission){ + return this.expandedElements.includes(element); + } + + public toggleExpanded(element: ApiSubmission){ + if(this.isExpanded(element)){ + this.expandedElements.splice(this.expandedElements.indexOf(element), 1); + }else{ + this.expandedElements.push(element); + } + } + trackById(_:number, item: ApiSubmission){ return item.submissionId; } diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html index 82c739833..b253b06ec 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html +++ b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.html @@ -8,7 +8,7 @@

Submission list for task {{(taskId | async).substring(0,8)}} | {{taskTemplat refresh
- Poll for updates (every {{pollingFrequencyInSeconds}}s + Poll for updates (every {{pollingFrequencyInSeconds}}s)
Anonymize

diff --git a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts index ee0eea279..ec997494f 100644 --- a/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts +++ b/frontend/src/app/evaluation/admin/submission/submissions-list/submissions-list.component.ts @@ -71,12 +71,15 @@ export class SubmissionsListComponent implements AfterViewInit, OnDestroy{ }) ) .subscribe((s: ApiSubmissionInfo[]) => { - this.taskRunIds = []; - this.submissionInfosByRunId.clear(); - s.forEach((si) => { - this.taskRunIds.push(si.taskId); - this.submissionInfosByRunId.set(si.taskId, si); - }) + /* The assumption here is, that task runs do not magically disappear */ + if(this.taskRunIds.length < s.length){ + s.forEach((si) => { + if(!this.taskRunIds.includes(si.taskId)){ + this.taskRunIds.push(si.taskId); + this.submissionInfosByRunId.set(si.taskId, si); + } + }) + } }) this.sub = this.runId.pipe( switchMap((r) => this.evalService.getApiV2EvaluationByEvaluationIdInfo(r)), diff --git a/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html index d27c71f2b..fca385a69 100644 --- a/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html +++ b/frontend/src/app/evaluation/admin/submission/template-info/template-info.component.html @@ -1,6 +1,6 @@

Target Information

-
+
Task Type: {{template.taskType}}, Task Group: {{template.taskGroup}}
@@ -47,7 +47,7 @@

Target Information

-
+
diff --git a/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts b/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts index 95f0758b2..f1e8f929b 100644 --- a/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts +++ b/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts @@ -21,16 +21,12 @@ export class ResolveMediaItemUrlPipe implements PipeTransform { } transform(value: ApiMediaItem, ...args: unknown[]): string { - console.log('[ResolveMediaItemUrlPipe] item and args', value, args) let suffix = ''; if(args && args.length > 0){ - console.log("Args given", typeof(args[0])) if(args[0] && isOptions(args[0])){ - console.log("args 0 is options") const range = args[0].range; const startInSeconds = TimeUtilities.point2Milliseconds(range.start, value.fps) / 1000; const endInSeconds = TimeUtilities.point2Milliseconds(range.end, value.fps) / 1000; - suffix = `#t=${startInSeconds},${endInSeconds}`; } } diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html index 76f72c877..b1f431d31 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html @@ -1,5 +1,5 @@ -

Name{{item.name}} ID{{item.mediaItemId.substring(0,8)}}

+

Name: {{item.name}} ({{item.mediaItemId.substring(0,8)}})

The image media item diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 6307c411d..a90c8016b 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -39,7 +39,8 @@ import { MediaItemViewerComponent } from './media-item-viewer/media-item-viewer. DownloadButtonComponent, UploadJsonButtonComponent, ActionableDynamicTable, - TargetMediaViewerComponent + TargetMediaViewerComponent, + MediaItemViewerComponent ], imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule] }) diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 7b18514fe..0944abf6f 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -118,3 +118,7 @@ $warn: mat-palette($mat-red, 500); .width-full { width: 100%; } + +.strong { + font-weight: bold; +} From 82794005e410d00dce8e8a078c95422dcdfb2f42 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 08:20:13 +0200 Subject: [PATCH 299/498] Added (untested) githooks for client code regeneration. Requires manual instalation --- githooks/README.md | 9 +++++++++ githooks/post-checkout | 9 +++++++++ githooks/post-merge | 10 ++++++++++ 3 files changed, 28 insertions(+) create mode 100644 githooks/README.md create mode 100644 githooks/post-checkout create mode 100644 githooks/post-merge diff --git a/githooks/README.md b/githooks/README.md new file mode 100644 index 000000000..d7a495a73 --- /dev/null +++ b/githooks/README.md @@ -0,0 +1,9 @@ +# README githooks + +Development tooling sometimes requires client-side hooks, particularly when using code generation. + +Files in this directory should be symlinked to `.git/hooks` for them to work. + +``` +ln -s {hook} ../.git/hooks/ +``` diff --git a/githooks/post-checkout b/githooks/post-checkout new file mode 100644 index 000000000..55f8e9650 --- /dev/null +++ b/githooks/post-checkout @@ -0,0 +1,9 @@ +#!/bin/sh + +# Inspired by https://stackoverflow.com/a/16853458 + +CHANGED=`git diff $1 $2 --stat -- $GIT_DIR/../docs/oas.json | wc -l ` +if [ $CHANGED -gt 0 ]; + echo "OpenApi specifications changed. Regenerating client bindings." + gradlew openApiGenerate +fi diff --git a/githooks/post-merge b/githooks/post-merge new file mode 100644 index 000000000..1ba408437 --- /dev/null +++ b/githooks/post-merge @@ -0,0 +1,10 @@ +#!/bin/sh + +# Inspired by https://stackoverflow.com/a/16853458 + +CHANGED=`git diff HEAD#{1} --stat -- $GIT_DIR/../docs/oas.json | wc -l` +if [ $CHANGED -gt 0 ]; +then + echo "OpenApi specifications changed. Regenerating client bindings" + $GIT_DIR/../gradlew openApiGenerate +fi From f63be8eeeb5a8fc2e9ccefe58b73eea991e263fd Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 09:04:31 +0200 Subject: [PATCH 300/498] More verbose git hooks --- githooks/post-checkout | 3 +++ githooks/post-merge | 2 ++ 2 files changed, 5 insertions(+) diff --git a/githooks/post-checkout b/githooks/post-checkout index 55f8e9650..f233c60ec 100644 --- a/githooks/post-checkout +++ b/githooks/post-checkout @@ -4,6 +4,9 @@ CHANGED=`git diff $1 $2 --stat -- $GIT_DIR/../docs/oas.json | wc -l ` if [ $CHANGED -gt 0 ]; +then echo "OpenApi specifications changed. Regenerating client bindings." gradlew openApiGenerate +else + echo "OpenApi specifications not changed." fi diff --git a/githooks/post-merge b/githooks/post-merge index 1ba408437..d61951d12 100644 --- a/githooks/post-merge +++ b/githooks/post-merge @@ -7,4 +7,6 @@ if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings" $GIT_DIR/../gradlew openApiGenerate +else + echo "No changes in OpenApi specifications" fi From 2dfc97b84f15ec19d3cb8f1dfcdf931ca39950ea Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 09:09:51 +0200 Subject: [PATCH 301/498] Moved previously introduced hooks to husky --- {githooks => frontend/.husky}/post-checkout | 3 +++ {githooks => frontend/.husky}/post-merge | 3 +++ githooks/README.md | 9 --------- 3 files changed, 6 insertions(+), 9 deletions(-) rename {githooks => frontend/.husky}/post-checkout (83%) rename {githooks => frontend/.husky}/post-merge (83%) delete mode 100644 githooks/README.md diff --git a/githooks/post-checkout b/frontend/.husky/post-checkout similarity index 83% rename from githooks/post-checkout rename to frontend/.husky/post-checkout index f233c60ec..2a2457a65 100644 --- a/githooks/post-checkout +++ b/frontend/.husky/post-checkout @@ -1,5 +1,8 @@ #!/bin/sh +. "$(dirname "$0")/_/husky.sh" +. "$(dirname "$0")/common.sh" + # Inspired by https://stackoverflow.com/a/16853458 CHANGED=`git diff $1 $2 --stat -- $GIT_DIR/../docs/oas.json | wc -l ` diff --git a/githooks/post-merge b/frontend/.husky/post-merge similarity index 83% rename from githooks/post-merge rename to frontend/.husky/post-merge index d61951d12..0b9db5818 100644 --- a/githooks/post-merge +++ b/frontend/.husky/post-merge @@ -1,5 +1,8 @@ #!/bin/sh +. "$(dirname "$0")/_/husky.sh" +. "$(dirname "$0")/common.sh" + # Inspired by https://stackoverflow.com/a/16853458 CHANGED=`git diff HEAD#{1} --stat -- $GIT_DIR/../docs/oas.json | wc -l` diff --git a/githooks/README.md b/githooks/README.md deleted file mode 100644 index d7a495a73..000000000 --- a/githooks/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# README githooks - -Development tooling sometimes requires client-side hooks, particularly when using code generation. - -Files in this directory should be symlinked to `.git/hooks` for them to work. - -``` -ln -s {hook} ../.git/hooks/ -``` From 99e5944ce62ee9b16d7514e77d3b6b0ca41b9e7a Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 10:25:25 +0200 Subject: [PATCH 302/498] Fixed path in git hooks --- frontend/.husky/post-checkout | 2 +- frontend/.husky/post-merge | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/.husky/post-checkout b/frontend/.husky/post-checkout index 2a2457a65..527eadc0b 100644 --- a/frontend/.husky/post-checkout +++ b/frontend/.husky/post-checkout @@ -5,7 +5,7 @@ # Inspired by https://stackoverflow.com/a/16853458 -CHANGED=`git diff $1 $2 --stat -- $GIT_DIR/../docs/oas.json | wc -l ` +CHANGED=`git diff $1 $2 --stat -- docs/oas.json | wc -l ` if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings." diff --git a/frontend/.husky/post-merge b/frontend/.husky/post-merge index 0b9db5818..fc7a0f6db 100644 --- a/frontend/.husky/post-merge +++ b/frontend/.husky/post-merge @@ -5,7 +5,7 @@ # Inspired by https://stackoverflow.com/a/16853458 -CHANGED=`git diff HEAD#{1} --stat -- $GIT_DIR/../docs/oas.json | wc -l` +CHANGED=`git diff HEAD#{1} --stat -- docs/oas.json | wc -l` if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings" From c97cceb397021754b7da9dacd9a890efbd487e17 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 10:34:33 +0200 Subject: [PATCH 303/498] Adjusted path in git hooks once more --- frontend/.husky/post-checkout | 2 +- frontend/.husky/post-merge | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/.husky/post-checkout b/frontend/.husky/post-checkout index 527eadc0b..57c49d41a 100644 --- a/frontend/.husky/post-checkout +++ b/frontend/.husky/post-checkout @@ -5,7 +5,7 @@ # Inspired by https://stackoverflow.com/a/16853458 -CHANGED=`git diff $1 $2 --stat -- docs/oas.json | wc -l ` +CHANGED=`git diff $1 $2 --stat -- doc/oas.json | wc -l ` if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings." diff --git a/frontend/.husky/post-merge b/frontend/.husky/post-merge index fc7a0f6db..6e33fb6a2 100644 --- a/frontend/.husky/post-merge +++ b/frontend/.husky/post-merge @@ -5,7 +5,7 @@ # Inspired by https://stackoverflow.com/a/16853458 -CHANGED=`git diff HEAD#{1} --stat -- docs/oas.json | wc -l` +CHANGED=`git diff HEAD#{1} --stat -- doc/oas.json | wc -l` if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings" From 337cb99793e7588fd9bfa6e4b1fab1b23c8116db Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 10:54:38 +0200 Subject: [PATCH 304/498] Fixed broken gradle wrapper path in githooks --- frontend/.husky/post-checkout | 2 +- frontend/.husky/post-merge | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/.husky/post-checkout b/frontend/.husky/post-checkout index 57c49d41a..b91015b54 100644 --- a/frontend/.husky/post-checkout +++ b/frontend/.husky/post-checkout @@ -9,7 +9,7 @@ CHANGED=`git diff $1 $2 --stat -- doc/oas.json | wc -l ` if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings." - gradlew openApiGenerate + ./gradlew openApiGenerate else echo "OpenApi specifications not changed." fi diff --git a/frontend/.husky/post-merge b/frontend/.husky/post-merge index 6e33fb6a2..bbb136035 100644 --- a/frontend/.husky/post-merge +++ b/frontend/.husky/post-merge @@ -9,7 +9,7 @@ CHANGED=`git diff HEAD#{1} --stat -- doc/oas.json | wc -l` if [ $CHANGED -gt 0 ]; then echo "OpenApi specifications changed. Regenerating client bindings" - $GIT_DIR/../gradlew openApiGenerate + ./gradlew openApiGenerate else echo "No changes in OpenApi specifications" fi From 67fef3926fc694f3e0fd2b7c71f5305def812012 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 11:56:31 +0200 Subject: [PATCH 305/498] Addressing #387 by having login page more prominent --- frontend/src/app/app.component.html | 6 +++++- .../admin/submission/answer/answer.component.html | 2 +- .../media-item-viewer/media-item-viewer.component.html | 2 +- .../shared/media-item-viewer/media-item-viewer.component.ts | 3 +++ frontend/src/app/user/user.module.ts | 5 ++++- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index a36188893..f56f91fdb 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -65,6 +65,10 @@
-
+
+ + + + diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html index 1ccff96f8..31a00e6a2 100644 --- a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html @@ -22,7 +22,7 @@ Media Item - + diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html index b1f431d31..b65f72f20 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html @@ -1,5 +1,5 @@ -

Name: {{item.name}} ({{item.mediaItemId.substring(0,8)}})

+

Name: {{item.name}} ({{item.mediaItemId.substring(0,8)}})

The image media item diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts index 806cdc4e8..b77a12e26 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts @@ -10,6 +10,9 @@ import { AppConfig } from "../../app.config"; export class MediaItemViewerComponent { + @Input() + public displayNameAndId: boolean = true; + @Input() public item: ApiMediaItem; diff --git a/frontend/src/app/user/user.module.ts b/frontend/src/app/user/user.module.ts index 6e7d7d76b..574ed6dee 100644 --- a/frontend/src/app/user/user.module.ts +++ b/frontend/src/app/user/user.module.ts @@ -39,7 +39,10 @@ import { MatSortModule } from '@angular/material/sort'; ClipboardModule, SharedModule, MatSortModule, - FormsModule, + FormsModule ], + exports: [ + LoginComponent + ] }) export class UserModule {} From 0839c2b7218e907ee60b579599e77bbc9fda61c6 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 11 May 2023 15:19:14 +0200 Subject: [PATCH 306/498] #310: Switched to trash bin icon for removing tasks, task groups and task types --- .../submission/answer/answer.component.html | 2 +- .../submission/answer/answer.component.ts | 26 ++++++++++++++++++- .../actionable-dynamic-table.component.html | 4 +-- .../actionable-dynamic-table.component.ts | 7 +++++ .../media-item-viewer.component.html | 7 ++++- .../media-item-viewer.component.ts | 5 ++++ .../task-templates-list.component.html | 2 +- 7 files changed, 47 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html index 31a00e6a2..3a7e2550b 100644 --- a/frontend/src/app/evaluation/admin/submission/answer/answer.component.html +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.html @@ -22,7 +22,7 @@ Media Item - + diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts index 43ac526bf..74674a8af 100644 --- a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from "@angular/core"; -import { ApiAnswer, ApiAnswerType } from "../../../../../../openapi"; +import { ApiAnswer, ApiAnswerType, ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit } from "../../../../../../openapi"; @Component({ selector: 'app-answer', @@ -15,6 +15,30 @@ export class AnswerComponent { public displayedColumnsText = ['type', 'text'] public displayedHeaders = ['type', 'text', 'item', 'start', 'end', 'preview'] + /** + * + * @param answer + */ + public transformToRange(answer: ApiAnswer): ApiTemporalRange | null{ + console.log(answer.temporalRange); + if(answer.type == ApiAnswerType.TEMPORAL && answer.temporalRange){ + /* In case leakage of temporalRange is part of api */ + /*return { + start: {value: answer.temporalRange.start['millisecond'], unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, + end: {value: answer.temporalRange.start['millisecond'], unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, + } as ApiTemporalRange;*/ + if(answer.start && answer.end){ + if(answer.start !== answer.end){ + return { + start: {value: ''+answer.start, unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, + end: {value: ''+answer.end, unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, + } as ApiTemporalRange + } + } + } + return undefined; + } + public hasTextType = (index: number, rowData: ApiAnswer) => rowData.type === ApiAnswerType.TEXT; public hasNotTextType = (index: number, rowData: ApiAnswer) => rowData.type !== ApiAnswerType.TEXT; diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index 3c452e0a1..e6a7bf759 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -12,10 +12,10 @@

{{tableTitle}}

diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts index 271c01779..7559a18bd 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts @@ -48,6 +48,13 @@ export class ActionableDynamicTable { @Input() public trackByProperty?: string; + @Input() + public addIcon = 'add'; + @Input() + public removeIcon = 'delete'; + @Input() + public editIcon = 'edit'; + @Input() public onEdit?: (element: T) => void; diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html index b65f72f20..61d63f51d 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html @@ -1,4 +1,9 @@ - + + + +

Name: {{item.name}} ({{item.mediaItemId.substring(0,8)}})

diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts index b77a12e26..e4d115e7e 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts @@ -9,6 +9,11 @@ import { AppConfig } from "../../app.config"; }) export class MediaItemViewerComponent { + @Input() + public toggleable: boolean = false; + + @Input() + public showing: boolean = true; @Input() public displayNameAndId: boolean = true; diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html index 422a8a09a..9dec9d155 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html @@ -44,7 +44,7 @@

Tasks

edit diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html index 00386884d..23efc400f 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html @@ -5,19 +5,57 @@ [displayedColumns]="displayedColumns" [onRemove]="remove" tableTitle="Task types" + style="min-width: 100%;" > - - - - - + + + + + + - - - - - - + + + + + + + + + Hint Options + + +
    +
  • {{hint}}
  • +
+
+ {{element.hintOptions[0]}} + +
+ + Submission Options + + +
    +
  • {{elem}}
  • +
+
+ {{element.submissionOptions[0]}} + +
+ + Task Options + + +
    +
  • {{hint}}
  • +
+
+ {{element.taskOptions[0]}} + +
diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts index 7c2c59174..246ae0470 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts @@ -2,8 +2,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; import { TemplateBuilderService } from "../../template-builder.service"; import { MatDialog } from "@angular/material/dialog"; -import { MatSnackBar } from "@angular/material/snack-bar"; -import { ApiTaskGroup, ApiTaskType } from "../../../../../../openapi"; +import { ApiTaskType } from "../../../../../../openapi"; import { Observable } from "rxjs"; import { filter, map } from "rxjs/operators"; import { @@ -77,11 +76,14 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp {key: 'name', header: 'Name', property: 'name', type: ActionableDynamicTableColumnType.TEXT}, {key: 'duration', header: 'Duration', property: 'duration', type: ActionableDynamicTableColumnType.TEXT}, {key: 'target', header: 'Target', property: 'targetOption', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'hints', header: 'Hint Options', type: ActionableDynamicTableColumnType.CUSTOM}, + {key: 'submissions', header: 'Submission Options', type: ActionableDynamicTableColumnType.CUSTOM}, + {key: 'tasks', header: 'Task Options', type: ActionableDynamicTableColumnType.CUSTOM}, {key: 'score', header: 'Score', property: 'scoreOption', type: ActionableDynamicTableColumnType.TEXT}, {key: 'actions', header: 'Actions', type: ActionableDynamicTableColumnType.ACTION, actions: [ActionableDynamicTableActionType.REMOVE],} ]; - displayedColumns= ['name', 'duration', 'target', 'score', 'actions']; + displayedColumns= ['name', 'duration', 'target', 'hints','submissions', 'tasks','score', 'actions']; @ViewChild("typesTable") table: ActionableDynamicTable; diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 160131bb8..17e6c6359 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -40,9 +40,9 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} -
- - +
+ +
From 3e91fdcb62b4fde1c03c75807dfb9bd51332f752 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 18 May 2023 17:32:27 +0200 Subject: [PATCH 310/498] #387 Some adjustements in submission view and api --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 10 +++++ .../handler/preview/PreviewImageHandler.kt | 3 +- .../handler/preview/PreviewVideoHandler.kt | 3 +- .../types/competition/team/ApiTeamGroup.kt | 7 +++- .../api/rest/types/evaluation/ApiAnswer.kt | 11 ++++- .../api/rest/types/evaluation/ApiAnswerSet.kt | 2 + doc/oas.json | 42 +++++-------------- frontend/src/app/app.config.ts | 4 ++ .../submission/answer/answer.component.ts | 34 ++++++--------- .../pipes/resolve-media-item-preview.pipe.ts | 26 ++++++++++++ .../pipes/resolve-media-item-url.pipe.ts | 9 ++-- frontend/src/app/services/services.module.ts | 5 ++- .../media-item-viewer.component.html | 7 +++- .../media-item-viewer.component.ts | 10 +++++ .../template-builder.component.html | 4 +- frontend/src/app/utilities/time.utilities.ts | 4 ++ frontend/src/flex.scss | 16 +++++++ 17 files changed, 130 insertions(+), 67 deletions(-) create mode 100644 frontend/src/app/services/pipes/resolve-media-item-preview.pipe.ts diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 502731a3a..b6966987a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -42,6 +42,8 @@ import io.javalin.apibuilder.ApiBuilder.* import io.javalin.http.staticfiles.Location import io.javalin.community.ssl.SSLPlugin import io.javalin.openapi.CookieAuth +import io.javalin.openapi.OpenApiContact +import io.javalin.openapi.OpenApiLicense import io.javalin.openapi.plugin.* import io.javalin.openapi.plugin.swagger.SwaggerConfiguration import io.javalin.openapi.plugin.swagger.SwaggerPlugin @@ -218,6 +220,14 @@ object RestApi { t.title = "DRES API" t.version = DRES.VERSION t.description = "API for DRES (Distributed Retrieval Evaluation Server), Version ${DRES.VERSION}" + val contact = OpenApiContact() + contact.url = "https://dres.dev" + contact.name = "The DRES Dev Team" + t.contact = contact + val license = OpenApiLicense() + license.name = "MIT" + license.identifier = "MIT" + t.license = license } u.withSecurity(SecurityComponentConfiguration() .withSecurityScheme("CookieAuth", CookieAuth(AccessManager.SESSION_COOKIE_NAME)) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt index 01ef5fbd3..19e83cc61 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt @@ -34,7 +34,6 @@ class PreviewImageHandler(store: TransientEntityStore, cache: CacheManager) : Ab OpenApiResponse("400"), OpenApiResponse("404") ], - ignore = true, methods = [HttpMethod.GET] ) override fun get(ctx: Context) { @@ -50,4 +49,4 @@ class PreviewImageHandler(store: TransientEntityStore, cache: CacheManager) : Ab override fun doGet(ctx: Context): Any { throw UnsupportedOperationException("PreviewImageHandler::doGet() is not supported and should not be executed!") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewVideoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewVideoHandler.kt index 1685f3dee..c44ec6258 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewVideoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewVideoHandler.kt @@ -35,7 +35,6 @@ class PreviewVideoHandler(store: TransientEntityStore, cache: CacheManager) : Ab OpenApiResponse("400"), OpenApiResponse("404") ], - ignore = true, methods = [HttpMethod.GET] ) override fun get(ctx: Context) { @@ -52,4 +51,4 @@ class PreviewVideoHandler(store: TransientEntityStore, cache: CacheManager) : Ab override fun doGet(ctx: Context): Any { throw UnsupportedOperationException("PreviewImageHandler::doGet() is not supported and should not be executed!") } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt index b1e8efcca..3c9a3e710 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/team/ApiTeamGroup.kt @@ -9,4 +9,9 @@ import dev.dres.data.model.template.team.TeamGroupId * @author Loris Sauter * @version 1.0.0 */ -data class ApiTeamGroup(val id: TeamGroupId? = null, val name: String? = null, val teams: List = emptyList(), val aggregation: String? = null) \ No newline at end of file +data class ApiTeamGroup( + val id: TeamGroupId? = null, + val name: String? = null, + val teams: List = emptyList(), + val aggregation: String? = null +) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt index 7495f5bfa..79cf0a1f7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt @@ -3,9 +3,12 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.Answer import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswer +import io.javalin.openapi.OpenApiContentProperty +import io.javalin.openapi.OpenApiIgnore import kotlinx.dnq.query.filter import kotlinx.dnq.query.firstOrNull @@ -13,10 +16,14 @@ data class ApiAnswer( val type: ApiAnswerType, override val item: ApiMediaItem?, override val text: String?, - override val start: Long?, - override val end: Long? + + override val start: Long?, // ms + override val end: Long? // ms ) : Answer { + override val temporalRange: TemporalRange? + @OpenApiIgnore get() = super.temporalRange // Do not leak temporal range into api + /** * Creates a new [DbAnswer] for this [ApiAnswer]. Requires an ongoing transaction. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt index 6aa7baf9b..04a3e4e6c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.Task import dev.dres.data.model.run.TaskId import dev.dres.data.model.submissions.* +import io.javalin.openapi.OpenApi import io.javalin.openapi.OpenApiIgnore import kotlinx.dnq.query.addAll import kotlinx.dnq.query.filter @@ -27,6 +28,7 @@ data class ApiAnswerSet( ) : AnswerSet { @JsonIgnore + @get: OpenApiIgnore override lateinit var submission: ApiSubmission internal set diff --git a/doc/oas.json b/doc/oas.json index 8dd0dc3da..3ee4da43e 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -5,8 +5,16 @@ "summary" : null, "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", "termsOfService" : null, - "contact" : null, - "license" : null, + "contact" : { + "name" : "The DRES Dev Team", + "url" : "https://dres.dev", + "email" : null + }, + "license" : { + "name" : "MIT", + "identifier" : "MIT", + "url" : null + }, "version" : "2.0.0" }, "paths" : { @@ -5469,9 +5477,6 @@ "end" : { "type" : "integer", "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" } }, "required" : [ "type" ] @@ -5494,12 +5499,9 @@ "items" : { "$ref" : "#/components/schemas/ApiAnswer" } - }, - "submission" : { - "$ref" : "#/components/schemas/ApiSubmission" } }, - "required" : [ "id", "status", "taskId", "answers", "submission" ] + "required" : [ "id", "status", "taskId", "answers" ] }, "ApiAnswerType" : { "type" : "string", @@ -6181,28 +6183,6 @@ }, "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, "RunProperties" : { "type" : "object", "additionalProperties" : false, diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 8f4b267ac..7038ed447 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -70,6 +70,10 @@ export class AppConfig { return this.resolveApiUrl(`/media/${mediaItemId}`, version); } + public resolveImagePreviewUrl(mediaItemId: string, timeInMs?:string,version: string = "v2"): string{ + return this.resolveApiUrl(`/preview/${mediaItemId}${timeInMs ? `/${timeInMs}` : ''}`, version) + } + /** * (Re-)loads the default configuration from a JSON file. */ diff --git a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts index 74674a8af..2671a2552 100644 --- a/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts +++ b/frontend/src/app/evaluation/admin/submission/answer/answer.component.ts @@ -2,38 +2,30 @@ import { Component, Input } from "@angular/core"; import { ApiAnswer, ApiAnswerType, ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit } from "../../../../../../openapi"; @Component({ - selector: 'app-answer', - templateUrl: './answer.component.html', - styleUrls: ['./answer.component.scss'] + selector: "app-answer", + templateUrl: "./answer.component.html", + styleUrls: ["./answer.component.scss"] }) export class AnswerComponent { @Input() public answers: ApiAnswer[]; - public displayedColumnsWithoutText = ['type', 'item', 'start', 'end', 'preview'] - public displayedColumnsText = ['type', 'text'] - public displayedHeaders = ['type', 'text', 'item', 'start', 'end', 'preview'] + public displayedColumnsWithoutText = ["type", "item", "start", "end", "preview"]; + public displayedColumnsText = ["type", "text"]; + public displayedHeaders = ["type", "text", "item", "start", "end", "preview"]; /** * * @param answer */ - public transformToRange(answer: ApiAnswer): ApiTemporalRange | null{ - console.log(answer.temporalRange); - if(answer.type == ApiAnswerType.TEMPORAL && answer.temporalRange){ - /* In case leakage of temporalRange is part of api */ - /*return { - start: {value: answer.temporalRange.start['millisecond'], unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, - end: {value: answer.temporalRange.start['millisecond'], unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, - } as ApiTemporalRange;*/ - if(answer.start && answer.end){ - if(answer.start !== answer.end){ - return { - start: {value: ''+answer.start, unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, - end: {value: ''+answer.end, unit: ApiTemporalUnit.MILLISECONDS} as ApiTemporalPoint, - } as ApiTemporalRange - } + public transformToRange(answer: ApiAnswer): ApiTemporalRange | null { + if (answer.type == ApiAnswerType.TEMPORAL) { + if (answer.start && answer.end) { + return { + start: { value: "" + answer.start, unit: ApiTemporalUnit.MILLISECONDS } as ApiTemporalPoint, + end: { value: "" + answer.end, unit: ApiTemporalUnit.MILLISECONDS } as ApiTemporalPoint + } as ApiTemporalRange; } } return undefined; diff --git a/frontend/src/app/services/pipes/resolve-media-item-preview.pipe.ts b/frontend/src/app/services/pipes/resolve-media-item-preview.pipe.ts new file mode 100644 index 000000000..ad81cee26 --- /dev/null +++ b/frontend/src/app/services/pipes/resolve-media-item-preview.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { AppConfig } from "../../app.config"; +import { ApiMediaItem } from "../../../../openapi"; +import { TimeUtilities } from "../../utilities/time.utilities"; +import { isMediaItemUrlOptions } from "./resolve-media-item-url.pipe"; + +@Pipe({ + name: 'resolveMediaItemPreview' +}) +export class ResolveMediaItemPreviewPipe implements PipeTransform { + + constructor(private config: AppConfig){ + + } + + transform(value: ApiMediaItem, ...args: unknown[]): string { + let suffix = ''; + if(args && args.length > 0){ + if(args[0] && isMediaItemUrlOptions(args[0]) && args[0].time){ + return this.config.resolveImagePreviewUrl(value.mediaItemId, `${args[0].time}`) + } + } + return this.config.resolveImagePreviewUrl(value.mediaItemId) + } + +} diff --git a/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts b/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts index f1e8f929b..ca8c06443 100644 --- a/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts +++ b/frontend/src/app/services/pipes/resolve-media-item-url.pipe.ts @@ -4,11 +4,12 @@ import { AppConfig } from "../../app.config"; import { TimeUtilities } from "../../utilities/time.utilities"; export interface MediaItemUrlOptions { - range: ApiTemporalRange + time?: number + range?: ApiTemporalRange } -function isOptions(obj: any): obj is MediaItemUrlOptions{ - return 'range' in obj; +export function isMediaItemUrlOptions(obj: any): obj is MediaItemUrlOptions{ + return 'range' in obj || 'time' in obj; } @Pipe({ @@ -23,7 +24,7 @@ export class ResolveMediaItemUrlPipe implements PipeTransform { transform(value: ApiMediaItem, ...args: unknown[]): string { let suffix = ''; if(args && args.length > 0){ - if(args[0] && isOptions(args[0])){ + if(args[0] && isMediaItemUrlOptions(args[0]) && args[0].range){ const range = args[0].range; const startInSeconds = TimeUtilities.point2Milliseconds(range.start, value.fps) / 1000; const endInSeconds = TimeUtilities.point2Milliseconds(range.end, value.fps) / 1000; diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 617037e92..dfb715c4f 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -17,6 +17,7 @@ import { FormatTemporalUnitPipe } from './pipes/format-temporal-unit.pipe'; import { FormatTemporalPointPipe } from './pipes/format-temporal-point.pipe'; import { ResolveMediaItemPipe } from './pipes/resolve-mediaitem.pipe'; import { ResolveMediaItemUrlPipe } from './pipes/resolve-media-item-url.pipe'; +import { ResolveMediaItemPreviewPipe } from './pipes/resolve-media-item-preview.pipe'; /** * Provides the {@link AppConfig} reference. @@ -47,7 +48,8 @@ export function initializeApiConfig(appConfig: AppConfig) { SpaceToNewlinePipe, FormatTemporalPointPipe, ResolveMediaItemPipe, - ResolveMediaItemUrlPipe + ResolveMediaItemUrlPipe, + ResolveMediaItemPreviewPipe ], declarations: [ RoundPipePipe, @@ -63,6 +65,7 @@ export function initializeApiConfig(appConfig: AppConfig) { FormatTemporalPointPipe, ResolveMediaItemPipe, ResolveMediaItemUrlPipe, + ResolveMediaItemPreviewPipe, ], providers: [AuthenticationService, NavigationService, CanDeactivateGuard, FormatTemporalPointPipe, FormatTemporalUnitPipe, FormatTimePipePipe, Epoch2DatePipePipe], }) diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html index 61d63f51d..6b8d738e2 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.html @@ -10,7 +10,12 @@ The image media item - + + A preview image of the media item + + + + (Text:) {{item.name}} diff --git a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts index e4d115e7e..b4e78d594 100644 --- a/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts +++ b/frontend/src/app/shared/media-item-viewer/media-item-viewer.component.ts @@ -1,6 +1,7 @@ import { Component, Input } from "@angular/core"; import { ApiMediaItem, ApiTemporalRange } from "../../../../openapi"; import { AppConfig } from "../../app.config"; +import { TimeUtilities } from "../../utilities/time.utilities"; @Component({ selector: 'app-media-item-viewer', @@ -24,5 +25,14 @@ export class MediaItemViewerComponent { @Input() public range?: ApiTemporalRange; + isRangeSingular():boolean { + return this.range && TimeUtilities.temporalPointEquals(this.range.start, this.range.end) + } + + time():number{ + const t = TimeUtilities.point2Milliseconds(this.range.start, this.item.fps) + return t; + } + } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 17e6c6359..2eb12f680 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -40,9 +40,9 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} -
+
- +
diff --git a/frontend/src/app/utilities/time.utilities.ts b/frontend/src/app/utilities/time.utilities.ts index 7aeb3395c..0114741ec 100644 --- a/frontend/src/app/utilities/time.utilities.ts +++ b/frontend/src/app/utilities/time.utilities.ts @@ -85,4 +85,8 @@ for testing at e.g. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe static timeCode2Milliseconds24fps(timecode: string): number { return this.timeCode2Milliseconds(timecode, 24); } + + static temporalPointEquals(a: ApiTemporalPoint, b: ApiTemporalPoint){ + return a.value === b.value && a.unit === b.unit + } } diff --git a/frontend/src/flex.scss b/frontend/src/flex.scss index 3c90787d3..778dec995 100644 --- a/frontend/src/flex.scss +++ b/frontend/src/flex.scss @@ -97,6 +97,22 @@ Flex items min-width: 100%; } +.flex-tiny-gap { + gap: 0.25em; +} + +.flex-small-gap { + gap: 0.5em; +} + +.flex-gap { + gap: 1em; +} + +.flex-large-gap { + gap: 2em; +} + $growFlex: 1,2,3,4,5; @each $f in $growFlex { From 3f7267772c1f9c0a3b6d34e0bcfc388b83bc58e4 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 22 May 2023 10:50:58 +0200 Subject: [PATCH 311/498] Added CLI command to get currently active configuration --- backend/src/main/kotlin/dev/dres/DRES.kt | 15 ++++++----- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 3 ++- .../kotlin/dev/dres/api/cli/ConfigCommand.kt | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/cli/ConfigCommand.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 2bd5fb57e..b101b4585 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -56,6 +56,9 @@ object DRES { val EXTERNAL_ROOT: Path get() = DATA_ROOT.resolve("external") + lateinit var CONFIG : Config + internal set + init { //redirect log of JLine3 from jdk logger to log4j System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") @@ -63,7 +66,7 @@ object DRES { @JvmStatic fun main(args: Array) { - val config = if (args.isNotEmpty()) { + CONFIG = if (args.isNotEmpty()) { val configPath = Paths.get(args[0]) val config = Config.read(configPath) DATA_ROOT = configPath.absolute().parent @@ -76,27 +79,27 @@ object DRES { println("Initializing...") /* Initialize Xodus based data store. */ - val store = this.prepareDatabase(config) + val store = this.prepareDatabase(CONFIG) /* Initialize the global Cache Manager. */ - val global = CacheManager(config, store) + val global = CacheManager(CONFIG, store) /* Initialize RunExecutor. */ - RunExecutor.init(config, store, global) + RunExecutor.init(CONFIG, store, global) /* Initialize EventStreamProcessor */ EventStreamProcessor.register( /* Add handlers here */) EventStreamProcessor.init() /* Initialize Rest API. */ - RestApi.init(config, store, global) + RestApi.init(CONFIG, store, global) println("Initialization complete!") if (args.isNotEmpty() && args.first() == "openapi") { OpenApiCommand().parse(args) } else { - Cli.loop(config, store, global) //blocks until quit command is given + Cli.loop(CONFIG, store, global) //blocks until quit command is given } /* Stop. */ diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 3ecfe38cc..492ce4b7a 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -49,7 +49,8 @@ object Cli { EvaluationCommand(store), OpenApiCommand(), ExecutionCommand(), - AuditCommand(store) + AuditCommand(store), + ConfigCommand() ) val terminal = try { diff --git a/backend/src/main/kotlin/dev/dres/api/cli/ConfigCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/ConfigCommand.kt new file mode 100644 index 000000000..668424b98 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/cli/ConfigCommand.kt @@ -0,0 +1,25 @@ +package dev.dres.api.cli + +import com.github.ajalt.clikt.core.CliktCommand +import dev.dres.DRES + +class ConfigCommand:CliktCommand(name="config", help="Command that prints the current config in use") { + + override fun run() { + println("Currently active configuration: ") + println(" Application root: ${DRES.APPLICATION_ROOT}") + println(" Data root: ${DRES.DATA_ROOT}") + println(" External root: ${DRES.EXTERNAL_ROOT}") + println(" HTTP Port: ${DRES.CONFIG.httpPort}") + println(" HTTPS Port: ${DRES.CONFIG.httpsPort}") + println(" Enable SSL: ${DRES.CONFIG.enableSsl}") + println(" Cache information") + println(" Cleanup interval (ms): ${DRES.CONFIG.cache.cleanupIntervalMs}") + println(" Eviction threshold (ms): ${DRES.CONFIG.cache.evictionThresholdMs}") + println(" FFMPEG: ${DRES.CONFIG.cache.ffmpegPath()}") + println(" Logo max size: ${DRES.CONFIG.cache.logoMaxSize}") + println(" Max rendering threads: ${DRES.CONFIG.cache.maxRenderingThreads}") + println(" Preview image max size: ${DRES.CONFIG.cache.previewImageMaxSize}") + println(" Preview video max size: ${DRES.CONFIG.cache.previewVideoMaxSize}") + } +} From e647d9736575c0fcd1630d64df0b7b9e0c380136 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 22 May 2023 11:17:39 +0200 Subject: [PATCH 312/498] #310 Added confirmation dialog to removing task template --- .../task-templates-list.component.ts | 58 ++++++++++++------- .../template-builder.module.ts | 16 ++--- .../template-builder.service.ts | 3 + 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index 86be339a8..5a4fe1866 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -5,9 +5,14 @@ import { ApiEvaluationTemplate, ApiHint, ApiTarget, ApiTaskGroup, ApiTaskTemplat import { MatTable } from "@angular/material/table"; import { Observable, Subscription } from "rxjs"; import { SelectionModel } from "@angular/cdk/collections"; -import { map, tap } from "rxjs/operators"; - -export interface TaskTemplateEditorLauncher{ +import { map, switchMap, tap } from "rxjs/operators"; +import { + ConfirmationDialogComponent, + ConfirmationDialogComponentData +} from "../../../../shared/confirmation-dialog/confirmation-dialog.component"; +import { MatDialog } from "@angular/material/dialog"; + +export interface TaskTemplateEditorLauncher { editTask(taskType: ApiTaskType, taskGroup: ApiTaskGroup, task?: ApiTaskTemplate); } @@ -19,23 +24,23 @@ export interface TaskTemplateEditorLauncher{ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { @Input() - editorLauncher: TaskTemplateEditorLauncher + editorLauncher: TaskTemplateEditorLauncher; // TODO After dynact table fanciness (conditional multi component projection), rewrite to use dynact table - @ViewChild('taskTable') + @ViewChild("taskTable") taskTable: MatTable; tasks: Observable; - displayedColumns = ['name', 'group', 'type', 'duration', 'actions']; + displayedColumns = ["name", "group", "type", "duration", "actions"]; - groups : Observable; + groups: Observable; selection = new SelectionModel(false, [], false); private selectedTaskSub: Subscription; - constructor(builder: TemplateBuilderService) { + constructor(builder: TemplateBuilderService, private dialog: MatDialog) { super(builder); } @@ -44,21 +49,21 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent this.tasks = this.builderService.taskTemplatesAsObservable(); this.groups = this.builderService.taskGroupsAsObservable(); this.selectedTaskSub = this.builderService.selectedTaskTemplateAsObservable().subscribe((t) => { - if(!t){ + if (!t) { this.selection.clear(); } - }) + }); } ngOnDestroy(): void { this.onDestroy(); } - public logTasks(){ - console.log("TRIGGER", this.builderService.getTemplate().tasks) + public logTasks() { + console.log("TRIGGER", this.builderService.getTemplate().tasks); } - public addTask(group: ApiTaskGroup){ + public addTask(group: ApiTaskGroup) { const newTask = new class implements ApiTaskTemplate { collectionId: string; duration: number; @@ -77,14 +82,14 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent this.selection.toggle(newTask); } - public editTask(task: ApiTaskTemplate){ + public editTask(task: ApiTaskTemplate) { this.builderService.selectTaskTemplate(task); this.selection.toggle(task); } - public moveTaskUp(task: ApiTaskTemplate){ + public moveTaskUp(task: ApiTaskTemplate) { const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); - if(oldIndex > 0){ + if (oldIndex > 0) { const buffer = this.builderService.getTemplate().tasks[oldIndex - 1]; this.builderService.getTemplate().tasks[oldIndex - 1] = task; this.builderService.getTemplate().tasks[oldIndex] = buffer; @@ -93,9 +98,9 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent } } - public moveTaskDown(task: ApiTaskTemplate){ + public moveTaskDown(task: ApiTaskTemplate) { const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); - if(oldIndex < this.builderService.getTemplate().tasks.length - 1){ + if (oldIndex < this.builderService.getTemplate().tasks.length - 1) { const buffer = this.builderService.getTemplate().tasks[oldIndex + 1]; this.builderService.getTemplate().tasks[oldIndex + 1] = task; this.builderService.getTemplate().tasks[oldIndex] = buffer; @@ -104,12 +109,23 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent } } - public tasksLength(){ + public tasksLength() { return this.builderService.getTemplate().tasks.length; } - public removeTask(task: ApiTaskTemplate){ - this.builderService.removeTask(task); + public removeTask(task: ApiTaskTemplate) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + text: "Really want to delete this task template?", + color: "warn" + } as ConfirmationDialogComponentData + }); + dialogRef.afterClosed().subscribe((result) => { + if (result) { + this.builderService.removeTask(task); + } + }); + } onChange() { diff --git a/frontend/src/app/template/template-builder/template-builder.module.ts b/frontend/src/app/template/template-builder/template-builder.module.ts index 4ec80f3be..ff4842b78 100644 --- a/frontend/src/app/template/template-builder/template-builder.module.ts +++ b/frontend/src/app/template/template-builder/template-builder.module.ts @@ -5,19 +5,21 @@ import {TemplateBuilderComponentsModule} from './components/template-builder-com import {MatIconModule} from '@angular/material/icon'; import {MatTabsModule} from '@angular/material/tabs'; import {SharedModule} from '../../shared/shared.module'; +import { MatTooltipModule } from "@angular/material/tooltip"; @NgModule({ declarations: [ TemplateBuilderComponent ], - imports: [ - CommonModule, - TemplateBuilderComponentsModule, - MatIconModule, - MatTabsModule, - SharedModule, - ], + imports: [ + CommonModule, + TemplateBuilderComponentsModule, + MatIconModule, + MatTabsModule, + SharedModule, + MatTooltipModule + ], exports: [TemplateBuilderComponent] }) export class TemplateBuilderModule { diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index a876cfe4e..7e096807b 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -229,6 +229,9 @@ export class TemplateBuilderService { public removeTask(task: ApiTaskTemplate){ this.getTemplate().tasks.splice(this.getTemplate().tasks.indexOf(task), 1); this.update(this.getTemplate()); + if(this.getSelectedTaskTemplate() == task){ + this.selectTaskTemplate(null); + } } } From dcf3f0bd4a9fc21a02755b0c30602e03da219b29 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 22 May 2023 11:27:15 +0200 Subject: [PATCH 313/498] #310 Fixed image preview for collection images --- .../task-template-editor/task-template-editor.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index cbd036d44..0b781888b 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -240,7 +240,7 @@

From be5ded9ba11c0a3da7483881023d46cdb84902af Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 22 May 2023 15:02:32 +0200 Subject: [PATCH 314/498] #310 Added support for previews of external images (ui) and generally for external media (api endpoint) --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 2 + .../collection/ListExternalItemHandler.kt | 20 ++++- .../preview/GetExternalMediaHandler.kt | 72 +++++++++++++++ frontend/src/app/app.config.ts | 21 +++++ .../competition-form.builder.ts | 35 +++----- .../task-template-editor.component.html | 87 +++++++++---------- .../task-template-editor.component.ts | 30 +++++-- 7 files changed, 190 insertions(+), 77 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetExternalMediaHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index b6966987a..51a424357 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -20,6 +20,7 @@ import dev.dres.api.rest.handler.evaluation.viewer.* import dev.dres.api.rest.handler.judgement.* import dev.dres.api.rest.handler.log.QueryLogHandler import dev.dres.api.rest.handler.log.ResultLogHandler +import dev.dres.api.rest.handler.preview.GetExternalMediaHandler import dev.dres.api.rest.handler.template.* import dev.dres.api.rest.handler.preview.GetMediaHandler import dev.dres.api.rest.handler.preview.PreviewImageHandler @@ -102,6 +103,7 @@ object RestApi { // Media PreviewImageHandler(store, cache), PreviewVideoHandler(store, cache), + GetExternalMediaHandler(), // Must be registered before GetMediaHandler, as route is similar GetMediaHandler(store), // Collection diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index 00327d7be..cf3ee1705 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -7,6 +7,7 @@ import io.javalin.http.Context import io.javalin.openapi.* import java.nio.file.FileVisitOption import java.nio.file.Files +import java.util.stream.Collectors import kotlin.io.path.name import kotlin.streams.toList @@ -41,9 +42,22 @@ class ListExternalItemHandler : GetRestHandler> { override fun doGet(ctx: Context): List { // TODO https://github.com/javalin/javalin-openapi/issues/178 Apparently, we cannot use the slash-included notation here (https://javalin.io/documentation#endpoint-handlers) val startsWith = ctx.pathParamMap()["startsWith"] ?: "" - return Files.walk(DRES.EXTERNAL_ROOT, 1, FileVisitOption.FOLLOW_LINKS).filter { - Files.isRegularFile(it) && it.name.startsWith(startsWith) && (it.name.endsWith(".jpg", ignoreCase = true) || it.name.endsWith(".mkv", ignoreCase = true) || it.name.endsWith(".mp4", ignoreCase = true)) - }.sorted { o1, o2 -> o1.name.length - o2.name.length }.limit(50).map { it.toFile().name }.toList() + val files = Files.walk(DRES.EXTERNAL_ROOT, 1, FileVisitOption.FOLLOW_LINKS) + val list = files + .filter { + Files.isRegularFile(it) && + it.name.startsWith(startsWith) && + ( + it.name.endsWith(".jpg", ignoreCase = true) || + it.name.endsWith(".mkv", ignoreCase = true) || + it.name.endsWith(".mp4", ignoreCase = true) + ) + }.sorted { o1, o2 -> o1.name.length - o2.name.length } + .limit(50) + + return list.map { it.toFile().name }.collect(Collectors.toList()) + + } override val route: String = "external/{startsWith}" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetExternalMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetExternalMediaHandler.kt new file mode 100644 index 000000000..0842c296e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetExternalMediaHandler.kt @@ -0,0 +1,72 @@ +package dev.dres.api.rest.handler.preview + +import dev.dres.DRES +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.utilities.extensions.errorResponse +import dev.dres.utilities.extensions.streamFile +import io.javalin.http.ContentType +import io.javalin.http.Context +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import java.nio.file.Files +import kotlin.io.path.Path + +/** + * An [GetRestHandler] used to access files from the external media collection. + * + * @author Loris sauter + * @version 1.0.0 + */ +class GetExternalMediaHandler : GetRestHandler, AccessManagedRestHandler { + + // not used, as the medium is directly served + override fun doGet(ctx: Context): Any = "" + + /** All [GetMediaHandler] can be used by [ApiRole.VIEWER], [ApiRole.PARTICIPANT] and [ApiRole.ADMIN]. */ + override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.ADMIN) + + override val route = "media/external/{file}" + + /** + * The API version, currently `v2` + */ + override val apiVersion = "v2" + + @OpenApi( + summary = "Returns the requested external mediu, if present", + path = "/api/v2/media/external/{file}", + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [ + OpenApiParam("file", String::class, "The file name including suffix") + ], + tags = ["Media"], + responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse( + "404" + )], + ignore = true, + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + /* Extract parameter */ + val file = ctx.pathParamMap()["file"] + if (file == null) { + ctx.errorResponse(400, "Missing parameter file") + return + } + + /* Lookup */ + val path = DRES.EXTERNAL_ROOT.resolve(Path(file)) + if (path == null || !Files.exists(path)) { + ctx.errorResponse(404, "External file with name ${file} was not found") + } + + ctx.contentType(ContentType.getContentTypeByExtension(path.toFile().extension)!!) + try { + ctx.streamFile(path) + } catch (e: org.eclipse.jetty.io.EofException) { + //is triggered by a client abruptly stopping playback, can be safely ignored + } + } +} diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 7038ed447..3e6df2e68 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -66,14 +66,35 @@ export class AppConfig { return `${this.baseUrl}/api/${version}${path.startsWith('/') ? '' : '/'}${path}`; } + /** + * Resolves the given media item's url so to fetch the actual content. Provides a URL compliant with the GetMediaHandler (backend) + * @param mediaItemId The media item's id to resolve the url for + * @param version The API version + */ public resolveMediaItemUrl(mediaItemId: string, version: string = "v2"): string{ return this.resolveApiUrl(`/media/${mediaItemId}`, version); } + /** + * Resolves the given media item's PREVIEW url so to fetch the actual content. Provides a URL compliant with the PreviewImageHandler (backend) + * @param mediaItemId The media item's id to resolve the PREVIEW url for + * @param version The API version + */ public resolveImagePreviewUrl(mediaItemId: string, timeInMs?:string,version: string = "v2"): string{ return this.resolveApiUrl(`/preview/${mediaItemId}${timeInMs ? `/${timeInMs}` : ''}`, version) } + /** + * Resolves the given external file URL. Provides a URL compliant with the GetExternalMediaHandler (backend) + * @param file The file including extension to resolve the URL for + * @param version The API version + */ + public resolveExternalUrl(file: string, version: string= "v2"): string { + return this.resolveApiUrl(`/media/external/${file}`, version) + } + + + /** * (Re-)loads the default configuration from a JSON file. */ diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 5da45177a..505207c81 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -574,17 +574,20 @@ export class CompetitionFormBuilder { /* Initialize start, end and time unit based on target. */ // fetch target time unit - const targetTimeUnit = (this.form.get('target') as UntypedFormArray).controls[0].get('segment_time_unit').value; - if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_time_unit').setValue(targetTimeUnit, {emitEvent: false}); - } + const targetTimeUnit = (this.form.get('target') as UntypedFormArray).controls[0]?.get('segment_time_unit')?.value ?? undefined; + // Wrap fetching of target temporal information only when such information is present + if(targetTimeUnit){ + if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { + group.get('segment_time_unit').setValue(targetTimeUnit, {emitEvent: false}); + } - if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_start').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_start').value, {emitEvent: false}); - } + if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { + group.get('segment_start').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_start').value, {emitEvent: false}); + } - if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_end').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_end').value, {emitEvent: false}); + if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { + group.get('segment_end').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_end').value, {emitEvent: false}); + } } /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ @@ -691,20 +694,6 @@ export class CompetitionFormBuilder { Validators.required ), }); - /* Initialize start, end and time unit based on target. */ - // fetch target time unit - const targetTimeUnit = (this.form.get('target') as UntypedFormArray).controls[0].get('segment_time_unit').value; - if (targetTimeUnit && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_time_unit').setValue(targetTimeUnit, {emitEvent: false}); - } - - if (!group.get('segment_start').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_start').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_start').value, {emitEvent: false}); - } - - if (!group.get('segment_end').value && this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT') { - group.get('segment_end').setValue((this.form.get('target') as UntypedFormArray).controls[0].get('segment_end').value, {emitEvent: false}); - } /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ group.get('end').setValue(this.taskType.duration, {emitEvent: false}); diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 0b781888b..8b56243a3 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -102,7 +102,7 @@

@@ -450,7 +450,7 @@

remove_red_eye - + @@ -472,52 +472,49 @@

+ - - - - - - - - - - {{ unit }} - - - - + ) : toggleExternalImagePreview(description.get('path').value)" + > + {{externalPreviewActive() ? 'visibility_off' : 'visibility'}} + @@ -618,6 +615,8 @@

+ +

diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index 37d2e3e9b..6bc607934 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -54,6 +54,11 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { showVideo = false; videoSegmentData: VideoPlayerSegmentBuilderData; + externalImagePreviewActive = false; + externalImagePreviewUrl = '' + externalVideoPreviewActive = false; + externalVideoData: VideoPlayerSegmentBuilderData + private imagePreviewMap = new Set(); private taskSub: Subscription; @@ -236,6 +241,24 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { unitControl.setValue('SECONDS'); } + toggleExternalVideoPreview(path: string, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { + + } + + externalPreviewActive():boolean{ + return this.externalImagePreviewActive || this.externalVideoPreviewActive + } + + toggleExternalImagePreview(path: string){ + if(this.externalImagePreviewActive){ + this.externalImagePreviewActive = false + this.externalImagePreviewUrl = '' + }else{ + this.externalImagePreviewActive = true + this.externalImagePreviewUrl = this.config.resolveExternalUrl(path) + } + } + toggleVideoPlayer(mediaItem: ApiMediaItem, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { /* Add to toggleVideoPlayer button if [disabled]="!target.get('mediaItem').value && !target.get('segment_start').value && !target.get('segment_end').value" @@ -312,13 +335,6 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { } } - getImageUrl(mi: ApiMediaItem) { - if (mi && mi.type === 'IMAGE') { - return this.config.resolveApiUrl(`/media/${mi.collectionId}/${mi.mediaItemId}`); - } - return ''; - } - batchAddTargets() { const config = { From a901995b0ac99735c43785d8a3af6bc8d2f0faa2 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 23 May 2023 14:54:24 +0200 Subject: [PATCH 315/498] #310 Reworked task template editor --- ...ideo-player-segment-builder.component.html | 121 +++++----- .../video-player-segment-builder.component.ts | 26 ++- .../services/pipes/format-media-item.pipe.ts | 32 +++ frontend/src/app/services/services.module.ts | 12 +- ...ription-external-form-field.component.html | 1 + ...ription-external-form-field.component.scss | 0 ...scription-external-form-field.component.ts | 25 +++ ...n-external-image-form-field.component.html | 25 +++ ...n-external-image-form-field.component.scss | 0 ...ion-external-image-form-field.component.ts | 17 ++ ...n-external-video-form-field.component.html | 59 +++++ ...n-external-video-form-field.component.scss | 0 ...ion-external-video-form-field.component.ts | 67 ++++++ ...uery-description-form-field.component.html | 55 +++++ ...uery-description-form-field.component.scss | 0 .../query-description-form-field.component.ts | 63 ++++++ ...ption-media-item-form-field.component.html | 1 + ...ption-media-item-form-field.component.scss | 0 ...ription-media-item-form-field.component.ts | 30 +++ ...media-item-image-form-field.component.html | 22 ++ ...media-item-image-form-field.component.scss | 0 ...n-media-item-image-form-field.component.ts | 21 ++ ...media-item-video-form-field.component.html | 55 +++++ ...media-item-video-form-field.component.scss | 0 ...n-media-item-video-form-field.component.ts | 67 ++++++ ...description-text-form-field.component.html | 3 + ...description-text-form-field.component.scss | 0 ...y-description-text-form-field.component.ts | 14 ++ .../task-template-editor.component.html | 208 +----------------- .../task-template-editor.component.ts | 22 ++ .../task-templates-list.component.ts | 2 +- .../template-builder-components.module.ts | 93 +++++--- .../template-builder.module.ts | 27 ++- 33 files changed, 765 insertions(+), 303 deletions(-) create mode 100644 frontend/src/app/services/pipes/format-media-item.pipe.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.ts create mode 100644 frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.html create mode 100644 frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.scss create mode 100644 frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.ts diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html index 0304e0263..cb644b9c6 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.html @@ -1,65 +1,66 @@ -

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

-
- -
-
- +
+

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

+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + loop + +
-
- +
+
{{ video.nativeElement.currentTime | formatTime: false }} + / {{ durationInSeconds | formatTime: false }}
+
+ +
-
- +
+
Start: {{ startInSeconds | formatTime:false }}
+ + Start [s] + + + + End [s] + + +
+
End: {{ endInSeconds | formatTime:false }}
- -
-
- loop -
-
-
-
{{ video.nativeElement.currentTime | formatTime: false }} / {{ durationInSeconds | formatTime: false }}
-
- -
-
-
-
Start: {{ startInSeconds | formatTime:false }}
- - Start [s] - - - - End [s] - - -
-
End: {{ endInSeconds | formatTime:false }}
diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts index 92e878bdf..8aeb033ef 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts @@ -3,12 +3,19 @@ import { Observable, of, Subscription } from 'rxjs'; import { ApiMediaItem, ApiTemporalPoint, ApiTemporalRange } from '../../../../../../openapi'; import { AppConfig } from '../../../../app.config'; +/** + * DTO for [VideoPlayerSegmentBuilder] configuration. + * Requires either `mediaItem` or `externalPath` to be set. + * `segmentStart` and `segmentEnd` are optional + */ export interface VideoPlayerSegmentBuilderData { - mediaItem: ApiMediaItem; - segmentStart: number; - segmentEnd: number; + mediaItem?: ApiMediaItem; + segmentStart?: number; + segmentEnd?: number; + externalPath?:string; } + @Component({ selector: 'app-video-player-segment-builder', templateUrl: './video-player-segment-builder.component.html', @@ -17,6 +24,7 @@ export interface VideoPlayerSegmentBuilderData { export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDestroy { @Input() data: VideoPlayerSegmentBuilderData; @Output() rangeChange = new EventEmitter(); + @Input() showTitle = true @ViewChild('videoPlayer', { static: false }) video: ElementRef; videoUrl: Observable; @@ -30,11 +38,17 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest private requestSub: Subscription; + isMediaItemPlayer = false; + constructor( public config: AppConfig /*, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: VideoPlayerSegmentBuilderData*/ - ) {} + ) { + if(this.data){ + this.isMediaItemPlayer = this.data.mediaItem && !this.data.externalPath; + } + } ngAfterViewInit(): void { setTimeout(() => { @@ -42,9 +56,9 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest * timeout because of value changed after checking thingy * https://blog.angular-university.io/angular-debugging/ */ - if (this.data.mediaItem) { + if (this.data) { this.videoUrl = of( - this.config.resolveApiUrl(`/media/${this.data?.mediaItem?.mediaItemId}`) + this.isMediaItemPlayer ? this.config.resolveMediaItemUrl(this.data.mediaItem.mediaItemId) : this.config.resolveExternalUrl(this.data.externalPath) ); } if (this.data.segmentStart) { diff --git a/frontend/src/app/services/pipes/format-media-item.pipe.ts b/frontend/src/app/services/pipes/format-media-item.pipe.ts new file mode 100644 index 000000000..f2e14b25a --- /dev/null +++ b/frontend/src/app/services/pipes/format-media-item.pipe.ts @@ -0,0 +1,32 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ApiMediaItem } from "../../../../openapi"; + +export interface MediaItemDisplayOptions { + showId?: boolean + shortenId?: boolean + showType?: boolean +} + +@Pipe({ + name: 'formatMediaItem' +}) +export class FormatMediaItemPipe implements PipeTransform { + + transform(value: ApiMediaItem, options?: MediaItemDisplayOptions): string { + if(value){ + let out = value.name + if(options){ + if(options.showId){ + const end = options.shortenId ? 8 : value.mediaItemId.length + out += ' ('+value.mediaItemId.substring(0,end)+')' + } + if(options.showType){ + out += ' (' + value.type + ')' + } + } + return out; + } + return '' + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index dfb715c4f..bf2dfe9b9 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -18,6 +18,7 @@ import { FormatTemporalPointPipe } from './pipes/format-temporal-point.pipe'; import { ResolveMediaItemPipe } from './pipes/resolve-mediaitem.pipe'; import { ResolveMediaItemUrlPipe } from './pipes/resolve-media-item-url.pipe'; import { ResolveMediaItemPreviewPipe } from './pipes/resolve-media-item-preview.pipe'; +import { FormatMediaItemPipe } from './pipes/format-media-item.pipe'; /** * Provides the {@link AppConfig} reference. @@ -66,7 +67,16 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveMediaItemPipe, ResolveMediaItemUrlPipe, ResolveMediaItemPreviewPipe, + FormatMediaItemPipe, ], - providers: [AuthenticationService, NavigationService, CanDeactivateGuard, FormatTemporalPointPipe, FormatTemporalUnitPipe, FormatTimePipePipe, Epoch2DatePipePipe], + providers: [ + AuthenticationService, + NavigationService, + CanDeactivateGuard, + FormatTemporalPointPipe, + FormatTemporalUnitPipe, + FormatTimePipePipe, + Epoch2DatePipePipe, + FormatMediaItemPipe], }) export class ServicesModule {} diff --git a/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.html new file mode 100644 index 000000000..5dc2a55e4 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.html @@ -0,0 +1 @@ +This component shouldn't be used for rendering diff --git a/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.ts new file mode 100644 index 000000000..fb8c91c05 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-external-form-field/query-description-external-form-field.component.ts @@ -0,0 +1,25 @@ +import { Component, Input } from "@angular/core"; +import { UntypedFormControl } from "@angular/forms"; +import { + CompetitionFormBuilder +} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; +import { AppConfig } from "../../../../app.config"; + +@Component({ + selector: 'app-query-description-external-form-field', + templateUrl: './query-description-external-form-field.component.html', + styleUrls: ['./query-description-external-form-field.component.scss'] +}) +export class QueryDescriptionExternalFormFieldComponent { + @Input() + pathControl: UntypedFormControl + @Input() + formBuilder: CompetitionFormBuilder + @Input() + index: number + + showing: boolean = false; + + constructor(public config: AppConfig) { + } +} diff --git a/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.html new file mode 100644 index 000000000..c8d762819 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.html @@ -0,0 +1,25 @@ +
+ + + + + {{ path }} + + + Please select a external image, i.e., use the autocomplete to select a value. + + + + +
+ diff --git a/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.ts new file mode 100644 index 000000000..bce5d1d69 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-external-image-form-field/query-description-external-image-form-field.component.ts @@ -0,0 +1,17 @@ +import { Component, Input } from "@angular/core"; +import { + QueryDescriptionExternalFormFieldComponent +} from "../query-description-external-form-field/query-description-external-form-field.component"; +import { + CompetitionFormBuilder +} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; +import { AppConfig } from "../../../../app.config"; + +@Component({ + selector: 'app-query-description-external-image-form-field', + templateUrl: './query-description-external-image-form-field.component.html', + styleUrls: ['./query-description-external-image-form-field.component.scss'] +}) +export class QueryDescriptionExternalImageFormFieldComponent extends QueryDescriptionExternalFormFieldComponent{ + +} diff --git a/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.html new file mode 100644 index 000000000..f784f46bf --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.html @@ -0,0 +1,59 @@ +
+
+
+ + + + + {{ path }} + + + Please select a valid media item, i.e., use the autocomplete to select a value. + + + + +
+
+ + + + + + + + + {{ unit }} + + +
+
+ +
+ diff --git a/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.ts new file mode 100644 index 000000000..ca6fcfb01 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-external-video-form-field/query-description-external-video-form-field.component.ts @@ -0,0 +1,67 @@ +import { Component, Input } from "@angular/core"; +import { + QueryDescriptionExternalFormFieldComponent +} from "../query-description-external-form-field/query-description-external-form-field.component"; +import { UntypedFormControl } from "@angular/forms"; +import { ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit } from "../../../../../../openapi"; +import { + VideoPlayerSegmentBuilderData +} from "../../../../competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component"; +import { TimeUtilities } from "../../../../utilities/time.utilities"; + +@Component({ + selector: 'app-query-description-external-video-form-field', + templateUrl: './query-description-external-video-form-field.component.html', + styleUrls: ['./query-description-external-video-form-field.component.scss'] +}) +export class QueryDescriptionExternalVideoFormFieldComponent extends QueryDescriptionExternalFormFieldComponent { + + @Input() + startControl: UntypedFormControl + @Input() + endControl: UntypedFormControl + @Input() + unitControl + + units = [ApiTemporalUnit.FRAME_NUMBER, ApiTemporalUnit.MILLISECONDS, ApiTemporalUnit.SECONDS, ApiTemporalUnit.TIMECODE] + + timeUnitChanged($event, start: HTMLInputElement, end: HTMLInputElement){ + const type = $event.value === 'TIMECODE' ? 'text' :'number'; + if(start){ + start.type =type; + } + if(end){ + end.type = type; + } + } + + segmentBuilderData(): VideoPlayerSegmentBuilderData { + const mediaItem = this.pathControl.value + let start = -1; + let end = -1; + const unit = this.unitControl?.value ? (this.unitControl.value as ApiTemporalUnit) : ApiTemporalUnit.SECONDS; + if (this.startControl && this.startControl.value) { + if (this.unitControl.value === 'TIMECODE') { + start = TimeUtilities.timeCode2Milliseconds24fps(this.startControl.value) / 1000; + } else { + start = + TimeUtilities.point2Milliseconds24fps({ value: this.startControl.value, unit } as ApiTemporalPoint) / 1000; + } + } + if (this.endControl && this.endControl.value) { + if (this.unitControl.value === 'TIMECODE') { + end = TimeUtilities.timeCode2Milliseconds24fps(this.endControl.value) / 1000; + } else { + end = TimeUtilities.point2Milliseconds24fps({ value: this.endControl.value, unit } as ApiTemporalPoint) / 1000; + } + } + return { externalPath: this.pathControl.value, segmentStart: start, segmentEnd: end } as VideoPlayerSegmentBuilderData; + } + + rangeChanged(range: ApiTemporalRange){ + this.startControl.setValue(range.start.value) + this.endControl.setValue(range.end.value) + this.unitControl.setValue(ApiTemporalUnit.SECONDS) + } + +} diff --git a/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.html new file mode 100644 index 000000000..f40f90b2f --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.html @@ -0,0 +1,55 @@ +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
+
+ +
+ +
+
+ +
diff --git a/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.ts new file mode 100644 index 000000000..dc8083cff --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-form-field/query-description-form-field.component.ts @@ -0,0 +1,63 @@ +import { Component, Input } from "@angular/core"; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from "@angular/forms"; +import { + CompetitionFormBuilder +} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; +import { ApiHintOption, ApiHintType } from "../../../../../../openapi"; + +@Component({ + selector: 'app-query-description-form-field', + templateUrl: './query-description-form-field.component.html', + styleUrls: ['./query-description-form-field.component.scss'] +}) +export class QueryDescriptionFormFieldComponent { + + @Input() + startControl: UntypedFormControl + @Input() + endControl: UntypedFormControl + @Input() + typeControl: UntypedFormControl + @Input() + externalControl: UntypedFormControl + @Input() + itemControl: UntypedFormControl + @Input() + descriptionControl: UntypedFormControl + @Input() + pathControl: UntypedFormControl + @Input() + segmentStartControl: UntypedFormControl + @Input() + segmentEndControl: UntypedFormControl + @Input() + unitControl: UntypedFormControl + @Input() + formBuilder: CompetitionFormBuilder + @Input() + index: number + + public addQueryComponent(componentType: ApiHintOption, previous: number = null){ + switch(componentType){ + case 'IMAGE_ITEM': + this.formBuilder.addComponentForm(ApiHintType.IMAGE, previous); + break; + case 'VIDEO_ITEM_SEGMENT': + this.formBuilder.addComponentForm(ApiHintType.VIDEO, previous); + break; + case 'TEXT': + this.formBuilder.addComponentForm(ApiHintType.TEXT, previous); + break; + case 'EXTERNAL_IMAGE': + this.formBuilder.addComponentForm(ApiHintType.IMAGE, previous, true); + break; + case 'EXTERNAL_VIDEO': + this.formBuilder.addComponentForm(ApiHintType.VIDEO, previous, true); + break; + } + } + + public removeQueryComponent(index: number) { + this.formBuilder.removeComponentForm(index); + } +} diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.html new file mode 100644 index 000000000..91e269c02 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.html @@ -0,0 +1 @@ +This component should not be used for rendering diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts new file mode 100644 index 000000000..aaf7e0fe6 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from "@angular/core"; +import { UntypedFormControl } from "@angular/forms"; +import { + CompetitionFormBuilder +} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; +import { FormatMediaItemPipe, MediaItemDisplayOptions } from "../../../../services/pipes/format-media-item.pipe"; +import { ApiMediaItem } from "../../../../../../openapi"; + +@Component({ + selector: 'app-query-description-media-item-form-field', + templateUrl: './query-description-media-item-form-field.component.html', + styleUrls: ['./query-description-media-item-form-field.component.scss'] +}) +export class QueryDescriptionMediaItemFormFieldComponent { + @Input() + itemControl: UntypedFormControl + @Input() + formBuilder: CompetitionFormBuilder + @Input() + index: number + + showing: boolean; + + constructor(private formatMediaItem: FormatMediaItemPipe) { + } + + public displayWithMediaItem(value: ApiMediaItem): string{ + return this.formatMediaItem.transform(value, {showType: true} as MediaItemDisplayOptions) + } +} diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html new file mode 100644 index 000000000..309e5467c --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html @@ -0,0 +1,22 @@ +
+ + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} + + + Please select a valid media item, i.e., use the autocomplete to select a value. + + + + +
+ diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts new file mode 100644 index 000000000..7fb837679 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from "@angular/core"; +import { UntypedFormControl } from "@angular/forms"; +import { + CompetitionFormBuilder +} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; +import { ApiMediaItem } from "../../../../../../openapi"; +import { FormatMediaItemPipe, MediaItemDisplayOptions } from "../../../../services/pipes/format-media-item.pipe"; +import { + QueryDescriptionMediaItemFormFieldComponent +} from "../query-description-media-item-form-field/query-description-media-item-form-field.component"; + +@Component({ + selector: 'app-query-description-media-item-image-form-field', + templateUrl: './query-description-media-item-image-form-field.component.html', + styleUrls: ['./query-description-media-item-image-form-field.component.scss'] +}) +export class QueryDescriptionMediaItemImageFormFieldComponent extends QueryDescriptionMediaItemFormFieldComponent{ + + + +} diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html new file mode 100644 index 000000000..3aa79d6bb --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html @@ -0,0 +1,55 @@ +
+ + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} + + + Please select a valid media item i.e. use the autocomplete to select a value. + + + + + + + + + + + + + {{ unit }} + + + +
+ diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.ts new file mode 100644 index 000000000..45335354f --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.ts @@ -0,0 +1,67 @@ +import { Component, Input } from "@angular/core"; +import { + QueryDescriptionMediaItemFormFieldComponent +} from "../query-description-media-item-form-field/query-description-media-item-form-field.component"; +import { UntypedFormControl } from "@angular/forms"; +import { ApiTemporalPoint, ApiTemporalRange, ApiTemporalUnit } from "../../../../../../openapi"; +import { + VideoPlayerSegmentBuilderData +} from "../../../../competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component"; +import { TimeUtilities } from "../../../../utilities/time.utilities"; + +@Component({ + selector: 'app-query-description-media-item-video-form-field', + templateUrl: './query-description-media-item-video-form-field.component.html', + styleUrls: ['./query-description-media-item-video-form-field.component.scss'] +}) +export class QueryDescriptionMediaItemVideoFormFieldComponent extends QueryDescriptionMediaItemFormFieldComponent{ + + @Input() + startControl: UntypedFormControl + @Input() + endControl: UntypedFormControl + @Input() + unitControl + + units = [ApiTemporalUnit.FRAME_NUMBER, ApiTemporalUnit.MILLISECONDS, ApiTemporalUnit.SECONDS, ApiTemporalUnit.TIMECODE] + + timeUnitChanged($event, start: HTMLInputElement, end: HTMLInputElement){ + const type = $event.value === 'TIMECODE' ? 'text' :'number'; + if(start){ + start.type =type; + } + if(end){ + end.type = type; + } + } + + segmentBuilderData(): VideoPlayerSegmentBuilderData { + const mediaItem = this.itemControl.value + let start = -1; + let end = -1; + const unit = this.unitControl?.value ? (this.unitControl.value as ApiTemporalUnit) : ApiTemporalUnit.SECONDS; + if (this.startControl && this.startControl.value) { + if (this.unitControl.value === 'TIMECODE') { + start = TimeUtilities.timeCode2Milliseconds(this.startControl.value, mediaItem.fps) / 1000; + } else { + start = + TimeUtilities.point2Milliseconds({ value: this.startControl.value, unit } as ApiTemporalPoint, mediaItem.fps) / 1000; + } + } + if (this.endControl && this.endControl.value) { + if (this.unitControl.value === 'TIMECODE') { + end = TimeUtilities.timeCode2Milliseconds(this.endControl.value, mediaItem.fps) / 1000; + } else { + end = TimeUtilities.point2Milliseconds({ value: this.endControl.value, unit } as ApiTemporalPoint, mediaItem.fps) / 1000; + } + } + return { mediaItem, segmentStart: start, segmentEnd: end } as VideoPlayerSegmentBuilderData; + } + + rangeChanged(range: ApiTemporalRange){ + this.startControl.setValue(range.start.value) + this.endControl.setValue(range.end.value) + this.unitControl.setValue(ApiTemporalUnit.SECONDS) + } + +} diff --git a/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.html new file mode 100644 index 000000000..c94ddef64 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.html @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.scss b/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.ts new file mode 100644 index 000000000..410bad060 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/query-description-text-form-field/query-description-text-form-field.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from "@angular/core"; +import { UntypedFormControl } from "@angular/forms"; + +@Component({ + selector: 'app-query-description-text-form-field', + templateUrl: './query-description-text-form-field.component.html', + styleUrls: ['./query-description-text-form-field.component.scss'] +}) +export class QueryDescriptionTextFormFieldComponent { + + @Input() + control: UntypedFormControl + +} diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 8b56243a3..e99b48125 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -414,209 +414,13 @@

At least one query component is required!
-
-
-

- - - - - - - - - - - - - {{ mediaItem.name }} | - Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} - - - Please select a valid media item, i.e., use the autocomplete to select a value. - - - - - - - - - - - - {{ path }} - - - Please select a valid media item, i.e., use the autocomplete to select a value. - - - - - - - - - - - - - - {{ unit }} - - - - - - - - - - - - - - - - - - - {{ mediaItem.name }} | - Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} - - - Please select a valid media item i.e. use the autocomplete to select a value. - - - - - - - - - - - - - {{ unit }} - - - - - - -

-
-
-
- -
-
- -
+
+
- -
diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index 6bc607934..f012d4e53 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -81,6 +81,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { this.taskType = null; } }) + } ngOnDestroy() { @@ -242,7 +243,28 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { } toggleExternalVideoPreview(path: string, startControl?: UntypedFormControl, endControl?: UntypedFormControl, unitControl?: UntypedFormControl) { + let start = -1; + let end = -1; + const unit = unitControl?.value ? (unitControl.value as ApiTemporalUnit) : ApiTemporalUnit.SECONDS; + if (startControl && startControl.value) { + if (unitControl.value === 'TIMECODE') { + start = TimeUtilities.timeCode2Milliseconds24fps(startControl.value) / 1000; + } else { + start = + TimeUtilities.point2Milliseconds24fps({ value: startControl.value, unit } as ApiTemporalPoint) / 1000; + } + } + if (endControl && endControl.value) { + if (unitControl.value === 'TIMECODE') { + end = TimeUtilities.timeCode2Milliseconds24fps(endControl.value) / 1000; + } else { + end = TimeUtilities.point2Milliseconds24fps({ value: endControl.value, unit } as ApiTemporalPoint) / 1000; + } + } + console.log('Start=' + start + ', End=' + end); + this.externalVideoData = { externalPath: path, segmentStart: start, segmentEnd: end } as VideoPlayerSegmentBuilderData; + this.externalVideoPreviewActive = !this.externalVideoPreviewActive; } externalPreviewActive():boolean{ diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index 5a4fe1866..40097c182 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -121,7 +121,7 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent } as ConfirmationDialogComponentData }); dialogRef.afterClosed().subscribe((result) => { - if (result) { + if (result === true) { this.builderService.removeTask(task); } }); diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index 424a804b3..d81508b6d 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -1,39 +1,68 @@ -import {NgModule} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {TemplateInformationComponent} from './template-information/template-information.component'; -import {ReactiveFormsModule} from '@angular/forms'; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatInputModule} from '@angular/material/input'; -import { JudgesListComponent } from './judges-list/judges-list.component'; -import {MatButtonModule} from '@angular/material/button'; -import {MatTooltipModule} from '@angular/material/tooltip'; -import {MatIconModule} from '@angular/material/icon'; -import {MatMenuModule} from '@angular/material/menu'; -import {MatAutocompleteModule} from '@angular/material/autocomplete'; -import {MatTableModule} from '@angular/material/table'; -import { TeamsListComponent } from './teams-list/teams-list.component'; -import { TaskTypesListComponent } from './task-types-list/task-types-list.component'; +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { TemplateInformationComponent } from "./template-information/template-information.component"; +import { ReactiveFormsModule } from "@angular/forms"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatInputModule } from "@angular/material/input"; +import { JudgesListComponent } from "./judges-list/judges-list.component"; +import { MatButtonModule } from "@angular/material/button"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { MatIconModule } from "@angular/material/icon"; +import { MatMenuModule } from "@angular/material/menu"; +import { MatAutocompleteModule } from "@angular/material/autocomplete"; +import { MatTableModule } from "@angular/material/table"; +import { TeamsListComponent } from "./teams-list/teams-list.component"; +import { TaskTypesListComponent } from "./task-types-list/task-types-list.component"; import { MatListModule } from "@angular/material/list"; -import { TaskGroupsListComponent } from './task-groups-list/task-groups-list.component'; +import { TaskGroupsListComponent } from "./task-groups-list/task-groups-list.component"; import { SharedModule } from "../../../shared/shared.module"; -import { TaskTemplatesListComponent } from './tasks-list/task-templates-list.component'; -import { TaskTemplateEditorComponent } from './task-template-editor/task-template-editor.component'; +import { TaskTemplatesListComponent } from "./tasks-list/task-templates-list.component"; +import { TaskTemplateEditorComponent } from "./task-template-editor/task-template-editor.component"; import { MatSelectModule } from "@angular/material/select"; import { MatButtonToggleModule } from "@angular/material/button-toggle"; import { MatGridListModule } from "@angular/material/grid-list"; import { CompetitionBuilderModule } from "../../../competition/competition-builder/competition-builder.module"; +import { TemplateBuilderModule } from "../template-builder.module"; +import { QueryDescriptionFormFieldComponent } from "./query-description-form-field/query-description-form-field.component"; +import { QueryDescriptionTextFormFieldComponent } from "./query-description-text-form-field/query-description-text-form-field.component"; +import { + QueryDescriptionExternalFormFieldComponent +} from "./query-description-external-form-field/query-description-external-form-field.component"; +import { + QueryDescriptionMediaItemFormFieldComponent +} from "./query-description-media-item-form-field/query-description-media-item-form-field.component"; +import { + QueryDescriptionMediaItemImageFormFieldComponent +} from "./query-description-media-item-image-form-field/query-description-media-item-image-form-field.component"; +import { + QueryDescriptionMediaItemVideoFormFieldComponent +} from "./query-description-media-item-video-form-field/query-description-media-item-video-form-field.component"; +import { + QueryDescriptionExternalVideoFormFieldComponent +} from "./query-description-external-video-form-field/query-description-external-video-form-field.component"; +import { + QueryDescriptionExternalImageFormFieldComponent +} from "./query-description-external-image-form-field/query-description-external-image-form-field.component"; @NgModule({ - declarations: [ - TemplateInformationComponent, - JudgesListComponent, - TeamsListComponent, - TaskTypesListComponent, - TaskGroupsListComponent, - TaskTemplatesListComponent, - TaskTemplateEditorComponent - ], + declarations: [ + TemplateInformationComponent, + JudgesListComponent, + TeamsListComponent, + TaskTypesListComponent, + TaskGroupsListComponent, + TaskTemplatesListComponent, + TaskTemplateEditorComponent, + QueryDescriptionFormFieldComponent, + QueryDescriptionTextFormFieldComponent, + QueryDescriptionExternalFormFieldComponent, + QueryDescriptionMediaItemFormFieldComponent, + QueryDescriptionMediaItemImageFormFieldComponent, + QueryDescriptionMediaItemVideoFormFieldComponent, + QueryDescriptionExternalVideoFormFieldComponent, + QueryDescriptionExternalImageFormFieldComponent + ], imports: [ CommonModule, ReactiveFormsModule, @@ -50,9 +79,15 @@ import { CompetitionBuilderModule } from "../../../competition/competition-build MatSelectModule, MatButtonToggleModule, MatGridListModule, - CompetitionBuilderModule + CompetitionBuilderModule, ], - exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, TaskTypesListComponent, TaskGroupsListComponent, TaskTemplatesListComponent, TaskTemplateEditorComponent] + exports: [TemplateInformationComponent, + JudgesListComponent, + TeamsListComponent, + TaskTypesListComponent, + TaskGroupsListComponent, + TaskTemplatesListComponent, + TaskTemplateEditorComponent] }) export class TemplateBuilderComponentsModule { } diff --git a/frontend/src/app/template/template-builder/template-builder.module.ts b/frontend/src/app/template/template-builder/template-builder.module.ts index ff4842b78..ded7e1415 100644 --- a/frontend/src/app/template/template-builder/template-builder.module.ts +++ b/frontend/src/app/template/template-builder/template-builder.module.ts @@ -6,21 +6,40 @@ import {MatIconModule} from '@angular/material/icon'; import {MatTabsModule} from '@angular/material/tabs'; import {SharedModule} from '../../shared/shared.module'; import { MatTooltipModule } from "@angular/material/tooltip"; +import { QueryDescriptionFormFieldComponent } from './components/query-description-form-field/query-description-form-field.component'; +import { QueryDescriptionTextFormFieldComponent } from './components/query-description-text-form-field/query-description-text-form-field.component'; +import { QueryDescriptionExternalFormFieldComponent } from './components/query-description-external-form-field/query-description-external-form-field.component'; +import { QueryDescriptionMediaItemFormFieldComponent } from './components/query-description-media-item-form-field/query-description-media-item-form-field.component'; +import { MatInputModule } from "@angular/material/input"; +import { ReactiveFormsModule } from "@angular/forms"; +import { QueryDescriptionMediaItemImageFormFieldComponent } from './components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component'; +import { MatAutocompleteModule } from "@angular/material/autocomplete"; +import { QueryDescriptionMediaItemVideoFormFieldComponent } from './components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component'; +import { MatSelectModule } from "@angular/material/select"; +import { CompetitionBuilderModule } from "../../competition/competition-builder/competition-builder.module"; +import { QueryDescriptionExternalVideoFormFieldComponent } from './components/query-description-external-video-form-field/query-description-external-video-form-field.component'; +import { QueryDescriptionExternalImageFormFieldComponent } from './components/query-description-external-image-form-field/query-description-external-image-form-field.component'; @NgModule({ declarations: [ - TemplateBuilderComponent + TemplateBuilderComponent, + ], imports: [ CommonModule, - TemplateBuilderComponentsModule, MatIconModule, MatTabsModule, SharedModule, - MatTooltipModule + MatTooltipModule, + MatInputModule, + ReactiveFormsModule, + MatAutocompleteModule, + MatSelectModule, + CompetitionBuilderModule, + TemplateBuilderComponentsModule ], - exports: [TemplateBuilderComponent] + exports: [TemplateBuilderComponent] }) export class TemplateBuilderModule { } From 0d511928c13c7e384882d2840169dc80c0b4dac0 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 1 Jun 2023 14:41:52 +0200 Subject: [PATCH 316/498] #310 Fixed target / query description previewing --- .../video-player-segment-builder.component.ts | 18 +++++--- .../actionable-dynamic-table.component.html | 2 +- .../template-builder.component.html | 18 ++++---- .../template-builder.component.scss | 45 +++++++++++++++++++ 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts index 8aeb033ef..f2e4b5c2b 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/video-player-segment-builder/video-player-segment-builder.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from "@angular/core"; import { Observable, of, Subscription } from 'rxjs'; import { ApiMediaItem, ApiTemporalPoint, ApiTemporalRange } from '../../../../../../openapi'; import { AppConfig } from '../../../../app.config'; @@ -21,7 +21,7 @@ export interface VideoPlayerSegmentBuilderData { templateUrl: './video-player-segment-builder.component.html', styleUrls: ['./video-player-segment-builder.component.scss'], }) -export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDestroy { +export class VideoPlayerSegmentBuilderComponent implements OnInit, AfterViewInit, OnDestroy { @Input() data: VideoPlayerSegmentBuilderData; @Output() rangeChange = new EventEmitter(); @Input() showTitle = true @@ -45,12 +45,11 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: VideoPlayerSegmentBuilderData*/ ) { - if(this.data){ - this.isMediaItemPlayer = this.data.mediaItem && !this.data.externalPath; - } } ngAfterViewInit(): void { + console.log("VIDEO DATA: ",this.data); + console.log("MEDIA ITEM", this.isMediaItemPlayer) setTimeout(() => { /* * timeout because of value changed after checking thingy @@ -167,4 +166,13 @@ export class VideoPlayerSegmentBuilderComponent implements AfterViewInit, OnDest console.log(`Fetched: ${out}`); return out; } + + ngOnInit(): void { + if(this.data){ + if(this.data.mediaItem && this.data.mediaItem.mediaItemId){ + console.log("media item!") + this.isMediaItemPlayer = true; + } + } + } } diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index 9584f73fa..ffe826e3d 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -3,7 +3,7 @@

{{tableTitle}}

- +
diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index d81508b6d..393c30f68 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -1,5 +1,5 @@ import { NgModule } from "@angular/core"; -import { CommonModule } from "@angular/common"; +import {CommonModule, NgOptimizedImage} from "@angular/common"; import { TemplateInformationComponent } from "./template-information/template-information.component"; import { ReactiveFormsModule } from "@angular/forms"; import { MatFormFieldModule } from "@angular/material/form-field"; @@ -63,24 +63,25 @@ import { QueryDescriptionExternalVideoFormFieldComponent, QueryDescriptionExternalImageFormFieldComponent ], - imports: [ - CommonModule, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatTooltipModule, - MatIconModule, - MatMenuModule, - MatAutocompleteModule, - MatTableModule, - MatListModule, - SharedModule, - MatSelectModule, - MatButtonToggleModule, - MatGridListModule, - CompetitionBuilderModule, - ], + imports: [ + CommonModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatMenuModule, + MatAutocompleteModule, + MatTableModule, + MatListModule, + SharedModule, + MatSelectModule, + MatButtonToggleModule, + MatGridListModule, + CompetitionBuilderModule, + NgOptimizedImage, + ], exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, From 4abdc33ec3e4f9f5013d87d696e726c4407d3988 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 5 Jul 2023 11:36:16 +0200 Subject: [PATCH 336/498] Resolvved #310 by adding a spinner to indicate saving --- .../template-builder/template-builder.component.html | 3 ++- .../template-builder/template-builder.component.ts | 9 +++++++-- .../template/template-builder/template-builder.module.ts | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index fdbcc0c40..ea4ecd512 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -9,10 +9,11 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}}
- +
+ +
diff --git a/frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.scss b/frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.ts b/frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.ts new file mode 100644 index 000000000..08d51eebb --- /dev/null +++ b/frontend/src/app/template/template-builder/components/teamgroups-dialog/teamgroups-dialog.component.ts @@ -0,0 +1,84 @@ +import { Component, Inject } from "@angular/core"; +import { FormControl, FormGroup, UntypedFormGroup, Validators } from "@angular/forms"; +import { ApiTeam, ApiTeamGroup } from "../../../../../../openapi"; +import { Observable } from "rxjs"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { AppConfig } from "../../../../app.config"; +import { map, shareReplay } from "rxjs/operators"; +import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete"; + +@Component({ + selector: 'app-teamgroups-dialog', + templateUrl: './teamgroups-dialog.component.html', + styleUrls: ['./teamgroups-dialog.component.scss'] +}) +export class TeamgroupsDialogComponent { + + form: UntypedFormGroup; + availableTeams: Observable; + + constructor( + private dialogRef: MatDialogRef, + private builderService: TemplateBuilderService, + private config: AppConfig, + @Inject(MAT_DIALOG_DATA) private teamgroup?: ApiTeamGroup + ) { + + this.form = new UntypedFormGroup({ + id: new FormControl(teamgroup?.id), + name: new FormControl(teamgroup?.name, [Validators.required, Validators.minLength(3)]), + aggregation: new FormControl(teamgroup?.aggregation, [Validators.required, Validators.minLength(3)]), + teams: new FormControl(teamgroup?.teams || [], [Validators.minLength(1)]), + teamInput: new FormControl('') + }); + + this.availableTeams = this.builderService.templateAsObservable().pipe(map(t => { + if(t){ + return t.teams + }else{ + return []; + } + }), shareReplay(1)); + + } + + public addTeam(event: MatAutocompleteSelectedEvent){ + this.form.get('teams').value.push(event.option.value); + this.form.get('teamInput').setValue(null); + } + + public removeTeam(team: ApiTeam){ + const idx = this.form.get('teams').value.indexOf(team); + if(idx >= 0){ + this.form.get('teams').value.splice(idx, 1); + } + } + + public teamForId(id: string): Observable{ + return this.availableTeams.pipe(map((teams) => teams.find((t) => t.teamId === id))); + } + + private fetchData(){ + return { + id: this.form.get('id')?.value, + name: this.form.get('name').value, + aggregation: this.form.get('aggregation').value, + teams: this.form.get('teams').value + } as ApiTeamGroup + } + + public save(){ + if(this.form.valid){ + this.dialogRef.close(this.fetchData()) + }else{ + console.log("TeamGroup Dialog", this.fetchData()) + console.log(this.form.get('teams').errors) + } + } + + public close(){ + this.dialogRef.close(null); + } + +} diff --git a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.html b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.html new file mode 100644 index 000000000..8b830f8a4 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.html @@ -0,0 +1,19 @@ + + + + + diff --git a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.scss b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts new file mode 100644 index 000000000..5a560be91 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts @@ -0,0 +1,91 @@ +import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { ApiTeam, ApiTeamGroup } from "../../../../../../openapi"; +import { Observable } from "rxjs"; +import { filter, map } from "rxjs/operators"; +import { MatDialog } from "@angular/material/dialog"; +import { + ActionableDynamicTable, + ActionableDynamicTableActionType, + ActionableDynamicTableColumnDefinition, + ActionableDynamicTableColumnType +} from "../../../../shared/actionable-dynamic-table/actionable-dynamic-table.component"; +import { TeamgroupsDialogComponent } from "../teamgroups-dialog/teamgroups-dialog.component"; + +@Component({ + selector: 'app-teamgroups-list', + templateUrl: './teamgroups-list.component.html', + styleUrls: ['./teamgroups-list.component.scss'] +}) +export class TeamgroupsListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy{ + + teamGroups : Observable = new Observable((o) => o.next([])) + + columns: ActionableDynamicTableColumnDefinition[] = [ + {key: 'name', header: 'Name', property: 'name', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'aggregation', header: 'Aggregation', property: 'aggregation', type: ActionableDynamicTableColumnType.TEXT}, + {key: 'actions', header: 'Actions', type: ActionableDynamicTableColumnType.ACTION, actions: [ActionableDynamicTableActionType.EDIT, ActionableDynamicTableActionType.REMOVE]} + ] + + displayedColumns = ['name', 'aggregation', 'actions'] + + @ViewChild("teamgroupsTable") table: ActionableDynamicTable; + + constructor( + builderService: TemplateBuilderService, + private dialog: MatDialog + ){ + super(builderService); + } + + ngOnInit() { + this.onInit(); + } + + ngOnDestroy() { + this.onDestroy(); + } + + onChange() { + this.teamGroups = this.builderService.templateAsObservable().pipe( + map((t) => { + if(t){ + return t.teamGroups; + }else{ + return []; + } + }) + ); + } + + public remove(teamGroup: ApiTeamGroup){ + this.builderService.removeTeamGroup(teamGroup); + this.table?.renderRows(); + } + + public trackById(index: number, teamGroup: ApiTeamGroup){ + return teamGroup?.id || String(index) + } + + public add(){ + const dialogRef = this.dialog.open(TeamgroupsDialogComponent, {width: '600px'}); + dialogRef.afterClosed().pipe(filter((t) => t != null)).subscribe((t) => { + this.builderService.getTemplate().teamGroups.push(t); + this.builderService.update(); + this.table.renderRows(); + }) + } + + public edit(teamGroup: ApiTeamGroup){ + const index = this.builderService.getTemplate().teamGroups.indexOf(teamGroup); + if(index > -1){ + const dialogRef = this.dialog.open(TeamgroupsDialogComponent, {data: teamGroup, width: '600px'}); + dialogRef.afterClosed().pipe(filter((t) => t != null)).subscribe((t) => { + this.builderService.getTemplate().teamGroups[index] = teamGroup; + this.builderService.update(); + this.table.renderRows(); + }) + } + } +} diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index ea4ecd512..df24a0528 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -9,11 +9,11 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}}
- -
-
diff --git a/frontend/src/app/services/pipes/order-by.pipe.ts b/frontend/src/app/services/pipes/order-by.pipe.ts new file mode 100644 index 000000000..80f4a2d40 --- /dev/null +++ b/frontend/src/app/services/pipes/order-by.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: "orderBy" +}) +export class OrderByPipe implements PipeTransform { + + transform(value: any[], order = "", compare: (a: any, b: any) => number): any[] { + if (!value || order === "" || !order) { + return value; + } + if (value.length <= 1) { + return value; + } + if (!compare) { + if (order === "asc") { + return value.sort(); + } else { + return value.sort().reverse(); + } + } else { + return value.sort(compare); + } + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 8cc4fc148..7a0d762e3 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -19,6 +19,7 @@ import { ResolveMediaItemPipe } from './pipes/resolve-mediaitem.pipe'; import { ResolveMediaItemUrlPipe } from './pipes/resolve-media-item-url.pipe'; import { ResolveMediaItemPreviewPipe } from './pipes/resolve-media-item-preview.pipe'; import { FormatMediaItemPipe } from './pipes/format-media-item.pipe'; +import { OrderByPipe } from './pipes/order-by.pipe'; /** * Provides the {@link AppConfig} reference. @@ -51,7 +52,8 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveMediaItemPipe, ResolveMediaItemUrlPipe, ResolveMediaItemPreviewPipe, - FormatMediaItemPipe + FormatMediaItemPipe, + OrderByPipe ], declarations: [ RoundPipePipe, @@ -69,6 +71,7 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveMediaItemUrlPipe, ResolveMediaItemPreviewPipe, FormatMediaItemPipe, + OrderByPipe, ], providers: [ AuthenticationService, diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index e99b48125..604ecb5e0 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -90,147 +90,6 @@

-->

-
- - - - - - - - -

- - - - - {{ mediaItem.name }} | - Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} - - - - - Please select a valid media item i.e. use the autocomplete to select a value. - - - - - -

-

- - - - - - - - - {{ unit }} - - - - - - -

-
- - -
- -
-
-
-
-
[formControl]="target.get('mediaItem')" [matAutocomplete]="autoGroup" /> - + {{ mediaItem.name }} | - Type: {{ mediaItem.type }}, ID: {{ mediaItem.id }} + Type: {{ mediaItem.type }} -

Edit competition {{(builderService.templateAsObservable() | async)?.id}} (unsaved changes)

+

Edit evaluation template {{(builderService.templateAsObservable() | async)?.id}} (unsaved changes)

@@ -26,7 +26,8 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} + [inline]="true" + matTooltip="Download the entire evaluation template as JSON">

diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index f7aa7e157..b6983243b 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -78,10 +78,11 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i this.snackBar.open(s.description, null, {duration: 5000}); this.builderService.unmarkDirty(); console.log("TemplateBuilder: Saved successfully", this.builderService.isDirty()) + this.isSaving = false; this.refresh(); }, (r) => { this.snackBar.open(`Error: ${r?.error?.description}`, null, {duration: 5000}) - //this.isSaving = false + this.isSaving = false }, () => { this.isSaving = false }); diff --git a/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.html b/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.html index c5e33ee81..126f60640 100644 --- a/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.html +++ b/frontend/src/app/user/admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component.html @@ -31,10 +31,12 @@

Create User

+
From 34d8c34a63ad194c6c4e4bb7037e7cf0034a6ff2 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 17 Jul 2023 15:48:50 +0200 Subject: [PATCH 352/498] Fixed #418 --- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 5 +++-- .../api/rest/handler/download/EvaluationDownloadHandler.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 98507d43c..c4e62e49c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -9,6 +9,7 @@ import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler import dev.dres.api.rest.handler.collection.* import dev.dres.api.rest.handler.download.EvaluationDownloadHandler import dev.dres.api.rest.handler.download.EvaluationTemplateDownloadHandler +import dev.dres.api.rest.handler.download.ScoreDownloadHandler import dev.dres.api.rest.handler.evaluation.admin.* import dev.dres.api.rest.handler.evaluation.client.ClientListEvaluationsHandler import dev.dres.api.rest.handler.evaluation.client.ClientTaskInfoHandler @@ -205,8 +206,8 @@ object RestApi { // Downloads EvaluationDownloadHandler(store), - EvaluationTemplateDownloadHandler(store) - /* DownloadHandler.CompetitionRunScoreHandler(store), */ + EvaluationTemplateDownloadHandler(store), + ScoreDownloadHandler(store) ) javalin = Javalin.create { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index e10e707a6..cd1a83e6b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -41,7 +41,7 @@ class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadH ) override fun doGet(ctx: Context): String { /* Obtain run id and run. */ - val evaluationId = ctx.pathParamMap().getOrElse("runId") { throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", ctx) } + val evaluationId = ctx.pathParamMap().getOrElse("evaluationId") { throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", ctx) } val evaluation = this.store.transactional(true) { DbEvaluation.query(DbEvaluation::id eq evaluationId).firstOrNull()?.toApi() ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) From 905b87ae4eb97e8b3e09f987a25cb1921fd49b00 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 17 Jul 2023 16:03:11 +0200 Subject: [PATCH 353/498] Fixed some naming inconsistencies --- README.md | 19 +++++++++---------- .../run/InteractiveAsynchronousRunManager.kt | 1 - .../main/kotlin/dev/dres/run/RunExecutor.kt | 17 +---------------- .../utilities/extensions/ContextExtensions.kt | 4 ++-- .../src/app/run/admin-run-list.component.html | 12 ++++++------ .../run-admin-submissions-list.component.ts | 2 +- .../run-admin-toolbar.component.html | 12 ++++++------ .../app/viewer/model/run-viewer-widgets.ts | 4 ++-- 8 files changed, 27 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 65c8c40b2..ae9baa408 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ DRES> user create -u admin -p password -r ADMIN ``` Additional users are required in order to perform an evaluation. At least one user per participating system instance -(i.e. with `-r PARTICIPANT`). To simply have a user to view competitions, but otherwise not interact with the system, +(i.e. with `-r PARTICIPANT`). To simply have a user to view evaluation runs, but otherwise not interact with the system, we recommend to create a viewer user: `-r VIEWER`. ### Create Media Collection @@ -117,30 +117,29 @@ DRES> collection scan -c first -vt mp4 Obviously, for an image collection one would replace `-vt mp4` with `-it png` or `-it jpg`. For mixed collections, use both. -### Create Competition +### Create Evaluations **Below instructions require an admin user and are performed in the UI** | -------------------------------------------------------------------------- -The next step is to create a competition. This is done using the web frontend of DRES. +The next step is to create an evaluation template. This is done using the web frontend of DRES. Using your browser, navigate to your DRES address and perform the login using the administrator user created previously. -Then, navigate to _Competition Builder_ and create a new competition. Follow the instructions there. +Then, navigate to _Evaluation Template Builder_ and create a new competition. Follow the instructions there. ### Create Competition Run -A competiton serves as the template for one or more _competition runs_. -Please keep in mind, that once a _run_ was created, changes on the competition are not reflected in the run. +An evaluation template serves as the template for one or more _evaluation runs_. +Please keep in mind, that once a _run_ was created, changes on the template are not reflected in the run. -Competition runs are created from the _Competitoin Runs_ view, where one uses the "+" button to create a new one. -Currently (DRES v0.3.2), only _SYNCHRONOUS_ runs are supported, so please chose this option. +Evaluation runs are created from the _Evaluations_ view, where one uses the "+" button to create a new one. In a non distributed setting, it might be desirable, that participants cannot view the actual run from the frontend, but require an external source for the query hints (e.g. a large monitor). This could be achieved by unchecking the corresponding option in the dialog. -### Runnig the competition +### Runnig the evaluation -As competition _operator_, one has to first start the run, then switch to a fitting task and ultimately start the task. +As evaluation _operator_, one has to first start the run, then switch to a fitting task and ultimately start the task. Query hints are displayed as configured to all viewers, once they are all loaded (depending on the setup, this might take a breif moment). Viewers and participants are shown the message "_Waiting for host to start task_". In case this seems to take too long, the operator can switch to the admin view and force all participants to be ready, by clicking the red ones. diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index c8394bb58..7dd0e7c78 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -686,7 +686,6 @@ class InteractiveAsynchronousRunManager( // oh shit, something went horribly, horribly wrong if (errorCounter >= MAXIMUM_RUN_LOOP_ERROR_COUNT) { LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_RUN_LOOP_ERROR_COUNT; terminating loop...") - RunExecutor.dump(this.evaluation) break } else { LOGGER.error("This is the ${++errorCounter}-th in a row. Run manager will terminate loop after $MAXIMUM_RUN_LOOP_ERROR_COUNT errors") diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index d0fa7e81f..62a24950a 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -50,7 +50,7 @@ object RunExecutor : Consumer { /** List of [WsContext] that are currently connected. */ private val connectedClients = HashMap() - /** List of session IDs that are currently observing a competition. */ + /** List of session IDs that are currently observing an evaluation. */ private val observingClients = HashMap>() /** Lock for accessing and changing all data structures related to WebSocket clients. */ @@ -238,19 +238,4 @@ object RunExecutor : Consumer { this.executor.shutdownNow() } - - /** - * Dumps the given [EvaluationRun] to a file. - * - * @param competition [EvaluationRun] that should be dumped. - */ - fun dump(competition: EvaluationRun) { //FIXME can no longer properly dump things, since they refer to dnq objects - try { - val file = File("run_dump_${competition.id}.json") - jacksonObjectMapper().writeValue(file, competition) - this.logger.info("Wrote current run state to ${file.absolutePath}") - } catch (e: Exception){ - this.logger.error("Could not write run to disk: ", e) - } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 986269962..41ebcb5f7 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -116,8 +116,8 @@ fun Context.activeManagerForUser(): RunManager { val managers = AccessManager.getRunManagerForUser(userId).filter { it.status != RunManagerStatus.CREATED && it.status != RunManagerStatus.TERMINATED } - if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", this) - if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", this) + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible evaluation with an active task.", this) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible evaluation found: ${managers.joinToString { it.template.name }}", this) return managers.first() } diff --git a/frontend/src/app/run/admin-run-list.component.html b/frontend/src/app/run/admin-run-list.component.html index 4c18d1117..69047e0d5 100644 --- a/frontend/src/app/run/admin-run-list.component.html +++ b/frontend/src/app/run/admin-run-list.component.html @@ -11,8 +11,8 @@

diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index f876097d1..bc507efaf 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -103,28 +103,6 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent this.selection.toggle(task); } - // public moveTaskUp(task: ApiTaskTemplate) { - // const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); - // if (oldIndex > 0) { - // const buffer = this.builderService.getTemplate().tasks[oldIndex - 1]; - // this.builderService.getTemplate().tasks[oldIndex - 1] = task; - // this.builderService.getTemplate().tasks[oldIndex] = buffer; - // this.builderService.update(); - // this.taskTable.renderRows(); - // } - // } - // - // public moveTaskDown(task: ApiTaskTemplate) { - // const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); - // if (oldIndex < this.builderService.getTemplate().tasks.length - 1) { - // const buffer = this.builderService.getTemplate().tasks[oldIndex + 1]; - // this.builderService.getTemplate().tasks[oldIndex + 1] = task; - // this.builderService.getTemplate().tasks[oldIndex] = buffer; - // this.builderService.update(); - // this.taskTable.renderRows(); - // } - // } - public tasksLength() { return this.builderService.getTemplate().tasks.length; } From 686d71554ec634222645970280b80e49f38c9189 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 12:35:57 +0200 Subject: [PATCH 400/498] WiP: Steps towards better frontend logging --- frontend/src/app/app.component.ts | 9 ++- frontend/src/app/app.module.ts | 4 +- .../app/services/logging/log-entry.model.ts | 9 +++ .../app/services/logging/log-level.enum.ts | 10 +++ .../services/logging/log-service.factory.ts | 6 ++ .../services/logging/log-service.provider.ts | 9 +++ .../src/app/services/logging/log.service.ts | 69 +++++++++++++++++++ .../services/logging/logger-config.token.ts | 9 +++ .../src/app/services/logging/logger.config.ts | 3 + .../app/services/logging/logging.module.ts | 19 +++++ .../services/pipes/space-to-newline.pipe.ts | 2 +- .../server-info/server-info.component.ts | 14 ++-- 12 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 frontend/src/app/services/logging/log-entry.model.ts create mode 100644 frontend/src/app/services/logging/log-level.enum.ts create mode 100644 frontend/src/app/services/logging/log-service.factory.ts create mode 100644 frontend/src/app/services/logging/log-service.provider.ts create mode 100644 frontend/src/app/services/logging/log.service.ts create mode 100644 frontend/src/app/services/logging/logger-config.token.ts create mode 100644 frontend/src/app/services/logging/logger.config.ts create mode 100644 frontend/src/app/services/logging/logging.module.ts diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 96c0c768c..4fa7f47b0 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, inject } from "@angular/core"; import { Router } from '@angular/router'; import { AuthenticationService } from './services/session/authentication.sevice'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -8,6 +8,7 @@ import { AppConfig } from './app.config'; import {ApiRole, ApiUser} from '../../openapi'; import { MatDialog } from "@angular/material/dialog"; import { ServerInfoComponent } from "./shared/server-info/server-info.component"; +import { LogService } from "./services/logging/log.service"; @Component({ selector: 'app-root', @@ -15,6 +16,9 @@ import { ServerInfoComponent } from "./shared/server-info/server-info.component" styleUrls: ['./app.component.scss'], }) export class AppComponent { + + private readonly LOGGER = inject(LogService); + title = 'dres-frontend'; user: Observable; @@ -27,7 +31,7 @@ export class AppComponent { private router: Router, private snackBar: MatSnackBar, public config: AppConfig, - private dialog: MatDialog + private dialog: MatDialog, ) { this.user = this.authenticationService.user; this.loggedIn = this.authenticationService.isLoggedIn; @@ -40,6 +44,7 @@ export class AppComponent { */ public toggleMute() { this.config.config.effects.mute = !this.config.config.effects.mute; + this.LOGGER.fatal("Mute state", this.config.config.effects.mute) } public logout() { diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 76c248137..ee4f4091a 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -28,6 +28,7 @@ import { EvaluationModule } from './evaluation/evaluation.module'; import { ErrorModule } from "./error/error.module"; import {DragDropModule} from '@angular/cdk/drag-drop'; import {MatTableModule} from '@angular/material/table'; +import { LoggingModule } from "./services/logging/logging.module"; /** * Method used to load application config. @@ -68,7 +69,8 @@ export function initializeApp(appConfig: AppConfig) { JudgementModule, TemplateModule, EvaluationModule, - ErrorModule + ErrorModule, + LoggingModule, ], providers: [ AppConfig, diff --git a/frontend/src/app/services/logging/log-entry.model.ts b/frontend/src/app/services/logging/log-entry.model.ts new file mode 100644 index 000000000..9d7ce7490 --- /dev/null +++ b/frontend/src/app/services/logging/log-entry.model.ts @@ -0,0 +1,9 @@ +import { LogLevel } from "./log-level.enum"; + +export interface LogEntry { + timestamp: Date + level: LogLevel + source: string + message: string + data?: any[] +} diff --git a/frontend/src/app/services/logging/log-level.enum.ts b/frontend/src/app/services/logging/log-level.enum.ts new file mode 100644 index 000000000..74e674a26 --- /dev/null +++ b/frontend/src/app/services/logging/log-level.enum.ts @@ -0,0 +1,10 @@ +export enum LogLevel { + ALL, + TRACE, + DEBUG, + INFO, + WARN, + ERROR, + FATAL, + OFF +} diff --git a/frontend/src/app/services/logging/log-service.factory.ts b/frontend/src/app/services/logging/log-service.factory.ts new file mode 100644 index 000000000..4eff1d59b --- /dev/null +++ b/frontend/src/app/services/logging/log-service.factory.ts @@ -0,0 +1,6 @@ +import { LogService } from "./log.service"; +import { LoggerConfig } from "./logger.config"; + +export const logServiceFactory = (config: LoggerConfig) => { + return new LogService(config) +} diff --git a/frontend/src/app/services/logging/log-service.provider.ts b/frontend/src/app/services/logging/log-service.provider.ts new file mode 100644 index 000000000..e94dd60e9 --- /dev/null +++ b/frontend/src/app/services/logging/log-service.provider.ts @@ -0,0 +1,9 @@ +import { LogService } from "./log.service"; +import { logServiceFactory } from "./log-service.factory"; +import { LOGGER_CONFIG } from "./logger-config.token"; + +export const logServiceProvider = { + provide: LogService, + useFactory: logServiceFactory, + deps: [LOGGER_CONFIG], +}; diff --git a/frontend/src/app/services/logging/log.service.ts b/frontend/src/app/services/logging/log.service.ts new file mode 100644 index 000000000..33c7c7016 --- /dev/null +++ b/frontend/src/app/services/logging/log.service.ts @@ -0,0 +1,69 @@ +import { Inject, Injectable, isDevMode } from "@angular/core"; +import { LogEntry } from "./log-entry.model"; +import { LogLevel } from "./log-level.enum"; +import { LOGGER_CONFIG } from "./logger-config.token"; +import { LoggerConfig } from "./logger.config"; + +@Injectable({providedIn: 'root'}) +export class LogService { + + private readonly level: LogLevel = isDevMode() ? LogLevel.ALL : LogLevel.INFO + + private readonly source; + + static callingTrace(){ + const e = new Error(); + const regex = /\((.*):(\d+):(\d+)\)$/ + const match = regex.exec(e.stack.split("\n")[2]); + return { + filepath: match[1], + line: match[2], + column: match[3] + }; + } + + constructor(@Inject(LOGGER_CONFIG) config: LoggerConfig) { + this.source = config.identifier; + } + + public trace(msg: string, ...data: any[]){ + this.log({ source: this.source, timestamp: new Date(), level: LogLevel.TRACE, message: msg, data: data}) + } + public debug(msg: string, ...data: any[]){ + this.log({ source: this.source, timestamp: new Date(), level: LogLevel.DEBUG, message: msg, data: data}) + } + + public info(msg: string, ...data: any[]){ + this.log({ source: this.source, timestamp: new Date(), level: LogLevel.INFO, message: msg, data: data}) + } + + public warn(msg: string, ...data: any[]){ + this.log({ source: this.source, timestamp: new Date(), level: LogLevel.WARN, message: msg, data: data}) + } + + public error(msg: string, ...data: any[]){ + this.log({ source: this.source, timestamp: new Date(), level: LogLevel.ERROR, message: msg, data: data}) + } + + public fatal(msg: string, ...data: any[]){ + this.log({ source: this.source, timestamp: new Date(), level: LogLevel.FATAL, message: msg, data: data}) + } + + private log(log: LogEntry){ + if(!this.checkLogLevel(log.level) || (this.level === LogLevel.OFF)){ + return; + } + /* We have FATAl but console doesnt */ + const lvl = (log.level === LogLevel.FATAL ? 'ERROR' : LogLevel[log.level]).toLowerCase() + const prefix = `${log.timestamp.toISOString()} [${log.source}] ${lvl}:` + if(log?.data?.length > 0){ + console[lvl](prefix, log.message, ...log.data) + }else{ + console[lvl](prefix, log.message) + } + } + + private checkLogLevel(level: LogLevel){ + return level >= this.level; + } +} diff --git a/frontend/src/app/services/logging/logger-config.token.ts b/frontend/src/app/services/logging/logger-config.token.ts new file mode 100644 index 000000000..51cb8b41d --- /dev/null +++ b/frontend/src/app/services/logging/logger-config.token.ts @@ -0,0 +1,9 @@ +import { InjectionToken } from "@angular/core"; +import { LoggerConfig } from "./logger.config"; + +export const LOGGER_CONFIG = new InjectionToken("__LOGGER_CONFIG__", { + providedIn: "root", + factory: () => { + return {identifier: "Root"} as LoggerConfig + } +}); diff --git a/frontend/src/app/services/logging/logger.config.ts b/frontend/src/app/services/logging/logger.config.ts new file mode 100644 index 000000000..421cf78b1 --- /dev/null +++ b/frontend/src/app/services/logging/logger.config.ts @@ -0,0 +1,3 @@ +export interface LoggerConfig { + identifier: string +} diff --git a/frontend/src/app/services/logging/logging.module.ts b/frontend/src/app/services/logging/logging.module.ts new file mode 100644 index 000000000..4e9be9c88 --- /dev/null +++ b/frontend/src/app/services/logging/logging.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { LogService } from "./log.service"; +import { LOGGER_CONFIG } from "./logger-config.token"; +import { LoggerConfig } from "./logger.config"; +import { logServiceProvider } from "./log-service.provider"; + + +@NgModule({ + declarations: [], + imports: [ + CommonModule + ], providers: [ + LogService, + logServiceProvider + ] +}) +export class LoggingModule { +} diff --git a/frontend/src/app/services/pipes/space-to-newline.pipe.ts b/frontend/src/app/services/pipes/space-to-newline.pipe.ts index 8be8c567f..d82eec33d 100644 --- a/frontend/src/app/services/pipes/space-to-newline.pipe.ts +++ b/frontend/src/app/services/pipes/space-to-newline.pipe.ts @@ -7,7 +7,7 @@ export class SpaceToNewlinePipe implements PipeTransform { transform(value: string, lineBreak= false): string { const nl: string = lineBreak ? '
' : '\n'; - return value.split(' ').join(nl); + return value?.split(' ').join(nl) || ''; } } diff --git a/frontend/src/app/shared/server-info/server-info.component.ts b/frontend/src/app/shared/server-info/server-info.component.ts index d9c71f0af..5618976fc 100644 --- a/frontend/src/app/shared/server-info/server-info.component.ts +++ b/frontend/src/app/shared/server-info/server-info.component.ts @@ -1,15 +1,21 @@ -import { AfterViewInit, Component, OnDestroy, OnInit } from "@angular/core"; +import { AfterViewInit, Component, inject, OnDestroy, OnInit } from "@angular/core"; import { AuthenticationService } from "../../services/session/authentication.sevice"; import { Observable, Subscription } from "rxjs"; import { ApiRole, DresInfo, StatusService } from "../../../../openapi"; +import { LogService } from "../../services/logging/log.service"; +import { LOGGER_CONFIG } from "../../services/logging/logger-config.token"; +import { LoggerConfig } from "../../services/logging/logger.config"; @Component({ selector: "app-server-info", templateUrl: "./server-info.component.html", - styleUrls: ["./server-info.component.scss"] + styleUrls: ["./server-info.component.scss"], + providers:[LogService, {provide: LOGGER_CONFIG, useValue: {identifier: 'ServerInfoComponent'} as LoggerConfig}] }) export class ServerInfoComponent implements OnInit, OnDestroy, AfterViewInit { + private readonly LOGGER: LogService = inject(LogService) + public info: Observable; private authSub: Subscription; @@ -20,7 +26,7 @@ export class ServerInfoComponent implements OnInit, OnDestroy, AfterViewInit { constructor(private auth: AuthenticationService, private status: StatusService) { this.authSub = this.auth.user.subscribe( u => { - this.isAdmin = u?.role === ApiRole.ADMIN + this.isAdmin = u?.role === ApiRole.ADMIN; } ); } @@ -34,7 +40,7 @@ export class ServerInfoComponent implements OnInit, OnDestroy, AfterViewInit { } ngAfterViewInit() { - + this.LOGGER.info("Hi") } ngOnDestroy(): void { From 38d389bd5882e97e4ecf3e09ce93da10b66682bb Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 15:50:44 +0200 Subject: [PATCH 401/498] Added a global error handler --- frontend/src/app/app.module.ts | 2 ++ .../error-handling/error-handling.module.ts | 16 ++++++++++ .../global-error-handler.service.ts | 22 ++++++++++++++ .../src/app/error/error-dialog.service.ts | 29 +++++++++++++++++++ .../error-dialog/error-dialog.component.html | 14 +++++++++ .../error-dialog/error-dialog.component.scss | 4 +++ .../error-dialog/error-dialog.component.ts | 13 +++++++++ frontend/src/app/error/error.module.ts | 18 ++++++++---- .../src/app/services/logging/log.service.ts | 5 ++-- 9 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 frontend/src/app/error-handling/error-handling.module.ts create mode 100644 frontend/src/app/error-handling/global-error-handler.service.ts create mode 100644 frontend/src/app/error/error-dialog.service.ts create mode 100644 frontend/src/app/error/error-dialog/error-dialog.component.html create mode 100644 frontend/src/app/error/error-dialog/error-dialog.component.scss create mode 100644 frontend/src/app/error/error-dialog/error-dialog.component.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index f9253a0d3..21867a83c 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -28,6 +28,7 @@ import { ErrorModule } from "./error/error.module"; import {DragDropModule} from '@angular/cdk/drag-drop'; import {MatTableModule} from '@angular/material/table'; import { LoggingModule } from "./services/logging/logging.module"; +import { ErrorHandlingModule } from "./error-handling/error-handling.module"; /** * Method used to load application config. @@ -69,6 +70,7 @@ export function initializeApp(appConfig: AppConfig) { EvaluationModule, ErrorModule, LoggingModule, + ErrorHandlingModule, ], providers: [ AppConfig, diff --git a/frontend/src/app/error-handling/error-handling.module.ts b/frontend/src/app/error-handling/error-handling.module.ts new file mode 100644 index 000000000..b77da0cf5 --- /dev/null +++ b/frontend/src/app/error-handling/error-handling.module.ts @@ -0,0 +1,16 @@ +import { ErrorHandler, NgModule } from "@angular/core"; +import { CommonModule } from '@angular/common'; +import { GlobalErrorHandlerService } from "./global-error-handler.service"; + + + +@NgModule({ + declarations: [], + imports: [ + CommonModule + ], + providers: [ + {provide: ErrorHandler, useClass: GlobalErrorHandlerService} + ] +}) +export class ErrorHandlingModule { } diff --git a/frontend/src/app/error-handling/global-error-handler.service.ts b/frontend/src/app/error-handling/global-error-handler.service.ts new file mode 100644 index 000000000..cacf2be3d --- /dev/null +++ b/frontend/src/app/error-handling/global-error-handler.service.ts @@ -0,0 +1,22 @@ +import { Injectable, NgZone } from "@angular/core"; +import { ErrorDialogService } from "../error/error-dialog.service"; +import { HttpErrorResponse } from "@angular/common/http"; + +@Injectable() +export class GlobalErrorHandlerService { + + constructor( + private errorDialogService: ErrorDialogService, + private zone: NgZone + ) {} + + handleError(error: Error) { + this.zone.run(() => + this.errorDialogService.openDialog( + error?.message + ) + ); + + console.error('Error from global error handler', error); + } +} diff --git a/frontend/src/app/error/error-dialog.service.ts b/frontend/src/app/error/error-dialog.service.ts new file mode 100644 index 000000000..019cdcdaf --- /dev/null +++ b/frontend/src/app/error/error-dialog.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { MatDialog } from "@angular/material/dialog"; +import { ErrorDialogComponent } from "./error-dialog/error-dialog.component"; + +@Injectable() +export class ErrorDialogService { + + private opened = false; + + constructor(private dialog: MatDialog) { + + } + + openDialog(message: string):void { + if(!this.opened){ + this.opened = true; + const dialogRef = this.dialog.open(ErrorDialogComponent, { + data: {message}, + maxHeight: '100%;', + width: '800px', + maxWidth: '100%', + disableClose: true, + hasBackdrop: true + }); + + dialogRef.afterClosed().subscribe(() => this.opened = false); + } + } +} diff --git a/frontend/src/app/error/error-dialog/error-dialog.component.html b/frontend/src/app/error/error-dialog/error-dialog.component.html new file mode 100644 index 000000000..faa488046 --- /dev/null +++ b/frontend/src/app/error/error-dialog/error-dialog.component.html @@ -0,0 +1,14 @@ +

An error occurred!

+
+

+ Something went wrong! See the browser console for the full stack trace. +

+ + + {{data?.message || 'FATAL: No error message present'}} + + +
+
+ +
diff --git a/frontend/src/app/error/error-dialog/error-dialog.component.scss b/frontend/src/app/error/error-dialog/error-dialog.component.scss new file mode 100644 index 000000000..20870d58c --- /dev/null +++ b/frontend/src/app/error/error-dialog/error-dialog.component.scss @@ -0,0 +1,4 @@ +.error { + font-family: monospace; + word-break: break-all; +} diff --git a/frontend/src/app/error/error-dialog/error-dialog.component.ts b/frontend/src/app/error/error-dialog/error-dialog.component.ts new file mode 100644 index 000000000..d656e5ae6 --- /dev/null +++ b/frontend/src/app/error/error-dialog/error-dialog.component.ts @@ -0,0 +1,13 @@ +import { Component, Inject } from "@angular/core"; +import { MAT_DIALOG_DATA } from "@angular/material/dialog"; + +@Component({ + selector: 'app-error-dialog', + templateUrl: './error-dialog.component.html', + styleUrls: ['./error-dialog.component.scss'] +}) +export class ErrorDialogComponent { + constructor(@Inject(MAT_DIALOG_DATA) public data: {message: string}) { + } + +} diff --git a/frontend/src/app/error/error.module.ts b/frontend/src/app/error/error.module.ts index 58dde6c9d..064e190a6 100644 --- a/frontend/src/app/error/error.module.ts +++ b/frontend/src/app/error/error.module.ts @@ -2,16 +2,24 @@ import {NgModule} from "@angular/core"; import {ForbiddenComponent} from "./forbidden.component"; import {NotFoundComponent} from "./not-found.component"; import {MatCardModule} from "@angular/material/card"; +import { ErrorDialogComponent } from './error-dialog/error-dialog.component'; +import { MatDialogModule } from "@angular/material/dialog"; +import { SharedModule } from "../shared/shared.module"; +import { ErrorDialogService } from "./error-dialog.service"; @NgModule({ declarations: [ ForbiddenComponent, - NotFoundComponent + NotFoundComponent, + ErrorDialogComponent ], - imports: [ - MatCardModule - ], - exports: [] + imports: [ + MatCardModule, + MatDialogModule, + SharedModule + ], + exports: [], + providers: [ErrorDialogService] }) export class ErrorModule { } diff --git a/frontend/src/app/services/logging/log.service.ts b/frontend/src/app/services/logging/log.service.ts index 33c7c7016..f15001536 100644 --- a/frontend/src/app/services/logging/log.service.ts +++ b/frontend/src/app/services/logging/log.service.ts @@ -56,10 +56,11 @@ export class LogService { /* We have FATAl but console doesnt */ const lvl = (log.level === LogLevel.FATAL ? 'ERROR' : LogLevel[log.level]).toLowerCase() const prefix = `${log.timestamp.toISOString()} [${log.source}] ${lvl}:` + const logFn = window.console[lvl].bind(window.console) // Attempt to get proper line numbers, which failed if(log?.data?.length > 0){ - console[lvl](prefix, log.message, ...log.data) + logFn(prefix, log.message, ...log.data) }else{ - console[lvl](prefix, log.message) + logFn(prefix, log.message) } } From 614f7263191f9abbafe20dcbcec689c8f4d0bca5 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 17:10:36 +0200 Subject: [PATCH 402/498] Resolved warning in evaluation start component --- .../evaluation-start-dialog.component.html | 2 +- .../evaluation-start-dialog.component.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html index a0df660e2..f776e5c56 100644 --- a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html +++ b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.html @@ -22,7 +22,7 @@

Start competition run

- + Shuffle Tasks (Async only)

diff --git a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts index 591ff5be7..c31787410 100644 --- a/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts +++ b/frontend/src/app/template/evaluation-start-dialog/evaluation-start-dialog.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { FormControl, FormGroup } from "@angular/forms"; import { ApiEvaluationType } from "../../../../openapi"; import { MatDialogRef } from "@angular/material/dialog"; +import { Observable, Subscription } from "rxjs"; export interface EvaluationStartDialogResult { name: string; @@ -30,9 +31,19 @@ export class EvaluationStartDialogComponent { evaluationTypes: ApiEvaluationType[] = [ApiEvaluationType.SYNCHRONOUS, ApiEvaluationType.ASYNCHRONOUS]; - typeObservable = this.form.get('type').valueChanges; + typeObservable: Observable = this.form.get('type').valueChanges; + + sub: Subscription constructor(public dialogRef: MatDialogRef) { + this.form.get('shuffleTasks')?.disable({emitEvent: false}) + this.sub = this.typeObservable.subscribe((type) => { + if(type !== "ASYNCHRONOUS"){ + this.form.get('shuffleTasks')?.disable({emitEvent: false}) + }else{ + this.form.get('shuffleTasks')?.enable({emitEvent: false}) + } + }) } public create(){ @@ -49,6 +60,7 @@ export class EvaluationStartDialogComponent { } public close(){ + this.sub.unsubscribe(); this.dialogRef.close(null); } From bdee180b94239fcba46a14a4bd1f327b72700613 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 17:50:10 +0200 Subject: [PATCH 403/498] Minor cosmetics: added consistency in component header plus had to add a refresh button for the run list, otherwise the views for admins woudlnt be consistent --- .../collection-list.component.html | 4 ++-- .../src/app/run/abstract-run-list.component.ts | 4 ++++ .../src/app/run/admin-run-list.component.html | 5 ----- frontend/src/app/run/run-list.component.html | 16 +++++++++++++++- frontend/src/app/run/run-list.component.scss | 0 frontend/src/app/run/run-list.component.ts | 17 ++++++++++++++++- .../src/app/run/viewer-run-list.component.html | 5 +---- .../services/navigation/navigation.service.ts | 2 +- .../back-button/back-button.component.scss | 3 +++ .../admin-user-list.component.html | 2 +- 10 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 frontend/src/app/run/run-list.component.scss diff --git a/frontend/src/app/collection/collection-list/collection-list.component.html b/frontend/src/app/collection/collection-list/collection-list.component.html index 231b2c11d..9407aa721 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.html +++ b/frontend/src/app/collection/collection-list/collection-list.component.html @@ -1,6 +1,6 @@ -
+
-

Media Collections

+

Media Collections

-
{{column.header}} diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 2eb12f680..fdbcc0c40 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -34,21 +34,21 @@

Edit competition {{(builderService.templateAsObservable() | async)?.id}} -
- - +
+ +
-
- - +
+ +
-
- - +
+ +
diff --git a/frontend/src/app/template/template-builder/template-builder.component.scss b/frontend/src/app/template/template-builder/template-builder.component.scss index e69de29bb..140469f9a 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.scss +++ b/frontend/src/app/template/template-builder/template-builder.component.scss @@ -0,0 +1,45 @@ +.content-left { + grid-area: left; +} + +.content-right { + grid-area: right; +} + +.tb-container-2col { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto; + grid-template-areas: + "left right"; + column-gap: 1em; + + > * { + min-width: 0; + } +} + +.tb-container-3col-left { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: auto; + grid-template-areas: "left left right"; + column-gap: 1em; + + > * { + min-width: 0; + } +} + +.tb-container-3col-right { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: auto; + grid-template-areas: "left right right"; + column-gap: 1em; + + > * { + min-width: 0; + } +} + From 0a5e6f6db009b9676c2ef2278942c66697fbd7b4 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 1 Jun 2023 15:21:16 +0200 Subject: [PATCH 317/498] #310 Fixed media item query description dropdown not working --- frontend/src/app/services/services.module.ts | 3 +- ...ription-media-item-form-field.component.ts | 31 +++++++++++++------ ...media-item-image-form-field.component.html | 31 ++++++++++--------- ...n-media-item-image-form-field.component.ts | 6 +++- ...media-item-video-form-field.component.html | 6 ++-- ...n-media-item-video-form-field.component.ts | 12 +++++-- .../template-builder.module.ts | 28 ++++++----------- 7 files changed, 67 insertions(+), 50 deletions(-) diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index bf2dfe9b9..8cc4fc148 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -50,7 +50,8 @@ export function initializeApiConfig(appConfig: AppConfig) { FormatTemporalPointPipe, ResolveMediaItemPipe, ResolveMediaItemUrlPipe, - ResolveMediaItemPreviewPipe + ResolveMediaItemPreviewPipe, + FormatMediaItemPipe ], declarations: [ RoundPipePipe, diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts index aaf7e0fe6..877ab9e84 100644 --- a/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-form-field/query-description-media-item-form-field.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, Injector, Input } from "@angular/core"; import { UntypedFormControl } from "@angular/forms"; import { CompetitionFormBuilder @@ -7,24 +7,35 @@ import { FormatMediaItemPipe, MediaItemDisplayOptions } from "../../../../servic import { ApiMediaItem } from "../../../../../../openapi"; @Component({ - selector: 'app-query-description-media-item-form-field', - templateUrl: './query-description-media-item-form-field.component.html', - styleUrls: ['./query-description-media-item-form-field.component.scss'] + selector: "app-query-description-media-item-form-field", + templateUrl: "./query-description-media-item-form-field.component.html", + styleUrls: ["./query-description-media-item-form-field.component.scss"] }) export class QueryDescriptionMediaItemFormFieldComponent { @Input() - itemControl: UntypedFormControl + itemControl: UntypedFormControl; @Input() - formBuilder: CompetitionFormBuilder + formBuilder: CompetitionFormBuilder; @Input() - index: number + index: number; showing: boolean; - constructor(private formatMediaItem: FormatMediaItemPipe) { + protected formatMediaItemPipe: FormatMediaItemPipe; + + constructor(injector: Injector) { + this.formatMediaItemPipe = injector.get(FormatMediaItemPipe); } - public displayWithMediaItem(value: ApiMediaItem): string{ - return this.formatMediaItem.transform(value, {showType: true} as MediaItemDisplayOptions) + public displayWithMediaItem(value: ApiMediaItem): string { + if(value){ + if (this.formatMediaItemPipe) { + return this.formatMediaItemPipe.transform(value, { showType: true } as MediaItemDisplayOptions); + } else { + return `${value.name} (${value.type})`; + } + }else{ + return ''; + } } } diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html index 309e5467c..927b9c329 100644 --- a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.html @@ -1,22 +1,23 @@
- - - - - {{ mediaItem.name }} | - Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} - + + + + + {{ mediaItem.name }} | + Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} + + Please select a valid media item, i.e., use the autocomplete to select a value. - - - + +
diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts index 7fb837679..e3990cde4 100644 --- a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, Injector, Input } from "@angular/core"; import { UntypedFormControl } from "@angular/forms"; import { CompetitionFormBuilder @@ -16,6 +16,10 @@ import { }) export class QueryDescriptionMediaItemImageFormFieldComponent extends QueryDescriptionMediaItemFormFieldComponent{ + constructor(injector: Injector) { + super(injector) + } + } diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html index 3aa79d6bb..73fbd2703 100644 --- a/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-video-form-field/query-description-media-item-video-form-field.component.html @@ -15,10 +15,10 @@ {{ mediaItem.name }} | Type: {{ mediaItem.type }}, ID: {{ mediaItem.mediaItemId }} - - Please select a valid media item i.e. use the autocomplete to select a value. - + + Please select a valid media item i.e. use the autocomplete to select a value. + Date: Sun, 4 Jun 2023 12:47:05 +0200 Subject: [PATCH 318/498] First complete version of new submission endpoint, untested --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 2 + .../handler/submission/SubmissionHandler.kt | 120 +++++++++++++++++- .../rest/types/evaluation/ApiClientAnswer.kt | 18 +++ .../types/evaluation/ApiClientAnswerSet.kt | 8 ++ .../types/evaluation/ApiClientSubmission.kt | 6 + 5 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 51a424357..562c695c6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -27,6 +27,7 @@ import dev.dres.api.rest.handler.preview.PreviewImageHandler import dev.dres.api.rest.handler.preview.PreviewVideoHandler import dev.dres.api.rest.handler.scores.ListEvaluationScoreHandler import dev.dres.api.rest.handler.submission.LegacySubmissionHandler +import dev.dres.api.rest.handler.submission.SubmissionHandler import dev.dres.api.rest.handler.system.CurrentTimeHandler import dev.dres.api.rest.handler.system.InfoHandler import dev.dres.api.rest.handler.system.LoginHandler @@ -133,6 +134,7 @@ object RestApi { // Submission LegacySubmissionHandler(store, cache), + SubmissionHandler(store, config), // Log QueryLogHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index b8f32f397..0d66b3aef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -3,13 +3,18 @@ package dev.dres.api.rest.handler.submission import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.* +import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.config.Config import dev.dres.data.model.audit.DbAuditLogSource +import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.interfaces.EvaluationRun +import dev.dres.data.model.submissions.AnswerType import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException @@ -18,20 +23,37 @@ import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass +import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first +import kotlinx.dnq.query.firstOrNull import org.slf4j.LoggerFactory class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): PostRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.PARTICIPANT) - /** All [LegacySubmissionHandler]s are part of the v1 API. */ override val apiVersion = "v2" override val route = "submit/{evaluationId}" private val logger = LoggerFactory.getLogger(this.javaClass) + @OpenApi( + summary = "Endpoint to accept submissions", + path = "/api/v2/submit/{evaluationId}", + operationId = OpenApiOperation.AUTO_GENERATE, + requestBody = OpenApiRequestBody([OpenApiContent(ApiClientSubmission::class)]), + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) + ], + tags = ["Submission"] + ) override fun doPost(ctx: Context): SuccessStatus { return this.store.transactional { @@ -42,12 +64,18 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con val rac = RunActionContext.runActionContext(ctx, runManager) - val apiSubmission = try { - ctx.bodyAsClass() + val apiClientSubmission = try { + ctx.bodyAsClass() } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid submission, cannot parse: ${e.message}", ctx) } + val apiSubmission = try { + transformClientSubmission(apiClientSubmission, runManager.evaluation, rac) + } catch (e: Exception) { + throw ErrorStatusException(400, "Invalid submission: ${e.message}", ctx) + } + try { runManager.postSubmission(rac, apiSubmission) } catch (e: SubmissionRejectedException) { @@ -69,4 +97,88 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } } + + private fun transformClientSubmission(apiClientSubmission: ApiClientSubmission, evaluationRun: EvaluationRun, rac: RunActionContext) : ApiSubmission{ + + if (rac.userId == null || rac.teamId == null) { + throw Exception("Invalid association between user and evaluation") + } + + val errorBuffer = StringBuffer() + + val answerSets = apiClientSubmission.answerSets.mapNotNull mapClientAnswerSet@{ clientAnswerSet -> + + val task = evaluationRun.tasks.find { it.template.name == clientAnswerSet.taskName } + + if (task == null) { + errorBuffer.append("task '${clientAnswerSet.taskName}' not found\n") + return@mapClientAnswerSet null + } + + val answers = clientAnswerSet.answers.mapNotNull mapClientAnswers@{ clientAnswer -> + + val item = if (clientAnswer.itemName != null) { + val collection = if (clientAnswer.itemCollectionName != null) { + DbMediaCollection.filter { it.name eq clientAnswer.itemCollectionName }.firstOrNull() + } else { + task.template.collection + } + collection?.items?.filter { it.name eq clientAnswer.itemName }?.firstOrNull() + } else { + null + }?.toApi() + + return@mapClientAnswers when(clientAnswer.type()) { + AnswerType.ITEM -> { + if (item == null) { + errorBuffer.append("item for answer $clientAnswer not found") + return@mapClientAnswers null + } + ApiAnswer(type = ApiAnswerType.ITEM, item = item, start = null, end = null, text = null) + } + AnswerType.TEMPORAL -> { + if (item == null) { + errorBuffer.append("item for answer $clientAnswer not found") + return@mapClientAnswers null + } + ApiAnswer(type = ApiAnswerType.TEMPORAL, item = item, start = clientAnswer.start, end = clientAnswer.end, text = null) + } + AnswerType.TEXT -> ApiAnswer(type = ApiAnswerType.TEXT, text = clientAnswer.text, item = null, start = null, end = null) + null -> { + errorBuffer.append("could not determine AnswerType of answer $clientAnswer\n") + null + } + } + + } + + ApiAnswerSet( + status = ApiVerdictStatus.INDETERMINATE, + taskId = task.taskId, + answers = answers + ) + + } + + if (errorBuffer.isNotBlank()) { + throw Exception(errorBuffer.toString()) + } + + if (answerSets.isEmpty()) { + throw Exception("Submission does not contain any AnswerSets") + } + + val userName = DbUser.filter { it.id eq rac.userId }.first().username + val teamName = evaluationRun.description.teams.filter { it.id eq rac.teamId }.first().name + + return ApiSubmission( + teamId = rac.teamId, + memberId = rac.userId, + teamName = teamName, + memberName = userName, + answers = answerSets, + evaluationId = evaluationRun.id + ) + + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt new file mode 100644 index 000000000..4f7e1483e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt @@ -0,0 +1,18 @@ +package dev.dres.api.rest.types.evaluation + +import dev.dres.data.model.submissions.AnswerType + +data class ApiClientAnswer( + val text: String?, + val itemName: String?, + val itemCollectionName: String?, + val start: Long?, // ms + val end: Long? // ms +) { + fun type() : AnswerType? = when { + this.text != null -> AnswerType.TEXT + this.itemName == null -> null + this.start != null && this.end != null -> AnswerType.TEMPORAL + else -> AnswerType.ITEM + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt new file mode 100644 index 000000000..094af9208 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt @@ -0,0 +1,8 @@ +package dev.dres.api.rest.types.evaluation + +data class ApiClientAnswerSet( + val taskName: String, + val answers: List +) { + +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt new file mode 100644 index 000000000..2c58d8938 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt @@ -0,0 +1,6 @@ +package dev.dres.api.rest.types.evaluation + +class ApiClientSubmission( + val answerSets: List +) { +} \ No newline at end of file From 75e7074cfc87041330f57beb509c52caba0e3f8a Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 9 Jun 2023 08:40:22 +0200 Subject: [PATCH 319/498] Fixed issue #404 and cleaned-up some default value handling. --- .../model/template/task/options/Defaults.kt | 6 ++++ .../run/InteractiveAsynchronousRunManager.kt | 29 +++++----------- .../run/InteractiveSynchronousRunManager.kt | 33 +++++++------------ .../main/kotlin/dev/dres/run/RunManager.kt | 7 ++++ .../run/updatables/ScoreboardsUpdatable.kt | 11 +++---- .../kotlin/dev/dres/utilities/ReadyLatch.kt | 2 +- 6 files changed, 39 insertions(+), 49 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt index b7e20157f..ca22b7fc8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/Defaults.kt @@ -8,4 +8,10 @@ object Defaults { const val PROLONG_ON_SUBMISSION_BY_DEFAULT = 5 const val PROLONG_ON_SUBMISSION_CORRECT_DEFAULT = false + + /** Default interval for scoreboard updates in ms. TODO: Make configurable. */ + const val SCOREBOARD_UPDATE_INTERVAL_DEFAULT = 5000L + + /** Default timeout for viewer in ms. TODO: make configurable */ + const val VIEWER_TIMEOUT_DEFAULT = 30_000L } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index e8ab964a1..c8394bb58 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -15,10 +15,9 @@ import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.data.model.template.task.options.DbSubmissionOption -import dev.dres.data.model.template.task.options.DbTaskOption -import dev.dres.data.model.template.task.options.Defaults -import dev.dres.data.model.template.task.options.Parameters +import dev.dres.data.model.template.task.options.Defaults.SCOREBOARD_UPDATE_INTERVAL_DEFAULT import dev.dres.data.model.template.team.TeamId +import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException @@ -28,7 +27,7 @@ import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* -import kotlinx.dnq.query.FilteringContext.eq +import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -45,7 +44,7 @@ import kotlin.math.max * can take place at different points in time for different teams and tasks, i.e., the competitions are executed * asynchronously. * - * @version 1.0.0 + * @version 2.0.0 * @author Ralph Gasser */ class InteractiveAsynchronousRunManager( @@ -54,9 +53,8 @@ class InteractiveAsynchronousRunManager( ) : InteractiveRunManager { companion object { + /** The [Logger] instance used by [InteractiveAsynchronousRunManager]. */ private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) - private const val SCOREBOARD_UPDATE_INTERVAL_MS = 1000L // TODO make configurable - private const val MAXIMUM_ERROR_COUNT = 5 } /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ @@ -80,7 +78,7 @@ class InteractiveAsynchronousRunManager( private val stateLock = ReentrantReadWriteLock() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_DEFAULT) /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this) @@ -296,9 +294,6 @@ class InteractiveAsynchronousRunManager( //FIXME since task run and competition run states are separated, this is not actually a state change this.statusMap[context.teamId] = RunManagerStatus.ACTIVE - /* Mark scoreboards for update. */ - this.scoreboardsUpdatable.dirty = true - /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( context.teamId, @@ -342,9 +337,6 @@ class InteractiveAsynchronousRunManager( val currentTaskRun = this.evaluation.IATaskRun(currentTaskTemplate, context.teamId) currentTaskRun.prepare() - /* Mark scoreboards and DAO for update. */ - this.scoreboardsUpdatable.dirty = true - /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( context.teamId, @@ -374,9 +366,6 @@ class InteractiveAsynchronousRunManager( ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") currentTask.end() - /* Mark scoreboards and DAO for update. */ - this.scoreboardsUpdatable.dirty = true - /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( context.teamId, @@ -695,12 +684,12 @@ class InteractiveAsynchronousRunManager( ) // oh shit, something went horribly, horribly wrong - if (errorCounter >= MAXIMUM_ERROR_COUNT) { - LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_ERROR_COUNT; terminating loop...") + if (errorCounter >= MAXIMUM_RUN_LOOP_ERROR_COUNT) { + LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_RUN_LOOP_ERROR_COUNT; terminating loop...") RunExecutor.dump(this.evaluation) break } else { - LOGGER.error("This is the ${++errorCounter}-th in a row. Run manager will terminate loop after $MAXIMUM_ERROR_COUNT errors") + LOGGER.error("This is the ${++errorCounter}-th in a row. Run manager will terminate loop after $MAXIMUM_RUN_LOOP_ERROR_COUNT errors") } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 83654c87e..0e8d87fdb 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -16,6 +16,9 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.task.options.DbTaskOption +import dev.dres.data.model.template.task.options.Defaults.SCOREBOARD_UPDATE_INTERVAL_DEFAULT +import dev.dres.data.model.template.task.options.Defaults.VIEWER_TIMEOUT_DEFAULT +import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent @@ -27,6 +30,7 @@ import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.ReadyLatch import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* +import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -42,14 +46,11 @@ import kotlin.math.max */ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynchronousEvaluation, override val store: TransientEntityStore) : InteractiveRunManager { - private val VIEWER_TIME_OUT = 30L //TODO make configurable - private val SCOREBOARD_UPDATE_INTERVAL_MS = 1000L // TODO make configurable - - private val LOGGER = LoggerFactory.getLogger(this.javaClass) - - /** Number of consecutive errors which have to occur within the main execution loop before it tries to gracefully terminate */ - private val maxErrorCount = 5 + companion object { + /** The [Logger] instance used by [InteractiveSynchronousRunManager]. */ + private val LOGGER = LoggerFactory.getLogger(InteractiveSynchronousRunManager::class.java) + } /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ override val runProperties: RunProperties @@ -92,7 +93,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private val readyLatch = ReadyLatch() /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) + private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_DEFAULT) /** The internal [ScoresUpdatable] instance for this [InteractiveSynchronousRunManager]. */ private val scoresUpdatable = ScoresUpdatable(this) @@ -218,9 +219,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Update active task. */ this.evaluation.goTo(index) - /* Mark scoreboards for update. */ - this.scoreboardsUpdatable.dirty = true - /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} set to task $index") @@ -245,11 +243,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Update status. */ this.evaluation.currentTask!!.prepare() - /* Mark scoreboards and dao for update. */ - this.scoreboardsUpdatable.dirty = true - /* Reset the ReadyLatch. */ - this.readyLatch.reset(VIEWER_TIME_OUT) + this.readyLatch.reset(VIEWER_TIMEOUT_DEFAULT) /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) @@ -265,9 +260,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* End TaskRun and persist. */ this.currentTask(context)?.end() - /* Mark scoreboards and dao for update. */ - this.scoreboardsUpdatable.dirty = true - /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.currentTask(context)?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") @@ -518,7 +510,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage(context.teamId!!, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) - return true } @@ -566,10 +557,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch return } catch (e: Throwable) { LOGGER.error("Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) - LOGGER.error("This is the ${++errorCounter}. in a row, will terminate loop after $maxErrorCount errors") + LOGGER.error("This is the ${++errorCounter}. in a row, will terminate loop after $MAXIMUM_RUN_LOOP_ERROR_COUNT errors") // oh shit, something went horribly, horribly wrong - if (errorCounter >= this.maxErrorCount) { + if (errorCounter >= MAXIMUM_RUN_LOOP_ERROR_COUNT) { LOGGER.error("Reached maximum consecutive error count, terminating loop") RunExecutor.dump(this.evaluation) break //terminate loop diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index c214f5db5..965b6557c 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -22,6 +22,13 @@ import jetbrains.exodus.database.TransientEntityStore * @version 2.0.0 */ interface RunManager : Runnable { + + companion object { + /** The maximum number of errors that may occur in a run loop befor aborting execution. */ + const val MAXIMUM_RUN_LOOP_ERROR_COUNT = 5 + } + + /** Unique, public [EvaluationId] for this [RunManager]. */ val id: EvaluationId diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt index b84e95d57..b7c6b39aa 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ScoreboardsUpdatable.kt @@ -16,16 +16,13 @@ import java.util.* * @author Ralph Gasser & Luca Rossetto * @version 1.1.1 */ -class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs: Long): StatefulUpdatable { +class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs: Long): Updatable { /** The [Phase] this [ScoreboardsUpdatable] belongs to. */ override val phase: Phase = Phase.MAIN - /** Indicates, that this [ScoreboardsUpdatable] has unprocessed changes. */ - @Volatile - override var dirty: Boolean = true - /** Timestamp of the last update. */ + @Volatile private var lastUpdate: Long = System.currentTimeMillis() /** List of all [ScoreTimePoint]s tracked by this [ScoreboardsUpdatable]. */ @@ -34,10 +31,10 @@ class ScoreboardsUpdatable(val manager: RunManager, private val updateIntervalMs val timeSeries: List get() = this._timeSeries + @Synchronized override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { val now = System.currentTimeMillis() - if (this.dirty && (now - lastUpdate) > this.updateIntervalMs) { - this.dirty = false + if ((now - this.lastUpdate) > this.updateIntervalMs) { this.lastUpdate = now this.manager.store.transactional(true) { this.manager.scoreboards.forEach { diff --git a/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt b/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt index 10c4a2b5f..f5955e867 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/ReadyLatch.kt @@ -78,7 +78,7 @@ class ReadyLatch { for (e in this.map.keys) { this.map[e] = false } - this.timeout = if (timeout != null) (1000L * timeout) + System.currentTimeMillis() else null + this.timeout = timeout?.let { it + System.currentTimeMillis() } } /** From b56c24de5a9961cdb0765c4f0b3e340d9440e43e Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Tue, 13 Jun 2023 08:55:06 +0200 Subject: [PATCH 320/498] Fixed issue with reloading of views and moved to new (Angular 15) authentication guards scheme. --- frontend/src/app/app-routing.module.ts | 68 +++++++---------- frontend/src/app/app.component.html | 2 +- frontend/src/app/app.component.ts | 4 +- .../services/navigation/navigation.service.ts | 4 +- .../services/session/authentication.guard.ts | 28 ------- .../services/session/authentication.sevice.ts | 75 +++++++++---------- frontend/src/app/services/session/guard.ts | 33 ++++++++ .../template-list/template-list.component.ts | 2 +- .../src/app/user/login/login.component.ts | 2 +- .../src/app/viewer/run-viewer.component.ts | 1 - .../src/app/viewer/task-viewer.component.ts | 12 +-- 11 files changed, 109 insertions(+), 122 deletions(-) delete mode 100644 frontend/src/app/services/session/authentication.guard.ts create mode 100644 frontend/src/app/services/session/guard.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 23e051ba8..88d5fab78 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,8 +1,6 @@ import { NgModule } from '@angular/core'; -import { RouterModule, Routes, UrlSerializer } from '@angular/router'; -import { CompetitionListComponent } from './competition/competition-list/competition-list.component'; +import {RouterModule, Routes, UrlSerializer} from '@angular/router'; import { LoginComponent } from './user/login/login.component'; -import { AuthenticationGuard } from './services/session/authentication.guard'; import { RunViewerComponent } from './viewer/run-viewer.component'; import { ProfileComponent } from './user/profile/profile.component'; import { AdminUserListComponent } from './user/admin-user-list/admin-user-list.component'; @@ -13,15 +11,19 @@ import { CollectionListComponent } from './collection/collection-list/collection import { CollectionViewerComponent } from './collection/collection-viewer/collection-viewer.component'; import { AdminAuditlogOverviewComponent } from './auditlog/admin-auditlog-overview/admin-auditlog-overview.component'; import { CanDeactivateGuard } from './services/can-deactivate.guard'; -import { RunAdminSubmissionsListComponent } from './run/run-admin-submissions-list/run-admin-submissions-list.component'; import { RunScoreHistoryComponent } from './run/score-history/run-score-history.component'; import { JudgementVotingViewerComponent } from './judgement/judgement-voting-viewer.component'; import { RunAsyncAdminViewComponent } from './run/run-async-admin-view/run-async-admin-view.component'; -import { NonescapingUrlserializerClass } from './nonescaping-urlserializer.class'; -import {ApiRole} from '../../openapi'; import {TemplateBuilderComponent} from './template/template-builder/template-builder.component'; import { TemplateListComponent } from "./template/template-list/template-list.component"; import { SubmissionsListComponent } from "./evaluation/admin/submission/submissions-list/submissions-list.component"; +import { + canActivateAdministrator, + canActivateAnyRole, + canActivateJudge, + canActivatePublicVote +} from "./services/session/guard"; +import {NonescapingUrlserializerClass} from "./nonescaping-urlserializer.class"; /** * The ROUTE for evaluation templates. @@ -38,107 +40,89 @@ const routes: Routes = [ { path: TEMPLATE_ROUTE + '/list', component: TemplateListComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: TEMPLATE_ROUTE+'/builder/:templateId', component: TemplateBuilderComponent, - canActivate: [AuthenticationGuard], - canDeactivate: [CanDeactivateGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator], + canDeactivate: [CanDeactivateGuard] }, { path: EVALUATION_ROUTE+'/list', component: RunListComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN, ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.JUDGE] }, + canActivate: [canActivateAnyRole] }, { path: EVALUATION_ROUTE+'/admin/:runId', component: RunAdminViewComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: EVALUATION_ROUTE+'/scores/:runId', component: RunScoreHistoryComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: EVALUATION_ROUTE+'/admin/async/:runId', component: RunAsyncAdminViewComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: EVALUATION_ROUTE+'/admin/submissions/:runId/:taskId', component: SubmissionsListComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: EVALUATION_ROUTE+'/viewer/:runId', component: RunViewerComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN, ApiRole.VIEWER, ApiRole.PARTICIPANT, ApiRole.JUDGE] }, + canActivate: [canActivateAnyRole] }, { path: 'judge/:runId', component: JudgementViewerComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN, ApiRole.JUDGE] }, + canActivate: [canActivateJudge] }, { path: 'vote/:runId', component: JudgementVotingViewerComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN, ApiRole.JUDGE, ApiRole.VIEWER] }, + canActivate: [canActivatePublicVote] }, - { path: 'login', component: LoginComponent }, - { path: 'user', component: ProfileComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN, ApiRole.VIEWER, ApiRole.JUDGE, ApiRole.PARTICIPANT] }, + canActivate: [canActivateAnyRole] }, { path: 'user/list', component: AdminUserListComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: 'collection/list', component: CollectionListComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: 'collection/:collectionId', component: CollectionViewerComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, { path: 'logs/list', component: AdminAuditlogOverviewComponent, - canActivate: [AuthenticationGuard], - data: { roles: [ApiRole.ADMIN] }, + canActivate: [canActivateAdministrator] }, - // otherwise redirect to home - { path: '**', redirectTo: '' }, + { path: 'login', component: LoginComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes, { enableTracing: false })], // enable tracing for debugging exports: [RouterModule], - providers: [{ provide: UrlSerializer, useClass: NonescapingUrlserializerClass }], + providers: [{ provide: UrlSerializer, useClass: NonescapingUrlserializerClass }] }) export class AppRoutingModule {} diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index f56f91fdb..b742695f5 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -60,7 +60,7 @@
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 8097e8f38..96c0c768c 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -45,12 +45,12 @@ export class AppComponent { public logout() { this.authenticationService.logout().subscribe(() => { this.snackBar.open(`Logout successful!`, null, { duration: 5000 }); - this.router.navigate(['/']); + this.router.navigate(['/']).then(r => {}); }); } public profile() { - this.router.navigate(['/user']); + this.router.navigate(['/user']).then(r => {}); } public openInfoDialog(){ diff --git a/frontend/src/app/services/navigation/navigation.service.ts b/frontend/src/app/services/navigation/navigation.service.ts index 6a2098f97..7b2efe92d 100644 --- a/frontend/src/app/services/navigation/navigation.service.ts +++ b/frontend/src/app/services/navigation/navigation.service.ts @@ -34,7 +34,7 @@ export class NavigationService { destination = this.history.pop(); } } - this.router.navigateByUrl(destination); + this.router.navigateByUrl(destination).then(r => {}); } /** @@ -42,7 +42,7 @@ export class NavigationService { */ forward(): void { if (this.alternateHistory.length > 0) { - this.router.navigateByUrl(this.alternateHistory.pop()); + this.router.navigateByUrl(this.alternateHistory.pop()).then(r => {}); } // Else: there is no forward anymore. } } diff --git a/frontend/src/app/services/session/authentication.guard.ts b/frontend/src/app/services/session/authentication.guard.ts deleted file mode 100644 index 6006f2b79..000000000 --- a/frontend/src/app/services/session/authentication.guard.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; -import { map, withLatestFrom } from 'rxjs/operators'; -import { AuthenticationService } from './authentication.sevice'; - -@Injectable({ - providedIn: 'root', -}) -export class AuthenticationGuard implements CanActivate { - constructor(private router: Router, private authenticationService: AuthenticationService) {} - - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - return this.authenticationService.isLoggedIn.pipe( - withLatestFrom(this.authenticationService.role), - map(([loggedIn, role]) => { - if (!loggedIn) { - this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); - return false; - } - if (route.data.roles && route.data.roles.indexOf(role) === -1) { - this.router.navigate(['/']); - return false; - } - return true; - }) - ); - } -} diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index 61e18eddc..84e9033e2 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -1,7 +1,8 @@ -import { Inject, Injectable } from '@angular/core'; -import { catchError, filter, flatMap, map, tap } from 'rxjs/operators'; -import { BehaviorSubject, Observable, of } from 'rxjs'; +import {Inject, Injectable} from '@angular/core'; +import {catchError, flatMap, map, tap, withLatestFrom} from 'rxjs/operators'; +import {Observable, of} from 'rxjs'; import {ApiRole, ApiUser, LoginRequest, UserRequest, UserService} from '../../../../openapi'; +import {ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree} from "@angular/router"; /** * This service class is used to facilitate login and logout through the UserService API. @@ -10,32 +11,11 @@ import {ApiRole, ApiUser, LoginRequest, UserRequest, UserService} from '../../.. providedIn: 'root', }) export class AuthenticationService { - /** ApiUser created during login. */ - private userDetails: BehaviorSubject = new BehaviorSubject(null); /** * Constructor */ - constructor(@Inject(UserService) private userService: UserService) { - // TODO there *must* be a more elegant OpenAPI way to do this ?!? - this.userService.getApiV2UserSession(null, null, null, {httpHeaderAccept: 'text/plain'}) - .pipe( - catchError((e) => { - console.log("error", e); - return of(null) - }), - filter((s) => s != null), - flatMap((s) => { - console.log('before getApiV2User') - return this.userService.getApiV2User() - }), - filter((u) => u != null) - ) - .subscribe((u) => { - this.userDetails.next(u); - console.log(`Resumed session! Successfully logged in as '${this.userDetails.value.username}'.`); - }); - } + constructor(@Inject(UserService) private userService: UserService, private router: Router) {} /** * Tries to login a user with the given username and password. Returns an Observable! @@ -46,10 +26,7 @@ export class AuthenticationService { public login(user: string, pass: string) { return this.userService.postApiV2Login({ username: user, password: pass } as LoginRequest).pipe( flatMap(() => this.userService.getApiV2User()), - tap((data) => { - this.userDetails.next(data); - console.log(`Successfully logged in as '${this.userDetails.value.username}'.`); - }) + tap((data) => console.log(`Successfully logged in as '${data.username}'.`)) ); } @@ -60,8 +37,7 @@ export class AuthenticationService { */ public updateUser(user: UserRequest) { return this.user.pipe( - flatMap((u: ApiUser) => this.userService.patchApiV2UserByUserId(u.id, user)), - tap((u: ApiUser) => this.userDetails.next(u)) + flatMap((u: ApiUser) => this.userService.patchApiV2UserByUserId(u.id, user)) ); } @@ -71,18 +47,18 @@ export class AuthenticationService { public logout() { return this.userService.getApiV2Logout().pipe( catchError((e) => of(null)), - tap(() => { - this.userDetails.next(null); - console.log(`User was logged out.`); - }) + tap(() => console.log(`User was logged out.`)) ); } /** * Returns the current login state as Observable. + * + * A call to this method always results in an API call to make sure, + * that the user is still logged in. */ get isLoggedIn(): Observable { - return this.userDetails.pipe( + return this.userService.getApiV2User().pipe( map((u) => u != null), catchError((e) => of(false)) ); @@ -92,13 +68,36 @@ export class AuthenticationService { * Returns the username of the current user as Observable. */ get user(): Observable { - return this.userDetails.asObservable(); + return this.userService.getApiV2User(); } /** * Returns the role of the current user as Observable. */ get role(): Observable { - return this.userDetails.pipe(map((u) => u?.role)); + return this.userService.getApiV2User().pipe(map((u) => u?.role)); + } + + /** + * This function is used to check if a particular route can be activated. It is + * used by the {@link CanActivateFn} defined in guards.ts + * + * @param rolesAllows The list of {@link ApiRole}s allowed + * @param route The {@link ActivatedRouteSnapshot} + * @param state The {@link RouterStateSnapshot} + */ + public canActivate(rolesAllows: Array, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + return this.isLoggedIn.pipe( + withLatestFrom(this.role), + map(([loggedIn, role]) => { + if (!loggedIn) { + return this.router.parseUrl(`/login?returnUrl=${state.url}`) + } + if (route.data.roles && route.data.roles.indexOf(role) === -1) { + return this.router.parseUrl('/forbidden'); + } + return true; + }) + ).toPromise(); } } diff --git a/frontend/src/app/services/session/guard.ts b/frontend/src/app/services/session/guard.ts new file mode 100644 index 000000000..5dd05b38f --- /dev/null +++ b/frontend/src/app/services/session/guard.ts @@ -0,0 +1,33 @@ +import {ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot} from "@angular/router"; +import {AuthenticationService} from "./authentication.sevice"; +import {inject} from "@angular/core"; +import {ApiRole} from "../../../../openapi"; + +/** + * Guard used to determine if a view can be activated that requires the user to hold the {@link ApiRole.ADMIN} role. + */ +export const canActivateAdministrator: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + return inject(AuthenticationService).canActivate([ApiRole.ADMIN], route, state); +}; + + +/** + * Guard used to determine if a view can be activated that requires the user to hold the {@link ApiRole.JUDGE} role. + */ +export const canActivateJudge: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + return inject(AuthenticationService).canActivate([ApiRole.ADMIN, ApiRole.JUDGE], route, state); +}; + + +/** + * Guard used to determine if a view can be activated that requires the user to hold the {@link ApiRole.JUDGE} role. + */ +export const canActivatePublicVote: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + return inject(AuthenticationService).canActivate([ApiRole.ADMIN, ApiRole.JUDGE, ApiRole.VIEWER], route, state); +}; +/** + * Guard used to determine if a view can be activated that requires the user to hold any role. + */ +export const canActivateAnyRole: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + return inject(AuthenticationService).canActivate([ApiRole.ADMIN, ApiRole.VIEWER, ApiRole.JUDGE, ApiRole.PARTICIPANT], route, state); +}; \ No newline at end of file diff --git a/frontend/src/app/template/template-list/template-list.component.ts b/frontend/src/app/template/template-list/template-list.component.ts index 929144bb9..7d0c61c31 100644 --- a/frontend/src/app/template/template-list/template-list.component.ts +++ b/frontend/src/app/template/template-list/template-list.component.ts @@ -80,7 +80,7 @@ export class TemplateListComponent implements AfterViewInit{ } public edit(templateId: string){ - this.router.navigate(['/template/builder', templateId]); + this.router.navigate(['/template/builder', templateId]).then(s => {}); } public delete(templateId: string){ diff --git a/frontend/src/app/user/login/login.component.ts b/frontend/src/app/user/login/login.component.ts index 3567addc3..04bc975ab 100644 --- a/frontend/src/app/user/login/login.component.ts +++ b/frontend/src/app/user/login/login.component.ts @@ -30,7 +30,7 @@ export class LoginComponent implements OnInit, OnDestroy { this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/'; this.authenticationServiceSubscription = this.authenticationService.isLoggedIn.subscribe((b) => { if (b) { - this.router.navigate([this.returnUrl]); + this.router.navigate([this.returnUrl]).then(r => {}) } }); } diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index afaf3a069..04f232609 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -85,7 +85,6 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /** Reference to the overlay template. Only available once view has been loaded. */ private overlaySubscription: Subscription; - /** Internal WebSocket subscription for pinging the server. */ private pingSubscription: Subscription; diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index 397f9526a..5013d2b23 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -88,8 +88,8 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /* Observable for the current query hint. */ const currentTaskHint = this.taskChanged.pipe( withLatestFrom(this.evaluationId), - switchMap(([task, runId]) => - this.runService.getApiV2EvaluationByEvaluationIdHintByTaskId(runId, task.templateId).pipe( + switchMap(([task, evaluationId]) => + this.runService.getApiV2EvaluationByEvaluationIdHintByTaskId(evaluationId, task.templateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current query hint due to an error.', e); return of(null); @@ -100,10 +100,10 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { ); /* Observable for the current query target. */ - const currentTaskTarget = this.state.pipe( - filter(s => s.taskStatus == ApiTaskStatus.ENDED), - switchMap((s) => - this.runService.getApiV2EvaluationByEvaluationIdTargetByTaskId(s.evaluationId, s.currentTemplate?.templateId).pipe( + const currentTaskTarget = this.taskEnded.pipe( + withLatestFrom(this.evaluationId), + switchMap(([task, evaluationId]) => + this.runService.getApiV2EvaluationByEvaluationIdTargetByTaskId(evaluationId, task.templateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); From 467090270fc5aead6c320eb877430434323642d6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 15 Jun 2023 08:28:37 +0200 Subject: [PATCH 321/498] DbUserManager now enforces lowercase usernames to prevent duplicate usernames. --- .../main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt index 8b8b60abf..d4e52ccf2 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt @@ -11,7 +11,7 @@ import java.util.* * User management class of DRES. Requires transaction context. * * @author Loris Sauter - * @version 2.0.0 + * @version 2.0.1 */ object DbUserManager { @@ -26,7 +26,7 @@ object DbUserManager { fun create(username: String, password: Password.Hashed, role: DbRole): Boolean { try { DbUser.new { - this.username = username + this.username = username.lowercase() this.password = password.password this.role = role } @@ -76,7 +76,7 @@ object DbUserManager { null } if (user == null) return false - if (username != null) user.username = username + if (username != null) user.username = username.lowercase() if (password != null) user.password = password.password if (role != null) user.role = role return true @@ -113,7 +113,7 @@ object DbUserManager { val user = if (id != null) { DbUser.query(DbUser::id eq id).firstOrNull() } else if (username != null) { - DbUser.query(DbUser::username eq username).firstOrNull() + DbUser.query(DbUser::username eq username.lowercase()).firstOrNull() } else { null } @@ -143,7 +143,7 @@ object DbUserManager { return if (id != null) { DbUser.query(DbUser::id eq id).isNotEmpty } else if (username != null) { - DbUser.query(DbUser::username eq username).isNotEmpty + DbUser.query(DbUser::username eq username.lowercase()).isNotEmpty } else { throw IllegalArgumentException("Either user ID or username must be non-null!") } @@ -160,7 +160,7 @@ object DbUserManager { DbUser.query(DbUser::id eq id).firstOrNull() } else if (username != null) { // Note: during after create, the query below is empty within a readonly transaction (unexpected), but non-empty out of the transaction - DbUser.query(DbUser::username eq username).firstOrNull() + DbUser.query(DbUser::username eq username.lowercase()).firstOrNull() } else { null } From b792098b4bca91254adda52e32d2ac01092a7f80 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 16 Jun 2023 12:12:21 +0200 Subject: [PATCH 322/498] SubmissionHandler only performs task lookup for non-interactive evaluations --- .../handler/submission/SubmissionHandler.kt | 22 +++++++++++++++---- .../types/evaluation/ApiClientAnswerSet.kt | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 0d66b3aef..9ffb45133 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -12,9 +12,13 @@ import dev.dres.data.model.admin.DbUser import dev.dres.data.model.config.Config import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.run.NonInteractiveEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.submissions.AnswerType +import dev.dres.run.InteractiveAsynchronousRunManager +import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunManager import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException @@ -71,7 +75,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } val apiSubmission = try { - transformClientSubmission(apiClientSubmission, runManager.evaluation, rac) + transformClientSubmission(apiClientSubmission, runManager, rac) } catch (e: Exception) { throw ErrorStatusException(400, "Invalid submission: ${e.message}", ctx) } @@ -98,20 +102,30 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } - private fun transformClientSubmission(apiClientSubmission: ApiClientSubmission, evaluationRun: EvaluationRun, rac: RunActionContext) : ApiSubmission{ + private fun transformClientSubmission(apiClientSubmission: ApiClientSubmission, runManager: RunManager, rac: RunActionContext) : ApiSubmission{ if (rac.userId == null || rac.teamId == null) { throw Exception("Invalid association between user and evaluation") } + val evaluationRun = runManager.evaluation + val errorBuffer = StringBuffer() val answerSets = apiClientSubmission.answerSets.mapNotNull mapClientAnswerSet@{ clientAnswerSet -> - val task = evaluationRun.tasks.find { it.template.name == clientAnswerSet.taskName } + val task = if (runManager is InteractiveRunManager) { + runManager.currentTask(rac) //only accept submissions for current task in interactive runs + } else { + evaluationRun.tasks.find { it.template.name == clientAnswerSet.taskName } //look up task by name + } if (task == null) { - errorBuffer.append("task '${clientAnswerSet.taskName}' not found\n") + if (runManager is InteractiveRunManager) { + errorBuffer.append("No active task\n") + } else { + errorBuffer.append("task '${clientAnswerSet.taskName}' not found\n") + } return@mapClientAnswerSet null } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt index 094af9208..a67dcbe74 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.evaluation data class ApiClientAnswerSet( - val taskName: String, + val taskName: String? = null, val answers: List ) { From 95f35cc07c802da5319e5345203e510304e5cbb1 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 18 Jun 2023 13:12:08 +0200 Subject: [PATCH 323/498] Added metadata map to DbMediaItem --- backend/src/main/kotlin/dev/dres/DRES.kt | 6 +- .../api/rest/types/collection/ApiMediaItem.kt | 3 +- .../collection/ApiMediaItemMetaDataEntry.kt | 3 + .../dev/dres/data/model/media/DbMediaItem.kt | 45 ++- .../model/media/DbMediaItemMetaDataEntry.kt | 27 ++ doc/oas-client.json | 282 ++++++++++++++++-- doc/oas.json | 252 +++++++++++++++- 7 files changed, 579 insertions(+), 39 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItemMetaDataEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItemMetaDataEntry.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index b101b4585..05b56e3c1 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -9,16 +9,13 @@ import dev.dres.data.model.admin.DbUser import dev.dres.data.model.audit.DbAuditLogEntry import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.audit.DbAuditLogType +import dev.dres.data.model.media.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.* import dev.dres.data.model.template.task.options.* import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamAggregator import dev.dres.data.model.template.team.DbTeamGroup -import dev.dres.data.model.media.DbMediaCollection -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.DbMediaSegment -import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.run.* import dev.dres.data.model.submissions.* import dev.dres.mgmt.cache.CacheManager @@ -130,6 +127,7 @@ object DRES { DbMediaType, DbMediaCollection, DbMediaItem, + DbMediaItemMetaDataEntry, DbMediaSegment, DbRole, DbScoreOption, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index 42cbbfec5..b6cba6813 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -20,7 +20,8 @@ data class ApiMediaItem( val collectionId: String, val location: String, val durationMs: Long? = null, - val fps: Float? = null + val fps: Float? = null, + val metadata: List ) : MediaItem { init { if (this.type == ApiMediaType.VIDEO) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItemMetaDataEntry.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItemMetaDataEntry.kt new file mode 100644 index 000000000..568ea37fe --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItemMetaDataEntry.kt @@ -0,0 +1,3 @@ +package dev.dres.api.rest.types.collection + +data class ApiMediaItemMetaDataEntry(val key: String, val value: String) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt index dd4af342a..342420e63 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItem.kt @@ -1,9 +1,14 @@ package dev.dres.data.model.media import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.api.rest.types.collection.ApiMediaItemMetaDataEntry import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.removeAll import kotlinx.dnq.simple.requireIf import java.nio.file.Path import java.nio.file.Paths @@ -41,7 +46,7 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { var fps by xdNullableFloatProp() { requireIf { this.type == DbMediaType.VIDEO } } /** Duration of the [DbMediaItem] in milliseconds. Null for type without temporal development. */ - var durationMs by xdNullableLongProp() { requireIf { this.type == DbMediaType.VIDEO } } + var durationMs by xdNullableLongProp() { requireIf { this.type == DbMediaType.VIDEO } } /** The [DbMediaCollection] this [DbMediaItem] belongs to. */ var collection: DbMediaCollection by xdParent(DbMediaCollection::items) @@ -50,13 +55,47 @@ class DbMediaItem(entity: Entity) : PersistentEntity(entity), MediaItem { /** List of [DbMediaSegment] that this [DbMediaItem] contains. */ val segments by xdChildren0_N(DbMediaSegment::item) + internal val metadata by xdChildren0_N(DbMediaItemMetaDataEntry::item) + + fun metaDataMap() = metadata.asSequence().map { it.asPair() }.toMap() + + fun getMetaDataValue(key: String): String? = metadata.filter { it.key eq key }.firstOrNull()?.value + + fun setMetaData(key: String, value: String) { + val existing = metadata.filter { it.key eq key }.firstOrNull() + if (existing != null) { + existing.value = value + } else { + metadata.add( + DbMediaItemMetaDataEntry.new { + this.key = key + this.value = value + } + ) + } + } + + fun setMetaData(metaData: Map) = metaData.forEach { (key, value) -> setMetaData(key, value) } + + fun deleteMetaData(key: String) { + metadata.removeAll(metadata.filter { it.key eq key }) + } + /** * Generates a [ApiMediaItem] this [DbMediaItem] and returns it. * * @return [ApiMediaItem] */ - fun toApi(): ApiMediaItem - = ApiMediaItem(this.id, this.name, this.type.toApi(), this.collection.id, this.location, this.durationMs, this.fps) + fun toApi(): ApiMediaItem = ApiMediaItem( + this.id, + this.name, + this.type.toApi(), + this.collection.id, + this.location, + this.durationMs, + this.fps, + this.metadata.asSequence().map { ApiMediaItemMetaDataEntry(it.key, it.value) }.toList() + ) /** * Returns the [Path] to the original file for this [DbMediaItem]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItemMetaDataEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItemMetaDataEntry.kt new file mode 100644 index 000000000..e36fe724a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaItemMetaDataEntry.kt @@ -0,0 +1,27 @@ +package dev.dres.data.model.media + +import dev.dres.api.rest.types.collection.ApiMediaItemMetaDataEntry +import jetbrains.exodus.entitystore.Entity +import kotlinx.dnq.XdEntity +import kotlinx.dnq.XdNaturalEntityType +import kotlinx.dnq.xdParent +import kotlinx.dnq.xdRequiredStringProp + +internal class DbMediaItemMetaDataEntry(entity: Entity) : XdEntity(entity) { + + companion object: XdNaturalEntityType() + + /** The key for this [DbMediaItemMetaDataEntry]. */ + var key by xdRequiredStringProp() + + /** The value for this [DbMediaItemMetaDataEntry]. */ + var value by xdRequiredStringProp() + + /** The [DbMediaItem] this [DbMediaItemMetaDataEntry] belongs to. */ + val item: DbMediaItem by xdParent(DbMediaItem::metadata) + + fun asPair() = key to value + + fun toApi() = ApiMediaItemMetaDataEntry(key, value) + +} \ No newline at end of file diff --git a/doc/oas-client.json b/doc/oas-client.json index 3a66a7162..b36a0bc11 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1065,6 +1065,113 @@ "security" : [ ] } }, + "/api/v2/preview/{mediaItemId}/{start}/{end}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview video from a media item. ", + "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item. Must be a video.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "start", + "in" : "path", + "description" : "Start timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "end", + "in" : "path", + "description" : "End timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "video/mp4" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{time}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview image from a media item.", + "operationId" : "getApiV2PreviewByMediaItemIdByTime", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Time into the video in milliseconds (for videos only).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "image/jpeg" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/status/info" : { "get" : { "tags" : [ "Status" ], @@ -1119,6 +1226,78 @@ "security" : [ ] } }, + "/api/v2/submit/{evaluationId}" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV2SubmitByEvaluationId", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiClientSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/user" : { "post" : { "tags" : [ "User" ], @@ -1541,9 +1720,28 @@ "fps" : { "type" : "number", "format" : "float" + }, + "metadata" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" + } + } + }, + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] + }, + "ApiMediaItemMetaDataEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "key" : { + "type" : "string" + }, + "value" : { + "type" : "string" } }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location" ] + "required" : [ "key", "value" ] }, "ApiMediaType" : { "type" : "string", @@ -1972,9 +2170,6 @@ "end" : { "type" : "integer", "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" } }, "required" : [ "type" ] @@ -1997,17 +2192,66 @@ "items" : { "$ref" : "#/components/schemas/ApiAnswer" } - }, - "submission" : { - "$ref" : "#/components/schemas/ApiSubmission" } }, - "required" : [ "id", "status", "taskId", "answers", "submission" ] + "required" : [ "id", "status", "taskId", "answers" ] }, "ApiAnswerType" : { "type" : "string", "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "itemName" : { + "type" : "string" + }, + "itemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + } + }, + "required" : [ "answers" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -2684,28 +2928,6 @@ }, "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, "RunProperties" : { "type" : "object", "additionalProperties" : false, diff --git a/doc/oas.json b/doc/oas.json index 3ee4da43e..d064ab4a6 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -3573,6 +3573,113 @@ "security" : [ ] } }, + "/api/v2/preview/{mediaItemId}/{start}/{end}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview video from a media item. ", + "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item. Must be a video.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "start", + "in" : "path", + "description" : "Start timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "end", + "in" : "path", + "description" : "End timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "video/mp4" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{time}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview image from a media item.", + "operationId" : "getApiV2PreviewByMediaItemIdByTime", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Time into the video in milliseconds (for videos only).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "image/jpeg" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/score/evaluation/{evaluationId}" : { "get" : { "tags" : [ "Evaluation Scores" ], @@ -3956,6 +4063,78 @@ "security" : [ ] } }, + "/api/v2/submit/{evaluationId}" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV2SubmitByEvaluationId", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiClientSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/template" : { "post" : { "tags" : [ "Template" ], @@ -5046,9 +5225,28 @@ "fps" : { "type" : "number", "format" : "float" + }, + "metadata" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" + } } }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location" ] + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] + }, + "ApiMediaItemMetaDataEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "key" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "key", "value" ] }, "ApiMediaType" : { "type" : "string", @@ -5507,6 +5705,58 @@ "type" : "string", "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "itemName" : { + "type" : "string" + }, + "itemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + } + }, + "required" : [ "answers" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, From f0c108f469404fcd248f98f7d5e8a0d903d6602a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 25 Jun 2023 12:21:18 +0200 Subject: [PATCH 324/498] Fixes in new submission handler --- .../handler/submission/SubmissionHandler.kt | 61 ++++++++++++++++--- .../api/rest/types/collection/ApiMediaItem.kt | 2 +- .../dev/dres/run/NonInteractiveRunManager.kt | 60 ++++++++---------- doc/oas-client.json | 16 ++++- doc/oas.json | 16 ++++- 5 files changed, 103 insertions(+), 52 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 9ffb45133..21b02c524 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -34,7 +34,8 @@ import kotlinx.dnq.query.first import kotlinx.dnq.query.firstOrNull import org.slf4j.LoggerFactory -class SubmissionHandler(private val store: TransientEntityStore, private val config: Config): PostRestHandler, AccessManagedRestHandler { +class SubmissionHandler(private val store: TransientEntityStore, private val config: Config) : + PostRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.PARTICIPANT) @@ -47,8 +48,12 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con @OpenApi( summary = "Endpoint to accept submissions", path = "/api/v2/submit/{evaluationId}", + methods = [HttpMethod.POST], operationId = OpenApiOperation.AUTO_GENERATE, requestBody = OpenApiRequestBody([OpenApiContent(ApiClientSubmission::class)]), + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), @@ -57,14 +62,23 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) ], tags = ["Submission"] - ) + ) override fun doPost(ctx: Context): SuccessStatus { return this.store.transactional { - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(401, "Authorization required.", ctx) + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( + 401, + "Authorization required.", + ctx + ) val evaluationId = ctx.pathParam("evaluationId") - val runManager = AccessManager.getRunManagerForUser(userId).find { it.id == evaluationId } ?: throw ErrorStatusException(404, "Evaluation with id '$evaluationId' not found.", ctx) + val runManager = + AccessManager.getRunManagerForUser(userId).find { it.id == evaluationId } ?: throw ErrorStatusException( + 404, + "Evaluation with id '$evaluationId' not found.", + ctx + ) val rac = RunActionContext.runActionContext(ctx, runManager) @@ -86,7 +100,11 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) } catch (e: IllegalRunStateException) { logger.info("Submission was received while run manager was not accepting submissions.") - throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + throw ErrorStatusException( + 400, + "Run manager is in wrong state and cannot accept any more submission.", + ctx + ) } catch (e: IllegalTeamIdException) { logger.info("Submission with unknown team id '${rac.teamId}' was received.") throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) @@ -102,7 +120,11 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } - private fun transformClientSubmission(apiClientSubmission: ApiClientSubmission, runManager: RunManager, rac: RunActionContext) : ApiSubmission{ + private fun transformClientSubmission( + apiClientSubmission: ApiClientSubmission, + runManager: RunManager, + rac: RunActionContext + ): ApiSubmission { if (rac.userId == null || rac.teamId == null) { throw Exception("Invalid association between user and evaluation") @@ -115,7 +137,11 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con val answerSets = apiClientSubmission.answerSets.mapNotNull mapClientAnswerSet@{ clientAnswerSet -> val task = if (runManager is InteractiveRunManager) { - runManager.currentTask(rac) //only accept submissions for current task in interactive runs + val currentTask = runManager.currentTask(rac) //only accept submissions for current task in interactive runs + if (currentTask?.isRunning != true) { + throw Exception("No active task") + } + currentTask } else { evaluationRun.tasks.find { it.template.name == clientAnswerSet.taskName } //look up task by name } @@ -142,7 +168,7 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con null }?.toApi() - return@mapClientAnswers when(clientAnswer.type()) { + return@mapClientAnswers when (clientAnswer.type()) { AnswerType.ITEM -> { if (item == null) { errorBuffer.append("item for answer $clientAnswer not found") @@ -150,14 +176,29 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con } ApiAnswer(type = ApiAnswerType.ITEM, item = item, start = null, end = null, text = null) } + AnswerType.TEMPORAL -> { if (item == null) { errorBuffer.append("item for answer $clientAnswer not found") return@mapClientAnswers null } - ApiAnswer(type = ApiAnswerType.TEMPORAL, item = item, start = clientAnswer.start, end = clientAnswer.end, text = null) + ApiAnswer( + type = ApiAnswerType.TEMPORAL, + item = item, + start = clientAnswer.start, + end = clientAnswer.end, + text = null + ) } - AnswerType.TEXT -> ApiAnswer(type = ApiAnswerType.TEXT, text = clientAnswer.text, item = null, start = null, end = null) + + AnswerType.TEXT -> ApiAnswer( + type = ApiAnswerType.TEXT, + text = clientAnswer.text, + item = null, + start = null, + end = null + ) + null -> { errorBuffer.append("could not determine AnswerType of answer $clientAnswer\n") null diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt index b6cba6813..73de65f91 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/collection/ApiMediaItem.kt @@ -21,7 +21,7 @@ data class ApiMediaItem( val location: String, val durationMs: Long? = null, val fps: Float? = null, - val metadata: List + val metadata: List = emptyList() ) : MediaItem { init { if (this.type == ApiMediaType.VIDEO) { diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 2301522ba..576eebfd1 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -22,7 +22,10 @@ import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write -class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation, override val store: TransientEntityStore) : RunManager { +class NonInteractiveRunManager( + override val evaluation: NonInteractiveEvaluation, + override val store: TransientEntityStore +) : RunManager { private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable @@ -30,7 +33,12 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ override val runProperties: RunProperties - get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) + get() = RunProperties( + this.evaluation.participantCanView, + false, + this.evaluation.allowRepeatedTasks, + this.evaluation.limitSubmissionPreviews + ) /** A lock for state changes to this [InteractiveSynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() @@ -51,7 +59,8 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation private val scoresUpdatable = ScoresUpdatable(this) /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) //TODO requires some changes + private val scoreboardsUpdatable = + ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) //TODO requires some changes /** The [List] of [Scoreboard]s maintained by this [NonInteractiveRunManager]. */ override val scoreboards: List @@ -63,7 +72,7 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation } else { RunManagerStatus.CREATED } - private set + private set /** */ override val judgementValidators: List @@ -123,50 +132,25 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation try { this.stateLock.read { - while (updatedTasks.isNotEmpty()) { - val idNamePair = updatedTasks.poll(1, TimeUnit.SECONDS) - if (idNamePair == null) { - LOGGER.error("Unable to retrieve task id from queue despite it being indicated not to be empty") - break - } + scoresUpdatable.update(this.status) - val task = this.evaluation.tasks.find { it.taskId == idNamePair.first } + scoreboardsUpdatable.update(this.status) - if (task == null) { - LOGGER.error("Unable to retrieve task with changed id ${idNamePair.first}") - break - } - - /* TODO: Redo. */ - /*val batches = idNamePair.second.mapNotNull { task.submissions[it] } - - val validator = task.validator - val scorer = task.scorer - - batches.forEach { - validator.validate(it) - scorer.computeScores(it) - }*/ - scoreboardsUpdatable.update(this.status) - } } } catch (ie: InterruptedException) { LOGGER.info("Interrupted NonInteractiveRunManager, exiting") return } - Thread.sleep(1000) - } LOGGER.info("NonInteractiveRunManager ${this.id} reached end of run logic.") } - private val updatedTasks = LinkedBlockingQueue>>>() /** * @@ -177,7 +161,8 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation override fun postSubmission(context: RunActionContext, submission: ApiSubmission) { - val submissionByTask = submission.answers.groupBy { it.taskId }.mapValues { submission.copy(answers = it.value) } + val submissionByTask = + submission.answers.groupBy { it.taskId }.mapValues { submission.copy(answers = it.value) } if (submissionByTask.keys.any { !taskMap.containsKey(it) }) { throw IllegalStateException("Unknown task") @@ -185,11 +170,13 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation this.stateLock.write { + val errorBuffer = StringBuilder() + submissionByTask.forEach { (taskId, submission) -> val task = taskMap[taskId] ?: throw IllegalStateException("Unknown task $taskId") - try{ + try { /* Check if ApiSubmission meets formal requirements. */ task.filter.acceptOrThrow(submission) @@ -215,10 +202,13 @@ class NonInteractiveRunManager(override val evaluation: NonInteractiveEvaluation this.scoresUpdatable.enqueue(task) } catch (e: SubmissionRejectedException) { - //TODO give feedback about parts of submissions that have been rejected somehow + errorBuffer.append(e.message) + errorBuffer.append('\n') } + } - + if (errorBuffer.isNotBlank()) { + throw SubmissionRejectedException(submission, errorBuffer.toString()) } } diff --git a/doc/oas-client.json b/doc/oas-client.json index b36a0bc11..92ebd30d6 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1227,11 +1227,21 @@ } }, "/api/v2/submit/{evaluationId}" : { - "get" : { + "post" : { "tags" : [ "Submission" ], "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV2SubmitByEvaluationId", - "parameters" : [ ], + "operationId" : "postApiV2SubmitByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "requestBody" : { "content" : { "application/json" : { diff --git a/doc/oas.json b/doc/oas.json index d064ab4a6..ff948e46e 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4064,11 +4064,21 @@ } }, "/api/v2/submit/{evaluationId}" : { - "get" : { + "post" : { "tags" : [ "Submission" ], "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV2SubmitByEvaluationId", - "parameters" : [ ], + "operationId" : "postApiV2SubmitByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "requestBody" : { "content" : { "application/json" : { From 5ee0681505152a477084a82a237c4232a305aec5 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 25 Jun 2023 18:56:47 +0200 Subject: [PATCH 325/498] Added comment field to task template --- .../handler/template/UpdateEvaluationTemplateHandler.kt | 1 + .../api/rest/types/competition/tasks/ApiTaskTemplate.kt | 3 ++- .../dev/dres/data/model/template/task/DbTaskTemplate.kt | 9 +++++++-- doc/oas.json | 5 ++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 37db761e4..992cb1a6b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -164,6 +164,7 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C t.collection = DbMediaCollection.query(DbMediaCollection::id eq task.collectionId).first() t.taskGroup = DbTaskGroup.query(DbTaskGroup::name eq task.taskGroup).first() + t.comment = task.comment /* Update task targets. */ t.targets.clear() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt index 13c2c5ca1..cd0fd8b85 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/competition/tasks/ApiTaskTemplate.kt @@ -19,5 +19,6 @@ class ApiTaskTemplate( val duration: Long, val collectionId: CollectionId, val targets: List, - val hints: List + val hints: List, + val comment: String = "" ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 7b4bfeed8..4fb383654 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -32,7 +32,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { get() = this.id set(value) { this.id = value } - /** The name held by this [DbTeam]. Must be unique!*/ + /** The name held by this [DbTeam]. Must be unique per evaluation*/ var name by xdRequiredStringProp(unique = false, trimmed = true) /** The [DbTaskGroup] this [DbTaskTemplate] belongs to. */ @@ -53,6 +53,9 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { /** The [DbHint]s that act as clues to find the target media. */ val hints by xdChildren0_N(DbHint::task) + /** Optional comment to be used by evaluation administrators */ + var comment by xdStringProp() + /** * Creates a [DbTaskTemplate] instance and returns it. * @@ -67,6 +70,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { this.collection = this@DbTaskTemplate.collection this.duration = this@DbTaskTemplate.duration this.taskGroup = forEvaluation.taskGroups.query(DbTaskGroup::name eq this@DbTaskTemplate.taskGroup.name).first() + this.comment = this@DbTaskTemplate.comment } /* Copy task targets. */ @@ -123,7 +127,8 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { this.duration, this.collection.id, this.targets.asSequence().map { it.toApi() }.toList(), - this.hints.asSequence().map { it.toApi() }.toList() + this.hints.asSequence().map { it.toApi() }.toList(), + this.comment ?: "" ) /** diff --git a/doc/oas.json b/doc/oas.json index ff948e46e..c40cf49e3 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -5547,9 +5547,12 @@ "items" : { "$ref" : "#/components/schemas/ApiHint" } + }, + "comment" : { + "type" : "string" } }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] }, "ApiTaskType" : { "type" : "object", From 66cc2ad2304b9f50f4b805fc4f301fa244a57d6e Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 26 Jun 2023 20:02:28 +0200 Subject: [PATCH 326/498] Some OpenAPI fixes --- .../dres/api/rest/handler/preview/PreviewImageHandler.kt | 2 +- doc/oas-client.json | 9 ++++++--- doc/oas.json | 4 ++-- .../tasks-list/task-templates-list.component.ts | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt index 19e83cc61..be3d6d0e1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt @@ -21,7 +21,7 @@ class PreviewImageHandler(store: TransientEntityStore, cache: CacheManager) : Ab override val route: String = "preview/{mediaItemId}/{timestamp}" @OpenApi( summary = "Returns a preview image from a media item.", - path = "/api/v2/preview/{mediaItemId}/{time}", + path = "/api/v2/preview/{mediaItemId}/{timestamp}", operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("mediaItemId", String::class, "Unique ID of the media item.", required = true, allowEmptyValue = false), diff --git a/doc/oas-client.json b/doc/oas-client.json index 92ebd30d6..8735d4d48 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1124,11 +1124,11 @@ "security" : [ ] } }, - "/api/v2/preview/{mediaItemId}/{time}" : { + "/api/v2/preview/{mediaItemId}/{timestamp}" : { "get" : { "tags" : [ "Media" ], "summary" : "Returns a preview image from a media item.", - "operationId" : "getApiV2PreviewByMediaItemIdByTime", + "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", "parameters" : [ { "name" : "mediaItemId", "in" : "path", @@ -2042,9 +2042,12 @@ "items" : { "$ref" : "#/components/schemas/ApiHint" } + }, + "comment" : { + "type" : "string" } }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints" ] + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] }, "ApiTaskType" : { "type" : "object", diff --git a/doc/oas.json b/doc/oas.json index c40cf49e3..28a791977 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -3632,11 +3632,11 @@ "security" : [ ] } }, - "/api/v2/preview/{mediaItemId}/{time}" : { + "/api/v2/preview/{mediaItemId}/{timestamp}" : { "get" : { "tags" : [ "Media" ], "summary" : "Returns a preview image from a media item.", - "operationId" : "getApiV2PreviewByMediaItemIdByTime", + "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", "parameters" : [ { "name" : "mediaItemId", "in" : "path", diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index 40097c182..9dc540051 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -73,6 +73,7 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent targets: Array; taskGroup: string; taskType: string; + comment: string; }; newTask.taskGroup = group.name; newTask.targets = []; From 38c9ed2ccfbd7bb50012d83e0824e49483711105 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Mon, 26 Jun 2023 20:02:56 +0200 Subject: [PATCH 327/498] Added total to scoreboard bar chart --- .../competition-scoreboard-viewer.component.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts index bb8710d44..d2b22e367 100644 --- a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts +++ b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts @@ -58,6 +58,15 @@ export class CompetitionScoreboardViewerComponent implements OnInit { plotOptions: ApexPlotOptions = { bar: { horizontal: true, + dataLabels: { + total: { + enabled: this.competitionOverview, //show total when bars are stacked + offsetX: 5, + style: { + color: '#fff' + } + }, + } }, } as ApexPlotOptions; From 760d7b2cc8e7e9dcb739775d9f730ab59807eaed Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 3 Jul 2023 16:36:36 +0200 Subject: [PATCH 328/498] Resolved #399 by reordering and moving often-used buttons --- .../src/app/run/admin-run-list.component.html | 57 ++++--------------- .../run-admin-toolbar.component.html | 2 +- 2 files changed, 11 insertions(+), 48 deletions(-) diff --git a/frontend/src/app/run/admin-run-list.component.html b/frontend/src/app/run/admin-run-list.component.html index 02daf052f..4c18d1117 100644 --- a/frontend/src/app/run/admin-run-list.component.html +++ b/frontend/src/app/run/admin-run-list.component.html @@ -63,6 +63,16 @@ play_arrow + + - - - - - - -
- + {{ (run | async)?.runInfo?.type === 'ASYNCHRONOUS' ? 'Asynchronous' : 'Synchronous' }} Run: {{ (run | async)?.runInfo?.name }} ({{ (run | async)?.runInfo?.id?.substring(0, 8) }}) From 5783ced1afdea0cef7f254a841ee74ccc392c776 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 4 Jul 2023 13:03:33 +0200 Subject: [PATCH 329/498] Updated Javalin + Javalin/OpenApi and corresonding code adjustements --- backend/build.gradle | 4 ++-- .../kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt | 8 +++++--- backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt | 2 +- build.gradle | 2 +- doc/oas-client.json | 6 +----- doc/oas.json | 10 +++------- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 39d253cdf..48aa86dd8 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -47,8 +47,8 @@ compileTestKotlin { } dependencies { - def javalin = '5.4.2' - def javalinOpenapi = '5.4.2' + def javalin = '5.6.1' + def javalinOpenapi = '5.6.1' def log4jVersion = '2.17.1' ///// Frontend files (produced by sub-project). diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt index e6bf97a24..fe7bb5a8d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientSwaggerPlugin.kt @@ -11,15 +11,17 @@ class ClientSwaggerPlugin : Plugin { val swaggerHandler = SwaggerHandler( title = "DRES Client API", documentationPath = "/client-oas", - swaggerVersion = "4.10.3", + swaggerVersion = SwaggerConfiguration().version, validatorUrl = "https://validator.swagger.io/validator", routingPath = app.cfg.routing.contextPath, basePath = null, tagsSorter = "'alpha'", - operationsSorter = "'alpha'" + operationsSorter = "'alpha'", + customJavaScriptFiles = emptyList(), + customStylesheetFiles = emptyList() ) app.get("/swagger-client", swaggerHandler) } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 562c695c6..b6a9dafb5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -246,7 +246,7 @@ object RestApi { it.plugins.register( SwaggerPlugin( SwaggerConfiguration().apply { - this.version = "4.10.3" + //this.version = "4.10.3" this.documentationPath = "/swagger-docs" this.uiPath = "/swagger-ui" } diff --git a/build.gradle b/build.gradle index 506f2e854..1a43f94c1 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ tasks.register('generateOAS', Download) { dest f } -task generateClientOAS(type: Download){ +tasks.register('generateClientOAS', Download) { def f = new File(clientOasFile) src clientOAS dest f diff --git a/doc/oas-client.json b/doc/oas-client.json index 8735d4d48..a9f1805ad 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2,11 +2,7 @@ "openapi" : "3.0.3", "info" : { "title" : "DRES Client API", - "summary" : null, "description" : "Client API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", - "termsOfService" : null, - "contact" : null, - "license" : null, "version" : "2.0.0" }, "paths" : { @@ -2968,8 +2964,8 @@ }, "securitySchemes" : { "CookieAuth" : { - "name" : "SESSIONID", "in" : "cookie", + "name" : "SESSIONID", "type" : "apiKey" } } diff --git a/doc/oas.json b/doc/oas.json index 28a791977..c818efbf3 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -2,18 +2,14 @@ "openapi" : "3.0.3", "info" : { "title" : "DRES API", - "summary" : null, "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", - "termsOfService" : null, "contact" : { "name" : "The DRES Dev Team", - "url" : "https://dres.dev", - "email" : null + "url" : "https://dres.dev" }, "license" : { "name" : "MIT", - "identifier" : "MIT", - "url" : null + "identifier" : "MIT" }, "version" : "2.0.0" }, @@ -6473,8 +6469,8 @@ }, "securitySchemes" : { "CookieAuth" : { - "name" : "SESSIONID", "in" : "cookie", + "name" : "SESSIONID", "type" : "apiKey" } } From e9cff9821fa613645cc411b847db9c15d81c3532 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 5 Jul 2023 09:52:36 +0200 Subject: [PATCH 330/498] Fixed #408 and cleaned-up the DRES routing configuration. --- frontend/src/app/app-routing.module.ts | 10 ++- frontend/src/app/app.component.html | 10 +-- frontend/src/app/app.module.ts | 6 +- frontend/src/app/error/error.module.ts | 17 ++++ frontend/src/app/error/forbidden.component.ts | 44 ++++++++++ frontend/src/app/error/not-found.component.ts | 44 ++++++++++ .../services/session/authentication.sevice.ts | 83 +++++++++++-------- frontend/src/app/services/session/guard.ts | 2 +- .../src/app/user/login/login.component.ts | 20 ++--- 9 files changed, 176 insertions(+), 60 deletions(-) create mode 100644 frontend/src/app/error/error.module.ts create mode 100644 frontend/src/app/error/forbidden.component.ts create mode 100644 frontend/src/app/error/not-found.component.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 88d5fab78..99aa88f81 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -24,6 +24,8 @@ import { canActivatePublicVote } from "./services/session/guard"; import {NonescapingUrlserializerClass} from "./nonescaping-urlserializer.class"; +import {ForbiddenComponent} from "./error/forbidden.component"; +import {NotFoundComponent} from "./error/not-found.component"; /** * The ROUTE for evaluation templates. @@ -117,7 +119,13 @@ const routes: Routes = [ canActivate: [canActivateAdministrator] }, - { path: 'login', component: LoginComponent } + /* The login + forbidden page is always accessible. */ + { path: 'login', component: LoginComponent }, + { path: 'forbidden', component: ForbiddenComponent}, + + /* Two important 'catch-all's. */ + { path: '', redirectTo: 'evaluation/list', pathMatch: 'full' }, + { path: '**', component: NotFoundComponent } ]; @NgModule({ diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index b742695f5..3561ccc60 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -53,7 +53,7 @@
-
+
@@ -65,10 +65,4 @@
-
- -
- - - - + \ No newline at end of file diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 8a6c046cb..ab91b16bd 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -23,8 +23,9 @@ import { AuditlogModule } from './auditlog/auditlog.module'; import { SharedModule } from './shared/shared.module'; import { CollectionModule } from './collection/collection.module'; import { CompetitionBuilderModule } from './competition/competition-builder/competition-builder.module'; -import {TemplateModule} from './template/template.module'; +import { TemplateModule } from './template/template.module'; import { EvaluationModule } from './evaluation/evaluation.module'; +import { ErrorModule } from "./error/error.module"; /** * Method used to load application config. @@ -62,7 +63,8 @@ export function initializeApp(appConfig: AppConfig) { CollectionModule, JudgementModule, TemplateModule, - EvaluationModule + EvaluationModule, + ErrorModule ], providers: [ AppConfig, diff --git a/frontend/src/app/error/error.module.ts b/frontend/src/app/error/error.module.ts new file mode 100644 index 000000000..58dde6c9d --- /dev/null +++ b/frontend/src/app/error/error.module.ts @@ -0,0 +1,17 @@ +import {NgModule} from "@angular/core"; +import {ForbiddenComponent} from "./forbidden.component"; +import {NotFoundComponent} from "./not-found.component"; +import {MatCardModule} from "@angular/material/card"; + +@NgModule({ + declarations: [ + ForbiddenComponent, + NotFoundComponent + ], + imports: [ + MatCardModule + ], + exports: [] +}) +export class ErrorModule { +} diff --git a/frontend/src/app/error/forbidden.component.ts b/frontend/src/app/error/forbidden.component.ts new file mode 100644 index 000000000..10d87dda1 --- /dev/null +++ b/frontend/src/app/error/forbidden.component.ts @@ -0,0 +1,44 @@ +import {Component} from "@angular/core"; + +@Component({ + selector: 'app-error-forbidden', + styles: [`:host { + display: flex; + justify-content: center; + margin: 100px 0px; + } + + .mat-mdc-form-field { + width: 100%; + min-width: 300px; + } + + /* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ + mat-card-title, + mat-card-content { + display: flex; + justify-content: center; + } + + .error { + padding: 16px; + width: 300px; + color: white; + background-color: darkred; + } + + .button { + display: flex; + justify-content: flex-end; + }`], + template: ` + + Access denied + + You are not allowed to view this page. Check with the administrator if you believe this to be a mistake. + + + `, +}) +export class ForbiddenComponent { +} \ No newline at end of file diff --git a/frontend/src/app/error/not-found.component.ts b/frontend/src/app/error/not-found.component.ts new file mode 100644 index 000000000..1d910c60f --- /dev/null +++ b/frontend/src/app/error/not-found.component.ts @@ -0,0 +1,44 @@ +import {Component} from "@angular/core"; + +@Component({ + selector: 'app-error-notfound', + styles: [`:host { + display: flex; + justify-content: center; + margin: 100px 0px; + } + + .mat-mdc-form-field { + width: 100%; + min-width: 300px; + } + + /* TODO(mdc-migration): The following rule targets internal classes of card that may no longer apply for the MDC version. */ + mat-card-title, + mat-card-content { + display: flex; + justify-content: center; + } + + .error { + padding: 16px; + width: 300px; + color: white; + background-color: darkred; + } + + .button { + display: flex; + justify-content: flex-end; + }`], + template: ` + + Page Not Found + + The page you have requested could not be found. Check with the administrator if you believe this to be an error. + + ` +}) +export class NotFoundComponent { + +} \ No newline at end of file diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index 84e9033e2..651d650b6 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -1,6 +1,6 @@ import {Inject, Injectable} from '@angular/core'; -import {catchError, flatMap, map, tap, withLatestFrom} from 'rxjs/operators'; -import {Observable, of} from 'rxjs'; +import {catchError, filter, flatMap, map, shareReplay, tap, withLatestFrom} from 'rxjs/operators'; +import {BehaviorSubject, Observable, of, Subscription} from 'rxjs'; import {ApiRole, ApiUser, LoginRequest, UserRequest, UserService} from '../../../../openapi'; import {ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree} from "@angular/router"; @@ -12,10 +12,18 @@ import {ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree} from "@ang }) export class AuthenticationService { + /** A {@link BehaviorSubject} that captures the current login-state. */ + private _loggedIn: BehaviorSubject = new BehaviorSubject(false) + /** * Constructor */ - constructor(@Inject(UserService) private userService: UserService, private router: Router) {} + constructor(@Inject(UserService) private userService: UserService, private router: Router) { + this.userService.getApiV2User().subscribe( + () => this._loggedIn.next(true), + () => this._loggedIn.next(false) + ) + } /** * Tries to login a user with the given username and password. Returns an Observable! @@ -26,7 +34,23 @@ export class AuthenticationService { public login(user: string, pass: string) { return this.userService.postApiV2Login({ username: user, password: pass } as LoginRequest).pipe( flatMap(() => this.userService.getApiV2User()), - tap((data) => console.log(`Successfully logged in as '${data.username}'.`)) + tap((data) => { + this._loggedIn.next(true); + console.log(`Successfully logged in as '${data.username}'.`) + }) + ); + } + + /** + * Tries to logout the current user. Returns an Observable! + */ + public logout() { + return this.userService.getApiV2Logout().pipe( + catchError((e) => of(null)), + tap(() => { + this._loggedIn.next(false); + console.log(`User was logged out.`) + }) ); } @@ -41,16 +65,6 @@ export class AuthenticationService { ); } - /** - * Tries to logout the current user. Returns an Observable! - */ - public logout() { - return this.userService.getApiV2Logout().pipe( - catchError((e) => of(null)), - tap(() => console.log(`User was logged out.`)) - ); - } - /** * Returns the current login state as Observable. * @@ -58,24 +72,28 @@ export class AuthenticationService { * that the user is still logged in. */ get isLoggedIn(): Observable { - return this.userService.getApiV2User().pipe( - map((u) => u != null), - catchError((e) => of(false)) - ); + return this._loggedIn.asObservable() } /** - * Returns the username of the current user as Observable. + * Returns the currently logged in {@link ApiUser} as Observable. */ - get user(): Observable { - return this.userService.getApiV2User(); + get user(): Observable { + return this.isLoggedIn.pipe( + flatMap(loggedIn=> { + if (loggedIn) { + return this.userService.getApiV2User() + } else { + return of(null) + } + })) } /** - * Returns the role of the current user as Observable. + * Returns the {@link ApiRole} of the current user as Observable. */ - get role(): Observable { - return this.userService.getApiV2User().pipe(map((u) => u?.role)); + get role(): Observable { + return this.user.pipe(map((u) => u?.role)) } /** @@ -86,18 +104,17 @@ export class AuthenticationService { * @param route The {@link ActivatedRouteSnapshot} * @param state The {@link RouterStateSnapshot} */ - public canActivate(rolesAllows: Array, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { - return this.isLoggedIn.pipe( - withLatestFrom(this.role), - map(([loggedIn, role]) => { - if (!loggedIn) { + public canActivate(rolesAllows: Array, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.role.pipe( + map((role) => { + if (!role) { return this.router.parseUrl(`/login?returnUrl=${state.url}`) - } - if (route.data.roles && route.data.roles.indexOf(role) === -1) { + } else if (route.data.roles && route.data.roles.indexOf(role) === -1) { return this.router.parseUrl('/forbidden'); + } else { + return true; } - return true; }) - ).toPromise(); + ); } } diff --git a/frontend/src/app/services/session/guard.ts b/frontend/src/app/services/session/guard.ts index 5dd05b38f..861612b02 100644 --- a/frontend/src/app/services/session/guard.ts +++ b/frontend/src/app/services/session/guard.ts @@ -28,6 +28,6 @@ export const canActivatePublicVote: CanActivateFn = (route: ActivatedRouteSnapsh /** * Guard used to determine if a view can be activated that requires the user to hold any role. */ -export const canActivateAnyRole: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { +export const canActivateAnyRole: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { return inject(AuthenticationService).canActivate([ApiRole.ADMIN, ApiRole.VIEWER, ApiRole.JUDGE, ApiRole.PARTICIPANT], route, state); }; \ No newline at end of file diff --git a/frontend/src/app/user/login/login.component.ts b/frontend/src/app/user/login/login.component.ts index 04bc975ab..9a77515a3 100644 --- a/frontend/src/app/user/login/login.component.ts +++ b/frontend/src/app/user/login/login.component.ts @@ -16,7 +16,7 @@ export class LoginComponent implements OnInit, OnDestroy { password: new UntypedFormControl(''), }); - private returnUrl = '/'; + private returnUrl = '/evaluation/list'; private authenticationServiceSubscription: Subscription; constructor( @@ -27,11 +27,9 @@ export class LoginComponent implements OnInit, OnDestroy { ) {} ngOnInit(): void { - this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/'; + this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/evaluation/list'; this.authenticationServiceSubscription = this.authenticationService.isLoggedIn.subscribe((b) => { - if (b) { - this.router.navigate([this.returnUrl]).then(r => {}) - } + if (b) this.router.navigate([this.returnUrl]).then(r => {}) }); } @@ -43,16 +41,8 @@ export class LoginComponent implements OnInit, OnDestroy { public submit() { if (this.form.valid) { this.authenticationService.login(this.form.controls.username.value, this.form.controls.password.value).subscribe( - (r) => { - this.snackBar.open(`Login successful!`, null, { duration: 5000 }); - }, - (err) => { - if (err?.error) { - this.snackBar.open(`Login failed: ${err?.error?.description}!`, null, { duration: 5000 }); - } else { - this.snackBar.open(`Login failed due to a connection issue!`, null, { duration: 5000 }); - } - } + (r) => this.router.navigateByUrl(this.returnUrl).then(r => this.snackBar.open(`Login successful!`, null, { duration: 5000 })), + (err) => this.snackBar.open(`Login failed due to error: ${err?.error?.description}!`, null, { duration: 5000 }) ); } } From d2afd5170944ae43be7bbd3c0f701960e602de48 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 5 Jul 2023 10:45:25 +0200 Subject: [PATCH 331/498] Cleanup of #400: re-added appropriate padding --- frontend/src/app/app.component.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 3561ccc60..47be100fb 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -65,4 +65,6 @@
- \ No newline at end of file +
+ +
From fb03c7da1cf1581dafe1dc6518de7ebea33795d7 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 5 Jul 2023 11:18:20 +0200 Subject: [PATCH 332/498] Resolvved #358 by adding a toggle. The default for the temporal context is off --- .../judgement/DequeueJudgementHandler.kt | 2 +- .../judgement-media-viewer.component.html | 8 +++- .../judgement-media-viewer.component.ts | 41 +++++++++++-------- .../src/app/judgement/judgement.module.ts | 4 ++ 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index 2c0ba8840..8b80a2dcb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -43,7 +43,7 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa ) override fun doGet(ctx: Context): ApiJudgementRequest { /* Start transaction. */ - val request = this.store.transactional(true) { + val request = this.store.transactional(false) { val evaluationManager = ctx.eligibleManagerForId() checkEligibility(ctx, evaluationManager) do { diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.html b/frontend/src/app/judgement/judgement-media-viewer.component.html index 9d53af84b..2f2562b00 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.html +++ b/frontend/src/app/judgement/judgement-media-viewer.component.html @@ -10,7 +10,7 @@ [muted]="(config.configAsObservable | async).effects.mute" >
-
+
-
+
+
+ + Temporal Context: +
; /** Font size in em. TODO: Make configurable. */ fontSize = 2.5; + + hasTemporalPadding = false; + private startInSeconds: number; private endInSeconds: number; private requestSub: Subscription; - private offset = 5; - private paddingEnabled: boolean; private startPaddingApplied: boolean; private relativePlaytimeSeconds = 0; private originalLengthInSeconds: number; + constructor(public config: AppConfig) {} private static log(msg: string) { @@ -150,7 +152,7 @@ export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, this.relativePlaytimeSeconds = Math.round(this.video.nativeElement.currentTime) - this.startInSeconds; // JudgementMediaViewerComponent.log(`t=${this.relativePlaytimeSeconds}, ol=${this.originalLengthInSeconds}, ct=${this.video.nativeElement.currentTime}`); if ( - this.paddingEnabled && + this.hasTemporalPadding && this.startPaddingApplied && this.video.nativeElement.currentTime < this.startInSeconds + this.padding ) { @@ -158,7 +160,7 @@ export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, JudgementMediaViewerComponent.log('Start padding'); this.addTemporalContextClass(); } else if ( - this.paddingEnabled && + this.hasTemporalPadding && this.video.nativeElement.currentTime > this.startInSeconds + (this.startPaddingApplied ? this.padding : 0) + this.originalLengthInSeconds ) { @@ -215,21 +217,26 @@ export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, this.originalLengthInSeconds = this.endInSeconds - this.startInSeconds; JudgementMediaViewerComponent.log(`Length: ${this.originalLengthInSeconds}, Threshold: ${this.tooShortThreshold}`); /* If only a frame is given OR too short is shown, add padding */ - if (this.originalLengthInSeconds < this.tooShortThreshold) { - JudgementMediaViewerComponent.log( - `Start: ${this.startInSeconds}, Padding: ${this.padding}, diff: ${this.startInSeconds - this.padding}` - ); - if (this.startInSeconds - this.padding < 0) { - this.startInSeconds = 0; - } else { - this.startInSeconds = this.startInSeconds - this.padding; - this.startPaddingApplied = true; + if(this.hasTemporalPadding){ + if (this.originalLengthInSeconds < this.tooShortThreshold) { + JudgementMediaViewerComponent.log( + `Start: ${this.startInSeconds}, Padding: ${this.padding}, diff: ${this.startInSeconds - this.padding}` + ); + if (this.startInSeconds - this.padding < 0) { + this.startInSeconds = 0; + } else { + this.startInSeconds = this.startInSeconds - this.padding; + this.startPaddingApplied = true; + } + this.endInSeconds = this.endInSeconds + this.padding; } - this.endInSeconds = this.endInSeconds + this.padding; - this.paddingEnabled = true; - JudgementMediaViewerComponent.log(`Padding: ${this.paddingEnabled}`); } - // JudgementMediaViewerComponent.log(`time=[${this.startInSeconds},${this.endInSeconds}] - original=${this.originalLengthInSeconds}`); + } + + onTemporalContextToggle(event){ + /* Reload everything to correctly recalculate the temporal context (either if its enabled or disabled) */ + this.stop(); + this.ngAfterViewInit(); } private resolvePath(req: ApiJudgementRequest, time = true): string { diff --git a/frontend/src/app/judgement/judgement.module.ts b/frontend/src/app/judgement/judgement.module.ts index 6142c31de..efb6b9641 100644 --- a/frontend/src/app/judgement/judgement.module.ts +++ b/frontend/src/app/judgement/judgement.module.ts @@ -14,6 +14,8 @@ import { MatDialogModule } from '@angular/material/dialog'; import { SharedModule } from '../shared/shared.module'; import { JudgementVotingViewerComponent } from './judgement-voting-viewer.component'; import { QRCodeModule } from 'angularx-qrcode'; +import { MatSlideToggleModule } from "@angular/material/slide-toggle"; +import { FormsModule } from "@angular/forms"; @NgModule({ declarations: [ @@ -34,6 +36,8 @@ import { QRCodeModule } from 'angularx-qrcode'; MatDialogModule, SharedModule, QRCodeModule, + MatSlideToggleModule, + FormsModule ], exports: [JudgementViewerComponent], }) From 392308bf7b1a88bd991022dfca08df91003e60a6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 5 Jul 2023 11:19:57 +0200 Subject: [PATCH 333/498] Updated dependencies and bumped RxJs to version 7. --- frontend/package-lock.json | 10439 ++++---- frontend/package.json | 58 +- .../collection-viewer.component.ts | 2 +- .../competition-builder.component.ts | 2 +- .../app/run/abstract-run-list.component.ts | 8 +- .../src/app/run/admin-run-list.component.ts | 10 +- .../run-admin-toolbar.component.ts | 6 +- .../src/app/run/run-admin-view.component.html | 2 +- .../src/app/run/run-admin-view.component.ts | 29 +- .../run-async-admin-view.component.html | 2 +- .../src/app/viewer/task-viewer.component.ts | 20 +- frontend/yarn.lock | 22252 +++++++--------- 12 files changed, 14813 insertions(+), 18017 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 99ec5f01c..6efe01fb7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,41 +9,39 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "@angular-slider/ngx-slider": "2.0.x", - "@angular/animations": "13.2.x", - "@angular/cdk": "13.2.x", - "@angular/common": "13.2.x", - "@angular/compiler": "13.2.x", - "@angular/core": "13.2.x", - "@angular/flex-layout": "^13.0.0-beta.38", - "@angular/forms": "13.2.x", - "@angular/material": "13.2.x", - "@angular/platform-browser": "13.2.x", - "@angular/platform-browser-dynamic": "13.2.x", - "@angular/router": "13.2.x", - "angularx-qrcode": "13.0.x", - "apexcharts": "3.37.x", - "ng-apexcharts": "1.7.x", - "ngx-color-picker": "12.0.x", - "rxjs": "6.5.x", - "tslib": "2.0.x", - "zone.js": "~0.11.4" + "@angular/animations": "15.x", + "@angular/cdk": "15.x", + "@angular/common": "15.x", + "@angular/compiler": "15x", + "@angular/core": "15.x", + "@angular/forms": "15.x", + "@angular/material": "15.x", + "@angular/platform-browser": "15.x", + "@angular/platform-browser-dynamic": "15.x", + "@angular/router": "15.x", + "angularx-qrcode": "15.x", + "apexcharts": "3.x", + "ng-apexcharts": "1.x", + "ngx-color-picker": "14.x", + "rxjs": "7.x", + "tslib": "2.x", + "zone.js": "0.13.x" }, "devDependencies": { - "@angular-devkit/build-angular": "13.3.x", - "@angular-eslint/builder": "13.2.0", - "@angular-eslint/eslint-plugin": "13.2.0", - "@angular-eslint/eslint-plugin-template": "13.2.0", - "@angular-eslint/schematics": "13.2.0", - "@angular-eslint/template-parser": "13.2.0", - "@angular/cli": "13.2.x", - "@angular/compiler-cli": "13.2.x", - "@angular/language-service": "13.2.x", + "@angular-devkit/build-angular": "15.x", + "@angular-eslint/builder": "15.x", + "@angular-eslint/eslint-plugin": "15.x", + "@angular-eslint/eslint-plugin-template": "15.x", + "@angular-eslint/schematics": "15.x", + "@angular-eslint/template-parser": "15.x", + "@angular/cli": "15.x", + "@angular/compiler-cli": "15.x", + "@angular/language-service": "15.x", "@types/jasmine": "3.6.x", "@types/jasminewd2": "2.0.x", "@types/node": "12.11.x", - "@typescript-eslint/eslint-plugin": "5.17.0", - "@typescript-eslint/parser": "5.17.0", + "@typescript-eslint/eslint-plugin": "5.17.x", + "@typescript-eslint/parser": "5.17.x", "eslint": "^8.12.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.0.0", @@ -67,16 +65,25 @@ "stylelint-config-standard": "^25.0.0", "stylelint-config-standard-scss": "^3.0.0", "ts-node": "8.3.x", - "typescript": "4.5.x" + "typescript": "4.x" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -84,16 +91,16 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1302.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1302.6.tgz", - "integrity": "sha512-NztzorUMfwJeRaT7SY00Y8WSqc2lQYuF11yNoyEm7Dae3V7VZ28rW2Z9RwibP27rYQL0RjSMaz2wKITHX2vOAw==", + "version": "0.1502.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.9.tgz", + "integrity": "sha512-CFn+LbtYeLG7WqO+BBSjogl764StHpwgfJnNAXQ/3UouUktZ92z4lxhUm0PwIPb5k0lILsf81ubcS1vzwoXEEg==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.2.6", + "@angular-devkit/core": "15.2.9", "rxjs": "6.6.7" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -117,98 +124,98 @@ "dev": true }, "node_modules/@angular-devkit/build-angular": { - "version": "13.3.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.10.tgz", - "integrity": "sha512-eKMjwr7XHlh/lYqYvdIrHZfVPM8fCxP4isKzCDiOjsJ+4fl+Ycq8RvjtOLntBN6A1U8h93rZNE+VOTEGCJcGig==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.9.tgz", + "integrity": "sha512-djOo2Q22zLrxPccSbINz93hD+pES/nNPoze4Ys/0IdtMlLmxO/YGsA+FG5eNeNAf2jK/JRoNydaYOh7XpGoCzA==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1303.10", - "@angular-devkit/build-webpack": "0.1303.10", - "@angular-devkit/core": "13.3.10", - "@babel/core": "7.16.12", - "@babel/generator": "7.16.8", - "@babel/helper-annotate-as-pure": "7.16.7", - "@babel/plugin-proposal-async-generator-functions": "7.16.8", - "@babel/plugin-transform-async-to-generator": "7.16.8", - "@babel/plugin-transform-runtime": "7.16.10", - "@babel/preset-env": "7.16.11", - "@babel/runtime": "7.16.7", - "@babel/template": "7.16.7", - "@discoveryjs/json-ext": "0.5.6", - "@ngtools/webpack": "13.3.10", - "ansi-colors": "4.1.1", - "babel-loader": "8.2.5", + "@angular-devkit/architect": "0.1502.9", + "@angular-devkit/build-webpack": "0.1502.9", + "@angular-devkit/core": "15.2.9", + "@babel/core": "7.20.12", + "@babel/generator": "7.20.14", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/helper-split-export-declaration": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.20.7", + "@babel/plugin-transform-async-to-generator": "7.20.7", + "@babel/plugin-transform-runtime": "7.19.6", + "@babel/preset-env": "7.20.2", + "@babel/runtime": "7.20.13", + "@babel/template": "7.20.7", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "15.2.9", + "ansi-colors": "4.1.3", + "autoprefixer": "10.4.13", + "babel-loader": "9.1.2", "babel-plugin-istanbul": "6.1.1", - "browserslist": "^4.9.1", - "cacache": "15.3.0", - "circular-dependency-plugin": "5.2.2", - "copy-webpack-plugin": "10.2.1", - "core-js": "3.20.3", + "browserslist": "4.21.5", + "cacache": "17.0.4", + "chokidar": "3.5.3", + "copy-webpack-plugin": "11.0.0", "critters": "0.0.16", - "css-loader": "6.5.1", - "esbuild-wasm": "0.14.22", - "glob": "7.2.0", - "https-proxy-agent": "5.0.0", - "inquirer": "8.2.0", - "jsonc-parser": "3.0.0", + "css-loader": "6.7.3", + "esbuild-wasm": "0.17.8", + "glob": "8.1.0", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.2.0", "karma-source-map-support": "1.4.0", - "less": "4.1.2", - "less-loader": "10.2.0", + "less": "4.1.3", + "less-loader": "11.1.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "mini-css-extract-plugin": "2.5.3", - "minimatch": "3.0.5", - "open": "8.4.0", + "magic-string": "0.29.0", + "mini-css-extract-plugin": "2.7.2", + "open": "8.4.1", "ora": "5.4.1", - "parse5-html-rewriting-stream": "6.0.1", + "parse5-html-rewriting-stream": "7.0.0", "piscina": "3.2.0", - "postcss": "8.4.5", - "postcss-import": "14.0.2", - "postcss-loader": "6.2.1", - "postcss-preset-env": "7.2.3", - "regenerator-runtime": "0.13.9", + "postcss": "8.4.21", + "postcss-loader": "7.0.2", "resolve-url-loader": "5.0.0", "rxjs": "6.6.7", - "sass": "1.49.9", - "sass-loader": "12.4.0", - "semver": "7.3.5", - "source-map-loader": "3.0.1", + "sass": "1.58.1", + "sass-loader": "13.2.0", + "semver": "7.5.3", + "source-map-loader": "4.0.1", "source-map-support": "0.5.21", - "stylus": "0.56.0", - "stylus-loader": "6.2.0", - "terser": "5.14.2", + "terser": "5.16.3", "text-table": "0.2.0", "tree-kill": "1.2.2", - "tslib": "2.3.1", - "webpack": "5.70.0", - "webpack-dev-middleware": "5.3.0", - "webpack-dev-server": "4.7.3", + "tslib": "2.5.0", + "webpack": "5.76.1", + "webpack-dev-middleware": "6.0.1", + "webpack-dev-server": "4.11.1", "webpack-merge": "5.8.0", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.14.22" + "esbuild": "0.17.8" }, "peerDependencies": { - "@angular/compiler-cli": "^13.0.0 || ^13.3.0-rc.0", - "@angular/localize": "^13.0.0 || ^13.3.0-rc.0", - "@angular/service-worker": "^13.0.0 || ^13.3.0-rc.0", + "@angular/compiler-cli": "^15.0.0", + "@angular/localize": "^15.0.0", + "@angular/platform-server": "^15.0.0", + "@angular/service-worker": "^15.0.0", "karma": "^6.3.0", - "ng-packagr": "^13.0.0", + "ng-packagr": "^15.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.4.3 <4.7" + "typescript": ">=4.8.2 <5.0" }, "peerDependenciesMeta": { "@angular/localize": { "optional": true }, + "@angular/platform-server": { + "optional": true + }, "@angular/service-worker": { "optional": true }, @@ -226,46 +233,234 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1303.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.10.tgz", - "integrity": "sha512-A8blp98GY9Lg5RdgZ4M/nT0DhWsFv+YikC6+ebJsUTn/L06GcQNhyZKGCwB69S4Xe/kcYJgKpI2nAYnOLDpJlQ==", + "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.3.10", - "rxjs": "6.6.7" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=6.0.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "13.3.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.10.tgz", - "integrity": "sha512-NSjyrccES+RkVL/wt1t1jNmJOV9z5H4/DtVjJQbAt/tDE5Mo0ygnhELd/QiUmjVfzfSkhr75LqQD8NtURoGBwQ==", + "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, "dependencies": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" }, - "peerDependencies": { - "chokidar": "^3.5.2" + "engines": { + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/cacache": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.4.tgz", + "integrity": "sha512-Z/nL3gU+zTUjz5pCA5vVjYM8pmaw2kxM7JEiE0fv3w77Wj+sFbi70CrBruUWH0uNcEdvLDixFpgA2JM4F4DBjA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^8.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { @@ -286,71 +481,48 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1303.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.10.tgz", - "integrity": "sha512-nthTy6r4YQQTrvOpOS3dqjoJog/SL9Hn5YLytqnEp2r2he5evYsKV2Jtqi49/VgW1ohrGzw+bI0c3dUGKweyfw==", + "node_modules/@angular-devkit/build-angular/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "@angular-devkit/architect": "0.1303.10", - "rxjs": "6.6.7" - }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" + "node": ">=0.10.0" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1303.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.10.tgz", - "integrity": "sha512-A8blp98GY9Lg5RdgZ4M/nT0DhWsFv+YikC6+ebJsUTn/L06GcQNhyZKGCwB69S4Xe/kcYJgKpI2nAYnOLDpJlQ==", + "node_modules/@angular-devkit/build-angular/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.3.10", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "13.3.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.10.tgz", - "integrity": "sha512-NSjyrccES+RkVL/wt1t1jNmJOV9z5H4/DtVjJQbAt/tDE5Mo0ygnhELd/QiUmjVfzfSkhr75LqQD8NtURoGBwQ==", + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1502.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1502.9.tgz", + "integrity": "sha512-VzMXoZjrbL1XlcSegqpZCBDbVvKFGPs3cKp4bXDD5ht95jcCyJPk5FA/wrh0pGGwbOF8ae/XOWFcPRzctC35iA==", "dev": true, "dependencies": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" + "@angular-devkit/architect": "0.1502.9", + "rxjs": "6.6.7" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" } }, "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { @@ -372,20 +544,19 @@ "dev": true }, "node_modules/@angular-devkit/core": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.6.tgz", - "integrity": "sha512-8h2mWdBTN/dYwZuzKMg2IODlOWMdbJcpQG4XVrkk9ejCPP+3aX5Aa3glCe/voN6eBNiRfs8YDM0jxmpN2aWVtg==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.9.tgz", + "integrity": "sha512-6u44YJ9tEG2hiWITL1rwA9yP6ot4a3cyN/UOMRkYSa/XO2Gz5/dM3U74E2kwg+P1NcxLXffBWl0rz8/Y/lSZyQ==", "dev": true, "dependencies": { - "ajv": "8.9.0", + "ajv": "8.12.0", "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", + "jsonc-parser": "3.2.0", "rxjs": "6.6.7", - "source-map": "0.7.3" + "source-map": "0.7.4" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -417,19 +588,19 @@ "dev": true }, "node_modules/@angular-devkit/schematics": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.6.tgz", - "integrity": "sha512-mPgSqdnZRuPSMeUA+T+mwVCrq2yhXpcYm1/Rjbhy09CyHs4wSrFv21WHCrE6shlvXpcmwr0n+I0DIeagAPmjUA==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.9.tgz", + "integrity": "sha512-o08nE8sTpfq/Fknrr1rzBsM8vY36BDox+8dOo9Zc/KqcVPwDy94YKRzHb+xxVaU9jy1VYeCjy63mkyELy7Z3zQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.2.6", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", + "@angular-devkit/core": "15.2.9", + "jsonc-parser": "3.2.0", + "magic-string": "0.29.0", "ora": "5.4.1", "rxjs": "6.6.7" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -453,239 +624,209 @@ "dev": true }, "node_modules/@angular-eslint/builder": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-13.2.0.tgz", - "integrity": "sha512-7V/bJwkMX+1OzxNUMxiOGdfdksjBmzCdPfxNEHXrT4la+vQQxg1oaIsgx74OkDqFTBv6NJO4PK8Flm9/QE4CDw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-15.2.1.tgz", + "integrity": "sha512-7x2DANebLRl997Mj4DhZrnz5+vnSjavGGveJ0mBuU7CEsL0ZYLftdRqL0e0HtU3ksseS7xpchD6OM08nkNgySw==", "dev": true, - "dependencies": { - "@nrwl/devkit": "13.1.3" - }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^7.20.0 || ^8.0.0", "typescript": "*" } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.2.0.tgz", - "integrity": "sha512-ZA9JPERpeSo+G/bmp8GS/WjBbYkPDVzN6IINHz9SVdv//LWE58yymFFjRabHJx46iAEOe8P0CoKduuJWtEvNrQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-15.2.1.tgz", + "integrity": "sha512-LO7Am8eVCr7oh6a0VmKSL7K03CnQEQhFO7Wt/YtbfYOxVjrbwmYLwJn+wZPOT7A02t/BttOD/WXuDrOWtSMQ/Q==", "dev": true }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-13.2.0.tgz", - "integrity": "sha512-8qscIBc4montFQ52+XfBk7qR675oXV8mvRpjDh3cTfIZCzV6CSbbbH2iLp/9egnn0Pgy2ZUIsWgcsbK4W3+4bw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-15.2.1.tgz", + "integrity": "sha512-OM7b1kS4E4CkXjkaWN+lEzawh4VxY6l7FO1Cuk4s7iv3/YpZG3rJxIZBqnFLTixwrBuqw8y4FNBzF3eDgmFAUw==", "dev": true, "dependencies": { - "@angular-eslint/utils": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0" + "@angular-eslint/utils": "15.2.1", + "@typescript-eslint/utils": "5.48.2" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^7.20.0 || ^8.0.0", "typescript": "*" } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-13.2.0.tgz", - "integrity": "sha512-Ej9bNGbpf8oaQGglPVWAQkBSIHhQv0FeJ/vVnB2fhHUoK9BbkGUYpjAFSSRlnMhl6ktPWKCX3yJiW+F4uIUTIw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-15.2.1.tgz", + "integrity": "sha512-IeiSLk6YxapFdH2z5o/O3R7VwtBd2T6fWmhLFPwDYMDknrwegnOjwswCdBplOccpUp0wqlCeGUx7LTsuzwaz7w==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0", - "aria-query": "^4.2.2", - "axobject-query": "^2.2.0" + "@angular-eslint/bundled-angular-compiler": "15.2.1", + "@angular-eslint/utils": "15.2.1", + "@typescript-eslint/type-utils": "5.48.2", + "@typescript-eslint/utils": "5.48.2", + "aria-query": "5.1.3", + "axobject-query": "3.1.1" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^7.20.0 || ^8.0.0", "typescript": "*" } }, "node_modules/@angular-eslint/schematics": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-13.2.0.tgz", - "integrity": "sha512-wxpfZ1L8Twa9Jfwqis7fY6jZvORqAv1k+9m1P6+6/l8gIm/8RYwD44o9Qm2MGt7YClnVthpCDhvfprcoKq6zgA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-15.2.1.tgz", + "integrity": "sha512-0ZfBCejHWIcgy3J5kFs9sS/jqi8i5AptxggOwFySOlCLJ+CzNrktjD4jff1Zy8K/VLzY0Ci0BSZXvgWfP0k9Rg==", "dev": true, "dependencies": { - "@angular-eslint/eslint-plugin": "13.2.0", - "@angular-eslint/eslint-plugin-template": "13.2.0", - "ignore": "5.2.0", + "@angular-eslint/eslint-plugin": "15.2.1", + "@angular-eslint/eslint-plugin-template": "15.2.1", + "ignore": "5.2.4", "strip-json-comments": "3.1.1", "tmp": "0.2.1" }, "peerDependencies": { - "@angular/cli": ">= 13.0.0 < 14.0.0" + "@angular/cli": ">= 15.0.0 < 16.0.0" } }, "node_modules/@angular-eslint/template-parser": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.2.0.tgz", - "integrity": "sha512-dv+bBFzvA4q/KiEDpO7BDUmB1EpVW0P0sX6OSs2eRzViEuXztkatQ4XOmgzuxP0+E1+zl/mK3F7uBDNXiaah9g==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-15.2.1.tgz", + "integrity": "sha512-ViCi79gC2aKJecmYLkOT+QlT5WMRNXeYz0Dr9Pr8qXzIbY0oAWE7nOT5jkXwQ9oUk+ybtGCWHma5JVJWVJsIog==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "eslint-scope": "^5.1.0" + "@angular-eslint/bundled-angular-compiler": "15.2.1", + "eslint-scope": "^7.0.0" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^7.20.0 || ^8.0.0", "typescript": "*" } }, "node_modules/@angular-eslint/utils": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-13.2.0.tgz", - "integrity": "sha512-ywkk+pVLaDLwzNKUcc/xWtJC4Bkm+qRrMOR8ZX3q84E5RTvIvc8IcPFBE4ey3lnGe+nE44OEGuLedCo9vn1Meg==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-15.2.1.tgz", + "integrity": "sha512-++FneAJHxJqcSu0igVN6uOkSoHxlzgLoMBswuovYJy3UKwm33/T6WFku8++753Ca/JucIoR1gdUfO7SoSspMDg==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "13.2.0", - "@typescript-eslint/experimental-utils": "5.17.0" + "@angular-eslint/bundled-angular-compiler": "15.2.1", + "@typescript-eslint/utils": "5.48.2" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^7.20.0 || ^8.0.0", "typescript": "*" } }, - "node_modules/@angular-slider/ngx-slider": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@angular-slider/ngx-slider/-/ngx-slider-2.0.4.tgz", - "integrity": "sha512-EccMcGlb2bJJWikXZBjRwdWgRiYYmWd3UCgf8l3KAlyqPAxPVgxI73wqipp4/nZwidq53a9s3OB+KC79enfM2A==", - "dependencies": { - "detect-passive-events": "^2.0.3", - "rxjs": "^6.5.2", - "tslib": "^1.9.0" - }, - "peerDependencies": { - "@angular/common": ">=6.1.0", - "@angular/core": ">=6.1.0", - "@angular/forms": ">=6.1.0" - } - }, - "node_modules/@angular-slider/ngx-slider/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/@angular/animations": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.2.7.tgz", - "integrity": "sha512-FthGqRPQ1AOcOx/NIW65xeFYkQZJ7PpXcX59Kt+qkoUzngAQEY+UUpOteG52tmL0iZSVwOCjtxRFi9w4heVgEg==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-15.2.9.tgz", + "integrity": "sha512-GQujLhI0cQFcl4Q8y0oSYKSRnW23GIeSL+Arl4eFufziJ9hGAAQNuesaNs/7i+9UlTHDMkPH3kd5ScXuYYz6wg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/core": "13.2.7" + "@angular/core": "15.2.9" } }, - "node_modules/@angular/animations/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/cdk": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.2.6.tgz", - "integrity": "sha512-epuXmaHqfukwPsYvIksbuHLXDtb6GALV2Vgv6W2asj4TorD584CeQTs0EcdPGmCzhGUYI8U8QV63WOxu9YFcNA==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-15.2.9.tgz", + "integrity": "sha512-koaM07N1AIQ5oHU27l0/FoQSSoYAwlAYwVZ4Di3bYrJsTBNCN2Xsby7wI8gZxdepMnV4Fe9si382BDBov+oO4Q==", "dependencies": { "tslib": "^2.3.0" }, "optionalDependencies": { - "parse5": "^5.0.0" + "parse5": "^7.1.2" }, "peerDependencies": { - "@angular/common": "^13.0.0 || ^14.0.0-0", - "@angular/core": "^13.0.0 || ^14.0.0-0", + "@angular/common": "^15.0.0 || ^16.0.0", + "@angular/core": "^15.0.0 || ^16.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/cdk/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/cli": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.2.6.tgz", - "integrity": "sha512-xIjEaQI5sWemXXc7GXLm4u9UL5sjtrQL/y1PJvvk/Jsa8+kIT+MutOfZfC7zcdAh9fqHd8mokH3guFV8BJdFxA==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-15.2.9.tgz", + "integrity": "sha512-mI6hkGyIJDKd8MRiBl3p5chsUhgnluwmpsq3g1FFPw+wv+eXsPYgCiHqXS/OsK+shFxii9XMxoZQO28bJ4NAOQ==", "dev": true, - "hasInstallScript": true, "dependencies": { - "@angular-devkit/architect": "0.1302.6", - "@angular-devkit/core": "13.2.6", - "@angular-devkit/schematics": "13.2.6", - "@schematics/angular": "13.2.6", + "@angular-devkit/architect": "0.1502.9", + "@angular-devkit/core": "15.2.9", + "@angular-devkit/schematics": "15.2.9", + "@schematics/angular": "15.2.9", "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.3.3", - "ini": "2.0.0", - "inquirer": "8.2.0", - "jsonc-parser": "3.0.0", - "npm-package-arg": "8.1.5", - "npm-pick-manifest": "6.1.1", - "open": "8.4.0", + "ansi-colors": "4.1.3", + "ini": "3.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.2.0", + "npm-package-arg": "10.1.0", + "npm-pick-manifest": "8.0.1", + "open": "8.4.1", "ora": "5.4.1", - "pacote": "12.0.3", - "resolve": "1.22.0", - "semver": "7.3.5", + "pacote": "15.1.0", + "resolve": "1.22.1", + "semver": "7.5.3", "symbol-observable": "4.0.0", - "uuid": "8.3.2" + "yargs": "17.6.2" }, "bin": { "ng": "bin/ng.js" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular/common": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.2.7.tgz", - "integrity": "sha512-gSkv9aMz5q2ynIqSwgp5HEVVwlmpMYGVZFNZnEnezGY96Hza0eXlb/AYdqO7S3VQVvx+FXpvXP/eq/SsCw7rFA==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-15.2.9.tgz", + "integrity": "sha512-LM9/UHG2dRrOzlu2KovrFwWIziFMjRxHzSP3Igw6Symw/wIl0kXGq8Fn6RpFP78zmLqnv+IQOoRiby9MCXsI4g==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/core": "13.2.7", + "@angular/core": "15.2.9", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/common/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/compiler": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.2.7.tgz", - "integrity": "sha512-auRMo+k+xCQmIBkZ5UgkAAmhbpcoOUWQrJN2PQnPl88DPquui3tXC4R6RANpWCu59oT8m29FQMviHcN3ZTFl6Q==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-15.2.9.tgz", + "integrity": "sha512-MoKugbjk+E0wRBj12uvIyDLELlVLonnqjA2+XiF+7FxALIeyds3/qQeEoMmYIqAbN3NnTT5pV92RxWwG4tHFwA==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "15.2.9" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } } }, "node_modules/@angular/compiler-cli": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.7.tgz", - "integrity": "sha512-EZFWHyC2PO3ECEgX/WTMaTEvH4isvtw8E/l+48YHvINeHoxPGF7Or8qEeu/lvGlXMzBd89QH1ohutfz93vNz+g==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-15.2.9.tgz", + "integrity": "sha512-zsbI8G2xHOeYWI0hjFzrI//ZhZV9il/uQW5dAimfwJp06KZDeXZ3PdwY9JQslf6F+saLwOObxy6QMrIVvfjy9w==", "dev": true, "dependencies": { - "@babel/core": "^7.17.2", + "@babel/core": "7.19.3", + "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", "dependency-graph": "^0.11.0", - "magic-string": "^0.26.0", + "magic-string": "^0.27.0", "reflect-metadata": "^0.1.2", "semver": "^7.0.0", - "sourcemap-codec": "^1.4.8", "tslib": "^2.3.0", "yargs": "^17.2.1" }, @@ -695,226 +836,144 @@ "ngcc": "bundles/ngcc/main-ngcc.js" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/compiler": "13.2.7", - "typescript": ">=4.4.2 <4.6" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" + "@angular/compiler": "15.2.9", + "typescript": ">=4.8.2 <5.0" } }, "node_modules/@angular/compiler-cli/node_modules/magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.13" }, "engines": { "node": ">=12" } }, - "node_modules/@angular/compiler-cli/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/@angular/compiler/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/core": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.2.7.tgz", - "integrity": "sha512-6J6C2ymIy+cZudock25BLEZFckhqFfXOSZw5YBIoAJuntV4hckLNfRTn/dxn5qmWF/Vw60NKdhV367YYejz6Gg==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-15.2.9.tgz", + "integrity": "sha512-w46Z1yUXCQfKV7XfnamOoLA2VD0MVUUYVrUjO73mHSskDXSXxfZAEHO9kfUS71Cj35PvhP3mbkqWscpea2WeYg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4" - } - }, - "node_modules/@angular/core/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/@angular/flex-layout": { - "version": "13.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-13.0.0-beta.38.tgz", - "integrity": "sha512-kcWb7CcoHbvw7fjo/knizWVmSSmvaTnr8v1ML6zOdxu1PK9UPPOcOS8RTm6fy61zoC2LABivP1/6Z2jF5XfpdQ==", - "deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/cdk": "^13.0.0", - "@angular/common": "^13.0.0", - "@angular/core": "^13.0.0", - "@angular/platform-browser": "^13.0.0", - "rxjs": "^6.5.3 || ^7.4.0" + "zone.js": "~0.11.4 || ~0.12.0 || ~0.13.0" } }, - "node_modules/@angular/flex-layout/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/forms": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.2.7.tgz", - "integrity": "sha512-Did5ShmHTu52cljmNtMxUBUGEYHJ/FV5ZpKhbI7sd/VSFhXp9KWYUbfma0m7+CUJMGmpt6bmDaN0G2WS8Es1LQ==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-15.2.9.tgz", + "integrity": "sha512-sk0pC2EFi2Ohg5J0q0NYptbT+2WOkoiERSMYA39ncDvlSZBWsNlxpkbGUSck7NIxjK2QfcVN1ldGbHlZTFvtqg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/common": "13.2.7", - "@angular/core": "13.2.7", - "@angular/platform-browser": "13.2.7", + "@angular/common": "15.2.9", + "@angular/core": "15.2.9", + "@angular/platform-browser": "15.2.9", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/forms/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/language-service": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-13.2.7.tgz", - "integrity": "sha512-hkZ1du/XxG6efri3FSOPc7JBwWHgHUAwI8dAUC+xFL3ZcVhFMPwrouMo8y4w/P5RctCO0ls4dDxThn/uDEZBQA==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-15.2.9.tgz", + "integrity": "sha512-B7lP4q/eHge2lZezOXS96EYzVf4stMCWfOnz7+pUUi0HbF+A5QCV65SWQddS/M+NM2jj8N2L/j+6UCH8lJjTQA==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" } }, "node_modules/@angular/material": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-13.2.6.tgz", - "integrity": "sha512-/h5wa/tXE0DMIIEQX+rozFkUWHYUWg1Xf1R2tXUFLslLQ0KRCGyNo225Sv/1wrxXHxfrML787lA9ex4p90Feqw==", - "dependencies": { + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-15.2.9.tgz", + "integrity": "sha512-emuFF/7+91Jq+6kVCl3FiVoFLtAZoh+woFQWNuK8nhx0HmD4ckLFI8d9a6ERYR3zRuKhq5deSRE2kYsfpjrrsQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/auto-init": "15.0.0-canary.684e33d25.0", + "@material/banner": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/button": "15.0.0-canary.684e33d25.0", + "@material/card": "15.0.0-canary.684e33d25.0", + "@material/checkbox": "15.0.0-canary.684e33d25.0", + "@material/chips": "15.0.0-canary.684e33d25.0", + "@material/circular-progress": "15.0.0-canary.684e33d25.0", + "@material/data-table": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dialog": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/drawer": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/fab": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/floating-label": "15.0.0-canary.684e33d25.0", + "@material/form-field": "15.0.0-canary.684e33d25.0", + "@material/icon-button": "15.0.0-canary.684e33d25.0", + "@material/image-list": "15.0.0-canary.684e33d25.0", + "@material/layout-grid": "15.0.0-canary.684e33d25.0", + "@material/line-ripple": "15.0.0-canary.684e33d25.0", + "@material/linear-progress": "15.0.0-canary.684e33d25.0", + "@material/list": "15.0.0-canary.684e33d25.0", + "@material/menu": "15.0.0-canary.684e33d25.0", + "@material/menu-surface": "15.0.0-canary.684e33d25.0", + "@material/notched-outline": "15.0.0-canary.684e33d25.0", + "@material/radio": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/segmented-button": "15.0.0-canary.684e33d25.0", + "@material/select": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/slider": "15.0.0-canary.684e33d25.0", + "@material/snackbar": "15.0.0-canary.684e33d25.0", + "@material/switch": "15.0.0-canary.684e33d25.0", + "@material/tab": "15.0.0-canary.684e33d25.0", + "@material/tab-bar": "15.0.0-canary.684e33d25.0", + "@material/tab-indicator": "15.0.0-canary.684e33d25.0", + "@material/tab-scroller": "15.0.0-canary.684e33d25.0", + "@material/textfield": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tooltip": "15.0.0-canary.684e33d25.0", + "@material/top-app-bar": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/animations": "^13.0.0 || ^14.0.0-0", - "@angular/cdk": "13.2.6", - "@angular/common": "^13.0.0 || ^14.0.0-0", - "@angular/core": "^13.0.0 || ^14.0.0-0", - "@angular/forms": "^13.0.0 || ^14.0.0-0", - "@angular/platform-browser": "^13.0.0 || ^14.0.0-0", + "@angular/animations": "^15.0.0 || ^16.0.0", + "@angular/cdk": "15.2.9", + "@angular/common": "^15.0.0 || ^16.0.0", + "@angular/core": "^15.0.0 || ^16.0.0", + "@angular/forms": "^15.0.0 || ^16.0.0", + "@angular/platform-browser": "^15.0.0 || ^16.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/material/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/platform-browser": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.2.7.tgz", - "integrity": "sha512-3rpeS2n+mfey9FqJg/NQKPiyHC47vgldWXmuz5FmOCHrOY54AaFfoiwQcdxzh6Lxx/CUVm0TlOS8S/xI9iEqXw==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-15.2.9.tgz", + "integrity": "sha512-ufCHeSX+U6d43YOMkn3igwfqtlozoCXADcbyfUEG8m2y9XASobqmCKvdSk/zfl62oyiA8msntWBJVBE2l4xKXg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/animations": "13.2.7", - "@angular/common": "13.2.7", - "@angular/core": "13.2.7" + "@angular/animations": "15.2.9", + "@angular/common": "15.2.9", + "@angular/core": "15.2.9" }, "peerDependenciesMeta": { "@angular/animations": { @@ -923,54 +982,39 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.2.7.tgz", - "integrity": "sha512-3tKiUohQ8wl4hp1zYLKvMJ7GVYpg2K5dRrihtUKkJk8xUv3iuTUI0wbNCrUDZkrWc0GMhnQNXwE22gd+hKjfKg==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-15.2.9.tgz", + "integrity": "sha512-ZIYDM6MShblb8OyV1m4+18lJJ2LCeICmeg2uSbpFYptYBSOClrTiYOOFVDJvn7HLvNzljLs16XPrgyaYVqNpcw==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/common": "13.2.7", - "@angular/compiler": "13.2.7", - "@angular/core": "13.2.7", - "@angular/platform-browser": "13.2.7" + "@angular/common": "15.2.9", + "@angular/compiler": "15.2.9", + "@angular/core": "15.2.9", + "@angular/platform-browser": "15.2.9" } }, - "node_modules/@angular/platform-browser-dynamic/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/@angular/platform-browser/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@angular/router": { - "version": "13.2.7", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.2.7.tgz", - "integrity": "sha512-VzEFKyUE8CR23IbmAjmcSFY6pa4NsjaaTqT4mDYhzFeYc7R0s58Ow9d4Fy+0sWX6rzys01rcVNCg+ifJAnwYZA==", + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-15.2.9.tgz", + "integrity": "sha512-UCbh5DLSDhybv0xKYT7kGQMfOVdyhHIHOZz5EYVebbhste6S+W1LE57vTHq7QtxJsyKBa/WSkaUkCLXD6ntCAg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/common": "13.2.7", - "@angular/core": "13.2.7", - "@angular/platform-browser": "13.2.7", + "@angular/common": "15.2.9", + "@angular/core": "15.2.9", + "@angular/platform-browser": "15.2.9", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/router/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/@assemblyscript/loader": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", @@ -978,47 +1022,47 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", + "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", - "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", + "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.3", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.3", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.3", + "@babel/types": "^7.19.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "json5": "^2.2.1", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -1037,74 +1081,56 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", - "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", "dev": true, "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", + "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "@babel/compat-data": "^7.22.6", + "@babel/helper-validator-option": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1" }, "engines": { "node": ">=6.9.0" @@ -1113,29 +1139,21 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", - "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz", + "integrity": "sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@nicolo-ribaudo/semver-v6": "^6.3.3" }, "engines": { "node": ">=6.9.0" @@ -1145,24 +1163,25 @@ } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", - "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz", + "integrity": "sha512-nBookhLKxAWo/TUCmhnaEJyLz2dekjQvv5SRpE9epWQBcpedWLKt8aZdsuT9XV5ovzR3fENLjRXVT0GsSlGGhA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-annotate-as-pure": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", "regexpu-core": "^5.3.1" }, "engines": { @@ -1173,12 +1192,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1211,153 +1230,113 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", - "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", - "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", "dev": true, "dependencies": { - "@babel/types": "^7.21.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", + "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1367,175 +1346,133 @@ } }, "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", - "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", + "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.20.7", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, "dependencies": { - "@babel/types": "^7.20.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", + "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -1544,9 +1481,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", + "integrity": "sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1556,12 +1493,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1571,14 +1508,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1588,13 +1525,14 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -1802,9 +1740,9 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", @@ -1819,18 +1757,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", @@ -1910,6 +1836,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -2025,12 +1966,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", - "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2040,14 +1981,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -2057,12 +1998,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2072,12 +2013,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2087,19 +2028,19 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, "engines": { @@ -2110,25 +2051,25 @@ } }, "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", - "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/template": "^7.20.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2137,27 +2078,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", - "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2167,13 +2094,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2183,12 +2110,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2198,13 +2125,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2214,12 +2141,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", - "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2229,14 +2156,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2246,12 +2173,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2261,12 +2188,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2276,13 +2203,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2292,14 +2219,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", - "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-simple-access": "^7.20.2" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2309,15 +2236,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2327,13 +2254,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2343,13 +2270,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2359,12 +2286,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2374,13 +2301,30 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -2390,12 +2334,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", - "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2405,12 +2349,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2420,12 +2364,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", - "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.1" }, "engines": { @@ -2436,12 +2380,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2451,16 +2395,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", - "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" }, "engines": { @@ -2480,12 +2424,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2495,13 +2439,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2511,12 +2455,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2526,12 +2470,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2541,12 +2485,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2556,12 +2500,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", - "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2571,13 +2515,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2587,37 +2531,38 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", - "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -2627,44 +2572,44 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "engines": { @@ -2706,64 +2651,45 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz", - "integrity": "sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "dev": true, "dependencies": { - "core-js-pure": "^3.25.1", "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.6.tgz", + "integrity": "sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.6", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2771,43 +2697,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2823,40 +2720,10 @@ "node": ">=0.1.90" } }, - "node_modules/@cordobo/qrcode": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@cordobo/qrcode/-/qrcode-1.5.0.tgz", - "integrity": "sha512-aZ5n3MYw10t4v68EGvRGE1DL7iWfAiTUy4MSZRoqjHTRYdjX40sYgJf48NZa6zZeXVuJOEB/1Ni9KzS+C/EC0w==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^17.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, "node_modules/@csstools/selector-specificity": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", - "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", "dev": true, "engines": { "node": "^14 || ^16 || >=18" @@ -2866,45 +2733,420 @@ "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.4", "postcss-selector-parser": "^6.0.10" } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", - "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, "engines": { "node": ">=10.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", - "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", + "node_modules/@esbuild/android-arm": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.8.tgz", + "integrity": "sha512-0/rb91GYKhrtbeglJXOhAv9RuYimgI8h623TplY2X+vA4EXnk3Zj1fXZreJ0J3OJJu1bwmb0W7g+2cT/d8/l/w==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=12" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "node_modules/@esbuild/android-arm64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.8.tgz", + "integrity": "sha512-oa/N5j6v1svZQs7EIRPqR8f+Bf8g6HBDjD/xHC02radE/NjKHK7oQmtmLxPs1iVwYyvE+Kolo6lbpfEQ9xnhxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.8.tgz", + "integrity": "sha512-bTliMLqD7pTOoPg4zZkXqCDuzIUguEWLpeqkNfC41ODBHwoUgZ2w5JBeYimv4oP6TDVocoYmEhZrCLQTrH89bg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.8.tgz", + "integrity": "sha512-ghAbV3ia2zybEefXRRm7+lx8J/rnupZT0gp9CaGy/3iolEXkJ6LYRq4IpQVI9zR97ID80KJVoUlo3LSeA/sMAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.8.tgz", + "integrity": "sha512-n5WOpyvZ9TIdv2V1K3/iIkkJeKmUpKaCTdun9buhGRWfH//osmUjlv4Z5mmWdPWind/VGcVxTHtLfLCOohsOXw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.8.tgz", + "integrity": "sha512-a/SATTaOhPIPFWvHZDoZYgxaZRVHn0/LX1fHLGfZ6C13JqFUZ3K6SMD6/HCtwOQ8HnsNaEeokdiDSFLuizqv5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.8.tgz", + "integrity": "sha512-xpFJb08dfXr5+rZc4E+ooZmayBW6R3q59daCpKZ/cDU96/kvDM+vkYzNeTJCGd8rtO6fHWMq5Rcv/1cY6p6/0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.8.tgz", + "integrity": "sha512-6Ij8gfuGszcEwZpi5jQIJCVIACLS8Tz2chnEBfYjlmMzVsfqBP1iGmHQPp7JSnZg5xxK9tjCc+pJ2WtAmPRFVA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.8.tgz", + "integrity": "sha512-v3iwDQuDljLTxpsqQDl3fl/yihjPAyOguxuloON9kFHYwopeJEf1BkDXODzYyXEI19gisEsQlG1bM65YqKSIww==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.8.tgz", + "integrity": "sha512-8svILYKhE5XetuFk/B6raFYIyIqydQi+GngEXJgdPdI7OMKUbSd7uzR02wSY4kb53xBrClLkhH4Xs8P61Q2BaA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.8.tgz", + "integrity": "sha512-B6FyMeRJeV0NpyEOYlm5qtQfxbdlgmiGdD+QsipzKfFky0K5HW5Td6dyK3L3ypu1eY4kOmo7wW0o94SBqlqBSA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.8.tgz", + "integrity": "sha512-CCb67RKahNobjm/eeEqeD/oJfJlrWyw29fgiyB6vcgyq97YAf3gCOuP6qMShYSPXgnlZe/i4a8WFHBw6N8bYAA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.8.tgz", + "integrity": "sha512-bytLJOi55y55+mGSdgwZ5qBm0K9WOCh0rx+vavVPx+gqLLhxtSFU0XbeYy/dsAAD6xECGEv4IQeFILaSS2auXw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.8.tgz", + "integrity": "sha512-2YpRyQJmKVBEHSBLa8kBAtbhucaclb6ex4wchfY0Tj3Kg39kpjeJ9vhRU7x4mUpq8ISLXRXH1L0dBYjAeqzZAw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.8.tgz", + "integrity": "sha512-QgbNY/V3IFXvNf11SS6exkpVcX0LJcob+0RWCgV9OiDAmVElnxciHIisoSix9uzYzScPmS6dJFbZULdSAEkQVw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.8.tgz", + "integrity": "sha512-mM/9S0SbAFDBc4OPoyP6SEOo5324LpUxdpeIUUSrSTOfhHU9hEfqRngmKgqILqwx/0DVJBzeNW7HmLEWp9vcOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.8.tgz", + "integrity": "sha512-eKUYcWaWTaYr9zbj8GertdVtlt1DTS1gNBWov+iQfWuWyuu59YN6gSEJvFzC5ESJ4kMcKR0uqWThKUn5o8We6Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.8.tgz", + "integrity": "sha512-Vc9J4dXOboDyMXKD0eCeW0SIeEzr8K9oTHJU+Ci1mZc5njPfhKAqkRt3B/fUNU7dP+mRyralPu8QUkiaQn7iIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.8.tgz", + "integrity": "sha512-0xvOTNuPXI7ft1LYUgiaXtpCEjp90RuBBYovdd2lqAFxje4sEucurg30M1WIm03+3jxByd3mfo+VUmPtRSVuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.8.tgz", + "integrity": "sha512-G0JQwUI5WdEFEnYNKzklxtBheCPkuDdu1YrtRrjuQv30WsYbkkoixKxLLv8qhJmNI+ATEWquZe/N0d0rpr55Mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.8.tgz", + "integrity": "sha512-Fqy63515xl20OHGFykjJsMnoIWS+38fqfg88ClvPXyDbLtgXal2DTlhb1TfTX34qWi3u4I7Cq563QcHpqgLx8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.8.tgz", + "integrity": "sha512-1iuezdyDNngPnz8rLRDO2C/ZZ/emJLb72OsZeqQ6gL6Avko/XCXZw+NuxBSNhBAP13Hie418V7VMt9et1FMvpg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { @@ -2918,12 +3160,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -2939,36 +3175,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2982,24 +3194,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", - "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true - }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -3010,18 +3216,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -3041,6 +3235,102 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3057,6 +3347,89 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -3067,13 +3440,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -3098,857 +3472,1038 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, - "node_modules/@jridgewell/sourcemap-codec": { + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "node_modules/@material/animation": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-5osi1z4JQIXcklPALbH/zTfOm2pDzHt9Fxm7ZyURy250xIZj6QjULRzPTnzOhC2ropfix9ra2Cfggbf0dcRbEQ==", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "tslib": "^2.1.0" } }, - "node_modules/@ngtools/webpack": { - "version": "13.3.10", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.10.tgz", - "integrity": "sha512-QQ8ELLqW5PtvrEAMt99D0s982NW303k8UpZrQoQ9ODgnSVDMdbbzFPNTXq/20dg+nbp8nlOakUrkjB47TBwTNA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^13.0.0", - "typescript": ">=4.4.3 <4.7", - "webpack": "^5.30.0" + "node_modules/@material/auto-init": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-OigQTmrVzkcGvxNjOaIe5oItTFPgrO9xLewvharDI6m6yvO1z7OBnkcW+sFN6ggLNYNxd0O1u9v64vMsmeDABQ==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/banner": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-PqtGp3KWzdu58rWv/DIvSfe38m5YKOBbAAbBinSvgadBb/da+IE1t5F7YPNKE1T5lJsQBGVUYx6QBIeXm+aI/A==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/button": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/base": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-oOaqb/SfjWwTKsdJUZmeh/Qrs41nIJI0N+zELsxnvbGjSIN1ZMAKYZFPMahqvC68OJ6+5CvJM8PoTNs5l+B8IQ==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/button": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-Nkekk4edeX+ObVOa7UlwavaHdmckPV5wU4SAJf3iA3R61cmz+KsgAgpzfcwv5WfNhIlc2nLu8QYEecpHdo9d/w==", + "dependencies": { + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/card": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-xhyB7XX5KkEiCEqwSPkl58ZGYL6xFdnY62zimyBXJRG/Eaa0Swj3kW20hVCpt4f7c9Zmp8Se27rg8vnKmhvO3g==", + "dependencies": { + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/checkbox": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-NFpM3TS924PmVsk2KQLNU95OYCf8ZwYgzeqfnAexU0bEfjUJXINBun2Go0AaeOUMjuvWUe+byjrXgv8SFYbMUA==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/chips": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-z4ajQ4NnsAQ/Si9tZ4xmxzjj2Qb+vW++4QjCjjjwAGIZbCe0xglAnMh2t66XLJUxt7RoKZuZVEO7ZqcFZpvJFQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/checkbox": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@material/circular-progress": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-G6qD0nGNtEUwWnAMJuA9INYFpZoKtx7KFjBaPF4Ol2YLHtmShALNAYyn54TMAK8AZ2IpW08PXjGS7Ye88vrdEQ==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/progress-indicator": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" + "node_modules/@material/data-table": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-+wDw1DDDFfAsKAMzs84f/5GCjux39zjNfW8tL4wFbkWNwewmQrG9zaQMJhBpVOtLCrM8Gj6SOgOANqgqoCjvGg==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/checkbox": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/icon-button": "15.0.0-canary.684e33d25.0", + "@material/linear-progress": "15.0.0-canary.684e33d25.0", + "@material/list": "15.0.0-canary.684e33d25.0", + "@material/menu": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/select": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@material/density": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-661yEVRMGrlq6S6WuSbPRO+ZwpdUOg2glCc7y96doM6itSLOa3UEAldjOLfsYZVB74GnKCiuDp//QmfoRyYTfA==", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "dev": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" + "node_modules/@material/dialog": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-szn0dHnfeQTSOC6SSRSGAzX6Tnx+4NnSMUwNkXm+3bwjds8ZVK26+DXwLrP5f3ID5F1K5sFsRf2INo5/TNTHyQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/button": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/icon-button": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/git": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", - "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", - "dev": true, + "node_modules/@material/dom": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-7pEJLYov+tGgfuD8mZxoVU6rWtPI8ppjTAhz+F27Hz9FG0JETMWTKpDPBXLnKvX7vhIxL83GvZ9geNHCe8Hfog==", "dependencies": { - "@npmcli/promise-spawn": "^1.3.2", - "lru-cache": "^6.0.0", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^6.1.1", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, + "node_modules/@material/drawer": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-/KMckLf1PYU/H3PXnS4e0aFl03qG3JlSv4LGgX6juJufcONqGTl/m63EMO/L/eUy6H1CRrXmVDjik/jzHLyDhg==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/list": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/elevation": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-WDF8SsRtq3rXUbVVbd9K4DUijIPH0bUFSOreVYxudpuxAfTlDS5+aeS1EK9UIBFYLuba4u5wVT2tDv6e1RTfrQ==", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/git/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/@material/fab": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-KCu87rWOKEAe9vZcAm6K8XazYSWPNjMG+OhrbPjHW6bCO7as1YCgtmkBkhff7csY/rFmcVpIy884xtUfLmSudQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } }, - "node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", - "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", - "dev": true, + "node_modules/@material/feature-targeting": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-HyH1erNTSjS63sigNSUMaCd0nJhTNdDFeC+myrxwtDaQm+uYJ8troCNtQM3g6mx0XATNtX5aTOoPmrM6yVVi1A==", "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, + "node_modules/@material/floating-label": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-f7TPp6bKpGvV3sYYiZHSGlrixXKkXXITW3Esp7KB9jRq42c0H82novmdwvY0eTef4ootmA2JEysr78KQfHBUPg==", "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/node-gyp": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", - "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", - "dev": true + "node_modules/@material/focus-ring": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-ikw2RVUfgzXChpWIzPH1VzRvTjYb5ZKj4H+CZf7jqPUXMstFOZg90Bp7ARLZHqYiyNMuUq3zUTHozS6iHorSqg==", + "dependencies": { + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0" + } }, - "node_modules/@npmcli/promise-spawn": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", - "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", - "dev": true, + "node_modules/@material/form-field": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-vpF9N/uq5no/7+8GAbEH0868FhOuBgxAWRr1Sfb+jthKfBr8OS/wPU/AHzZHdHdAm7PQynbeOXfDsX2dI//PDA==", "dependencies": { - "infer-owner": "^1.0.4" + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/run-script": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", - "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", - "dev": true, + "node_modules/@material/icon-button": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-wMI+XGzmIN/o2ePBKg2hLyx7H4pXCRAyyIKMQS1FMp1UKa2tYmiHVX/V8skhKwCqxg3i6Ls/LxMjfPxTR18WvQ==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/image-list": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-Ol+uaHYBe5R/cgzlfh5ONnMVX0wO6fV74JMUcQCQlxP6lXau/edARo4tkRc7A7UJUkU3VRv0EpEjLoCRNUPGaA==", "dependencies": { - "@npmcli/node-gyp": "^1.0.2", - "@npmcli/promise-spawn": "^1.3.2", - "node-gyp": "^8.2.0", - "read-package-json-fast": "^2.0.1" + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-15.8.5.tgz", - "integrity": "sha512-voy16nUO1MxRMRqCpLlhDB9U4KyPfGHZABXtfMEIQk+W3alncatFMMSVvMQZmi8HXwubM8LxWSOnPtTtOCKBrQ==", - "dev": true, + "node_modules/@material/layout-grid": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-ALXE1mqFNb/RB2lVRQ3/r1Aufw2mFZnOjRE+boYDVepmAG/xWyPCyaGoavELJF5l4GAb0tXi8wA/8HeGbLOpuA==", "dependencies": { - "nx": "15.8.5" + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/@nrwl/tao": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-15.8.5.tgz", - "integrity": "sha512-pb/hUprOOv2vgvbevGz9hiu8LLOtK7KKuBe4JLSXrFxfHEQjMFsK/2aymnts0ZQrA83QlIG2Mr0tuSKj6/iWvg==", - "dev": true, + "node_modules/@material/line-ripple": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-7hRx8C/e9i0P6pgQpNOMfTwSS2r1fwEvBL72QDVGLtLuoKKwsjjgP6Z0Jat/GeHJe87u9LQvGBoD4upt+of/HA==", "dependencies": { - "nx": "15.8.5" - }, - "bin": { - "tao": "index.js" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/@material/linear-progress": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-iJclt7mKmcMk6pqD7ocXKfCWZhqBoODp7N593jYlxVpTJuEz2wiVAjZUDn/YGj/Uz3CRH+2YFfOiLr9pwWjhDg==", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/progress-indicator": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/@material/list": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-rQ+FCSdzmwTcT00IYE0uRV3CS4oGSccKFl9hkcF+aHFW61L7ORh/SCGUDPrEfQFrFkMn5f8qroVJjpUAMXBz4g==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } }, - "node_modules/@nrwl/cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node_modules/@material/menu": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-r7wzDLSGSI9629/mfpvsMzkVxpmV75kcD3IrW0Pcu6/Bv/1xi0EvjcUXzNJJoQlwN4Zj35Ymz/PCjZkIDIz68Q==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/list": "15.0.0-canary.684e33d25.0", + "@material/menu-surface": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/@material/menu-surface": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-RVO5GAYcfWPaKwxsF/NhUAmrYXQCQBKvRQW0TIlbmAJz6lcFeTs6YZqF3u1C7qrL3ZQGz+sur/7ywj6QU0oMow==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "node_modules/@material/notched-outline": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-9YHcBkvJLPVYzkHcWoTpBZAFrEd+j1hjhGxLhh0LuNrZe8VroUkZD1TTnUAPHRG3os6EqEWWaKb0RN+aPIF2yQ==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/floating-label": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/@material/progress-indicator": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-c0icji4faeNWUoqGENGC7Hav0Puxh0RwXIDVizffaUxKIGbajpIp5+4Zop73fK/xFLGMB/npg7TbP+aCGjQ3fw==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/@material/radio": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-U3Eh8sNUA8trDla1Bq8Bo02foxYvtoewaKeF8A8tAju81XZ4jRiftfOsOWZDZEHCVbbCB2QwvutvFlnay5n+Aw==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } }, - "node_modules/@nrwl/cli/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, + "node_modules/@material/ripple": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-RyePu7SjIm/OuyyEieZ/gxiPYkNZOZHeid72WRcN9ofdlljj2pifcdPvcfZA+v/DMS33xo5GjG2L/Qj6ClWrKw==", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, + "node_modules/@material/rtl": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-NqdJl8Ayupp1Th+vCNCpVQHbUFOuF7TCte9LD1norTIBUF/QizIxWby2W5uUEiPbnh5j9PmE1CJtfLwKun3pcw==", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, + "node_modules/@material/segmented-button": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-bEGgg8vgXNLyukyV8HRjFMuQ6t6nm5LQ4Pgm22um61Yc8qyi0BOqV41OR4SVdUrUqZxh1aVD+p+4NN03+LfQXw==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/touch-target": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/@material/select": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-kf178/2TeEinTv0mgmSBcmmExQ2h7a7dtR1E3WuqQgisJ/R6+zVLMkC2CnfIyzxYX2vkuUTG0ue3Reh/6XiqSg==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/floating-label": "15.0.0-canary.684e33d25.0", + "@material/line-ripple": "15.0.0-canary.684e33d25.0", + "@material/list": "15.0.0-canary.684e33d25.0", + "@material/menu": "15.0.0-canary.684e33d25.0", + "@material/menu-surface": "15.0.0-canary.684e33d25.0", + "@material/notched-outline": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "node_modules/@material/shape": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-aEelpaTFmpnCji3TUGP9bVCS/bRVjUmLTHBPZtuu1gOrUVVtJ6kYOg73dZNJF+XOoNL2yOX/LRcKwsop29tptA==", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true + "node_modules/@material/slider": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-WVyK+2pSNSZmj07M2K/a3TADoQ9FBCndfNC/vE7/wGIg4dddJJK5KvQ+yruf9R2cSzTL/S1sZ5WpyyeM8E9HTw==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } }, - "node_modules/@nrwl/cli/node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node_modules/@material/snackbar": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-itO+DCkOannZzR1/cCHcqAm7ifhuFvXmDItNoA8qLEcAyJDJJRkhpwj3XQ01yuo9gBFcSctp7Txt7e+Hncm/Jg==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/button": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/icon-button": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "node_modules/@material/switch": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-Jxi0gl92yvvZZsAPxvVHzXx2ga+T/djMow98jvEczmpUorWnAhgiCr9CsSSRoosahWyRB8NLZOxUQrACxvffjw==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/nx": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/nx/-/nx-15.8.5.tgz", - "integrity": "sha512-1c6Y3rPSzzlqQVJPo33Ej0HY/3t9ykeaPs074HpYxXH0+GU1BSIv/9EfXKQGvmBzjs5yAx6asGIv+H3QDrFt3A==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@nrwl/cli": "15.8.5", - "@nrwl/tao": "15.8.5", - "@parcel/watcher": "2.0.4", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "^3.0.0-rc.18", - "@zkochan/js-yaml": "0.0.6", - "axios": "^1.0.0", - "chalk": "^4.1.0", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^7.0.2", - "dotenv": "~10.0.0", - "enquirer": "~2.3.6", - "fast-glob": "3.2.7", - "figures": "3.2.0", - "flat": "^5.0.2", - "fs-extra": "^11.1.0", - "glob": "7.1.4", - "ignore": "^5.0.4", - "js-yaml": "4.1.0", - "jsonc-parser": "3.2.0", - "lines-and-columns": "~2.0.3", - "minimatch": "3.0.5", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "semver": "7.3.4", - "string-width": "^4.2.3", - "strong-log-transformer": "^2.1.0", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0", - "v8-compile-cache": "2.3.0", - "yargs": "^17.6.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "bin/nx.js" - }, - "optionalDependencies": { - "@nrwl/nx-darwin-arm64": "15.8.5", - "@nrwl/nx-darwin-x64": "15.8.5", - "@nrwl/nx-linux-arm-gnueabihf": "15.8.5", - "@nrwl/nx-linux-arm64-gnu": "15.8.5", - "@nrwl/nx-linux-arm64-musl": "15.8.5", - "@nrwl/nx-linux-x64-gnu": "15.8.5", - "@nrwl/nx-linux-x64-musl": "15.8.5", - "@nrwl/nx-win32-arm64-msvc": "15.8.5", - "@nrwl/nx-win32-x64-msvc": "15.8.5" - }, - "peerDependencies": { - "@swc-node/register": "^1.4.2", - "@swc/core": "^1.2.173" - }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true - }, - "@swc/core": { - "optional": true - } + "node_modules/@material/tab": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-WQL3wj9syHNcfe8KbgGGUcA34M8C/xZ+n0Fkkh8Kk6puVwaU+xqUNihsxPY6YzKpmh4PZ4oJaBdiN8zvFT1zqQ==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/focus-ring": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/tab-indicator": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-bar": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-SW/cMaDsIGGkM1ag3A7GJRlmr8eXmObWsvitQJzh6Azr5zzZtSI+GQygkMesAEE1gbpqOVN8d40rh3H7VVIAcA==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/tab": "15.0.0-canary.684e33d25.0", + "@material/tab-indicator": "15.0.0-canary.684e33d25.0", + "@material/tab-scroller": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, + "node_modules/@material/tab-indicator": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-kKICqSPqOlaf0lzaFFCmuOqPXJC+cK48Qmsc+m5o6fJhkmuZRCYpIwB2JeP+uZSOq/bTH+SrPtCtnVlgWg6ksA==", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/@material/tab-scroller": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-H6EU/TSiK/M2DyyORX5GEtXD9rKYxTMHC2VxsNWARPMFJGzgeW2ugYkFv+rKI1/c0bs0CJ4e+qFnOlBsQXZvyQ==", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/tab": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/cli/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/@nrwl/cli/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/@material/textfield": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-OvgpDXjvpyJTtAWskO69IDybFvDNzr9w2PN/Fk7yFm+uNVupaWz1Ew8lZ4gGslaTNSVmh2XcsvmzxcLINSiiNg==", + "dependencies": { + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/density": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/floating-label": "15.0.0-canary.684e33d25.0", + "@material/line-ripple": "15.0.0-canary.684e33d25.0", + "@material/notched-outline": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } }, - "node_modules/@nrwl/cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" + "node_modules/@material/theme": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-AZxaXXAvRKzAi20RlMxzt2U5UmkCWyv7DMWEBXsxtG5Tk54mi1HsbVUp3fxDPTlmL7Pq8p1/DESg/o7TgRCVlw==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/devkit": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-13.1.3.tgz", - "integrity": "sha512-TAAsZJvVc/obeH0rZKY6miVhyM2GHGb8qIWp9MAIdLlXf4VDcNC7rxwb5OrGVSwuTTjqGYBGPUx0yEogOOJthA==", - "dev": true, + "node_modules/@material/tokens": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-wVwbQOTCXDPKYPdHQHLr026y36MMFelID1CmbfRk6mSol4O8yE9U0fXcShfRDW8Qo5E3X31w9c2A6T3neJY7wQ==", "dependencies": { - "@nrwl/tao": "13.1.3", - "ejs": "^3.1.5", - "ignore": "^5.0.4", - "rxjs": "^6.5.4", - "semver": "7.3.4", - "tslib": "^2.0.0" + "@material/elevation": "15.0.0-canary.684e33d25.0" } }, - "node_modules/@nrwl/devkit/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, + "node_modules/@material/tooltip": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-dtm26QjxyQdinc8btgz6yys07b7bUW4FZgNF2EBPeGrICrPg7jf+JEvDziz5g8VMaTBQLOQRSCGy0MKuRlOjLw==", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/button": "15.0.0-canary.684e33d25.0", + "@material/dom": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/tokens": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/devkit/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, + "node_modules/@material/top-app-bar": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-1M+oupUxflfW7u81P1XlxoLZB8bLzwtpKofIfDNRbEsiKhlLTERJR3Yak3BGE9xakNMysAaBHlkb5MrN5bNPFw==", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "@material/animation": "15.0.0-canary.684e33d25.0", + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/elevation": "15.0.0-canary.684e33d25.0", + "@material/ripple": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/shape": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "@material/typography": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/devkit/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/@material/touch-target": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-zdE69Slg8+T7sTn1OwqZ6H7WBYac9mxJ/JlJqfTqthzIjZRcCxBSYymQJcDHjsrPnUojOtr9U4Tpm5YZ96TEkQ==", + "dependencies": { + "@material/base": "15.0.0-canary.684e33d25.0", + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/rtl": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" + } }, - "node_modules/@nrwl/nx-darwin-arm64": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.5.tgz", - "integrity": "sha512-/8yXbh1J3k85MAW/A6cDiPeEnbt66SE9BPnM2IPlGoZrXakQvAXEn+gsjQlvnP3q2EaEyv7e5+GA+8d+p6mT5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" + "node_modules/@material/typography": { + "version": "15.0.0-canary.684e33d25.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.684e33d25.0.tgz", + "integrity": "sha512-aVnvgMwcfNa/K4wujzpKDIxjGl2hbkEL+m+OKDSQqWYjKcP9QrbzCXJruJBqxrBoPRHLbqo47k5f9uT8raSgjw==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.684e33d25.0", + "@material/theme": "15.0.0-canary.684e33d25.0", + "tslib": "^2.1.0" } }, - "node_modules/@nrwl/nx-darwin-x64": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.5.tgz", - "integrity": "sha512-zEVoi0d+YChLrQMypoGFwu73t3YiD8UkXSozMtUEa2mg/se4e7jh+15tB6Te+Oq5aL0JKwQpr027GE4YtAmpLw==", - "cpu": [ - "x64" - ], + "node_modules/@ngtools/webpack": { + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-15.2.9.tgz", + "integrity": "sha512-nOXUGqKkAEMlCcrhkDwWDzcVdKNH7MNRUXfNzsFc9zdeR/5p3qt6SVMN7OOE3NREyI7P6nzARc3S+6QDBjf3Jg==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">= 10" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^15.0.0", + "typescript": ">=4.8.2 <5.0", + "webpack": "^5.54.0" } }, - "node_modules/@nrwl/nx-linux-arm-gnueabihf": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.5.tgz", - "integrity": "sha512-4C5wN0C7gQD6/lC9+UKUsB6mbHvowKhlaO529GIgtzrCLmfEh/LJ/CybnnKGpFEB/8Y5GpCa2uTWyA1XcPDzUw==", - "cpu": [ - "arm" - ], + "node_modules/@nicolo-ribaudo/semver-v6": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", + "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@nrwl/nx-linux-arm64-gnu": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.5.tgz", - "integrity": "sha512-SMQ+oIsyK75JiKeSMprmb8VXce6MLdfcS5GWWOihpoDWfUC9FoQHAu4X1OtxHbVTmJfoEOInJKAhPxXAi5obdw==", - "cpu": [ - "arm64" - ], + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/@nrwl/nx-linux-arm64-musl": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.5.tgz", - "integrity": "sha512-GVENjltZ17aJ6KOCibdBtLXQcGY5lpBqKolB9+rIYJvTWuV1k/uHOkYJDG7Vl70Rj46rC8K0Jp6BCpJHCv1ksQ==", - "cpu": [ - "arm64" - ], + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/@nrwl/nx-linux-x64-gnu": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.5.tgz", - "integrity": "sha512-AW8YjhZv3c+LRUoLvHLx4BZaDakQbPCPx70+c/uQyDkQP/ckYJc0gRjoZukolcI6+AvNcBhkI559RL9W4qb9iw==", - "cpu": [ - "x64" - ], + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/@nrwl/nx-linux-x64-musl": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.5.tgz", - "integrity": "sha512-m4Iy/pbzH0LTsADq/X+74nfVzm2Tt0zorOXXy/uQN4ozL/JNGVpwvxdOFxZ7e3RBXDX4u6awUzSE52Z2d2f0uA==", - "cpu": [ - "x64" - ], + "node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "semver": "^7.3.5" + }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/nx-win32-arm64-msvc": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.5.tgz", - "integrity": "sha512-4AT1PHo5At8AXvgE5XlQbimE0THeSji6J3XZ1UTqq7n3L26QicNdnZcaHGyL1ukMtXRIwT/yed+xu1PFkXF4QA==", - "cpu": [ - "arm64" - ], + "node_modules/@npmcli/git": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", + "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/nx-win32-x64-msvc": { - "version": "15.8.5", - "resolved": "https://registry.npmjs.org/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.5.tgz", - "integrity": "sha512-53vzsQErvN4OeF/qBgfPg6OZ3smX4V8Lza59bwql9aAjjlMe1Ff9Su0BgAqlhVfSiYGxAirfHljgA6aWFqpCHQ==", - "cpu": [ - "x64" - ], + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 10" + "node": ">=12" } }, - "node_modules/@nrwl/tao": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-13.1.3.tgz", - "integrity": "sha512-/IwJgSgCBD1SaF+n8RuXX2OxDAh8ut/+P8pMswjm8063ac30UlAHjQ4XTYyskLH8uoUmNi2hNaGgHUrkwt7tQA==", + "node_modules/@npmcli/git/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", "dev": true, "dependencies": { - "chalk": "4.1.0", - "enquirer": "~2.3.6", - "fs-extra": "^9.1.0", - "jsonc-parser": "3.0.0", - "nx": "13.1.3", - "rxjs": "^6.5.4", - "rxjs-for-await": "0.0.2", - "semver": "7.3.4", - "tmp": "~0.2.1", - "tslib": "^2.0.0", - "yargs-parser": "20.0.0" + "isexe": "^2.0.0" }, "bin": { - "tao": "index.js" + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz", + "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" }, - "engines": { - "node": ">=8" + "bin": { + "installed-package-contents": "lib/index.js" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", + "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "which": "^3.0.0" }, "engines": { - "node": ">=7.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@nrwl/tao/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@npmcli/run-script": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", + "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "isexe": "^2.0.0" }, "bin": { - "semver": "bin/semver.js" + "node-which": "bin/which.js" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/tao/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@schematics/angular": { + "version": "15.2.9", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-15.2.9.tgz", + "integrity": "sha512-0Lit6TLNUwcAYiEkXgZp3vY9xAO1cnZCBXuUcp+6v+Ddnrt2w/YOiGe74p21cYe0StkTpTljsqsKBTiX7TMjQg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@angular-devkit/core": "15.2.9", + "@angular-devkit/schematics": "15.2.9", + "jsonc-parser": "3.2.0" }, "engines": { - "node": ">=8" + "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@nrwl/tao/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@parcel/watcher": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", - "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", + "node_modules/@sigstore/protobuf-specs": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", + "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==", "dev": true, - "hasInstallScript": true, - "dependencies": { - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" - }, "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@schematics/angular": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.2.6.tgz", - "integrity": "sha512-8NzHMX9+FSgaB0lJYxlTJv9OcBuolwZJqo9M/yX3RPSqSHghA33jWwgVbV551hBJOpbVEePerG1DQkIC99DXKA==", + "node_modules/@sigstore/tuf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.2.tgz", + "integrity": "sha512-vjwcYePJzM01Ha6oWWZ9gNcdIgnzyFxfqfWzph483DPJTH8Tb7f7bQRRll3CYVkyH56j0AgcPAcl6Vg95DPF+Q==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.2.6", - "@angular-devkit/schematics": "13.2.6", - "jsonc-parser": "3.0.0" + "@sigstore/protobuf-specs": "^0.1.0", + "tuf-js": "^1.1.7" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@socket.io/component-emitter": { @@ -3958,12 +4513,58 @@ "dev": true }, "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 10" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", + "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", + "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", + "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@types/body-parser": { @@ -3995,9 +4596,9 @@ } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -4020,9 +4621,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", - "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", + "version": "8.40.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", + "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -4040,9 +4641,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", - "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "dev": true }, "node_modules/@types/express": { @@ -4058,20 +4659,27 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, "node_modules/@types/http-proxy": { - "version": "1.17.10", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", - "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", + "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", "dev": true, "dependencies": { "@types/node": "*" @@ -4093,15 +4701,15 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, "node_modules/@types/minimist": { @@ -4113,8 +4721,7 @@ "node_modules/@types/node": { "version": "12.11.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.7.tgz", - "integrity": "sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==", - "dev": true + "integrity": "sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -4129,9 +4736,9 @@ "dev": true }, "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "node_modules/@types/q": { @@ -4140,6 +4747,14 @@ "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", "dev": true }, + "node_modules/@types/qrcode": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.0.tgz", + "integrity": "sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -4159,11 +4774,27 @@ "dev": true }, "node_modules/@types/selenium-webdriver": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.20.tgz", - "integrity": "sha512-6d8Q5fqS9DWOXEhMDiF6/2FjyHdmP/jSTAUyeQR7QwrFeNmYyzmvGxD5aLIHL445HjWgibs0eAig+KPnbaesXA==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.22.tgz", + "integrity": "sha512-Nh76NUqvfsZHG5ot5gMlHNNHQvbRvv5UpM4FH3K1HuUGeq4scNlRoKVKSOP/EGIYHhJ2IUXyQc+38jvZLxfB2Q==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -4174,11 +4805,12 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", "dev": true, "dependencies": { + "@types/http-errors": "*", "@types/mime": "*", "@types/node": "*" } @@ -4193,9 +4825,9 @@ } }, "node_modules/@types/ws": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", - "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", + "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", "dev": true, "dependencies": { "@types/node": "*" @@ -4234,13 +4866,44 @@ } } }, - "node_modules/@typescript-eslint/experimental-utils": { + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.17.0.tgz", - "integrity": "sha512-U4sM5z0/ymSYqQT6I7lz8l0ZZ9zrya5VIwrwAP5WOJVabVtVsIpTMxPQe+D3qLyePT+VlETUTO2nA1+PufPx9Q==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.17.0" + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4253,16 +4916,182 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/@typescript-eslint/parser": { "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz", + "integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.48.2", + "@typescript-eslint/utils": "5.48.2", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz", + "integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz", + "integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.48.2", + "@typescript-eslint/visitor-keys": "5.48.2", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz", + "integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.48.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "debug": "^4.3.2" + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4271,23 +5100,26 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "node_modules/@typescript-eslint/utils": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz", + "integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.48.2", + "@typescript-eslint/types": "5.48.2", + "@typescript-eslint/typescript-estree": "5.48.2", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4295,17 +5127,19 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz", + "integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.17.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" + "@typescript-eslint/types": "5.48.2", + "@typescript-eslint/visitor-keys": "5.48.2" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4313,20 +5147,12 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz", + "integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4336,18 +5162,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz", + "integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.48.2", + "@typescript-eslint/visitor-keys": "5.48.2", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -4363,18 +5189,14 @@ } } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.48.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz", + "integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/types": "5.48.2", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4382,9 +5204,28 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" } }, "node_modules/@typescript-eslint/visitor-keys": { @@ -4568,43 +5409,6 @@ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true }, - "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.40", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz", - "integrity": "sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q==", - "dev": true, - "dependencies": { - "js-yaml": "^3.10.0", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.15.0" - } - }, - "node_modules/@yarnpkg/parsers/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/@zkochan/js-yaml": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", - "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@zkochan/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -4631,9 +5435,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4643,9 +5447,9 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "peerDependencies": { "acorn": "^8" @@ -4736,9 +5540,9 @@ } }, "node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -4781,26 +5585,22 @@ } }, "node_modules/angularx-qrcode": { - "version": "13.0.15", - "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-13.0.15.tgz", - "integrity": "sha512-4gNfG6JNfKVBAPEBOQ685tmFA4UnOzqhhlmeCsbFy7pX9QGm7KRFTfujhpNav+bEDGTMyRpPhvLFnsRaOWWH6g==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-15.0.1.tgz", + "integrity": "sha512-CirpL2rhhYX/QZ1OSaJ/fusABjDlwl1oYBqaLRqmyie0xTbscWqTBW0DEoht2yCNGN8Wt+JmZwTLxYG6tLuWeQ==", "dependencies": { - "@cordobo/qrcode": "1.5.0", + "@types/qrcode": "1.5.0", + "qrcode": "1.5.1", "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/core": "^13.0.0" + "@angular/core": "^15.0.0" } }, - "node_modules/angularx-qrcode/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -4868,9 +5668,9 @@ } }, "node_modules/apexcharts": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.37.1.tgz", - "integrity": "sha512-fmQ5Updeb/LASl+S1+mIxXUFxzY0Fa7gexfCs4o+OPP9f2NEBNjvybOtPrah44N4roK7U5o5Jis906QeEQu0cA==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.41.0.tgz", + "integrity": "sha512-FJXA7NVjxs1q+ptR3b1I+pN8K/gWuXn+qLZjFz8EHvJOokdgcuwa/HSe5aC465HW/LWnrjWLSTsOQejQbQ42hQ==", "dependencies": { "svg.draggable.js": "^2.2.2", "svg.easing.js": "^2.0.0", @@ -4906,25 +5706,31 @@ "dev": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" + "deep-equal": "^2.0.5" } }, - "node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" }, - "engines": { - "node": ">=6.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-flatten": { @@ -4987,39 +5793,12 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -5053,6 +5832,18 @@ "postcss": "^8.1.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -5068,68 +5859,30 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, - "node_modules/axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "node_modules/axobject-query": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" + "deep-equal": "^2.0.5" } }, - "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, "node_modules/babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz", + "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==", "dev": true, "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" + "find-cache-dir": "^3.3.2", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 8.9" + "node": ">= 14.15.0" }, "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, "node_modules/babel-plugin-istanbul": { @@ -5172,25 +5925,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -5329,18 +6082,16 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", "dev": true, "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", + "array-flatten": "^2.1.2", "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, "node_modules/boolbase": { @@ -5372,9 +6123,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -5384,13 +6135,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -5472,17 +6227,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, "node_modules/builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } }, "node_modules/bytes": { "version": "3.1.2", @@ -5494,51 +6246,91 @@ } }, "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", + "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", "dev": true, "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", + "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.1.tgz", + "integrity": "sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.10.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", + "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cacache/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/cacache/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/call-bind": { "version": "1.0.2", @@ -5566,7 +6358,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, "engines": { "node": ">=6" } @@ -5589,9 +6380,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001462", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz", - "integrity": "sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw==", + "version": "1.0.30001512", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", + "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==", "dev": true, "funding": [ { @@ -5601,6 +6392,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -5675,18 +6470,6 @@ "node": ">=6.0" } }, - "node_modules/circular-dependency-plugin": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", - "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "webpack": ">=4.0.1" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -5709,9 +6492,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", "dev": true, "engines": { "node": ">=6" @@ -5772,9 +6555,9 @@ } }, "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -5799,6 +6582,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -5874,9 +6658,9 @@ "dev": true }, "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, "node_modules/colors": { @@ -6006,9 +6790,9 @@ } }, "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "engines": { "node": ">=0.8" @@ -6090,20 +6874,20 @@ } }, "node_modules/copy-webpack-plugin": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.1.tgz", - "integrity": "sha512-nr81NhCAIpAWXGCK5thrKmfCQ6GDY0L5RN0U+BnIn/7Us55+UCex5ANNsNKmIVtDRnk0Ecf+/kzp9SUVrrBMLg==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", "dev": true, "dependencies": { - "fast-glob": "^3.2.7", + "fast-glob": "^3.2.11", "glob-parent": "^6.0.1", - "globby": "^12.0.2", + "globby": "^13.1.1", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0", "serialize-javascript": "^6.0.0" }, "engines": { - "node": ">= 12.20.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", @@ -6113,18 +6897,6 @@ "webpack": "^5.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/array-union": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", - "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/copy-webpack-plugin/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -6138,15 +6910,14 @@ } }, "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", - "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.1.tgz", + "integrity": "sha512-DPCBxctI7dN4EeIqjW2KGqgdcUMbrhJ9AzON+PlxCtvppWhubTLD4+a0GFxiym14ZvacUydTPjLPc2DlKz7EIg==", "dev": true, "dependencies": { - "array-union": "^3.0.1", "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.9", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^4.0.0" }, @@ -6157,25 +6928,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/copy-webpack-plugin/node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -6188,22 +6940,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/core-js": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-js-compat": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.0.tgz", - "integrity": "sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ==", + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.0.tgz", + "integrity": "sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw==", "dev": true, "dependencies": { "browserslist": "^4.21.5" @@ -6213,17 +6953,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-pure": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.0.tgz", - "integrity": "sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -6363,35 +7092,6 @@ "node": ">= 8" } }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, - "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, "node_modules/css-functions-list": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.1.0.tgz", @@ -6401,38 +7101,20 @@ "node": ">=12.22" } }, - "node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, "node_modules/css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.2.15", + "postcss": "^8.4.19", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" }, "engines": { "node": ">= 12.13.0" @@ -6445,21 +7127,6 @@ "webpack": "^5.0.0" } }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true, - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, "node_modules/css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -6476,6 +7143,58 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-select/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/css-select/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -6488,21 +7207,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssdb": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", - "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", - "dev": true - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -6543,9 +7247,9 @@ } }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -6563,7 +7267,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6593,27 +7296,30 @@ "node": ">=0.10.0" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", + "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", "dev": true, "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.0", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6776,25 +7482,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/detect-it/-/detect-it-4.0.1.tgz", - "integrity": "sha512-dg5YBTJYvogK1+dA2mBUDKzOWfYZtHVba89SyZUhc4+e3i2tzgjANFg5lDRCd3UOtRcw00vUTMK8LELcMdicug==" - }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, - "node_modules/detect-passive-events": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-passive-events/-/detect-passive-events-2.0.3.tgz", - "integrity": "sha512-QN/1X65Axis6a9D8qg8Py9cwY/fkWAmAH/edTbmLMcv4m5dboLJ7LcAi8CfaCON2tjk904KwKX/HTdsHC6yeRg==", - "dependencies": { - "detect-it": "^4.0.1" - } - }, "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -6811,9 +7504,9 @@ } }, "node_modules/dijkstrajs": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", - "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" }, "node_modules/dir-glob": { "version": "3.0.1", @@ -6840,28 +7533,15 @@ "dev": true }, "node_modules/dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-packet/node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "dev": true - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", + "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", "dev": true, "dependencies": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" } }, "node_modules/doctrine": { @@ -6889,14 +7569,15 @@ } }, "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "peer": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, "funding": { "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" @@ -6915,12 +7596,13 @@ ] }, "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "peer": true, "dependencies": { - "domelementtype": "^2.2.0" + "domelementtype": "^2.3.0" }, "engines": { "node": ">= 4" @@ -6930,34 +7612,20 @@ } }, "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, + "peer": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -6980,25 +7648,10 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, - "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/electron-to-chromium": { - "version": "1.4.324", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.324.tgz", - "integrity": "sha512-m+eBs/kh3TXnCuqDF6aHLLRwLK2U471JAbZ1KYigf0TM96fZglxv0/ZFBvyIxnLKsIWUoDiVnHTA2mhYz1fqdA==", + "version": "1.4.450", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.450.tgz", + "integrity": "sha512-BLG5HxSELlrMx7dJ2s+8SFlsCtJp37Zpk2VAxyC6CZtbc+9AJeZHfYHbrlSgdXp6saQ8StMqOTEDaBKgA7u1sw==", "dev": true }, "node_modules/emoji-regex": { @@ -7052,19 +7705,10 @@ "node": ">=0.10.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/engine.io": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", - "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz", + "integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -7075,7 +7719,7 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", + "engine.io-parser": "~5.1.0", "ws": "~8.11.0" }, "engines": { @@ -7083,18 +7727,18 @@ } }, "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz", + "integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==", "dev": true, "engines": { "node": ">=10.0.0" } }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -7104,18 +7748,6 @@ "node": ">=10.13.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", @@ -7123,10 +7755,13 @@ "dev": true }, "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "devOptional": true, + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -7168,374 +7803,93 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/esbuild": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.22.tgz", - "integrity": "sha512-CjFCFGgYtbFOPrwZNJf7wsuzesx8kqwAffOlbYcFDLFuUtP8xloK1GH+Ai13Qr0RZQf9tE7LMTHJ2iVGJ1SKZA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, - "optionalDependencies": { - "esbuild-android-arm64": "0.14.22", - "esbuild-darwin-64": "0.14.22", - "esbuild-darwin-arm64": "0.14.22", - "esbuild-freebsd-64": "0.14.22", - "esbuild-freebsd-arm64": "0.14.22", - "esbuild-linux-32": "0.14.22", - "esbuild-linux-64": "0.14.22", - "esbuild-linux-arm": "0.14.22", - "esbuild-linux-arm64": "0.14.22", - "esbuild-linux-mips64le": "0.14.22", - "esbuild-linux-ppc64le": "0.14.22", - "esbuild-linux-riscv64": "0.14.22", - "esbuild-linux-s390x": "0.14.22", - "esbuild-netbsd-64": "0.14.22", - "esbuild-openbsd-64": "0.14.22", - "esbuild-sunos-64": "0.14.22", - "esbuild-windows-32": "0.14.22", - "esbuild-windows-64": "0.14.22", - "esbuild-windows-arm64": "0.14.22" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.22.tgz", - "integrity": "sha512-k1Uu4uC4UOFgrnTj2zuj75EswFSEBK+H6lT70/DdS4mTAOfs2ECv2I9ZYvr3w0WL0T4YItzJdK7fPNxcPw6YmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.22.tgz", - "integrity": "sha512-d8Ceuo6Vw6HM3fW218FB6jTY6O3r2WNcTAU0SGsBkXZ3k8SDoRLd3Nrc//EqzdgYnzDNMNtrWegK2Qsss4THhw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.22.tgz", - "integrity": "sha512-YAt9Tj3SkIUkswuzHxkaNlT9+sg0xvzDvE75LlBo4DI++ogSgSmKNR6B4eUhU5EUUepVXcXdRIdqMq9ppeRqfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.22.tgz", - "integrity": "sha512-ek1HUv7fkXMy87Qm2G4IRohN+Qux4IcnrDBPZGXNN33KAL0pEJJzdTv0hB/42+DCYWylSrSKxk3KUXfqXOoH4A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.22.tgz", - "integrity": "sha512-zPh9SzjRvr9FwsouNYTqgqFlsMIW07O8mNXulGeQx6O5ApgGUBZBgtzSlBQXkHi18WjrosYfsvp5nzOKiWzkjQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.22.tgz", - "integrity": "sha512-SnpveoE4nzjb9t2hqCIzzTWBM0RzcCINDMBB67H6OXIuDa4KqFqaIgmTchNA9pJKOVLVIKd5FYxNiJStli21qg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.22.tgz", - "integrity": "sha512-Zcl9Wg7gKhOWWNqAjygyqzB+fJa19glgl2JG7GtuxHyL1uEnWlpSMytTLMqtfbmRykIHdab797IOZeKwk5g0zg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.22.tgz", - "integrity": "sha512-soPDdbpt/C0XvOOK45p4EFt8HbH5g+0uHs5nUKjHVExfgR7du734kEkXR/mE5zmjrlymk5AA79I0VIvj90WZ4g==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.22.tgz", - "integrity": "sha512-8q/FRBJtV5IHnQChO3LHh/Jf7KLrxJ/RCTGdBvlVZhBde+dk3/qS9fFsUy+rs3dEi49aAsyVitTwlKw1SUFm+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.22.tgz", - "integrity": "sha512-SiNDfuRXhGh1JQLLA9JPprBgPVFOsGuQ0yDfSPTNxztmVJd8W2mX++c4FfLpAwxuJe183mLuKf7qKCHQs5ZnBQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.22.tgz", - "integrity": "sha512-6t/GI9I+3o1EFm2AyN9+TsjdgWCpg2nwniEhjm2qJWtJyJ5VzTXGUU3alCO3evopu8G0hN2Bu1Jhz2YmZD0kng==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.22.tgz", - "integrity": "sha512-AyJHipZKe88sc+tp5layovquw5cvz45QXw5SaDgAq2M911wLHiCvDtf/07oDx8eweCyzYzG5Y39Ih568amMTCQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.22.tgz", - "integrity": "sha512-Sz1NjZewTIXSblQDZWEFZYjOK6p8tV6hrshYdXZ0NHTjWE+lwxpOpWeElUGtEmiPcMT71FiuA9ODplqzzSxkzw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.22.tgz", - "integrity": "sha512-TBbCtx+k32xydImsHxvFgsOCuFqCTGIxhzRNbgSL1Z2CKhzxwT92kQMhxort9N/fZM2CkRCPPs5wzQSamtzEHA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.22.tgz", - "integrity": "sha512-vK912As725haT313ANZZZN+0EysEEQXWC/+YE4rQvOQzLuxAQc2tjbzlAFREx3C8+uMuZj/q7E5gyVB7TzpcTA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", - "integrity": "sha512-/mbJdXTW7MTcsPhtfDsDyPEOju9EOABvCjeUU2OJ7fWpX/Em/H3WYDa86tzLUbcVg++BScQDzqV/7RYw5XNY0g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-wasm": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.22.tgz", - "integrity": "sha512-FOSAM29GN1fWusw0oLMv6JYhoheDIh5+atC72TkJKfIUMID6yISlicoQSd9gsNSFsNBvABvtE2jR4JB1j4FkFw==", - "dev": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - } + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true }, - "node_modules/esbuild-windows-32": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.22.tgz", - "integrity": "sha512-1vRIkuvPTjeSVK3diVrnMLSbkuE36jxA+8zGLUOrT4bb7E/JZvDRhvtbWXWaveUc/7LbhaNFhHNvfPuSw2QOQg==", - "cpu": [ - "ia32" - ], + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "es6-promise": "^4.0.3" } }, - "node_modules/esbuild-windows-64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.22.tgz", - "integrity": "sha512-AxjIDcOmx17vr31C5hp20HIwz1MymtMjKqX4qL6whPj0dT9lwxPexmLj6G1CpR3vFhui6m75EnBEe4QL82SYqw==", - "cpu": [ - "x64" - ], + "node_modules/esbuild": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.8.tgz", + "integrity": "sha512-g24ybC3fWhZddZK6R3uD2iF/RIPnRpwJAqLov6ouX3hMbY4+tKolP0VMF3zuIYCaXun+yHwS5IPQ91N2BT191g==", "dev": true, + "hasInstallScript": true, "optional": true, - "os": [ - "win32" - ], + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.8", + "@esbuild/android-arm64": "0.17.8", + "@esbuild/android-x64": "0.17.8", + "@esbuild/darwin-arm64": "0.17.8", + "@esbuild/darwin-x64": "0.17.8", + "@esbuild/freebsd-arm64": "0.17.8", + "@esbuild/freebsd-x64": "0.17.8", + "@esbuild/linux-arm": "0.17.8", + "@esbuild/linux-arm64": "0.17.8", + "@esbuild/linux-ia32": "0.17.8", + "@esbuild/linux-loong64": "0.17.8", + "@esbuild/linux-mips64el": "0.17.8", + "@esbuild/linux-ppc64": "0.17.8", + "@esbuild/linux-riscv64": "0.17.8", + "@esbuild/linux-s390x": "0.17.8", + "@esbuild/linux-x64": "0.17.8", + "@esbuild/netbsd-x64": "0.17.8", + "@esbuild/openbsd-x64": "0.17.8", + "@esbuild/sunos-x64": "0.17.8", + "@esbuild/win32-arm64": "0.17.8", + "@esbuild/win32-ia32": "0.17.8", + "@esbuild/win32-x64": "0.17.8" } }, - "node_modules/esbuild-windows-arm64": { - "version": "0.14.22", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.22.tgz", - "integrity": "sha512-5wvQ+39tHmRhNpu2Fx04l7QfeK3mQ9tKzDqqGR8n/4WUxsFxnVLfDRBGirIfk4AfWlxk60kqirlODPoT5LqMUg==", - "cpu": [ - "arm64" - ], + "node_modules/esbuild-wasm": { + "version": "0.17.8", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.17.8.tgz", + "integrity": "sha512-zCmpxv95E0FuCmvdw1K836UHnj4EdiQnFfjTby35y3LAjRPtXMj3sbHDRHjbD8Mqg5lTwq3knacr/1qIFU51CQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { "node": ">=12" } @@ -7544,6 +7898,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, "engines": { "node": ">=6" } @@ -7564,14 +7919,16 @@ } }, "node_modules/eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", - "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^2.0.0", - "@eslint/js": "8.35.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -7580,10 +7937,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7591,21 +7947,19 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -7621,9 +7975,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", - "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -7654,16 +8008,19 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { @@ -7694,12 +8051,15 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ajv": { @@ -7733,12 +8093,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7785,44 +8139,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7859,81 +8175,12 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7959,14 +8206,14 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", + "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8000,15 +8247,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -8021,7 +8259,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -8030,15 +8268,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -8110,6 +8339,12 @@ "node": ">= 0.8.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -8296,15 +8531,15 @@ "dev": true }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -8386,36 +8621,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -8491,25 +8696,19 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -8545,10 +8744,47 @@ "engines": { "node": ">=4.0" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/forever-agent": { @@ -8605,43 +8841,45 @@ "node": ">= 0.6" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=10" + "node": ">=6 <7 || >=8" } }, "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", + "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "minipass": "^5.0.0" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", "dev": true }, "node_modules/fs.realpath": { @@ -8722,13 +8960,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -8766,15 +9005,15 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -8882,16 +9121,28 @@ "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/handle-thing": { @@ -8987,6 +9238,15 @@ "node": ">=0.10.0" } }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9008,6 +9268,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -9059,35 +9331,26 @@ "dev": true }, "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "lru-cache": "^7.5.1" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -9100,6 +9363,12 @@ "wbuf": "^1.1.0" } }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -9131,10 +9400,20 @@ } }, "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] }, "node_modules/html-escaper": { "version": "2.0.2", @@ -9143,9 +9422,9 @@ "dev": true }, "node_modules/html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true, "engines": { "node": ">=8" @@ -9155,9 +9434,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", - "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -9169,68 +9448,9 @@ "peer": true, "dependencies": { "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "domutils": "^3.0.1", - "entities": "^4.3.0" - } - }, - "node_modules/htmlparser2/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "peer": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "peer": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", - "dev": true, - "peer": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "entities": "^4.4.0" } }, "node_modules/http-cache-semantics": { @@ -9291,12 +9511,12 @@ } }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, @@ -9356,9 +9576,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { "agent-base": "6", @@ -9446,24 +9666,48 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/ignore-walk": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", - "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz", + "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==", "dev": true, "dependencies": { - "minimatch": "^3.0.4" + "minimatch": "^9.0.0" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", + "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/image-size": { @@ -9486,9 +9730,9 @@ "dev": true }, "node_modules/immutable": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", - "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", + "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", "dev": true }, "node_modules/import-fresh": { @@ -9507,15 +9751,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -9543,12 +9778,6 @@ "node": ">=8" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -9566,18 +9795,18 @@ "dev": true }, "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", "dev": true, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -9590,13 +9819,14 @@ "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^7.2.0", + "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", - "through": "^2.3.6" + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, "node_modules/inquirer/node_modules/ansi-styles": { @@ -9657,15 +9887,6 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/inquirer/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9678,11 +9899,19 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/ip": { "version": "2.0.0", @@ -9691,9 +9920,9 @@ "dev": true }, "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", "dev": true, "engines": { "node": ">= 10" @@ -9715,12 +9944,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -9730,13 +9985,41 @@ "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -9823,6 +10106,15 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9832,6 +10124,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -9908,6 +10215,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -9920,6 +10248,55 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -9938,6 +10315,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-what": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", @@ -9957,9 +10356,9 @@ } }, "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, "node_modules/isbinaryfile": { @@ -10154,92 +10553,22 @@ "node": ">=8" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jackspeak": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "url": "https://github.com/sponsors/isaacs" }, - "engines": { - "node": ">=8" + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jasmine": { @@ -10324,16 +10653,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10341,13 +10660,12 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -10371,17 +10689,14 @@ "node": ">=4" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/json-schema": { "version": "0.4.0", @@ -10420,19 +10735,16 @@ } }, "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -10473,6 +10785,12 @@ "setimmediate": "^1.0.5" } }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/jszip/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -10613,6 +10931,25 @@ "source-map-support": "^0.5.5" } }, + "node_modules/karma-source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma-source-map-support/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/karma/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -10624,18 +10961,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/karma/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/karma/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10663,15 +10988,6 @@ "node": ">=10" } }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -10697,9 +11013,9 @@ "dev": true }, "node_modules/less": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", - "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", "dev": true, "dependencies": { "copy-anything": "^2.0.1", @@ -10718,20 +11034,20 @@ "image-size": "~0.5.0", "make-dir": "^2.1.0", "mime": "^1.4.1", - "needle": "^2.5.2", + "needle": "^3.1.0", "source-map": "~0.6.0" } }, "node_modules/less-loader": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz", - "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", + "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", "dev": true, "dependencies": { "klona": "^2.0.4" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", @@ -10796,14 +11112,8 @@ "dev": true, "optional": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/less/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true + "node": ">=0.10.0" + } }, "node_modules/levn": { "version": "0.4.1", @@ -10890,27 +11200,10 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/lint-staged/node_modules/supports-color": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.3.1.tgz", - "integrity": "sha512-knBY82pjmnIzK3NifMo3RxEIRD9E0kIzV4BKcyTZ9+9kWgLMxd4PrsTSMoFQUabgRBbF8KOLRDCyKgNV+iK44Q==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", "dev": true, "engines": { "node": ">=12" @@ -11004,15 +11297,6 @@ "node": ">=8" } }, - "node_modules/listr2/node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/listr2/node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -11027,12 +11311,6 @@ "node": ">=8" } }, - "node_modules/listr2/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -11052,15 +11330,18 @@ } }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -11265,9 +11546,9 @@ } }, "node_modules/log4js": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.0.tgz", - "integrity": "sha512-sAGxJKqxXFlK+05OlMH6SIDAdvgQHj95EfSDOfkHW6BQUlkz+fZw8PWhydfRHq+0UuWYWR55mVnR+KTnWE2JDA==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", "dev": true, "dependencies": { "date-format": "^4.0.14", @@ -11280,23 +11561,6 @@ "node": ">=8.0" } }, - "node_modules/log4js/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/loglevel": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", @@ -11385,12 +11649,15 @@ } }, "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz", + "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.4" + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" } }, "node_modules/make-dir": { @@ -11424,49 +11691,48 @@ "dev": true }, "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", + "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", "dev": true, "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", + "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/make-fetch-happen/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/map-obj": { "version": "4.3.0", @@ -11500,12 +11766,12 @@ } }, "node_modules/memfs": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -11537,6 +11803,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/meow/node_modules/type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", @@ -11549,14 +11854,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } + "node_modules/meow/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -11653,9 +11955,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", - "integrity": "sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", "dev": true, "dependencies": { "schema-utils": "^4.0.0" @@ -11671,25 +11973,6 @@ "webpack": "^5.0.0" } }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -11697,9 +11980,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -11732,13 +12015,10 @@ } }, "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { "node": ">=8" } @@ -11755,21 +12035,48 @@ "node": ">= 8" } }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", + "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", "dev": true, "dependencies": { - "minipass": "^3.1.0", + "minipass": "^5.0.0", "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" + "minizlib": "^2.1.2" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { - "encoding": "^0.1.12" + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/minipass-flush": { @@ -11784,6 +12091,24 @@ "node": ">= 8" } }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minipass-json-stream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", @@ -11794,6 +12119,24 @@ "minipass": "^3.0.0" } }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -11806,6 +12149,24 @@ "node": ">=8" } }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", @@ -11818,7 +12179,19 @@ "node": ">=8" } }, - "node_modules/minipass/node_modules/yallist": { + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", @@ -11830,11 +12203,23 @@ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "dependencies": { - "minipass": "^3.0.0", + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { "yallist": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, "node_modules/minizlib/node_modules/yallist": { @@ -11844,15 +12229,15 @@ "dev": true }, "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, "bin": { "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/ms": { @@ -11862,24 +12247,18 @@ "dev": true }, "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "dependencies": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", - "dev": true - }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -11887,10 +12266,16 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -11905,14 +12290,14 @@ "dev": true }, "node_modules/needle": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", - "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", "dev": true, "optional": true, "dependencies": { "debug": "^3.2.6", - "iconv-lite": "^0.4.4", + "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, "bin": { @@ -11932,6 +12317,19 @@ "ms": "^2.1.1" } }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -11948,23 +12346,23 @@ "dev": true }, "node_modules/ng-apexcharts": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.4.tgz", - "integrity": "sha512-XpykR8qos14i2ly/QrmGkQIUQfqaZAe2+7tDg5An1akws3VwYg6O643g9LwdTIG3YeWlkKtt4zxYJpEJF3uLUg==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.7.7.tgz", + "integrity": "sha512-sj0jAsLLb8/6057vewZHzupjxnApmFs3nSSRnnR6imdz/Ao36kCmqNa12cG4xX7o6j6p7rO/nMO0qkUo/Nl2sQ==", "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@angular/common": ">=13.0.0", "@angular/core": ">=13.0.0", - "apexcharts": "^3.36.0", + "apexcharts": "^3.40.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/ngx-color-picker": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-12.0.1.tgz", - "integrity": "sha512-ODLePeqN7dnhTmjHGJrxFHaEXb3o2vH4n8qUzkUAIFmz5vOOxCCEYY4/bg+5VK62PonaK7j0fFf8AjnwrqhFHQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-14.0.0.tgz", + "integrity": "sha512-w28zx2DyVpIJeNsTB3T2LUI4Ed/Ujf5Uhxuh0dllputfpxXwZG9ocSJM/0L67+fxA3UnfvvXVZNUX1Ny5nZIIw==", "dependencies": { "tslib": "^2.3.0" }, @@ -11974,11 +12372,6 @@ "@angular/forms": ">=9.0.0" } }, - "node_modules/ngx-color-picker/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -11998,7 +12391,8 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true + "dev": true, + "optional": true }, "node_modules/node-forge": { "version": "1.3.1", @@ -12010,16 +12404,17 @@ } }, "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", + "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", "dev": true, "dependencies": { "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", + "make-fetch-happen": "^11.0.3", + "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", @@ -12030,7 +12425,7 @@ "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">= 10.12.0" + "node": "^12.13 || ^14.13 || >=16" } }, "node_modules/node-gyp-build": { @@ -12038,6 +12433,7 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", "dev": true, + "optional": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -12045,39 +12441,39 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "dev": true, "dependencies": { - "abbrev": "1" + "abbrev": "^1.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", "dev": true, "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-path": { @@ -12099,313 +12495,105 @@ } }, "node_modules/npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz", + "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==", "dev": true, "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-install-checks": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", - "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.1.tgz", + "integrity": "sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==", "dev": true, "dependencies": { "semver": "^7.1.1" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "node_modules/npm-package-arg": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", - "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "semver": "^7.3.4", - "validate-npm-package-name": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-packlist": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", - "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", "dev": true, "dependencies": { - "glob": "^7.1.6", - "ignore-walk": "^4.0.1", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/npm-pick-manifest": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", - "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", - "dev": true, - "dependencies": { - "npm-install-checks": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^8.1.2", - "semver": "^7.3.4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", - "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "node_modules/npm-packlist": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", + "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", "dev": true, "dependencies": { - "make-fetch-happen": "^10.0.1", - "minipass": "^3.1.6", - "minipass-fetch": "^1.4.1", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^8.1.5" + "ignore-walk": "^6.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "node_modules/npm-pick-manifest": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz", + "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==", "dev": true, "dependencies": { - "@gar/promisify": "^1.1.3", + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm-registry-fetch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "node_modules/npm-registry-fetch": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", + "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "node_modules/npm-registry-fetch/node_modules/minipass": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen/node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dev": true, - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm-registry-fetch/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-registry-fetch/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm-registry-fetch/node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dev": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dev": true, - "dependencies": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, "node_modules/npm-run-path": { @@ -12447,18 +12635,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/nx": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/nx/-/nx-13.1.3.tgz", - "integrity": "sha512-clM0NQhQKYkqcNz2E3uYRMLwhp2L/9dBhJhQi9XBX4IAyA2gWAomhRIlLm5Xxg3g4h1xwSpP3eJ5t89VikY8Pw==", - "dev": true, - "dependencies": { - "@nrwl/cli": "*" - }, - "bin": { - "nx": "bin/nx.js" - } - }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -12511,6 +12687,24 @@ "node": ">= 0.4" } }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -12563,9 +12757,9 @@ } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz", + "integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==", "dev": true, "dependencies": { "define-lazy-prop": "^2.0.0", @@ -12580,17 +12774,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -12699,30 +12893,33 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -12766,42 +12963,40 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "engines": { "node": ">=6" } }, "node_modules/pacote": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", - "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.1.0.tgz", + "integrity": "sha512-FFcjtIl+BQNfeliSm7MZz5cpdohvUV1yjGnqgVM4UnVF7JslRY0ImXAygdaCDV0jjUADEWu4y5xsDV8brtrTLg==", "dev": true, "dependencies": { - "@npmcli/git": "^2.1.0", - "@npmcli/installed-package-contents": "^1.0.6", - "@npmcli/promise-spawn": "^1.2.0", - "@npmcli/run-script": "^2.0.0", - "cacache": "^15.0.5", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.3", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^3.0.0", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^12.0.0", + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^4.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", "promise-retry": "^2.0.1", - "read-package-json-fast": "^2.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.1.0" + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" }, "bin": { "pacote": "lib/bin.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/pako": { @@ -12840,6 +13035,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", @@ -12850,27 +13051,31 @@ } }, "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "devOptional": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, "node_modules/parse5-html-rewriting-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", - "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", + "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", "dev": true, "dependencies": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", @@ -12887,20 +13092,17 @@ "dev": true }, "node_modules/parse5-sax-parser": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", - "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", "dev": true, "dependencies": { - "parse5": "^6.0.1" + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse5-sax-parser/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -12914,7 +13116,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -12949,6 +13150,40 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz", + "integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -13012,360 +13247,136 @@ "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/piscina": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", - "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", - "dev": true, - "dependencies": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0" - }, - "optionalDependencies": { - "nice-napi": "^1.0.2" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", - "dev": true, - "dependencies": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/postcss": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", - "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", - "dev": true, - "dependencies": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" + "node": ">=0.10.0" } }, - "node_modules/postcss-custom-properties": { - "version": "12.1.11", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", - "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "pinkie": "^2.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=0.10.0" } }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "node_modules/piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" }, - "peerDependencies": { - "postcss": "^8.3" + "optionalDependencies": { + "nice-napi": "^1.0.2" } }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.10" + "find-up": "^4.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=8" } }, - "node_modules/postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=8" } }, - "node_modules/postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "p-locate": "^4.1.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=8" } }, - "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" + "p-try": "^2.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=6" }, - "peerDependencies": { - "postcss": "^8.4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" + "p-limit": "^2.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=8" } }, - "node_modules/postcss-font-variant": { + "node_modules/pngjs": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "peerDependencies": { - "postcss": "^8.1.0" + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "engines": { + "node": ">=10.13.0" } }, - "node_modules/postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "node_modules/postcss": { + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "dev": true, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, - "peerDependencies": { - "postcss": "^8.2" + "engines": { + "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-html": { @@ -13391,83 +13402,18 @@ "dev": true, "peer": true }, - "node_modules/postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-import": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", - "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", - "dev": true, - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, "node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", "dev": true, "dependencies": { "cosmiconfig": "^7.0.0", "klona": "^2.0.5", - "semver": "^7.3.5" + "semver": "^7.3.8" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", @@ -13478,30 +13424,6 @@ "webpack": "^5.0.0" } }, - "node_modules/postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, "node_modules/postcss-media-query-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", @@ -13521,9 +13443,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", "dev": true, "dependencies": { "icss-utils": "^5.0.0", @@ -13564,149 +13486,7 @@ "node": "^10 || ^12 || >= 14" }, "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", - "dev": true, - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", - "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", - "dev": true, - "dependencies": { - "autoprefixer": "^10.4.2", - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001299", - "css-blank-pseudo": "^3.0.2", - "css-has-pseudo": "^3.0.3", - "css-prefers-color-scheme": "^6.0.2", - "cssdb": "^5.0.0", - "postcss-attribute-case-insensitive": "^5.0.0", - "postcss-color-functional-notation": "^4.2.1", - "postcss-color-hex-alpha": "^8.0.2", - "postcss-color-rebeccapurple": "^7.0.2", - "postcss-custom-media": "^8.0.0", - "postcss-custom-properties": "^12.1.2", - "postcss-custom-selectors": "^6.0.0", - "postcss-dir-pseudo-class": "^6.0.3", - "postcss-double-position-gradients": "^3.0.4", - "postcss-env-function": "^4.0.4", - "postcss-focus-visible": "^6.0.3", - "postcss-focus-within": "^5.0.3", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.2", - "postcss-image-set-function": "^4.0.4", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.0.3", - "postcss-logical": "^5.0.3", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.2", - "postcss-overflow-shorthand": "^3.0.2", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.3", - "postcss-pseudo-class-any-link": "^7.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^5.0.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.3" + "postcss": "^8.1.0" } }, "node_modules/postcss-resolve-nested-selector": { @@ -13731,22 +13511,32 @@ "postcss": "^8.3.3" } }, - "node_modules/postcss-selector-not": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", - "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", + "node_modules/postcss-scss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + } + ], + "engines": { + "node": ">=12.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.4.19" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -13845,6 +13635,15 @@ "ansi-styles": "^3.2.0" } }, + "node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -13985,22 +13784,56 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/protractor/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/protractor/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/protractor/node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "node_modules/protractor/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "source-map": "^0.5.6" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/protractor/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/protractor/node_modules/strip-ansi": { @@ -14137,12 +13970,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -14184,6 +14011,162 @@ "node": ">=0.9" } }, + "node_modules/qrcode": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.1.tgz", + "integrity": "sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==", + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/qrcode/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -14261,26 +14244,87 @@ "node": ">= 0.8" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "node_modules/read-package-json": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", + "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", "dev": true, "dependencies": { - "pify": "^2.3.0" + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/read-package-json-fast": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", - "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.1.tgz", + "integrity": "sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.10.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", + "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/read-pkg": { @@ -14315,6 +14359,58 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/read-pkg-up/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -14361,9 +14457,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -14424,9 +14520,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "node_modules/regenerator-transform": { @@ -14445,14 +14541,14 @@ "dev": true }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -14474,9 +14570,9 @@ } }, "node_modules/regexpu-core": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.1.tgz", - "integrity": "sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, "dependencies": { "@babel/regjsgen": "^0.8.0", @@ -14552,16 +14648,6 @@ "node": ">=0.6" } }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14582,8 +14668,7 @@ "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "node_modules/require-relative": { "version": "0.8.7", @@ -14598,12 +14683,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -14615,12 +14700,12 @@ } }, "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/resolve-url-loader": { @@ -14748,30 +14833,13 @@ } }, "node_modules/rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/rxjs-for-await": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", - "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true, - "peerDependencies": { - "rxjs": "^6.0.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -14798,10 +14866,15 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/safevalues": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/safevalues/-/safevalues-0.3.4.tgz", + "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==" + }, "node_modules/sass": { - "version": "1.49.9", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", - "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", + "version": "1.58.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz", + "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -14816,16 +14889,16 @@ } }, "node_modules/sass-loader": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", - "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "dev": true, "dependencies": { "klona": "^2.0.4", "neo-async": "^2.6.2" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", @@ -14833,8 +14906,9 @@ }, "peerDependencies": { "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "sass": "^1.3.0", + "sass-embedded": "*", "webpack": "^5.0.0" }, "peerDependenciesMeta": { @@ -14846,6 +14920,9 @@ }, "sass": { "optional": true + }, + "sass-embedded": { + "optional": true } } }, @@ -14902,54 +14979,24 @@ "dev": true }, "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" } }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -15008,9 +15055,9 @@ } }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -15202,8 +15249,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/setimmediate": { "version": "1.0.5", @@ -15270,6 +15316,23 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/sigstore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.7.0.tgz", + "integrity": "sha512-KP7QULhWdlu3hlp+jw2EvgWKlOGOY9McLj/jrchLjHNlNPK0KWIwF919cbmOp6QiKXLmPijR2qH/5KYWlbtG9Q==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.1.0", + "@sigstore/tuf": "^1.0.1", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -15318,17 +15381,18 @@ } }, "node_modules/socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", + "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.4.1", + "engine.io": "~6.5.0", "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" + "socket.io-parser": "~4.2.4" }, "engines": { "node": ">=10.0.0" @@ -15344,9 +15408,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -15367,6 +15431,15 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -15382,9 +15455,9 @@ } }, "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "dependencies": { "agent-base": "^6.0.2", @@ -15396,9 +15469,9 @@ } }, "node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "engines": { "node": ">= 8" @@ -15414,24 +15487,24 @@ } }, "node_modules/source-map-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", - "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", + "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", "dev": true, "dependencies": { - "abab": "^2.0.5", + "abab": "^2.0.6", "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" + "source-map-js": "^1.0.2" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^5.0.0" + "webpack": "^5.72.1" } }, "node_modules/source-map-loader/node_modules/iconv-lite": { @@ -15446,43 +15519,24 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "source-map": "^0.5.6" } }, "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -15510,9 +15564,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, "node_modules/spdy": { @@ -15577,15 +15631,24 @@ } }, "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", + "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", "dev": true, "dependencies": { - "minipass": "^3.1.1" + "minipass": "^5.0.0" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/statuses": { @@ -15597,6 +15660,18 @@ "node": ">= 0.6" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -15611,55 +15686,6 @@ "node": ">=8.0" } }, - "node_modules/streamroller/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/streamroller/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/streamroller/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/streamroller/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -15670,18 +15696,33 @@ } }, "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "engines": { - "node": ">=0.6.19" + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/string-width": { + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -15691,6 +15732,15 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -15710,21 +15760,34 @@ "node": ">=8" } }, - "node_modules/strip-ansi/node_modules/ansi-regex": { + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-final-newline": { @@ -15760,23 +15823,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strong-log-transformer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", - "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", - "dev": true, - "dependencies": { - "duplexer": "^0.1.1", - "minimist": "^1.2.0", - "through": "^2.3.4" - }, - "bin": { - "sl-log-transformer": "bin/sl-log-transformer.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -15913,53 +15959,6 @@ "stylelint": "^14.0.0" } }, - "node_modules/stylelint-config-recommended-scss/node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "peer": true, - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/stylelint-config-recommended-scss/node_modules/postcss-scss": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", - "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - } - ], - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.19" - } - }, "node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", @@ -16022,16 +16021,15 @@ } }, "node_modules/stylelint-scss": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.4.0.tgz", - "integrity": "sha512-Qy66a+/30aylFhPmUArHhVsHOun1qrO93LGT15uzLuLjWS7hKDfpFm34mYo1ndR4MCo8W4bEZM1+AlJRJORaaw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.7.0.tgz", + "integrity": "sha512-TSUgIeS0H3jqDZnby1UO1Qv3poi1N8wUYIJY6D1tuUq2MN3lwp/rITVo0wD+1SWTmRm0tNmGO0b7nKInnqF6Hg==", "dev": true, "dependencies": { - "lodash": "^4.17.21", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0" + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "stylelint": "^14.5.1 || ^15.0.0" @@ -16043,96 +16041,13 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, - "node_modules/stylelint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/stylelint/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/stylelint/node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/stylus": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", - "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", - "dev": true, - "dependencies": { - "css": "^3.0.0", - "debug": "^4.3.2", - "glob": "^7.1.6", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.2.0.tgz", - "integrity": "sha512-5dsDc7qVQGRoc6pvCL20eYgRUxepZ9FpeK28XhdXaIPP6kXr6nI1zAAKFQgP5OBkOfKaURp4WUpJzspg1f01Gg==", + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "dependencies": { - "fast-glob": "^3.2.7", - "klona": "^2.0.4", - "normalize-path": "^3.0.0" - }, "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "stylus": ">=0.52.4", - "webpack": "^5.0.0" + "node": ">=8" } }, "node_modules/supports-color": { @@ -16376,14 +16291,14 @@ } }, "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -16392,31 +16307,51 @@ "node": ">=10" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "minipass": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/tar/node_modules/minipass": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", - "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, "engines": { "node": ">=8" } }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -16424,9 +16359,9 @@ "dev": true }, "node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "version": "5.16.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz", + "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.2", @@ -16442,16 +16377,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "engines": { "node": ">= 10.13.0" @@ -16513,9 +16448,9 @@ "dev": true }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -16530,14 +16465,33 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", - "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.2.tgz", + "integrity": "sha512-Ah19JS86ypbJzTzvUCX7KOsEIhDaRONungA4aYBjEP3JZRf4ocuDzTg4QWZnPn9DEMiMYGJPiSOy7aykoCc70w==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -16554,6 +16508,25 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -16681,24 +16654,29 @@ "typescript": ">=2.0" } }, - "node_modules/tsconfig-paths": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", - "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", + "node_modules/ts-node/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" + } + }, + "node_modules/ts-node/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, "node_modules/tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -16721,6 +16699,20 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tuf-js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", + "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", + "dev": true, + "dependencies": { + "@tufjs/models": "1.0.4", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -16783,9 +16775,9 @@ "dev": true }, "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -16796,9 +16788,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.34.tgz", - "integrity": "sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ==", + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", "dev": true, "funding": [ { @@ -16855,30 +16847,36 @@ } }, "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", "dev": true, "dependencies": { - "unique-slug": "^2.0.0" + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/unpipe": { @@ -16891,9 +16889,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -16903,6 +16901,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -16910,7 +16912,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -16941,12 +16943,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, "bin": { - "uuid": "dist/bin/uuid" + "uuid": "bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -16966,12 +16969,15 @@ } }, "node_modules/validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", "dev": true, "dependencies": { - "builtins": "^1.0.3" + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/vary": { @@ -17030,28 +17036,6 @@ "eslint": ">=6.0.0" } }, - "node_modules/vue-eslint-parser/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -17204,9 +17188,9 @@ } }, "node_modules/webpack": { - "version": "5.70.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", - "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -17214,24 +17198,24 @@ "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.2", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { @@ -17251,82 +17235,63 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", - "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.0.1.tgz", + "integrity": "sha512-PZPZ6jFinmqVPJZbisfggDiC+2EeGZ1ZByyMP5sOFJcPPWSexalISz+cvm+j+oYPT7FIJyxT76esjnw9DhE5sw==", "dev": true, "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.2.2", + "memfs": "^3.4.12", "mime-types": "^2.1.31", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "webpack": "^5.0.0" } }, "node_modules/webpack-dev-server": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", - "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", "dev": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", + "@types/ws": "^8.5.1", "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", "colorette": "^2.0.10", "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", + "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", + "express": "^4.17.3", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", + "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", "open": "^8.0.9", "p-retry": "^4.5.0", - "portfinder": "^1.0.28", + "rimraf": "^3.0.2", "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", + "selfsigned": "^2.1.1", "serve-index": "^1.9.1", - "sockjs": "^0.3.21", + "sockjs": "^0.3.24", "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -17334,6 +17299,10 @@ "engines": { "node": ">= 12.13.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, "peerDependencies": { "webpack": "^4.37.0 || ^5.0.0" }, @@ -17343,59 +17312,17 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/webpack-dev-server/node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dev": true, - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" }, "engines": { "node": ">= 12.13.0" @@ -17403,21 +17330,9 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, "node_modules/webpack-merge": { @@ -17494,6 +17409,34 @@ "ajv": "^6.9.1" } }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -17501,9 +17444,9 @@ "dev": true }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -17556,11 +17499,61 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/wide-align": { "version": "1.1.5", @@ -17572,24 +17565,34 @@ } }, "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi": { + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -17602,10 +17605,44 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -17620,6 +17657,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -17630,7 +17668,8 @@ "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/wrappy": { "version": "1.0.2", @@ -17698,6 +17737,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "engines": { "node": ">=10" } @@ -17718,9 +17758,10 @@ } }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -17735,9 +17776,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", - "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -17747,6 +17788,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "engines": { "node": ">=12" } @@ -17773,17 +17815,12 @@ } }, "node_modules/zone.js": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", - "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.1.tgz", + "integrity": "sha512-+bIeDAFEBYuXRuU3qGQvzdPap+N1zjM4KkBAiiQuVVCrHrhjDuY6VkUhNa5+U27+9w0q3fbKiMCbpJ0XzMmSWA==", "dependencies": { "tslib": "^2.3.0" } - }, - "node_modules/zone.js/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } } diff --git a/frontend/package.json b/frontend/package.json index 1b7b87ee0..0459f5c7b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,39 +21,39 @@ }, "private": true, "dependencies": { - "@angular/animations": "15.2.7", - "@angular/cdk": "15.2.7", - "@angular/common": "15.2.7", - "@angular/compiler": "15.2.7", - "@angular/core": "15.2.7", - "@angular/forms": "15.2.7", - "@angular/material": "15.2.7", - "@angular/platform-browser": "15.2.7", - "@angular/platform-browser-dynamic": "15.2.7", - "@angular/router": "15.2.7", - "angularx-qrcode": "15.0.1", - "apexcharts": "3.37.x", - "ng-apexcharts": "1.7.x", - "ngx-color-picker": "12.0.x", - "rxjs": "6.5.x", - "tslib": "2.0.x", - "zone.js": "~0.11.4" + "@angular/animations": "15.x", + "@angular/cdk": "15.x", + "@angular/common": "15.x", + "@angular/compiler": "15x", + "@angular/core": "15.x", + "@angular/forms": "15.x", + "@angular/material": "15.x", + "@angular/platform-browser": "15.x", + "@angular/platform-browser-dynamic": "15.x", + "@angular/router": "15.x", + "angularx-qrcode": "15.x", + "apexcharts": "3.x", + "ng-apexcharts": "1.x", + "ngx-color-picker": "14.x", + "rxjs": "7.x", + "tslib": "2.x", + "zone.js": "0.13.x" }, "devDependencies": { - "@angular-devkit/build-angular": "15.2.6", - "@angular-eslint/builder": "15.2.1", - "@angular-eslint/eslint-plugin": "15.2.1", - "@angular-eslint/eslint-plugin-template": "15.2.1", - "@angular-eslint/schematics": "15.2.1", - "@angular-eslint/template-parser": "15.2.1", - "@angular/cli": "15.2.6", - "@angular/compiler-cli": "15.2.7", - "@angular/language-service": "15.2.7", + "@angular-devkit/build-angular": "15.x", + "@angular-eslint/builder": "15.x", + "@angular-eslint/eslint-plugin": "15.x", + "@angular-eslint/eslint-plugin-template": "15.x", + "@angular-eslint/schematics": "15.x", + "@angular-eslint/template-parser": "15.x", + "@angular/cli": "15.x", + "@angular/compiler-cli": "15.x", + "@angular/language-service": "15.x", "@types/jasmine": "3.6.x", "@types/jasminewd2": "2.0.x", "@types/node": "12.11.x", - "@typescript-eslint/eslint-plugin": "5.17.0", - "@typescript-eslint/parser": "5.17.0", + "@typescript-eslint/eslint-plugin": "5.17.x", + "@typescript-eslint/parser": "5.17.x", "eslint": "^8.12.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.0.0", @@ -77,7 +77,7 @@ "stylelint-config-standard": "^25.0.0", "stylelint-config-standard-scss": "^3.0.0", "ts-node": "8.3.x", - "typescript": "4.8.4" + "typescript": "4.x" }, "packageManager": "yarn@3.2.0" } diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index 2c991fd43..15a857571 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -38,7 +38,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { collection: Observable; /** A subject used to trigger refrehs of the list. */ - refreshSubject: Subject = new BehaviorSubject(null); + refreshSubject: Subject = new BehaviorSubject(null); /** Reference to the subscription held by this component. */ private subscription: Subscription; diff --git a/frontend/src/app/competition/competition-builder/competition-builder.component.ts b/frontend/src/app/competition/competition-builder/competition-builder.component.ts index fc0d3ba21..b59b0337a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder.component.ts @@ -25,7 +25,7 @@ import { ApiRole, ApiTaskGroup, ApiTaskTemplate, ApiTaskType, ApiTeam, ApiUser, DownloadService, - EvaluationService, SuccessStatus, + SuccessStatus, TemplateService, UserService } from '../../../../openapi'; diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index 66beece51..9af3ba90e 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -28,7 +28,7 @@ export class AbstractRunListComponent { displayedColumns = ['actions', 'id', 'name', 'status', 'currentTask', 'timeLeft', 'description', 'teamCount']; runs: Observable; updateInterval = 5000; /* TODO: Make configurable. */ - update = new Subject(); + refreshSubject: Subject = new Subject(); constructor( protected runService: EvaluationService, @@ -109,7 +109,7 @@ export class AbstractRunListComponent { public nextTask(runId: string) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskNext(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -121,7 +121,7 @@ export class AbstractRunListComponent { public startTask(runId: string) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskStart(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -159,7 +159,7 @@ export class AbstractRunListComponent { * state whenever a manual update is triggered. */ const query = combineLatest([this.runService.getApiV2EvaluationInfoList(), this.runService.getApiV2EvaluationStateList()]); - this.runs = merge(timer(0, this.updateInterval), this.update).pipe( + this.runs = merge(timer(0, this.updateInterval), this.refreshSubject).pipe( flatMap((t) => query), map(([info, state]) => { return info.map((v, i) => { diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index 12cb613b8..90b112689 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -42,7 +42,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { public start(runId: string) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdStart(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -62,7 +62,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { if (result) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTerminate(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -76,7 +76,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { public previousTask(runId: string) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskPrevious(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -96,7 +96,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { if (result) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskAbort(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -108,7 +108,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { } protected initStateUpdates() { - this.runs = merge(timer(0, this.updateInterval), this.update).pipe( + this.runs = merge(timer(0, this.updateInterval), this.refreshSubject).pipe( flatMap((t) => this.runService.getApiV2EvaluationInfoList()), map((runInfo) => runInfo.map((run) => diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index c5c999960..c6b5ed494 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -27,7 +27,7 @@ import { export class RunAdminToolbarComponent implements OnInit { @Input() runId: BehaviorSubject = new BehaviorSubject(''); @Input() run: Observable; - @Input() update = new Subject(); + @Input() refreshSubject: Subject = new Subject(); constructor( private router: Router, @@ -47,7 +47,7 @@ export class RunAdminToolbarComponent implements OnInit { const runId = this.runId.value; this.runAdminService.postApiV2EvaluationAdminByEvaluationIdStart(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -70,7 +70,7 @@ export class RunAdminToolbarComponent implements OnInit { const runId = this.runId.value; this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTerminate(runId).subscribe( (r) => { - this.update.next(); + this.refreshSubject.next(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); this.navigation.back(); }, diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 92a65a5d3..12150fcb9 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -1,4 +1,4 @@ - + - - The logo of the team - - -

+ +
+
+ + Team Name + + Name must consist of at least three characters. + + + + Team Members (Users) + + + {{ user.username }} + cancel + + + + + + {{ user.username }} + + + + + + Team Color + + + +
+
diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.scss b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.scss new file mode 100644 index 000000000..1d74e08b9 --- /dev/null +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.scss @@ -0,0 +1,14 @@ +.team-grid { + display: grid; + grid-template-columns: 100px 1fr; + grid-gap: 20px; + width: 100%; +} +.grid-element-logo { + margin: 10px; + width: 100px; +} + +.grid-element-form { + margin: 10px; +} \ No newline at end of file diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index 210d3e4cf..fd663b0a4 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { map, shareReplay } from 'rxjs/operators'; @@ -10,9 +10,10 @@ import {ApiTeam, ApiUser, UserService} from '../../../../../openapi'; @Component({ selector: 'app-competition-builder-add-team-dialog', templateUrl: './competition-builder-team-dialog.component.html', + styleUrls: ['./competition-builder-team-dialog.component.scss'] }) export class CompetitionBuilderTeamDialogComponent { - form: UntypedFormGroup; + form: FormGroup; logoName = ''; availableUsers: Observable; colorPalette = [ @@ -35,7 +36,6 @@ export class CompetitionBuilderTeamDialogComponent { '#9800BF', '#BF00AC', '#BF0072', - '#BF0039', ]; constructor( @@ -44,39 +44,24 @@ export class CompetitionBuilderTeamDialogComponent { private config: AppConfig, @Inject(MAT_DIALOG_DATA) private team?: ApiTeam ) { - console.log("TEAM", team); - this.form = new UntypedFormGroup({ - id: new UntypedFormControl(team?.id), - name: new UntypedFormControl(team?.name, [Validators.required, Validators.minLength(3)]), - color: new UntypedFormControl(team?.color ? team.color : CompetitionBuilderTeamDialogComponent.randomColor(), [ + this.form = new FormGroup({ + id: new FormControl(team?.id), + name: new FormControl(team?.name, [Validators.required, Validators.minLength(3)]), + color: new FormControl(team?.color || this.colorPalette[Math.floor(Math.random() * this.colorPalette.length)], [ Validators.required, Validators.minLength(7), Validators.maxLength(7), ]), - logoData: new UntypedFormControl(team?.logoData), - users: new UntypedFormControl(team?.users != null ? team.users : []), - userInput: new UntypedFormControl(''), + logoData: new FormControl(team?.logoData), + users: new FormControl(team?.users || []), + userInput: new FormControl(''), }); this.availableUsers = this.userService.getApiV2UserList().pipe( - map((value) => { - return value.filter((user) => user.role !== 'JUDGE' && user.role !== 'VIEWER'); - }), + map((value) => value.filter((user) => user.role !== 'JUDGE' && user.role !== 'VIEWER')), shareReplay(1) ); } - /** - * Generates a random HTML color. - */ - private static randomColor(): string { - const letters = '0123456789ABCDEF'; - let color = '#'; - for (let i = 0; i < 6; i++) { - color += letters[Math.floor(Math.random() * 16)]; - } - return color; - } - fileProvider = () => (this.fetchData()?.name ? this.fetchData().name : 'team-download.json'); downloadProvider = () => this.asJson(); @@ -103,15 +88,6 @@ export class CompetitionBuilderTeamDialogComponent { } } - /** - * Called by the color picker when the selected color changes. - * - * @param color New color value (hex RGB). - */ - public onColorChange(color: string) { - this.form.get('color').setValue(color); - } - /** * Returns the user for the given user id or null * From 249b4a6439c19fa4ba0a059ee97a234c213afbb1 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 5 Jul 2023 11:30:35 +0200 Subject: [PATCH 335/498] Fixed formatting issue. --- .../teams-list/teams-list.component.html | 2 +- .../template-builder-components.module.ts | 39 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html index d38000469..4e9121252 100644 --- a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html @@ -13,7 +13,7 @@

Teams

Logo - The {{team.name}}'s logo + The {{team.name}}'s logo.
-
From f5de7fb2c8ef91d3e394769b20813ebc401b859a Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 18 Jul 2023 13:16:00 +0200 Subject: [PATCH 357/498] Addressing #415: Team colour picker with presets --- frontend/package.json | 2 +- ...etition-builder-team-dialog.component.html | 19 +++++++++++++------ ...mpetition-builder-team-dialog.component.ts | 11 +++++++++++ frontend/yarn.lock | 4 ++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 0459f5c7b..9e05e154c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "angularx-qrcode": "15.x", "apexcharts": "3.x", "ng-apexcharts": "1.x", - "ngx-color-picker": "14.x", + "ngx-color-picker": "^14.0.0", "rxjs": "7.x", "tslib": "2.x", "zone.js": "0.13.x" diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html index 6dcff0289..96eb585b7 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.html @@ -19,23 +19,30 @@

Add team

Team Members (Users) - + {{ user.username }} cancel - + {{ user.username }} - - Team Color - - +

+
+ +

diff --git a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts index fd663b0a4..3a3a7c648 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-team-dialog/competition-builder-team-dialog.component.ts @@ -88,6 +88,8 @@ export class CompetitionBuilderTeamDialogComponent { } } + public sortUsersByName = (userA: ApiUser, userB: ApiUser) => userA.username.localeCompare(userB.username); + /** * Returns the user for the given user id or null * @@ -154,4 +156,13 @@ export class CompetitionBuilderTeamDialogComponent { this.form.get('logoData').setValue(reader.result); }; } + + /** + * Called by the color picker when the selected color changes. + * + * @param color New color value (hex RGB). + */ + public onColorChange(color: string) { + this.form.get('color').setValue(color); + } } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f4b89c97e..edff5a25b 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5592,7 +5592,7 @@ __metadata: karma-jasmine-html-reporter: 1.5.x lint-staged: ^12.3.7 ng-apexcharts: 1.x - ngx-color-picker: 14.x + ngx-color-picker: ^14.0.0 prettier: 2.6.2 prettier-eslint: ^14.0.1 protractor: 7.0.x @@ -8867,7 +8867,7 @@ __metadata: languageName: node linkType: hard -"ngx-color-picker@npm:14.x": +"ngx-color-picker@npm:^14.0.0": version: 14.0.0 resolution: "ngx-color-picker@npm:14.0.0" dependencies: From 2eb7e7c4143e13549c2a687ae0c2dce7cc345449 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 18 Jul 2023 13:22:50 +0200 Subject: [PATCH 358/498] Addressing #415: Removed Target section for target-less tasktypes in task editor --- .../task-template-editor.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 604ecb5e0..4abf3da4f 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -11,8 +11,8 @@

UID: {{ form.get('id').value }}

-

Task group:{{taskGroup.name}}

-

Task type:{{taskType.name}}

+

Task group: {{taskGroup.name}}

+

Task type: {{taskType.name}}

@@ -47,7 +47,7 @@

-
+

Target + + + (colorPickerChange)="onColorChange($event)" >

From 7d82017c836f36063b401284eca1755ecedc506f Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 18 Jul 2023 17:09:06 +0200 Subject: [PATCH 363/498] Addressing #415: Minor changes --- frontend/src/app/run/run-admin-view.component.ts | 15 +++++++++++---- .../template-list/template-list.component.html | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index b9259cf5e..4bdd3deb8 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AppConfig } from '../app.config'; import {BehaviorSubject, combineLatest, merge, mergeMap, Observable, of, Subject, timer} from 'rxjs'; -import { catchError, filter, map, shareReplay, switchMap } from 'rxjs/operators'; +import { catchError, filter, map, shareReplay, switchMap, tap } from "rxjs/operators"; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog } from '@angular/material/dialog'; import { @@ -30,6 +30,11 @@ export interface CombinedRun { styleUrls: ['./run-admin-view.component.scss'], }) export class RunAdminViewComponent { + + private static VIEWER_POLLING_FREQUENCY = 3 * 1000; //ms + private static STATE_POLLING_FREQUENCY = 5 * 1000; //ms + private static OVERVIEW_POLLING_FREQUENCY = 5 * 1000; //ms + runId: Observable; runIdAsSubject: BehaviorSubject = new BehaviorSubject(''); run: Observable; @@ -71,7 +76,7 @@ export class RunAdminViewComponent { }), filter((q) => q != null) ), - merge(timer(0, 1000), this.refreshSubject).pipe(switchMap((index) => this.runService.getApiV2EvaluationByEvaluationIdState(runId))), + merge(timer(0, RunAdminViewComponent.STATE_POLLING_FREQUENCY), this.refreshSubject).pipe(switchMap((index) => this.runService.getApiV2EvaluationByEvaluationIdState(runId))), ]) ), map(([i, s]) => { @@ -136,7 +141,7 @@ export class RunAdminViewComponent { }), filter((q) => q != null) ), - merge(timer(0, 1000), this.refreshSubject).pipe( + merge(timer(0, RunAdminViewComponent.OVERVIEW_POLLING_FREQUENCY), this.refreshSubject).pipe( switchMap((index) => this.runAdminService.getApiV2EvaluationAdminByEvaluationIdOverview(runId)) ), ]) @@ -148,11 +153,13 @@ export class RunAdminViewComponent { ); this.viewers = this.runId.pipe( - mergeMap((runId) => timer(0, 1000).pipe(switchMap((i) => this.runAdminService.getApiV2EvaluationAdminByEvaluationIdViewerList(runId)))) + mergeMap((runId) => timer(0, RunAdminViewComponent.VIEWER_POLLING_FREQUENCY).pipe(switchMap((i) => this.runAdminService.getApiV2EvaluationAdminByEvaluationIdViewerList(runId)))) ); + this.teams = this.run.pipe( switchMap((runAndInfo) => { + //return runAndInfo.info.teams return this.competitionService.getApiV2TemplateByTemplateIdTeamList(runAndInfo.info.templateId); }), shareReplay({ bufferSize: 1, refCount: true }) diff --git a/frontend/src/app/template/template-list/template-list.component.html b/frontend/src/app/template/template-list/template-list.component.html index bd44e535f..3a17d43b8 100644 --- a/frontend/src/app/template/template-list/template-list.component.html +++ b/frontend/src/app/template/template-list/template-list.component.html @@ -31,8 +31,8 @@

Evaluation Templates

From 35dbb7502f421ec31598ae33a6e36107a7b5637a Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 19 Jul 2023 09:58:07 +0200 Subject: [PATCH 364/498] Each sound effect gets its own audio element to avoid resource loading issues --- .../app/utilities/audio-player.utilities.ts | 15 ---------- .../src/app/viewer/task-viewer.component.html | 6 +++- .../src/app/viewer/task-viewer.component.ts | 29 ++++++++++++------- .../app/viewer/teams-viewer.component.html | 5 +++- .../src/app/viewer/teams-viewer.component.ts | 28 ++++++++++++------ 5 files changed, 47 insertions(+), 36 deletions(-) delete mode 100644 frontend/src/app/utilities/audio-player.utilities.ts diff --git a/frontend/src/app/utilities/audio-player.utilities.ts b/frontend/src/app/utilities/audio-player.utilities.ts deleted file mode 100644 index a8b8c7467..000000000 --- a/frontend/src/app/utilities/audio-player.utilities.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class AudioPlayerUtilities { - /** - * Wrapper for JavaScript playback of an HTML audio player element. Takes care of error handling. - * - * @param file Audio file that should be played. - * @param audio The HTMLAudioElement that should be played. - */ - static playOnce(file: string, audio: HTMLAudioElement) { - audio.src = file; - audio - .play() - .catch((reason) => console.warn(`Failed to play audio effects due to an error!`)) - .then(() => {}); - } -} diff --git a/frontend/src/app/viewer/task-viewer.component.html b/frontend/src/app/viewer/task-viewer.component.html index 5d8ba4103..b51dfeb7f 100644 --- a/frontend/src/app/viewer/task-viewer.component.html +++ b/frontend/src/app/viewer/task-viewer.component.html @@ -42,6 +42,10 @@

{{ (taskChanged | async)?.name }} (Task complete)

- + + + + + diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index e25589b7f..f8f119171 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -18,7 +18,6 @@ import { IWsMessage } from '../model/ws/ws-message.interface'; import { IWsClientMessage } from '../model/ws/ws-client-message.interface'; import { WebSocketSubject } from 'rxjs/webSocket'; import { AppConfig } from '../app.config'; -import { AudioPlayerUtilities } from '../utilities/audio-player.utilities'; import { ApiContentElement, ApiContentType, ApiEvaluationState, @@ -74,11 +73,24 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /** The subscription associated with the current viewer state. */ viewerStateSubscription: Subscription; - /** Reference to the audio element used during countdown. */ - @ViewChild('audio') audio: ElementRef; + /** Reference to the audio elements used during countdown. */ + @ViewChild('audio_beep_1') beep1: ElementRef; + @ViewChild('audio_beep_2') beep2: ElementRef; + @ViewChild('audio_ding') ding: ElementRef; + @ViewChild('audio_glass') glass: ElementRef; constructor(protected runService: EvaluationService, public config: AppConfig) {} + private playOnce(audio: HTMLAudioElement) { + if (this.config.config.effects.mute) { + return + } + audio + .play() + .catch((reason) => console.warn('Failed to play audio effects due to an error:', reason)) + .then(() => {}); + } + /** * Create a subscription for task changes. */ @@ -159,9 +171,9 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { this.viewerState.next(ViewerState.VIEWER_COUNTDOWN); this.taskCountdown.next(countdown); if (countdown > 0) { - AudioPlayerUtilities.playOnce('/immutable/assets/audio/beep_1.ogg', this.audio.nativeElement); + this.playOnce(this.beep1.nativeElement); } else { - AudioPlayerUtilities.playOnce('/immutable/assets/audio/beep_2.ogg', this.audio.nativeElement); + this.playOnce(this.beep2.nativeElement); } } else { this.viewerState.next(ViewerState.VIEWER_PLAYBACK); @@ -213,7 +225,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { delayWhen((c) => interval(Math.max(0, 1000 * (c.offset - actualTimeElapsed)))), map((t, index) => { if (index > 0) { - AudioPlayerUtilities.playOnce('assets/audio/ding.ogg', this.audio.nativeElement); + this.playOnce(this.ding.nativeElement); } return t; }) @@ -229,10 +241,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { map((s) => s.timeLeft) /* Compensating for added countdown. */, tap((t) => { if (t === 30 || t === 60) { - AudioPlayerUtilities.playOnce( - '/immutable/assets/audio/glass.ogg', - this.audio.nativeElement - ); /* Reminder that time is running out. */ + this.playOnce(this.glass.nativeElement); /* Reminder that time is running out. */ } }) ); diff --git a/frontend/src/app/viewer/teams-viewer.component.html b/frontend/src/app/viewer/teams-viewer.component.html index fcda2bab2..b4be32e93 100644 --- a/frontend/src/app/viewer/teams-viewer.component.html +++ b/frontend/src/app/viewer/teams-viewer.component.html @@ -43,7 +43,10 @@

{{ team.name }}

- + + + + diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index 9c7ad91ce..a657ae629 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -11,7 +11,6 @@ import { import {BehaviorSubject, combineLatest, merge, Observable, of, Subscription} from 'rxjs'; import {catchError, filter, flatMap, map, pairwise, retry, shareReplay, switchMap, withLatestFrom,} from 'rxjs/operators'; import {AppConfig} from '../app.config'; -import {AudioPlayerUtilities} from '../utilities/audio-player.utilities'; import {animate, keyframes, style, transition, trigger} from '@angular/animations'; import { ApiAnswerType, @@ -99,8 +98,11 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { /** Behaviour subject used to reset highlight animation state. */ resetHighlight: BehaviorSubject = new BehaviorSubject(null); - /** Reference to the audio file played during countdown. */ - @ViewChild('audio') audio: ElementRef; + /** Reference to the audio elements played during countdown. */ + @ViewChild('audio_correct') correct: ElementRef; + @ViewChild('audio_wrong') wrong: ElementRef; + @ViewChild('audio_applause') applause: ElementRef; + @ViewChild('audio_trombone') trombone: ElementRef; /** Internal subscription for playing sound effect of a task that has ended. */ taskEndedSoundEffect: Subscription; @@ -120,6 +122,16 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { }, 500); } + private playOnce(audio: HTMLAudioElement) { + if (this.config.config.effects.mute) { + return + } + audio + .play() + .catch((reason) => console.warn('Failed to play audio effects due to an error:', reason)) + .then(() => {}); + } + ngAfterViewInit(): void { /* Create source observable; list of all submissions. */ this.submissions = this.state.pipe( @@ -193,10 +205,10 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { for (const [key, value] of delta) { if (value.correct > value.wrong) { highlight.set(key, 'correct'); - AudioPlayerUtilities.playOnce('/immutable/assets/audio/correct.ogg', this.audio.nativeElement); + this.playOnce(this.correct.nativeElement); } else if (value.wrong > value.correct) { highlight.set(key, 'wrong'); - AudioPlayerUtilities.playOnce('/immutable/assets/audio/wrong.ogg', this.audio.nativeElement); + this.playOnce(this.wrong.nativeElement); } else { highlight.set(key, 'nohighlight'); } @@ -230,13 +242,11 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { }) ) .subscribe((success) => { - if (this.audio) { if (success) { - AudioPlayerUtilities.playOnce('immutable/assets/audio/applause.ogg', this.audio.nativeElement); + this.playOnce(this.applause.nativeElement); } else { - AudioPlayerUtilities.playOnce('immutable/assets/audio/sad_trombone.ogg', this.audio.nativeElement); + this.playOnce(this.trombone.nativeElement); } - } }); } From c86da2bd41e19c250ca02ee11828f42862c680b1 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 21 Jul 2023 12:49:28 +0200 Subject: [PATCH 365/498] Using new scorer for AVS --- .../run/InteractiveSynchronousEvaluation.kt | 4 +- .../dres/run/score/scorer/AvsTaskScorer.kt | 111 ++++++++++++------ .../run/score/scorer/LegacyAvsTaskScorer.kt | 50 ++++++++ .../dres/run/score/scorer/NewAvsTaskScorer.kt | 91 -------------- .../run/score/scorer/AvsTaskScorerTest.kt | 6 +- .../score/scorer/LegacyAvsTaskScorerTest.kt | 6 +- 6 files changed, 133 insertions(+), 135 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 2de8aac27..b65998dfa 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -13,7 +13,6 @@ import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.AllSubmissionFilter import dev.dres.run.filter.SubmissionFilter import dev.dres.run.filter.SubmissionFilterAggregator -import dev.dres.run.score.Scoreable import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard @@ -25,7 +24,6 @@ import dev.dres.run.transformer.SubmissionTaskMatchFilter import dev.dres.run.transformer.SubmissionTransformer import dev.dres.run.transformer.SubmissionTransformerAggregator import kotlinx.dnq.query.* -import kotlinx.dnq.query.FilteringContext.le import java.lang.IndexOutOfBoundsException import java.util.LinkedList @@ -108,7 +106,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /** * Represents a concrete [Run] of a [DbTaskTemplate]. [DbTask]s always exist within a [InteractiveSynchronousEvaluation]. - * As a [InteractiveSynchronousEvaluation], [DbTask]s can be started and ended and they can be used to register [DbSubmission]s. + * As a [InteractiveSynchronousEvaluation], [DbTask]s can be started and ended, and they can be used to register [DbSubmission]s. */ inner class ISTaskRun(task: DbTask): AbstractInteractiveTask(task) { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index 49b23593e..d53868479 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -1,50 +1,91 @@ package dev.dres.run.score.scorer + +import dev.dres.data.model.submissions.Submission +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId -import dev.dres.data.model.media.MediaItemType -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.* import dev.dres.run.score.Scoreable +import java.lang.Double.max +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write +import kotlin.math.abs /** - * A [TeamTaskScorer] used for AVS tasks. - * - * @author Luca Rossetto - * @version 1.1.0 + * The new AVS Scorer. */ -class AvsTaskScorer(override val scoreable: Scoreable) : TaskScorer { +class AvsTaskScorer(override val scoreable: Scoreable, private val penaltyConstant: Double, private val maxPointsPerTask: Double) : TaskScorer { + + private var lastScores: Map = emptyMap() + private val lastScoresLock = ReentrantReadWriteLock() + + + constructor(context: Scoreable, parameters: Map) : this( + context, + abs(parameters.getOrDefault("penalty", "$defaultPenalty").toDoubleOrNull() ?: defaultPenalty), + parameters.getOrDefault("maxPointsPerTask", "$defaultMaxPointsPerTask").toDoubleOrNull() ?: defaultMaxPointsPerTask + ) + + constructor(context: Scoreable) : this(context, defaultPenalty, defaultMaxPointsPerTask) + + companion object { + const val defaultPenalty: Double = 0.2 + private const val defaultMaxPointsPerTask: Double = 1000.0 + + /** + * Sanitised team scores: Either the team has score 0.0 (no submission) or the calculated score + */ + fun teamScoreMapSanitised(scores: Map, teamIds: Collection): Map { + + val cleanMap = teamIds.associateWith { 0.0 }.toMutableMap() + + scores.forEach { (teamId, score) -> + cleanMap[teamId] = max(0.0, score) + } + + return cleanMap + } + + } + /** - * Computes and returns the scores for this [KisTaskScorer]. Requires an ongoing database transaction. + * Computes and returns the scores for this [AvsTaskScorer]. Requires an ongoing database transaction. * - * @param submissions A [Sequence] of [Submission]s to obtain scores for. * @return A [Map] of [TeamId] to calculated task score. */ override fun scoreMap(submissions: Sequence): Map { - val correctSubmissions = submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.CORRECT } } - val wrongSubmissions = submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.WRONG } } - val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.teamId } - val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.teamId } - val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() - - return this.scoreable.teams.map { teamId -> - val correctSubs = correctSubmissionsPerTeam[teamId] ?: return@map teamId to 0.0 - val correct = correctSubs.size - val wrong = wrongSubmissionsPerTeam[teamId]?.size ?: 0 - teamId to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs.asSequence()).toDouble() / totalCorrectQuantized) - }.toMap() - } + /* Make necessary calculations. */ + val distinctCorrectVideos = submissions.flatMap { submission -> + submission.answerSets().filter { it.status() == VerdictStatus.CORRECT && it.answers().firstOrNull()?.item != null } + }.mapNotNullTo(mutableSetOf()) { it.answers().firstOrNull()?.item } + .size - private fun countQuantized(submissions: Sequence): Int = submissions - .filter { it.answers().firstOrNull()?.item != null } - .groupBy { it.answers().first().item } - .map { - when (it.key!!.type()) { - MediaItemType.IMAGE -> 1 - MediaItemType.VIDEO -> { - val ranges = it.value.asSequence().map { s -> s.answers().first().temporalRange!! }.toList() - TemporalRange.merge(ranges, overlap = 1).size - } - else -> throw IllegalStateException("Unsupported media type ${it.key!!.type()} for AVS task scorer.") + //no correct submissions yet + if (distinctCorrectVideos == 0) { + lastScores = this.lastScoresLock.write { + teamScoreMapSanitised(mapOf(), this.scoreable.teams) } - }.sum() + this.lastScoresLock.read { + return lastScores + } + } + + return teamScoreMapSanitised(submissions.groupBy { it.teamId }.map { submissionsPerTeam -> + val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { + it.answerSets() + .filter { v -> v.answers().firstOrNull()?.item != null && (v.status() == VerdictStatus.CORRECT || v.status() == VerdictStatus.WRONG) } + } + submissionsPerTeam.key to + max(0.0, //prevent negative total scores + verdicts.groupBy { it.answers().firstOrNull()?.item!! }.map { + val firstCorrectIdx = it.value.indexOfFirst { v -> v.status() == VerdictStatus.CORRECT } + if (firstCorrectIdx < 0) { //no correct submissions, only penalty + it.value.size * -penaltyConstant + } else { //apply penalty for everything before correct submission + 1.0 - firstCorrectIdx * penaltyConstant + } + }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize + ) + }.toMap(), this.scoreable.teams) + } } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt new file mode 100644 index 000000000..b23f51218 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt @@ -0,0 +1,50 @@ +package dev.dres.run.score.scorer + +import dev.dres.data.model.template.team.TeamId +import dev.dres.data.model.media.MediaItemType +import dev.dres.data.model.media.time.TemporalRange +import dev.dres.data.model.submissions.* +import dev.dres.run.score.Scoreable + +/** + * A [TeamTaskScorer] used for AVS tasks. + * + * @author Luca Rossetto + * @version 1.1.0 + */ +class LegacyAvsTaskScorer(override val scoreable: Scoreable) : TaskScorer { + /** + * Computes and returns the scores for this [TaskScorer]. Requires an ongoing database transaction. + * + * @param submissions A [Sequence] of [Submission]s to obtain scores for. + * @return A [Map] of [TeamId] to calculated task score. + */ + override fun scoreMap(submissions: Sequence): Map { + val correctSubmissions = submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.CORRECT } } + val wrongSubmissions = submissions.flatMap { s -> s.answerSets().filter { v -> v.status() == VerdictStatus.WRONG } } + val correctSubmissionsPerTeam = correctSubmissions.groupBy { it.submission.teamId } + val wrongSubmissionsPerTeam = wrongSubmissions.groupBy { it.submission.teamId } + val totalCorrectQuantized = countQuantized(correctSubmissions).toDouble() + + return this.scoreable.teams.map { teamId -> + val correctSubs = correctSubmissionsPerTeam[teamId] ?: return@map teamId to 0.0 + val correct = correctSubs.size + val wrong = wrongSubmissionsPerTeam[teamId]?.size ?: 0 + teamId to 100.0 * (correct / (correct + wrong / 2.0)) * (countQuantized(correctSubs.asSequence()).toDouble() / totalCorrectQuantized) + }.toMap() + } + + private fun countQuantized(submissions: Sequence): Int = submissions + .filter { it.answers().firstOrNull()?.item != null } + .groupBy { it.answers().first().item } + .map { + when (it.key!!.type()) { + MediaItemType.IMAGE -> 1 + MediaItemType.VIDEO -> { + val ranges = it.value.asSequence().map { s -> s.answers().first().temporalRange!! }.toList() + TemporalRange.merge(ranges, overlap = 1).size + } + else -> throw IllegalStateException("Unsupported media type ${it.key!!.type()} for AVS task scorer.") + } + }.sum() +} diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt deleted file mode 100644 index d4d247b48..000000000 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/NewAvsTaskScorer.kt +++ /dev/null @@ -1,91 +0,0 @@ -package dev.dres.run.score.scorer - - -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.template.team.TeamId -import dev.dres.run.score.Scoreable -import java.lang.Double.max -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read -import kotlin.concurrent.write -import kotlin.math.abs - -/** - * The new AVS Scorer. - */ -class NewAvsTaskScorer(override val scoreable: Scoreable, private val penaltyConstant: Double, private val maxPointsPerTask: Double) : TaskScorer { - - private var lastScores: Map = emptyMap() - private val lastScoresLock = ReentrantReadWriteLock() - - - constructor(context: Scoreable, parameters: Map) : this( - context, - abs(parameters.getOrDefault("penalty", "$defaultPenalty").toDoubleOrNull() ?: defaultPenalty), - parameters.getOrDefault("maxPointsPerTask", "$defaultMaxPointsPerTask").toDoubleOrNull() ?: defaultMaxPointsPerTask - ) - - constructor(context: Scoreable) : this(context, defaultPenalty, defaultMaxPointsPerTask) - - companion object { - const val defaultPenalty: Double = 0.2 - private const val defaultMaxPointsPerTask: Double = 1000.0 - - /** - * Sanitised team scores: Either the team has score 0.0 (no submission) or the calculated score - */ - fun teamScoreMapSanitised(scores: Map, teamIds: Collection): Map { - - val cleanMap = teamIds.associateWith { 0.0 }.toMutableMap() - - scores.forEach { (teamId, score) -> - cleanMap[teamId] = max(0.0, score) - } - - return cleanMap - } - - } - - /** - * Computes and returns the scores for this [NewAvsTaskScorer]. Requires an ongoing database transaction. - * - * @return A [Map] of [TeamId] to calculated task score. - */ - override fun scoreMap(submissions: Sequence): Map { - /* Make necessary calculations. */ - val distinctCorrectVideos = submissions.flatMap { submission -> - submission.answerSets().filter { it.status() == VerdictStatus.CORRECT && it.answers().firstOrNull()?.item != null } - }.mapNotNullTo(mutableSetOf()) { it.answers().firstOrNull()?.item } - .size - - //no correct submissions yet - if (distinctCorrectVideos == 0) { - lastScores = this.lastScoresLock.write { - teamScoreMapSanitised(mapOf(), this.scoreable.teams) - } - this.lastScoresLock.read { - return lastScores - } - } - - return teamScoreMapSanitised(submissions.groupBy { it.teamId }.map { submissionsPerTeam -> - val verdicts = submissionsPerTeam.value.sortedBy { it.timestamp }.flatMap { - it.answerSets() - .filter { v -> v.answers().firstOrNull()?.item != null && (v.status() == VerdictStatus.CORRECT || v.status() == VerdictStatus.WRONG) } - } - submissionsPerTeam.key to - max(0.0, //prevent negative total scores - verdicts.groupBy { it.answers().firstOrNull()?.item!! }.map { - val firstCorrectIdx = it.value.indexOfFirst { v -> v.status() == VerdictStatus.CORRECT } - if (firstCorrectIdx < 0) { //no correct submissions, only penalty - it.value.size * -penaltyConstant - } else { //apply penalty for everything before correct submission - 1.0 - firstCorrectIdx * penaltyConstant - } - }.sum() / distinctCorrectVideos * maxPointsPerTask //normalize - ) - }.toMap(), this.scoreable.teams) - } -} diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 80db78ff1..7990c17dd 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.evaluation.* import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable -import dev.dres.run.score.scorer.NewAvsTaskScorer +import dev.dres.run.score.scorer.AvsTaskScorer import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test class AvsTaskScorerTest { - private lateinit var scorer: NewAvsTaskScorer + private lateinit var scorer: AvsTaskScorer private val teams = listOf("team-1", "team-2", "team-3") private val defaultTaskDuration = 3 * 60L // 3min private val taskStartTime = 100_000L @@ -57,7 +57,7 @@ class AvsTaskScorerTest { @BeforeEach fun setup(){ - this.scorer = NewAvsTaskScorer(this.scoreable, penaltyConstant = penalty, maxPointsPerTask) + this.scorer = AvsTaskScorer(this.scoreable, penaltyConstant = penalty, maxPointsPerTask) } @Test diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index d7f502962..5681e6f2b 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -6,7 +6,7 @@ import dev.dres.api.rest.types.evaluation.* import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable -import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.LegacyAvsTaskScorer import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test class LegacyAvsTaskScorerTest { - private lateinit var scorer: AvsTaskScorer + private lateinit var scorer: LegacyAvsTaskScorer private val teams = listOf("team-1", "team-2", "team-3") private val defaultTaskDuration = 5 * 60L private val taskStartTime = System.currentTimeMillis() - 100_000 @@ -85,7 +85,7 @@ class LegacyAvsTaskScorerTest { @BeforeEach fun setup() { - this.scorer = AvsTaskScorer(this.scoreable) + this.scorer = LegacyAvsTaskScorer(this.scoreable) } @Test From 8c1f729889433851c21fd53e39914c6859a3e2e9 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 21 Jul 2023 14:39:12 +0200 Subject: [PATCH 366/498] Some cleanup of deprecated calls --- .../collection-list.component.ts | 40 +++++++------- .../collection-viewer.component.ts | 32 +++++------ .../competition-list.component.ts | 7 +-- .../app/run/abstract-run-list.component.ts | 33 ++++++------ .../src/app/run/admin-run-list.component.ts | 6 +-- .../run-score-history.component.ts | 6 +-- .../services/session/authentication.sevice.ts | 10 ++-- .../template-list/template-list.component.ts | 15 +++--- .../admin-user-list.component.ts | 53 ++++++++++--------- .../src/app/user/profile/profile.component.ts | 10 ++-- .../src/app/viewer/run-viewer.component.ts | 5 +- .../src/app/viewer/teams-viewer.component.ts | 6 +-- 12 files changed, 113 insertions(+), 110 deletions(-) diff --git a/frontend/src/app/collection/collection-list/collection-list.component.ts b/frontend/src/app/collection/collection-list/collection-list.component.ts index 0c905fd45..d72653c79 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.ts +++ b/frontend/src/app/collection/collection-list/collection-list.component.ts @@ -3,7 +3,7 @@ import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { CollectionBuilderDialogComponent } from '../collection-builder/collection-builder-dialog/collection-builder-dialog.component'; -import { filter, flatMap } from 'rxjs/operators'; +import { filter, mergeMap } from 'rxjs/operators'; import {ApiMediaCollection, CollectionService} from '../../../../openapi'; @Component({ @@ -23,14 +23,15 @@ export class CollectionListComponent implements AfterViewInit { ) {} refresh() { - this.collectionService.getApiV2CollectionList().subscribe( - (results: ApiMediaCollection[]) => { + this.collectionService.getApiV2CollectionList().subscribe({ + next: (results: ApiMediaCollection[]) => { this.collections = results; }, - (r) => { + error: (r) => { this.collections = []; this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); } + } ); } @@ -50,7 +51,7 @@ export class CollectionListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - flatMap((r: ApiMediaCollection) => { + mergeMap((r: ApiMediaCollection) => { if (id) { return this.collectionService.patchApiV2Collection(r); } else { @@ -58,14 +59,15 @@ export class CollectionListComponent implements AfterViewInit { } }) ) - .subscribe( - (r) => { - this.refresh(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } + .subscribe({ + next: (r) => { + this.refresh(); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); + }, + error: (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, {duration: 5000}); + } + } ); } @@ -75,15 +77,15 @@ export class CollectionListComponent implements AfterViewInit { delete(id: string) { if (confirm(`Do you really want to delete collection with ID ${id}?`)) { - this.collectionService.deleteApiV2CollectionByCollectionId(id).subscribe( - (r) => { + this.collectionService.deleteApiV2CollectionByCollectionId(id).subscribe({ + next: (r) => { this.refresh(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + error: (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, {duration: 5000}); } - ); + }); } } diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index 15a857571..45e45b930 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -2,8 +2,8 @@ import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; -import { BehaviorSubject, Observable, of, Subject, Subscription } from 'rxjs'; -import { catchError, filter, flatMap, map, retry, shareReplay, switchMap } from 'rxjs/operators'; +import {BehaviorSubject, mergeMap, Observable, of, Subject, Subscription} from 'rxjs'; +import { catchError, filter, map, retry, shareReplay, switchMap } from 'rxjs/operators'; import { AppConfig } from '../../app.config'; import { MediaItemBuilderData, @@ -70,7 +70,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { * IMPORTANT: Unsubscribe OnDestroy! */ this.collection = this.refreshSubject.pipe( - flatMap((s) => this.collectionId), + mergeMap((s) => this.collectionId), switchMap((id) => this.collectionService.getApiV2CollectionByCollectionId(id).pipe( retry(3), @@ -101,15 +101,15 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { delete(id: string) { if (confirm(`Do you really want to delete media item with ID ${id}?`)) { - this.collectionService.deleteApiV2MediaItemByMediaId(id).subscribe( - (r) => { + this.collectionService.deleteApiV2MediaItemByMediaId(id).subscribe({ + next: (r) => { this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + error: (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, {duration: 5000}); } - ); + }); } } @@ -136,7 +136,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { .afterClosed() .pipe( filter((r) => r != null), - flatMap((r: ApiMediaItem) => { + mergeMap((r: ApiMediaItem) => { if (id) { return this.collectionService.patchApiV2Mediaitem(r); } else { @@ -144,15 +144,15 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { } }) ) - .subscribe( - (r) => { + .subscribe({ + next: (r) => { this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + error: (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, {duration: 5000}); } - ); + }); }); } diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts index b8fc80689..b58889d71 100644 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ b/frontend/src/app/competition/competition-list/competition-list.component.ts @@ -1,7 +1,7 @@ import { AfterViewInit, Component } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { CompetitionCreateDialogComponent } from './competition-create-dialog.component'; -import { filter, flatMap, take, tap } from 'rxjs/operators'; +import { filter, take, tap } from 'rxjs/operators'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; import { CompetitionStartDialogComponent, CompetitionStartDialogResult } from './competition-start-dialog.component'; @@ -16,6 +16,7 @@ import { EvaluationAdministratorService, RunProperties, SuccessStatus, TemplateService } from "../../../../openapi"; +import {mergeMap} from 'rxjs'; /** * @deprecated Replaced by TemplateList @@ -46,7 +47,7 @@ export class CompetitionListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - flatMap((r: ApiCreateEvaluation) => { + mergeMap((r: ApiCreateEvaluation) => { return this.evaluationService.postApiV2Template(r); }) ) @@ -68,7 +69,7 @@ export class CompetitionListComponent implements AfterViewInit { .pipe( filter((r) => r != null), tap((r) => (this.waitingForRun[id] = true)), - flatMap((r: CompetitionStartDialogResult) => { + mergeMap((r: CompetitionStartDialogResult) => { const properties = { participantCanView: r.participantCanView, shuffleTasks: r.shuffleTasks, diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index 9af3ba90e..c65a7d3fb 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -1,9 +1,8 @@ -import { combineLatest, merge, Observable, Subject, timer } from 'rxjs'; -import { flatMap, map, take } from 'rxjs/operators'; +import {combineLatest, merge, mergeMap, Observable, Subject, timer} from 'rxjs'; +import { map, take } from 'rxjs/operators'; import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { - ApiEvaluationState, ApiTaskStatus, DownloadService, EvaluationAdministratorService, EvaluationScoresService, @@ -107,32 +106,32 @@ export class AbstractRunListComponent { } public nextTask(runId: string) { - this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskNext(runId).subscribe( - (r) => { + this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskNext(runId).subscribe({ + next: (r) => { this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + error: (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, {duration: 5000}); } - ); + }); } public startTask(runId: string) { - this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskStart(runId).subscribe( - (r) => { + this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskStart(runId).subscribe({ + next: (r) => { this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + this.snackBar.open(`Success: ${r.description}`, null, {duration: 5000}); }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + error: (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, {duration: 5000}); } - ); + }); } scoreDownloadProvider = (runId: string) => { return this.downloadService - .getApiV2DownloadEvaluationByEvaluationIdScores(runId, 'body', false, { httpHeaderAccept: 'text/plain' }) // FIXME was text/css, might require openapi specs adjustement + .getApiV2DownloadEvaluationByEvaluationIdScores(runId, 'body', false, { httpHeaderAccept: 'text/plain' }) // FIXME was text/css, might require openapi specs adjustment .pipe(take(1)); }; @@ -160,7 +159,7 @@ export class AbstractRunListComponent { */ const query = combineLatest([this.runService.getApiV2EvaluationInfoList(), this.runService.getApiV2EvaluationStateList()]); this.runs = merge(timer(0, this.updateInterval), this.refreshSubject).pipe( - flatMap((t) => query), + mergeMap((t) => query), map(([info, state]) => { return info.map((v, i) => { const s = state.find((_) => _.evaluationId === v.id); diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index 90b112689..864f1c6a9 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -7,8 +7,8 @@ import { ConfirmationDialogComponent, ConfirmationDialogComponentData, } from '../shared/confirmation-dialog/confirmation-dialog.component'; -import { forkJoin, merge, timer } from 'rxjs'; -import { flatMap, map, switchMap } from 'rxjs/operators'; +import {forkJoin, merge, mergeMap, timer} from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; import { ApiEvaluationInfo, ApiEvaluationOverview, ApiTaskStatus, DownloadService, @@ -109,7 +109,7 @@ export class AdminRunListComponent extends AbstractRunListComponent { protected initStateUpdates() { this.runs = merge(timer(0, this.updateInterval), this.refreshSubject).pipe( - flatMap((t) => this.runService.getApiV2EvaluationInfoList()), + mergeMap((t) => this.runService.getApiV2EvaluationInfoList()), map((runInfo) => runInfo.map((run) => this.runAdminService.getApiV2EvaluationAdminByEvaluationIdOverview(run.id).pipe( diff --git a/frontend/src/app/run/score-history/run-score-history.component.ts b/frontend/src/app/run/score-history/run-score-history.component.ts index 6dbbb1caf..d463d73de 100644 --- a/frontend/src/app/run/score-history/run-score-history.component.ts +++ b/frontend/src/app/run/score-history/run-score-history.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { catchError, filter, flatMap, map, shareReplay, switchMap, tap } from 'rxjs/operators'; -import { combineLatest, concat, interval, Observable, of } from 'rxjs'; +import { catchError, filter, map, shareReplay, switchMap, tap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; import { ApexAxisChartSeries, ApexChart, @@ -175,7 +175,7 @@ export class RunScoreHistoryComponent { /* Load time series data should be visualized. */ /*const scores = this.runId.pipe( - flatMap((r) => + mergeMap((r) => interval(2000).pipe( switchMap((i) => { return this.scoreService.getApiV1ScoreRunWithRunidSeriesWithScoreboard(r, this.selectedScoreboard).pipe( diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index 651d650b6..316c39f69 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -1,6 +1,6 @@ import {Inject, Injectable} from '@angular/core'; -import {catchError, filter, flatMap, map, shareReplay, tap, withLatestFrom} from 'rxjs/operators'; -import {BehaviorSubject, Observable, of, Subscription} from 'rxjs'; +import {catchError, filter, map, shareReplay, tap, withLatestFrom} from 'rxjs/operators'; +import {BehaviorSubject, mergeMap, Observable, of, Subscription} from 'rxjs'; import {ApiRole, ApiUser, LoginRequest, UserRequest, UserService} from '../../../../openapi'; import {ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree} from "@angular/router"; @@ -33,7 +33,7 @@ export class AuthenticationService { */ public login(user: string, pass: string) { return this.userService.postApiV2Login({ username: user, password: pass } as LoginRequest).pipe( - flatMap(() => this.userService.getApiV2User()), + mergeMap(() => this.userService.getApiV2User()), tap((data) => { this._loggedIn.next(true); console.log(`Successfully logged in as '${data.username}'.`) @@ -61,7 +61,7 @@ export class AuthenticationService { */ public updateUser(user: UserRequest) { return this.user.pipe( - flatMap((u: ApiUser) => this.userService.patchApiV2UserByUserId(u.id, user)) + mergeMap((u: ApiUser) => this.userService.patchApiV2UserByUserId(u.id, user)) ); } @@ -80,7 +80,7 @@ export class AuthenticationService { */ get user(): Observable { return this.isLoggedIn.pipe( - flatMap(loggedIn=> { + mergeMap(loggedIn=> { if (loggedIn) { return this.userService.getApiV2User() } else { diff --git a/frontend/src/app/template/template-list/template-list.component.ts b/frontend/src/app/template/template-list/template-list.component.ts index d93f685b6..32e35e686 100644 --- a/frontend/src/app/template/template-list/template-list.component.ts +++ b/frontend/src/app/template/template-list/template-list.component.ts @@ -10,12 +10,13 @@ import { Router } from "@angular/router"; import { MatDialog } from "@angular/material/dialog"; import { MatSnackBar } from "@angular/material/snack-bar"; import { TemplateCreateDialogComponent } from "../template-create-dialog/template-create-dialog.component"; -import { filter, flatMap, take, tap } from "rxjs/operators"; +import { filter, take, tap } from "rxjs/operators"; import { ConfirmationDialogComponent, ConfirmationDialogComponentData } from "../../shared/confirmation-dialog/confirmation-dialog.component"; import { EvaluationStartDialogComponent, EvaluationStartDialogResult } from "../evaluation-start-dialog/evaluation-start-dialog.component"; +import {mergeMap} from 'rxjs'; @Component({ selector: 'app-template-list', @@ -46,7 +47,7 @@ export class TemplateListComponent implements AfterViewInit{ .afterClosed() .pipe( filter((r) => r !=null), - flatMap((r: ApiCreateEvaluation) => { + mergeMap((r: ApiCreateEvaluation) => { return this.templateService.postApiV2Template(r); }) ).subscribe( @@ -61,15 +62,15 @@ export class TemplateListComponent implements AfterViewInit{ } public refresh(){ - this.templateService.getApiV2TemplateList().subscribe( - (results: ApiEvaluationTemplateOverview[]) => { + this.templateService.getApiV2TemplateList().subscribe({ + next: (results: ApiEvaluationTemplateOverview[]) => { this.templates = results; }, - (err) => { + error: (err) => { this.templates = []; this.snackBar.open(`Error: ${err?.error?.description}`, null, {duration: 5000}) } - ); + }); } public createEvaluation(id: string){ @@ -107,7 +108,7 @@ export class TemplateListComponent implements AfterViewInit{ dialogRef.afterClosed().pipe( filter((r) => r!= null), tap((r) => (this.waitingForRun[templateId] = true)), - flatMap((r: EvaluationStartDialogResult) => { + mergeMap((r: EvaluationStartDialogResult) => { return this.evaluationAdminService.postApiV2EvaluationAdminCreate({ templateId: templateId, name: r.name, diff --git a/frontend/src/app/user/admin-user-list/admin-user-list.component.ts b/frontend/src/app/user/admin-user-list/admin-user-list.component.ts index 8bba04796..5fd977578 100644 --- a/frontend/src/app/user/admin-user-list/admin-user-list.component.ts +++ b/frontend/src/app/user/admin-user-list/admin-user-list.component.ts @@ -2,11 +2,12 @@ import { AfterViewInit, Component, ViewChild } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatDialog } from '@angular/material/dialog'; import { AdminUserCreateOrEditDialogComponent } from '../admin-user-create-or-edit-dialog/admin-user-create-or-edit-dialog.component'; -import { filter, flatMap } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; import { MatSort, Sort } from '@angular/material/sort'; import { LiveAnnouncer } from '@angular/cdk/a11y'; import { MatTableDataSource } from '@angular/material/table'; import {ApiUser, UserRequest, UserService} from '../../../../openapi'; +import {mergeMap} from 'rxjs'; @Component({ selector: 'app-admin-user-list', @@ -37,19 +38,19 @@ export class AdminUserListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - flatMap((u: UserRequest) => { + mergeMap((u: UserRequest) => { return this.userService.postApiV2User(u); }) ) - .subscribe( - (r) => { + .subscribe({ + next: (r) => { this.refresh(); - this.snackBar.open(`Successfully created ${r.username}`, null, { duration: 5000 }); + this.snackBar.open(`Successfully created ${r.username}`, null, {duration: 5000}); }, - (err) => { - this.snackBar.open(`Error: ${err.error.description}`, null, { duration: 5000 }); + error: (err) => { + this.snackBar.open(`Error: ${err.error.description}`, null, {duration: 5000}); } - ); + }); } public edit(user: ApiUser) { @@ -58,48 +59,48 @@ export class AdminUserListComponent implements AfterViewInit { .afterClosed() .pipe( filter((r) => r != null), - flatMap((u: UserRequest) => { + mergeMap((u: UserRequest) => { console.debug(`Edit Result: ${u}`); return this.userService.patchApiV2UserByUserId(user.id, u); }) ) - .subscribe( - (r) => { + .subscribe({ + next: (r) => { this.refresh(); - this.snackBar.open(`Successfully updated ${r.username}`, null, { duration: 5000 }); + this.snackBar.open(`Successfully updated ${r.username}`, null, {duration: 5000}); }, - (err) => { - this.snackBar.open(`Error: ${err.error.description}`, null, { duration: 5000 }); + error: (err) => { + this.snackBar.open(`Error: ${err.error.description}`, null, {duration: 5000}); } - ); + }); } public delete(userId: string) { if (confirm(`Do you really want to delete user (${userId})?`)) { - this.userService.deleteApiV2UserByUserId(userId).subscribe( - (u: ApiUser) => { + this.userService.deleteApiV2UserByUserId(userId).subscribe({ + next: (u: ApiUser) => { this.refresh(); - this.snackBar.open(`Success: ${u.username} (${u.id}) deleted`, null, { duration: 5000 }); + this.snackBar.open(`Success: ${u.username} (${u.id}) deleted`, null, {duration: 5000}); }, - (err) => { - this.snackBar.open(`Error: ${err.error.description}`, null, { duration: 5000 }); + error: (err) => { + this.snackBar.open(`Error: ${err.error.description}`, null, {duration: 5000}); } - ); + }); } } public refresh() { - this.userService.getApiV2UserList().subscribe( - (users: ApiUser[]) => { + this.userService.getApiV2UserList().subscribe({ + next: (users: ApiUser[]) => { this.dataSource.data = users; this.dataSource.sort = this.sort; }, - (error) => { + error: (error) => { this.dataSource.data = []; this.dataSource.sort = this.sort; - this.snackBar.open(`Error: ${error.error.description}`, null, { duration: 5000 }); + this.snackBar.open(`Error: ${error.error.description}`, null, {duration: 5000}); } - ); + }); } ngAfterViewInit(): void { diff --git a/frontend/src/app/user/profile/profile.component.ts b/frontend/src/app/user/profile/profile.component.ts index 7eb6b4bbc..1299b78cd 100644 --- a/frontend/src/app/user/profile/profile.component.ts +++ b/frontend/src/app/user/profile/profile.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { first, flatMap, tap } from 'rxjs/operators'; +import { first } from 'rxjs/operators'; import { AuthenticationService } from '../../services/session/authentication.sevice'; import {ApiUser, UserRequest, UserService} from '../../../../openapi'; @@ -59,15 +59,15 @@ export class ProfileComponent implements OnInit, OnDestroy { this.authenticationService .updateUser(usr) .pipe(first()) - .subscribe( - (r: ApiUser) => { + .subscribe({ + next: (r: ApiUser) => { this.snackBar.open(`Save successful!`, null, { duration: 5000 }); this.toggleEdit(); }, - (error) => { + error: (error) => { this.snackBar.open(`Save failed: ${error.error.description}!`, null, { duration: 5000 }); } - ); + }); } } diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index 04f232609..23df0b35c 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -1,11 +1,10 @@ import {AfterViewInit, Component, Inject, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import {BehaviorSubject, interval, merge, Observable, of, Subscription, zip} from 'rxjs'; +import {BehaviorSubject, interval, merge, mergeMap, Observable, of, Subscription, zip} from 'rxjs'; import { catchError, delay, filter, - flatMap, map, pairwise, retryWhen, @@ -181,7 +180,7 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /* Basic observable for web socket messages received from the DRES server. */ this.webSocket = this.evaluationId.pipe( - flatMap((evaluationId) => + mergeMap((evaluationId) => this.webSocketSubject.multiplex( () => { return { evaluationId: evaluationId, type: 'REGISTER' } as IWsClientMessage; diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index a657ae629..d9a97cc90 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -8,8 +8,8 @@ import { OnDestroy, ViewChild, } from '@angular/core'; -import {BehaviorSubject, combineLatest, merge, Observable, of, Subscription} from 'rxjs'; -import {catchError, filter, flatMap, map, pairwise, retry, shareReplay, switchMap, withLatestFrom,} from 'rxjs/operators'; +import {BehaviorSubject, combineLatest, merge, mergeMap, Observable, of, Subscription} from 'rxjs'; +import {catchError, filter, map, pairwise, retry, shareReplay, switchMap, withLatestFrom,} from 'rxjs/operators'; import {AppConfig} from '../app.config'; import {animate, keyframes, style, transition, trigger} from '@angular/animations'; import { @@ -217,7 +217,7 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { }) ), this.resetHighlight.pipe( - flatMap(() => this.info), + mergeMap(() => this.info), map((info) => { const hightlight = new Map(); info.teams.forEach((t) => hightlight.set(t.id, 'nohighlight')); From fe91f0825ecb81ba404108100cb1be3d702d4fae Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 21 Jul 2023 16:12:42 +0200 Subject: [PATCH 367/498] Tasks can now be moved to a different group of the same type Resolves #381 --- .../competition-form.builder.ts | 24 ++++++++++--------- .../task-template-editor.component.html | 14 ++++++++++- .../task-template-editor.component.ts | 9 +++++-- .../template-builder.service.ts | 4 ++++ 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 505207c81..e12127a36 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -20,25 +20,26 @@ export class CompetitionFormBuilder { /** The default duration of a query hint. This is currently a hard-coded constant. */ private static DEFAULT_HINT_DURATION = 30; - /** The {@link FormGroup} held by this {@link CompetitionFormBuilder}. */ + /** The {@link UntypedFormGroup} held by this {@link CompetitionFormBuilder}. */ public form: UntypedFormGroup; /** * Constructor for CompetitionFormBuilder. * - * @param taskGroup The {@link TaskGroup} to create this {@link CompetitionFormBuilder} for. - * @param taskType The {@link TaskType} to create this {@link CompetitionFormBuilder} for. + * @param taskGroup The {@link ApiTaskGroup} to create this {@link CompetitionFormBuilder} for. + * @param taskType The {@link ApiTaskType} to create this {@link CompetitionFormBuilder} for. * @param collectionService The {@link CollectionService} reference used to fetch data through the DRES API. + * @param builderService The {@link TemplateBuilderService} reference * @param data The {@link ApiTaskTemplate} to initialize the form with. */ constructor( - private taskGroup: ApiTaskGroup, + taskGroup: ApiTaskGroup, private taskType: ApiTaskType, private collectionService: CollectionService, private builderService: TemplateBuilderService, private data?: ApiTaskTemplate ) { - this.initializeForm(); + this.initializeForm(taskGroup); } /** List of data sources managed by this CompetitionFormBuilder. */ @@ -58,7 +59,7 @@ export class CompetitionFormBuilder { } /** - * Returns the {@link Observable} for the given key. + * Returns the {@link Observable} for the given key. * * @param key Key to fetch the data source for. */ @@ -170,8 +171,8 @@ export class CompetitionFormBuilder { // FIXME semantic check for the entire fetching const data = { name: this.form.get('name').value, - taskGroup: this.taskGroup.name /* Cannot be edited! */, - taskType: this.taskGroup.type /* Cannot be edited! */, + taskGroup: this.form.get('taskGroup').value, + taskType: this.taskType.name /* Cannot be edited! */, duration: this.form.get('duration').value, collectionId: this.form.get('mediaCollection').value, hints: (this.form.get('components') as UntypedFormArray).controls.map((c) => { @@ -214,8 +215,8 @@ export class CompetitionFormBuilder { public storeFormData(){ this.data.name = this.form.get('name').value; - this.data.taskGroup = this.taskGroup.name /* Cannot be edited! */; - this.data.taskType = this.taskGroup.type /* Cannot be edited! */; + this.data.taskGroup = this.form.get('taskGroup').value; + this.data.taskType = this.taskType.name /* Cannot be edited! */; this.data.duration= this.form.get('duration').value; this.data.collectionId = this.form.get('mediaCollection').value; this.data.hints = (this.form.get('components') as UntypedFormArray).controls.map((c) => { @@ -277,10 +278,11 @@ export class CompetitionFormBuilder { /** * Initializes the {@link FormGroup}. */ - private initializeForm() { + private initializeForm(taskGroup: ApiTaskGroup) { this.form = new UntypedFormGroup({ id: new UntypedFormControl(this.data?.id), name: new UntypedFormControl(this.data?.name, [Validators.required]), + taskGroup: new UntypedFormControl(taskGroup.name), duration: new UntypedFormControl(this.durationInitValue, [Validators.required, Validators.min(1)]), mediaCollection: new UntypedFormControl(this.data?.collectionId ?? this.builderService.defaultCollection, [Validators.required]), }); diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 9305e7c7c..0829e09e0 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -11,7 +11,6 @@

UID: {{ form.get('id').value }}

-

Task group: {{taskGroup.name}}

Task type: {{taskType.name}}

@@ -23,6 +22,19 @@ +

+ + + + {{ group.name }} + + + +

+

Name diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index f9adbfdd7..1352ce0ef 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -61,7 +61,8 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { private imagePreviewMap = new Set(); private taskSub: Subscription; - + + taskGroupOptions: ApiTaskGroup[] constructor(private builderService: TemplateBuilderService, public collectionService: CollectionService, @@ -82,6 +83,9 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { } }) + + + } ngOnDestroy() { @@ -94,9 +98,10 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { this.form = this.formBuilder.form; this.form.valueChanges.subscribe(newValue => { this.formBuilder.storeFormData(); - this.builderService.markDirty() + this.builderService.markDirty(); }); this.mediaCollectionSource = this.collectionService.getApiV2CollectionList(); + this.taskGroupOptions = this.builderService.findGroupsByType(this.taskType) /* Close open video preview */ this.showVideo = false; } diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 63caee128..df30af855 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -92,6 +92,10 @@ export class TemplateBuilderService { return this.getTemplate().taskTypes.find((v) => v.name === name); } + public findGroupsByType(type: ApiTaskType) { + return this.getTemplate().taskGroups.filter(g => g.type === type.name); + } + public initialise(template: ApiEvaluationTemplate){ this.unmarkDirty(); this.templateSubject.next(template); From 4b2cade0e1b47e3e4c43d59642274dac03f97d67 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 24 Jul 2023 08:46:12 +0200 Subject: [PATCH 368/498] Optimised performance of UpdateEvaluationTemplateHandler. Fixes #407. --- .../UpdateEvaluationTemplateHandler.kt | 266 ++++++++---------- 1 file changed, 110 insertions(+), 156 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 935819482..d452ed962 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -21,7 +21,9 @@ import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.query.* +import kotlinx.dnq.util.getSafe import org.joda.time.DateTime +import kotlin.time.ExperimentalTime /** * A [AbstractEvaluationTemplateHandler] that can be used to create a new [DbEvaluationTemplate]. @@ -29,7 +31,7 @@ import org.joda.time.DateTime * @author Ralph Gasser * @author Luca Rossetto * @author Loris Sauter - * @version 2.0.0 + * @version 2.1.0 */ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: Config) : AbstractEvaluationTemplateHandler(store), PatchRestHandler { @@ -80,28 +82,23 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C /* Update task type information. */ val taskTypes = apiValue.taskTypes.map { it.name }.toTypedArray() existing.taskTypes.removeAll( - DbTaskType.query( - DbTaskType::evaluation eq existing and not( - DbTaskType::name.containsIn(*taskTypes) - ) - ) + DbTaskType.query(DbTaskType::evaluation eq existing and not(DbTaskType::name.containsIn(*taskTypes))) ) - for (type in apiValue.taskTypes) { - val t = - DbTaskType.findOrNew(DbTaskType.query((DbTaskType::name eq type.name) and (DbTaskType::evaluation eq existing))) { - this.name = type.name - } - t.duration = type.duration - t.score = type.scoreOption.toDb() - t.target = type.targetOption.toDb() - t.hints.clear() - t.hints.addAll(type.hintOptions.map { it.toDb() }) - t.submission.clear() - t.submission.addAll(type.submissionOptions.map { it.toDb() }) - t.options.clear() - t.options.addAll(type.taskOptions.map { it.toDb() }) - t.configurations.clear() - t.configurations.addAll(type.configuration.entries.map { + for (apiTaskType in apiValue.taskTypes) { + val taskType = DbTaskType.findOrNew(DbTaskType.query((DbTaskType::name eq apiTaskType.name) and (DbTaskType::evaluation eq existing))) { + this.name = apiTaskType.name + } + taskType.duration = apiTaskType.duration + taskType.score = apiTaskType.scoreOption.toDb() + taskType.target = apiTaskType.targetOption.toDb() + taskType.hints.clear() + taskType.hints.addAll(apiTaskType.hintOptions.map { it.toDb() }) + taskType.submission.clear() + taskType.submission.addAll(apiTaskType.submissionOptions.map { it.toDb() }) + taskType.options.clear() + taskType.options.addAll(apiTaskType.taskOptions.map { it.toDb() }) + taskType.configurations.clear() + taskType.configurations.addAll(apiTaskType.configuration.entries.map { DbConfiguredOption.new { this.key = it.key this.value = it.value @@ -109,202 +106,159 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C }) /* Establish relationship if entry is new. */ - if (t.isNew) { - existing.taskTypes.add(t) + if (taskType.isNew) { + existing.taskTypes.add(taskType) } } /* Update task group information. */ val taskGroups = apiValue.taskGroups.map { it.name }.toTypedArray() existing.taskGroups.removeAll( - DbTaskGroup.query( - DbTaskGroup::evaluation eq existing and not( - DbTaskGroup::name.containsIn(*taskGroups) - ) - ) + DbTaskGroup.query(DbTaskGroup::evaluation eq existing and not(DbTaskGroup::name.containsIn(*taskGroups))) ) - for (group in apiValue.taskGroups) { - val g = - DbTaskGroup.findOrNew(DbTaskGroup.query((DbTaskGroup::name eq group.name) and (DbTaskGroup::evaluation eq existing))) { - this.name = group.name - } - g.type = - DbTaskType.query((DbTaskType::name eq group.type) and (DbTaskType::evaluation eq existing)) - .firstOrNull() ?: throw ErrorStatusException( - 404, - "Unknown task group ${group.type} for evaluation ${apiValue.id}.", - ctx - ) + for (apiTaskGroup in apiValue.taskGroups) { + val taskGroup = DbTaskGroup.findOrNew(DbTaskGroup.query((DbTaskGroup::name eq apiTaskGroup.name) and (DbTaskGroup::evaluation eq existing))) { + this.name = apiTaskGroup.name + } + + /* Update task type if it has changed. */ + if (taskGroup.getSafe(DbTaskGroup::type)?.name != apiTaskGroup.name) { + taskGroup.type = DbTaskType.query((DbTaskType::name eq apiTaskGroup.type) and (DbTaskType::evaluation eq existing)).firstOrNull() + ?: throw ErrorStatusException(404, "Unknown task group ${apiTaskGroup.type} for evaluation ${apiValue.id}.", ctx) + } /* Establish relationship if entry is new. */ - if (g.isNew) { - existing.taskGroups.add(g) + if (taskGroup.isNew) { + existing.taskGroups.add(taskGroup) } } - /* Update task information. */ + /* Update task information: Remove deleted tasks. */ val taskIds = apiValue.tasks.mapNotNull { it.id }.toTypedArray() - DbTaskTemplate.query( - DbTaskTemplate::evaluation eq existing and not( - DbTaskTemplate::id.containsIn( - *taskIds - ) - ) - ).asSequence().forEach { - it.delete() - } - for (task in apiValue.tasks) { - val t = if (task.id != null) { - existing.tasks.filter { it.id eq task.id }.firstOrNull() - ?: throw ErrorStatusException( - 404, - "Unknown task ${task.id} for evaluation ${apiValue.id}.", - ctx - ) + existing.tasks.removeAll(DbTaskTemplate.query(DbTaskTemplate::evaluation eq existing and not(DbTaskTemplate::id.containsIn(*taskIds)))) + + /* Update task information: Remaining tasks. */ + for (apiTask in apiValue.tasks) { + val task = if (apiTask.id != null) { + existing.tasks.filter { it.id eq apiTask.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown task ${apiTask.id} for evaluation ${apiValue.id}.", ctx) } else { - DbTaskTemplate.new() + val t = DbTaskTemplate.new() + existing.tasks.add(t) + t + } + + /* Update parameters that do no require lookup. */ + task.name = apiTask.name + task.duration = apiTask.duration + task.comment = apiTask.comment + + /* Conditional updating of parameters that do!. */ + if (task.collection.id != apiTask.collectionId) { + task.collection = DbMediaCollection.query(DbMediaCollection::id eq apiTask.collectionId).first() + } + + if (task.taskGroup.name != apiTask.taskGroup) { + task.taskGroup = DbTaskGroup.query(DbTaskGroup::name eq apiTask.taskGroup).first() } - t.name = task.name - t.duration = task.duration - t.collection = - DbMediaCollection.query(DbMediaCollection::id eq task.collectionId).first() - t.taskGroup = DbTaskGroup.query(DbTaskGroup::name eq task.taskGroup).first() - t.comment = task.comment /* Update task targets. */ - t.targets.clear() - for (target in task.targets) { - t.targets.add(DbTaskTemplateTarget.new { + task.targets.clear() + for (target in apiTask.targets) { + task.targets.add(DbTaskTemplateTarget.new { this.type = target.type.toDb() this.start = target.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() this.end = target.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - }.also { when (target.type) { - ApiTargetType.TEXT -> it.text = target.target - else -> it.item = - DbMediaItem.query(DbMediaItem::id eq target.target).firstOrNull() + ApiTargetType.TEXT -> this.text = target.target + else -> this.item = target.target?.let { DbMediaItem.query(DbMediaItem::id eq it).firstOrNull() } } }) } /* Update task hints. */ - t.hints.clear() - for (hint in task.hints) { - val item = DbMediaItem.query(DbMediaItem::id eq hint.mediaItem).firstOrNull() - t.hints.add(DbHint.new { + task.hints.clear() + for (hint in apiTask.hints) { + task.hints.add(DbHint.new { this.type = hint.type.toDb() - this.item = item + this.item = hint.mediaItem?.let { DbMediaItem.query(DbMediaItem::id eq hint.mediaItem).firstOrNull() } this.text = hint.description this.path = hint.path this.start = hint.start this.end = hint.end - this.temporalRangeStart = - hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - this.temporalRangeEnd = - hint.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + this.temporalRangeStart = hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + this.temporalRangeEnd = hint.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() }) } - - /* Establish relationship if entry is new. */ - if (t.isNew) { - existing.tasks.add(t) - } } /* Update team information. */ val teamIds = apiValue.teams.map { it.id }.toTypedArray() - existing.teams.removeAll( - DbTeam.query( - DbTeam::evaluation eq existing and not( - DbTeam::id.containsIn( - *teamIds - ) - ) - ) - ) - for (team in apiValue.teams) { - val t = if (team.id != null) { - existing.teams.filter { it.id eq team.id }.firstOrNull() - ?: throw ErrorStatusException( - 404, - "Unknown team ${team.id} for evaluation ${apiValue.id}.", - ctx - ) + existing.teams.removeAll(DbTeam.query(DbTeam::evaluation eq existing and not(DbTeam::id.containsIn(*teamIds)))) + for (apiTeam in apiValue.teams) { + val team = if (apiTeam.id != null) { + existing.teams.filter { it.id eq apiTeam.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team ${apiTeam.id} for evaluation ${apiValue.id}.", ctx) } else { - DbTeam.new() + val t = DbTeam.new() + existing.teams.add(t) /* Establish new relationship. */ + t } - t.name = team.name ?: throw ErrorStatusException( - 404, - "Team name must be specified.", - ctx - ) - t.color = team.color ?: throw ErrorStatusException( - 404, - "Team colour must be specified.", - ctx - ) + team.name = apiTeam.name ?: throw ErrorStatusException(404, "Team name must be specified.", ctx) + team.color = apiTeam.color ?: throw ErrorStatusException(404, "Team colour must be specified.", ctx) /* Process logo data. */ - val logoData = team.logoStream() + val logoData = apiTeam.logoStream() if (logoData != null) { - t.logo = logoData + team.logo = logoData } - t.users.clear() - t.users.addAll(DbUser.query(DbUser::id.containsIn(*team.users.map { it.id } - .toTypedArray()))) - - /* Establish relationship if entry is new. */ - if (t.isNew) { - existing.teams.add(t) + /* Make association with users. */ + val userIds = apiTeam.users.map { it.id }.toTypedArray() + team.users.removeAll(DbUser.query(not(DbUser::id.containsIn(*userIds)))) + for (userId in userIds) { + val user = DbUser.filter { it.id eq userId }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown user $userId for evaluation ${apiValue.id}.", ctx) + if (!team.users.contains(user)) { + team.users.add(user) + } } } /* Update teamGroup information */ val teamGroupIds = apiValue.teamGroups.map { it.id }.toTypedArray() - existing.teamGroups.removeAll( - DbTeamGroup.query( - DbTeamGroup::evaluation eq existing and not( - DbTeamGroup::id.containsIn(*teamGroupIds) - ) - ) - ) - for (teamGroup in apiValue.teamGroups) { - val t = if (teamGroup.id != null) { - existing.teamGroups.filter { it.id eq teamGroup.id }.firstOrNull() - ?: throw ErrorStatusException( - 404, - "Unknown team groum ${teamGroup.id} for evaluation ${apiValue.id}.", - ctx - ) + existing.teamGroups.removeAll(DbTeamGroup.query(DbTeamGroup::evaluation eq existing and not(DbTeamGroup::id.containsIn(*teamGroupIds)))) + + for (apiTeamGroup in apiValue.teamGroups) { + val teamGroup = if (apiTeamGroup.id != null) { + existing.teamGroups.filter { it.id eq apiTeamGroup.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team group ${apiTeamGroup.id} for evaluation ${apiValue.id}.", ctx) } else { - DbTeamGroup.new() + val tg = DbTeamGroup.new() + existing.teamGroups.add(tg) + tg } - t.name = teamGroup.name ?: throw ErrorStatusException( - 404, - "Team group name must be specified.", - ctx - ) - t.defaultAggregator = teamGroup.aggregation.toDb() + teamGroup.name = apiTeamGroup.name ?: throw ErrorStatusException(404, "Team group name must be specified.", ctx) + teamGroup.defaultAggregator = apiTeamGroup.aggregation.toDb() - t.teams.clear() - t.teams.addAll(DbTeam.query(DbTeam::id.containsIn(*teamGroup.teams.map { it.id } - .toTypedArray()))) + teamGroup.teams.clear() + teamGroup.teams.addAll(DbTeam.query(DbTeam::id.containsIn(*apiTeamGroup.teams.map { it.id }.toTypedArray()))) /* Establish relationship if entry is new. */ - if (t.isNew) { - existing.teamGroups.add(t) + if (teamGroup.isNew) { + existing.teamGroups.add(teamGroup) } } /* Update judge information */ - existing.judges.clear() - existing.judges.addAll(DbUser.query(DbUser::id.containsIn(*apiValue.judges.toTypedArray()))) + val judgeIds = apiValue.judges.toTypedArray() + existing.judges.removeAll(DbUser.query(not(DbUser::id.containsIn(*judgeIds)))) + for (userId in judgeIds) { + val user = DbUser.filter { it.id eq userId }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown user $userId for evaluation ${apiValue.id}.", ctx) + if (!existing.judges.contains(user)) { + existing.judges.add(user) + } + } } return SuccessStatus("Competition with ID ${apiValue.id} was updated successfully.") } - } From d1e7e09469e2ae191102cb6f928b9bb35c4fe952 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 24 Jul 2023 09:16:02 +0200 Subject: [PATCH 369/498] Fixed issue in RunAdminViewComponent that would generate dozens of requests per second. Should address #419. --- .../evaluation/admin/ForceViewerHandler.kt | 4 ++-- .../run-admin-toolbar.component.html | 4 ++-- .../run-admin-toolbar.component.ts | 16 ++++++++-------- .../src/app/run/run-admin-view.component.html | 2 +- frontend/src/app/run/run-admin-view.component.ts | 14 +++++++------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index 59cb217a5..fc4452195 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -47,12 +47,12 @@ class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHa val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { if (evaluationManager.overrideReadyState(rac, viewerId)) { - SuccessStatus("State for viewer $viewerId forced successfully.") + SuccessStatus("State for viewer $viewerId (evaluation '$evaluationId') forced successfully.") } else { throw ErrorStatusException(404, "Viewer $viewerId does not exist!'", ctx) } } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Viewer state for viewer $viewerId (evaluation $evaluationId) could not be enforced because evaluationId is in the wrong state (state = ${evaluationManager.status}).", ctx) + throw ErrorStatusException(400, "State for viewer $viewerId (evaluation '$evaluationId') could not be enforced because evaluation is in the wrong state (state = ${evaluationManager.status}).", ctx) } } } diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.html b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.html index 80a1d59c8..14db6e703 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.html +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.html @@ -67,14 +67,14 @@ [icon]="'file_download'" [contentType]="'text/csv'" [fileName]="scoreFileProvider((run | async)?.runInfo?.name)" - [downloadProvider]="scoreDownloadProvider(runId | async)" + [downloadProvider]="scoreDownloadProvider(runId)" [inline]="true" matTooltip="Download the scores of this run as CSV" > diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index 4d7d38453..e6627a22a 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -25,7 +25,7 @@ import { styleUrls: ['./run-admin-toolbar.component.scss'], }) export class RunAdminToolbarComponent implements OnInit { - @Input() runId: BehaviorSubject = new BehaviorSubject(''); + @Input() runId: string; @Input() run: Observable; @Input() refreshSubject: Subject = new Subject(); @@ -44,7 +44,7 @@ export class RunAdminToolbarComponent implements OnInit { ) {} public start() { - const runId = this.runId.value; + const runId = this.runId; this.runAdminService.postApiV2EvaluationAdminByEvaluationIdStart(runId).subscribe( (r) => { this.refreshSubject.next(); @@ -67,7 +67,7 @@ export class RunAdminToolbarComponent implements OnInit { .afterClosed() .subscribe((result) => { if (result) { - const runId = this.runId.value; + const runId = this.runId; this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTerminate(runId).subscribe( (r) => { this.refreshSubject.next(); @@ -83,7 +83,7 @@ export class RunAdminToolbarComponent implements OnInit { } public navigateToViewer() { - const runId = this.runId.value; + const runId = this.runId; console.log("Navigate (Admin): ", runId) /* TODO: Setup depends on type of evaluation run. */ this.router.navigate([ @@ -99,7 +99,7 @@ export class RunAdminToolbarComponent implements OnInit { } public navigateToJudgement() { - const runId = this.runId.value; + const runId = this.runId; this.router.navigate(['/judge', runId]); } @@ -108,7 +108,7 @@ export class RunAdminToolbarComponent implements OnInit { * */ public navigateToVoting() { - const runId = this.runId.value; + const runId = this.runId; this.router.navigate(['/vote', runId]); } @@ -118,7 +118,7 @@ export class RunAdminToolbarComponent implements OnInit { * @param runId ID of the run to navigate to. */ public navigateToAdmin() { - const runId = this.runId.value; + const runId = this.runId; this.router.navigate(['/evaluation/admin', runId]); } @@ -128,7 +128,7 @@ export class RunAdminToolbarComponent implements OnInit { * @param runId ID of the run to navigate to. */ public navigateToScoreHistory() { - const runId = this.runId.value; + const runId = this.runId; this.router.navigate(['/evaluation/scores', runId]); } diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 12150fcb9..655aa47d0 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -1,4 +1,4 @@ - + diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts index 5acb1caf7..77aabbc22 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-builder-task-dialog.component.ts @@ -84,7 +84,7 @@ export class CompetitionBuilderTaskDialogComponent { /** * Handler for (+) button for query target form component. */ - public addQueryTarget(targetType: ApiTargetOption | 'MULTI' = 'MULTI') { + public addQueryTarget(targetType: ApiTargetOption) { this.builder.addTargetForm(targetType); } diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index e12127a36..61f7cd45a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -122,15 +122,22 @@ export class CompetitionFormBuilder { * * @param type The {@link TaskType.TargetTypeEnum} to add a {@link FormGroup} for. */ - public addTargetForm(type: ApiTargetOption | 'MULTI') { + public addTargetForm(type: ApiTargetOption) { const array = this.form.get('target') as UntypedFormArray; const newIndex = array.length; switch (type) { - // FIXME to make compiler happy. obviously this is semantically not appropriate - case 'MULTI': - const targetForm = this.singleMediaItemTargetForm(newIndex); + case "SINGLE_MEDIA_ITEM": + const f = this.singleMediaItemTargetForm(newIndex); + array.push(f) + return f; + case "SINGLE_MEDIA_SEGMENT": + const targetForm = this.singleMediaSegmentTargetForm(newIndex); array.push(targetForm); return targetForm; + case "JUDGEMENT": + case "VOTE": + console.warn("Judgement and Vote shouldn't have access to add targets. This is a programmer's error.") + break; case 'TEXT': const form = this.singleTextTargetForm(); array.push(form); @@ -298,7 +305,7 @@ export class CompetitionFormBuilder { case 'SINGLE_MEDIA_ITEM': return new UntypedFormArray([this.singleMediaItemTargetForm(0, this.data?.targets[0] ?? null)]); case 'SINGLE_MEDIA_SEGMENT': - return new UntypedFormArray([this.singleMediaSegmentTargetForm(this.data?.targets[0] ?? null)]); + return new UntypedFormArray([this.singleMediaSegmentTargetForm(0,this.data?.targets[0] ?? null)]); case 'JUDGEMENT': return new UntypedFormArray([new UntypedFormGroup({type: new UntypedFormControl(ApiTargetType.JUDGEMENT)})]); case 'VOTE': @@ -361,15 +368,16 @@ export class CompetitionFormBuilder { /** * Returns FormGroup for a single Media Segment Target. * + * @param index Index of the FormControl * @param initialize The optional {RestTaskDescriptionTargetItem} to initialize the form with. */ - private singleMediaSegmentTargetForm(initialize?: ApiTarget) { + private singleMediaSegmentTargetForm(index: number, initialize?: ApiTarget) { /* Prepare auto complete field. */ const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); const typeFormControl = new UntypedFormControl(ApiTargetType.MEDIA_ITEM_TEMPORAL_RANGE); this.dataSources.set( - `target.0.mediaItem`, + `target.${index}.mediaItem`, mediaItemFormControl.valueChanges.pipe( filter((s) => s.length >= 1), switchMap((s) => diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 0829e09e0..195b8837e 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -64,18 +64,8 @@

Target - diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index 1352ce0ef..e6315faa1 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -382,7 +382,7 @@ export class TaskTemplateEditorComponent implements OnInit, OnDestroy { const mediaCollectionId = this.formBuilder.form.get('mediaCollection').value; this.collectionService.postApiV2CollectionByCollectionIdResolve(mediaCollectionId, r).subscribe((items) => { items.forEach((item) => { - const form = this.formBuilder.addTargetForm("MULTI"); + const form = this.formBuilder.addTargetForm("SINGLE_MEDIA_ITEM"); console.log(`Adding new mediaItem as target ${mediaCollectionId}/${item.name}`); form.get('mediaItem').setValue(item); }); From 12892e94cc248ab10f29554192022db0024cafea Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 24 Jul 2023 20:42:28 +0200 Subject: [PATCH 374/498] Resolved #338 and fixed display of multiple targets --- .../competition-form.builder.ts | 22 +++++++++----- .../app/services/pipes/filter-not-in.pipe.ts | 29 +++++++++++++++++++ frontend/src/app/services/services.module.ts | 5 +++- .../task-template-editor.component.html | 2 +- .../task-template-editor.component.ts | 1 - .../template-builder-components.module.ts | 1 - 6 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 frontend/src/app/services/pipes/filter-not-in.pipe.ts diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index 61f7cd45a..a3a288405 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -1,5 +1,5 @@ import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; -import { filter, first, switchMap } from 'rxjs/operators'; +import { filter, first, map, switchMap } from "rxjs/operators"; import { Observable } from 'rxjs'; import { RequireMatch } from './require-match'; import { TimeUtilities } from '../../../utilities/time.utilities'; @@ -67,6 +67,10 @@ export class CompetitionFormBuilder { return this.dataSources.get(key); } + public getTargetMediaItems(): ApiMediaItem[]{ + return this.form.get('target')['controls'].map(it => it.get('mediaItem').value) + } + /** * Adds a new {@link FormGroup} for the given {@link ConfiguredOptionQueryComponentType.OptionEnum}. * @@ -302,10 +306,6 @@ export class CompetitionFormBuilder { */ private formForTarget() { switch (this.taskType.targetOption) { - case 'SINGLE_MEDIA_ITEM': - return new UntypedFormArray([this.singleMediaItemTargetForm(0, this.data?.targets[0] ?? null)]); - case 'SINGLE_MEDIA_SEGMENT': - return new UntypedFormArray([this.singleMediaSegmentTargetForm(0,this.data?.targets[0] ?? null)]); case 'JUDGEMENT': return new UntypedFormArray([new UntypedFormGroup({type: new UntypedFormControl(ApiTargetType.JUDGEMENT)})]); case 'VOTE': @@ -319,11 +319,19 @@ export class CompetitionFormBuilder { text.push(this.singleTextTargetForm()); } return new UntypedFormArray(text); - default: + case 'SINGLE_MEDIA_SEGMENT': + case 'SINGLE_MEDIA_ITEM': // Handling multiple here, since it's the default. const content: UntypedFormGroup[] = []; + const targetOption = this.taskType.targetOption if (this.data?.targets) { - this.data?.targets?.forEach((t, i) => content.push(this.singleMediaItemTargetForm(i, t))); + this.data?.targets?.forEach((t, i) => { + if(targetOption === "SINGLE_MEDIA_ITEM"){ + content.push(this.singleMediaItemTargetForm(i, t)) + }else{ + content.push(this.singleMediaSegmentTargetForm(i, t)) + } + }); } else { content.push(this.singleMediaItemTargetForm(0)); } diff --git a/frontend/src/app/services/pipes/filter-not-in.pipe.ts b/frontend/src/app/services/pipes/filter-not-in.pipe.ts new file mode 100644 index 000000000..e8d3e6974 --- /dev/null +++ b/frontend/src/app/services/pipes/filter-not-in.pipe.ts @@ -0,0 +1,29 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +/** + * Simple pipe to filter an array such that those element not in the haytack are kept. + */ +@Pipe({ + name: 'filterNotIn' +}) +export class FilterNotInPipe implements PipeTransform { + + /** + * Filters a given array such that none of the elements in haystack are in the resulting array. + * + * @param value The array to filter + * @param haystack The haytack to search in + * @param propertyKey The key of the property to compare on. + */ + transform(value: any[], haystack: any[], propertyKey : string = null): any[] { + if(value){ + if(propertyKey){ + return value.filter(it => !haystack.map(item => item[propertyKey]).includes(it[propertyKey])) + } + return value.filter(it => !haystack.includes(it)); + }else{ + return []; + } + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 7a0d762e3..349b13233 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -20,6 +20,7 @@ import { ResolveMediaItemUrlPipe } from './pipes/resolve-media-item-url.pipe'; import { ResolveMediaItemPreviewPipe } from './pipes/resolve-media-item-preview.pipe'; import { FormatMediaItemPipe } from './pipes/format-media-item.pipe'; import { OrderByPipe } from './pipes/order-by.pipe'; +import { FilterNotInPipe } from './pipes/filter-not-in.pipe'; /** * Provides the {@link AppConfig} reference. @@ -53,7 +54,8 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveMediaItemUrlPipe, ResolveMediaItemPreviewPipe, FormatMediaItemPipe, - OrderByPipe + OrderByPipe, + FilterNotInPipe ], declarations: [ RoundPipePipe, @@ -72,6 +74,7 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveMediaItemPreviewPipe, FormatMediaItemPipe, OrderByPipe, + FilterNotInPipe, ], providers: [ AuthenticationService, diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html index 195b8837e..2bcdc5a12 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.html @@ -114,7 +114,7 @@

/> {{ mediaItem.name }} | diff --git a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts index e6315faa1..89a572090 100644 --- a/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts +++ b/frontend/src/app/template/template-builder/components/task-template-editor/task-template-editor.component.ts @@ -1,5 +1,4 @@ import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; -import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; import { TemplateBuilderService } from "../../template-builder.service"; import { Observable, Subscription } from "rxjs"; import { diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index b20aae255..e7be43508 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -22,7 +22,6 @@ import { MatSelectModule } from "@angular/material/select"; import { MatButtonToggleModule } from "@angular/material/button-toggle"; import { MatGridListModule } from "@angular/material/grid-list"; import { CompetitionBuilderModule } from "../../../competition/competition-builder/competition-builder.module"; -import { TemplateBuilderModule } from "../template-builder.module"; import { QueryDescriptionFormFieldComponent } from "./query-description-form-field/query-description-form-field.component"; import { QueryDescriptionTextFormFieldComponent } from "./query-description-text-form-field/query-description-text-form-field.component"; import { From 3d259e1df27d74bb61896127149b398a3fd8cc42 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 25 Jul 2023 09:45:33 +0200 Subject: [PATCH 375/498] Resolved #416 by enabling on team to be associated with multiple groups --- .../rest/handler/template/UpdateEvaluationTemplateHandler.kt | 5 ++--- .../dev/dres/data/model/template/DbEvaluationTemplate.kt | 2 +- .../main/kotlin/dev/dres/data/model/template/team/DbTeam.kt | 4 ++-- .../kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index a341f6db4..d183c80e9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -139,7 +139,6 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C /* Update task information: Remaining tasks. */ for (apiTask in apiValue.tasks) { - val newTask = apiTask.id == null val task = if (apiTask.id != null) { existing.tasks.filter { it.id eq apiTask.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown task ${apiTask.id} for evaluation ${apiValue.id}.", ctx) } else { @@ -154,11 +153,11 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C task.comment = apiTask.comment /* Conditional updating of parameters that do!. */ - if (newTask || task.collection.id != apiTask.collectionId) { + if (task.isNew || task.collection.id != apiTask.collectionId) { task.collection = DbMediaCollection.query(DbMediaCollection::id eq apiTask.collectionId).first() } - if (newTask || task.taskGroup.name != apiTask.taskGroup) { + if (task.isNew || task.taskGroup.name != apiTask.taskGroup) { task.taskGroup = DbTaskGroup.query(DbTaskGroup::name eq apiTask.taskGroup).first() } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 6f53fc619..249f1b898 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -159,7 +159,7 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio name = t.name color = t.color logo = t.logo - group = DbTeamGroup.query((DbTeamGroup::name eq t.group?.name)).firstOrNull() + groups.addAll(t.groups) users.addAll(t.users) } ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt index 06b6f0c54..5862ea441 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeam.kt @@ -43,8 +43,8 @@ class DbTeam(entity: Entity) : PersistentEntity(entity), Team { /** The [DbEvaluationTemplate] this [DbTeam] belongs to. */ val evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::teams) - /** The [DbTeamGroup] this [DbTeam] belongs to (or null if not assigned to a group). */ - var group: DbTeamGroup? by xdLink0_1(DbTeamGroup::teams) //FIXME a team should be able to belong to arbitrarily many team groups + /** The [DbTeamGroup]s this [DbTeam] belongs to (or 0 if not assigned to a group). */ + val groups by xdLink0_N(DbTeamGroup, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) /** The [DbUser]s that belong to this [DbTeam]. */ val users by xdLink0_N(DbUser, onDelete = OnDeletePolicy.CLEAR, onTargetDelete = OnDeletePolicy.CLEAR) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt index a3b80bdb9..574696e47 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt @@ -38,7 +38,7 @@ class DbTeamGroup(entity: Entity) : PersistentEntity(entity) { val evaluation: DbEvaluationTemplate by xdParent(DbEvaluationTemplate::teamGroups) /** The [DbTeam]s that belong to this [DbTeamGroup]. */ - val teams by xdLink1_N(DbTeam::group) + val teams by xdLink1_N(DbTeam::groups) /** * Converts this [DbTeamGroup] to a RESTful API representation [ApiTeamGroup]. From 958a94cba0d6120cff6ac4b753314fd9cc076cf8 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 25 Jul 2023 13:31:40 +0200 Subject: [PATCH 376/498] #415: Added support for backend-served task type presets --- backend/src/main/kotlin/dev/dres/DRES.kt | 7 ++ .../main/kotlin/dev/dres/api/rest/RestApi.kt | 3 +- .../template/ListTaskTypePresetsHandler.kt | 102 ++++++++++++++++++ .../rest/types/template/tasks/ApiTaskType.kt | 31 +++++- .../Ad-hoc-Video-Search.json | 17 +++ .../Textual-Known-Item-Search.json | 20 ++++ .../Visual-Known-Item-Search.json | 18 ++++ doc/oas.json | 55 ++++++++++ .../actionable-dynamic-table.component.html | 5 +- .../actionable-dynamic-table.component.ts | 16 ++- .../task-types-list.component.html | 1 + .../task-types-list.component.ts | 11 +- 12 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt create mode 100644 backend/src/main/resources/dres-type-presets/Ad-hoc-Video-Search.json create mode 100644 backend/src/main/resources/dres-type-presets/Textual-Known-Item-Search.json create mode 100644 backend/src/main/resources/dres-type-presets/Visual-Known-Item-Search.json diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 05b56e3c1..615f0ce9c 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -53,6 +53,13 @@ object DRES { val EXTERNAL_ROOT: Path get() = DATA_ROOT.resolve("external") + /** Path to the directory that contains task type presets. */ + val TASK_TYPE_PRESETS_EXTERNAL_LOCATION: Path + get() = DATA_ROOT.resolve("type-presets") + + /** Path to the classpath directory that contains task type presets shipped with DRES. */ + val TASK_TYPE_PRESETS_LOCATION = "dres-type-presets" + lateinit var CONFIG : Config internal set diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 430b43523..9e6db296c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -125,7 +125,7 @@ object RestApi { ListMediaItemHandler(store), ListExternalItemHandler(), - // Competition + // Template ListEvaluationTemplatesHandler(store), CreateEvaluationTemplateHandler(store), UpdateEvaluationTemplateHandler(store, config), @@ -133,6 +133,7 @@ object RestApi { DeleteEvaluationTemplateHandler(store), ListTeamHandler(store), ListTasksHandler(store), + ListTaskTypePresetsHandler(), GetTeamLogoHandler(store), // Submission diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt new file mode 100644 index 000000000..7f964c284 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt @@ -0,0 +1,102 @@ +package dev.dres.api.rest.handler.template + +import com.fasterxml.jackson.databind.ObjectMapper +import dev.dres.DRES +import dev.dres.api.rest.handler.AccessManagedRestHandler +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.handler.collection.AbstractCollectionHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskType +import dev.dres.api.rest.types.users.ApiRole +import io.javalin.http.Context +import io.javalin.openapi.* +import io.javalin.security.RouteRole +import org.slf4j.LoggerFactory +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.* +import kotlin.streams.toList + +/** + * A [GetRestHandler] that can be used to list [ApiTaskType] presets stored on disk. + * + * @author Loris Sauter + * @version 1.0.0 + */ +class ListTaskTypePresetsHandler : AccessManagedRestHandler, GetRestHandler> { + override val route: String = "template/type-presets/list" + override val apiVersion: String = "v2" + + /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ + override val permittedRoles: Set = setOf(ApiRole.ADMIN) + + @OpenApi( + summary = "Lists the task type presets available.", + path = "/api/v2/template/type-presets/list", + operationId = OpenApiOperation.AUTO_GENERATE, + tags = ["Template"], + responses = [ + OpenApiResponse("200", [OpenApiContent(Array::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): List { + /** Internal presets first */ + val loader = this::class.java.classLoader + val uri = loader.getResource(DRES.TASK_TYPE_PRESETS_LOCATION).toURI() + LOGGER.debug("Found presets location: {}", uri) + val internalPresets = if ("jar" == uri.scheme) { + /** JAR Handling */ + try{ + val list = FileSystems.newFileSystem(uri, emptyMap(), loader).use { fs -> + val files = Files.walk(fs.getPath("/${DRES.TASK_TYPE_PRESETS_LOCATION}")) + .filter { it.isReadable() and (it.extension == "json") }.toList() + LOGGER.trace("Internal preset files (jar): {}", files.toList().toString()) + return@use files + } + val list2 = list.map{this.javaClass.getResource(it.toString())} + + LOGGER.trace("Internal preset files (jar) resources: {}", list2.toString()) + + list2.map{ ObjectMapper().readValue(it, ApiTaskType::class.java) } + }catch(e: Exception){ + LOGGER.error("Error upon loading internal presets.", e) + LOGGER.warn("An error occurred during loading of internal task type presets. Only external ones are available. Check logs accordingly.") + emptyList() + } + } else { + /** IDE handling */ + val files = Paths.get(uri).listDirectoryEntries("*.json").filter { it.isReadable() } + LOGGER.trace("Internal preset files (dir): {}", files.toString()) + files.map { ApiTaskType.read(it) } + } + LOGGER.trace("Found {} internal presets.", internalPresets.size) + + Files.createDirectories(DRES.TASK_TYPE_PRESETS_EXTERNAL_LOCATION) + + /** External presets second */ + val externalPresets = + DRES.TASK_TYPE_PRESETS_EXTERNAL_LOCATION.listDirectoryEntries("*.json").map { + ApiTaskType.read(it) + } + LOGGER.trace("External presets: {}", externalPresets.toString()) + LOGGER.trace("Found {} external presets", externalPresets.size) + + /** Merge such that external presets may override internal ones */ + val presets = mutableListOf() + presets.addAll(externalPresets) + presets.addAll(0,internalPresets.filter { + !externalPresets.map { it.name }.contains(it.name) + }) + return presets + } + + companion object { + private val LOGGER = LoggerFactory.getLogger(this::class.java) + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt index d02855e9a..88460e5b8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt @@ -1,13 +1,18 @@ package dev.dres.api.rest.types.template.tasks +import com.fasterxml.jackson.databind.ObjectMapper import dev.dres.api.rest.types.template.tasks.options.* import dev.dres.data.model.template.task.DbTaskType +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption /** * The RESTful API equivalent of a [DbTaskType]. * * @author Ralph Gasser - * @version 1.0.0 + * @author Loris Sauter + * @version 1.1.0 */ data class ApiTaskType( val name: String, @@ -17,5 +22,25 @@ data class ApiTaskType( val submissionOptions: List, val taskOptions: List, val scoreOption: ApiScoreOption, - val configuration: Map -) + val configuration: Map +) { + + constructor() : this("Default TaskType DO NOT USE!", + 300, + ApiTargetOption.TEXT, listOf(ApiHintOption.TEXT), + listOf(ApiSubmissionOption.TEXTUAL_SUBMISSION), + listOf(ApiTaskOption.HIDDEN_RESULTS), ApiScoreOption.KIS, mapOf() + ) + + companion object { + /** + * Reads an [ApiTaskType] from the given path + * + * @param file The path to read from + */ + fun read(file: Path): ApiTaskType = + Files.newInputStream(file, StandardOpenOption.READ).use { + ObjectMapper().readValue(it, ApiTaskType::class.java) + } + } +} diff --git a/backend/src/main/resources/dres-type-presets/Ad-hoc-Video-Search.json b/backend/src/main/resources/dres-type-presets/Ad-hoc-Video-Search.json new file mode 100644 index 000000000..14e605e0b --- /dev/null +++ b/backend/src/main/resources/dres-type-presets/Ad-hoc-Video-Search.json @@ -0,0 +1,17 @@ +{ + "name": "Ad-hoc Video Search", + "duration": 300, + "targetOption": "JUDGEMENT", + "hintOptions": [ + "TEXT" + ], + "submissionOptions": [ + "NO_DUPLICATES", + "TEMPORAL_SUBMISSION" + ], + "taskOptions": [ + "MAP_TO_SEGMENT" + ], + "scoreOption": "AVS", + "configuration": {} +} \ No newline at end of file diff --git a/backend/src/main/resources/dres-type-presets/Textual-Known-Item-Search.json b/backend/src/main/resources/dres-type-presets/Textual-Known-Item-Search.json new file mode 100644 index 000000000..572286aaa --- /dev/null +++ b/backend/src/main/resources/dres-type-presets/Textual-Known-Item-Search.json @@ -0,0 +1,20 @@ +{ + "name": "Textual Known Item Search", + "duration": 420, + "targetOption": "SINGLE_MEDIA_SEGMENT", + "hintOptions": [ + "TEXT" + ], + "submissionOptions": [ + "NO_DUPLICATES", + "LIMIT_CORRECT_PER_TEAM", + "TEMPORAL_SUBMISSION" + ], + "taskOptions": [ + "HIDDEN_RESULTS" + ], + "scoreOption": "KIS", + "configuration": { + "LIMIT_CORRECT_PER_TEAM.limit": "1" + } +} \ No newline at end of file diff --git a/backend/src/main/resources/dres-type-presets/Visual-Known-Item-Search.json b/backend/src/main/resources/dres-type-presets/Visual-Known-Item-Search.json new file mode 100644 index 000000000..54d904354 --- /dev/null +++ b/backend/src/main/resources/dres-type-presets/Visual-Known-Item-Search.json @@ -0,0 +1,18 @@ +{ + "name": "Visual Known Item Search", + "duration": 300, + "targetOption": "SINGLE_MEDIA_SEGMENT", + "hintOptions": [ + "VIDEO_ITEM_SEGMENT" + ], + "submissionOptions": [ + "NO_DUPLICATES", + "LIMIT_CORRECT_PER_TEAM", + "TEMPORAL_SUBMISSION" + ], + "taskOptions": [], + "scoreOption": "KIS", + "configuration": { + "LIMIT_CORRECT_PER_TEAM.limit": "1" + } +} \ No newline at end of file diff --git a/doc/oas.json b/doc/oas.json index 52c84ea7e..be64dfd34 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4427,6 +4427,61 @@ "security" : [ ] } }, + "/api/v2/template/type-presets/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task type presets available.", + "operationId" : "getApiV2TemplateTypePresetsList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/template/{templateId}" : { "delete" : { "tags" : [ "Template" ], diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index ffe826e3d..ddb38fccd 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -4,7 +4,7 @@

{{tableTitle}}

- + + - + @@ -97,3 +103,4 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect

{{column.header}} @@ -14,6 +14,9 @@

{{tableTitle}}

+ diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts index dcd56d9d7..e1400935b 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.ts @@ -19,7 +19,8 @@ export enum ActionableDynamicTableColumnType { export enum ActionableDynamicTableActionType{ EDIT= 'edit', - REMOVE = 'remove' + REMOVE = 'remove', + DOWNLOAD = 'download' } @Component({ @@ -50,6 +51,8 @@ export class ActionableDynamicTable implements AfterContentInit{ public removeIcon = 'delete'; @Input() public editIcon = 'edit'; + @Input() + public downloadIcon = 'cloud_download'; @Input() public onEdit?: (element: T) => void; @@ -57,6 +60,9 @@ export class ActionableDynamicTable implements AfterContentInit{ @Input() public onRemove?: (element: T) => void; + @Input() + public onDownload?: (element: T) => void; + @Input() public trackedBy?: TrackByFunction @@ -83,6 +89,12 @@ export class ActionableDynamicTable implements AfterContentInit{ } } + download(element: T){ + if(this.onDownload){ + this.onDownload(element); + } + } + public renderRows(){ this.table.renderRows(); } @@ -91,7 +103,7 @@ export class ActionableDynamicTable implements AfterContentInit{ return this.columnSchema.filter(cs => cs.type === ActionableDynamicTableColumnType.CUSTOM) } - nonCustonColumns(): ActionableDynamicTableColumnDefinition[]{ + nonCustomColumns(): ActionableDynamicTableColumnDefinition[]{ return this.columnSchema.filter(cs => cs.type !== ActionableDynamicTableColumnType.CUSTOM) } diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html index 23efc400f..2210bbb0c 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html @@ -4,6 +4,7 @@ [columnSchema]="columns" [displayedColumns]="displayedColumns" [onRemove]="remove" + [onDownload]="download" tableTitle="Task types" style="min-width: 100%;" > diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts index 9a6dc6221..8e904cbc3 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts @@ -84,7 +84,7 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp {key: 'submissions', header: 'Submission Options', type: ActionableDynamicTableColumnType.CUSTOM}, {key: 'tasks', header: 'Task Options', type: ActionableDynamicTableColumnType.CUSTOM}, {key: 'score', header: 'Score', property: 'scoreOption', type: ActionableDynamicTableColumnType.TEXT}, - {key: 'actions', header: 'Actions', type: ActionableDynamicTableColumnType.ACTION, actions: [ActionableDynamicTableActionType.REMOVE],} + {key: 'actions', header: 'Actions', type: ActionableDynamicTableColumnType.ACTION, actions: [ActionableDynamicTableActionType.DOWNLOAD, ActionableDynamicTableActionType.REMOVE],} ]; displayedColumns= ['name', 'duration', 'target', 'hints','submissions', 'tasks','score', 'actions']; @@ -137,6 +137,15 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp }) } + public download = (taskType: ApiTaskType) => { + const file = new Blob([JSON.stringify(taskType, null, ' ')], { type: "application/json" }); + const fake = document.createElement('a'); + fake.href = URL.createObjectURL(file); + fake.download = `${taskType.name.replace(/\s/g, '-')}.json` + fake.click(); + URL.revokeObjectURL(fake.href); + } + public removeTaskType(taskType: ApiTaskType) { this.builderService.removeTaskType(taskType); this.table?.renderRows(); From ecf02742d003cfa1c350addb58b9d624b4d7a130 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 25 Jul 2023 15:50:10 +0200 Subject: [PATCH 377/498] #415: Backend served presets and resolved #426 --- .../competition-form.builder.ts | 24 +++++--- .../abstract-template-builder.component.ts | 27 ++++++++- .../judges-list/judges-list.component.ts | 13 +++- .../task-groups-list.component.ts | 9 ++- .../task-types-list.component.html | 5 +- .../task-types-list.component.ts | 59 ++++--------------- .../task-templates-list.component.ts | 20 ++++++- .../teamgroups-list.component.ts | 9 ++- .../teams-list/teams-list.component.ts | 9 ++- .../template-information.component.ts | 22 +++++-- .../template-builder.component.html | 2 +- .../template-builder.component.scss | 12 ++++ .../template-builder.component.ts | 21 +++---- .../template-builder.service.ts | 2 +- 14 files changed, 139 insertions(+), 95 deletions(-) diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index a3a288405..3e35b2b1b 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -286,6 +286,16 @@ export class CompetitionFormBuilder { }; } + private maxDurationValidator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + if(control.value > (this.form.get('duration').value as number)){ + return {max: {max: this.form.get('duration').value as number, actual: control.value as number}} + }else{ + return null; + } + }; + } + /** * Initializes the {@link FormGroup}. */ @@ -520,7 +530,7 @@ export class CompetitionFormBuilder { Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), this.maxDurationValidator()]), type: new UntypedFormControl('IMAGE', [Validators.required]), mediaItem: mediaItemFormControl, }); @@ -576,9 +586,8 @@ export class CompetitionFormBuilder { Validators.max(this.taskType.duration), ]), end: new UntypedFormControl(initialize?.end, [ - Validators.required, Validators.min(0), - Validators.max(this.taskType.duration), + this.maxDurationValidator() ]), type: new UntypedFormControl('VIDEO', [Validators.required]), mediaItem: mediaItemFormControl, @@ -609,7 +618,8 @@ export class CompetitionFormBuilder { } /* Manually setting the duration of the hint equal to the duration of the task, this way the validators are happy */ - group.get('end').setValue(this.taskType.duration, {emitEvent: false}); + group.get('end').setValue(this.form.get('duration').value, {emitEvent: false}); + group .get('segment_start') @@ -637,7 +647,7 @@ export class CompetitionFormBuilder { Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), this.maxDurationValidator()]), type: new UntypedFormControl('TEXT', [Validators.required]), description: new UntypedFormControl(initialize?.description, [Validators.required]), }); @@ -668,7 +678,7 @@ export class CompetitionFormBuilder { Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), this.maxDurationValidator()]), type: new UntypedFormControl('IMAGE', [Validators.required]), external: new UntypedFormControl(true), path: pathFormControl, @@ -701,7 +711,7 @@ export class CompetitionFormBuilder { Validators.min(0), Validators.max(this.taskType.duration), ]), - end: new UntypedFormControl(initialize?.end, [Validators.min(0), Validators.max(this.taskType.duration)]), + end: new UntypedFormControl(initialize?.end, [Validators.min(0), this.maxDurationValidator()]), type: new UntypedFormControl('VIDEO', [Validators.required]), external: new UntypedFormControl(true), path: pathFormControl, diff --git a/frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts b/frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts index 058b55302..af31d2f55 100644 --- a/frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts +++ b/frontend/src/app/template/template-builder/components/abstract-template-builder.component.ts @@ -1,23 +1,44 @@ import { Component, OnInit } from '@angular/core'; -import {ApiEvaluationTemplate} from '../../../../../openapi'; +import { ApiEvaluationTemplate, TemplateService } from "../../../../../openapi"; import {Subscription} from 'rxjs'; import {TemplateBuilderService} from '../template-builder.service'; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; export abstract class AbstractTemplateBuilderComponent { subscription: Subscription; - protected constructor(protected builderService: TemplateBuilderService) { } + routeSub: Subscription; + + private templateId: string; + protected constructor( + protected builderService: TemplateBuilderService, + protected route: ActivatedRoute, + protected templateService: TemplateService, + protected snackBar: MatSnackBar + ) { } onInit(): void { this.subscription = this.builderService.templateAsObservable().subscribe((t) => { this.onChange(); - }) + }); + this.routeSub = this.route.params.subscribe( (p) => { + this.templateId = p.templateId; + }); } onDestroy(){ this.subscription?.unsubscribe(); + this.routeSub?.unsubscribe(); + this.subscription = null; + this.routeSub = null; + } + + protected getTemplateId(){ + return this.templateId; } + abstract onChange(); } diff --git a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts index d2dc1ee34..8466a6804 100644 --- a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts +++ b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts @@ -1,11 +1,13 @@ import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {MatTable} from '@angular/material/table'; -import {ApiRole, ApiUser, UserService} from '../../../../../../openapi'; +import { ApiRole, ApiUser, TemplateService, UserService } from "../../../../../../openapi"; import {combineLatest, Observable} from 'rxjs'; import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; import {TemplateBuilderService} from '../../template-builder.service'; import {filter, map, shareReplay, withLatestFrom} from 'rxjs/operators'; import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector: 'app-judges-list', @@ -23,8 +25,13 @@ export class JudgesListComponent extends AbstractTemplateBuilderComponent implem judges: Observable> = new Observable>((x) => x.next([])); - constructor(private userService: UserService, builderService: TemplateBuilderService) { - super(builderService); + constructor(private userService: UserService, + builderService: TemplateBuilderService, + route: ActivatedRoute, + templateService: TemplateService, + snackBar: MatSnackBar, + ) { + super(builderService,route,templateService,snackBar); this.refreshAvailableJudges(); } diff --git a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts index b21246e10..19932000c 100644 --- a/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts +++ b/frontend/src/app/template/template-builder/components/task-groups-list/task-groups-list.component.ts @@ -4,7 +4,7 @@ import { TemplateBuilderService } from "../../template-builder.service"; import { MatDialog } from "@angular/material/dialog"; import { filter, map, tap } from "rxjs/operators"; import { Observable } from "rxjs"; -import { ApiTaskGroup } from "../../../../../../openapi"; +import { ApiTaskGroup, TemplateService } from "../../../../../../openapi"; import { CompetitionBuilderTaskGroupDialogComponent, CompetitionBuilderTaskGroupDialogData @@ -19,6 +19,8 @@ import { ConfirmationDialogComponent, ConfirmationDialogComponentData } from "../../../../shared/confirmation-dialog/confirmation-dialog.component"; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector: "app-task-groups-list", @@ -40,9 +42,12 @@ export class TaskGroupsListComponent extends AbstractTemplateBuilderComponent im constructor( builderService: TemplateBuilderService, + route: ActivatedRoute, + templateService: TemplateService, + snackBar: MatSnackBar, private dialog: MatDialog ) { - super(builderService); + super(builderService,route,templateService,snackBar); } ngOnInit(): void { diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html index 2210bbb0c..a256f8a8e 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html @@ -20,10 +20,7 @@ - - - - +
diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts index 8e904cbc3..2899851c2 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts @@ -2,9 +2,9 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; import { TemplateBuilderService } from "../../template-builder.service"; import { MatDialog } from "@angular/material/dialog"; -import { ApiTaskType } from "../../../../../../openapi"; +import { ApiTaskType, TemplateService } from "../../../../../../openapi"; import { Observable } from "rxjs"; -import { filter, map } from "rxjs/operators"; +import { filter, map, shareReplay } from "rxjs/operators"; import { CompetitionBuilderTaskTypeDialogComponent } from "../../../../competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component"; @@ -18,6 +18,8 @@ import { ConfirmationDialogComponent, ConfirmationDialogComponentData } from "../../../../shared/confirmation-dialog/confirmation-dialog.component"; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector: "app-task-types-list", @@ -26,55 +28,10 @@ import { }) export class TaskTypesListComponent extends AbstractTemplateBuilderComponent implements OnInit, OnDestroy { - public static TKIS_PRESET = { - name: 'Textual Known Item Search', - duration: 420, - targetOption: "SINGLE_MEDIA_SEGMENT", - scoreOption: "KIS", - hintOptions: ["TEXT"], - submissionOptions: ["NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "TEMPORAL_SUBMISSION"], - taskOptions: ["HIDDEN_RESULTS"], - configuration: {["LIMIT_CORRECT_PER_TEAM.limit"]: "1"} - } as ApiTaskType; - - public static VKIS_PRESET = { - name: 'Visual Known Item Search', - duration: 300, - targetOption: "SINGLE_MEDIA_SEGMENT", - scoreOption: "KIS", - hintOptions: ["VIDEO_ITEM_SEGMENT"], - submissionOptions: ["NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "TEMPORAL_SUBMISSION"], - taskOptions: [], - configuration: {["LIMIT_CORRECT_PER_TEAM.limit"]: "1"} - } as ApiTaskType; - - public static AVS_PRESET = { - name: 'Ad-hoc Video Search', - duration: 300, - targetOption: "JUDGEMENT", - scoreOption: "AVS", - hintOptions: ["TEXT"], - submissionOptions: ["NO_DUPLICATES", "TEMPORAL_SUBMISSION"], - taskOptions: ["MAP_TO_SEGMENT"] - } as ApiTaskType; - - public static LSC_PRSET = { - name: 'Lifelog Search Challenge Topic', - duration: 300, - targetOption: "SINGLE_MEDIA_ITEM", // TODO MULTIPLE_MEDIA_ITEMS is missing - scoreOption: "KIS", - hintOptions: ["TEXT"], - submissionOptions: ["NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM"], - taskOptions: ["HIDDEN_RESULTS"], - configuration: {["LIMIT_CORRECT_PER_TEAM.limit"]: "1"} - } as ApiTaskType; types: Observable = new Observable((o) => o.next([])); - tkisPreset = TaskTypesListComponent.TKIS_PRESET; - vkisPreset = TaskTypesListComponent.VKIS_PRESET; - avsPreset = TaskTypesListComponent.AVS_PRESET; - lscPreset = TaskTypesListComponent.LSC_PRSET; + presets: Observable= new Observable((o) => o.next([])); columns: ActionableDynamicTableColumnDefinition[] = [ {key: 'name', header: 'Name', property: 'name', type: ActionableDynamicTableColumnType.TEXT}, @@ -93,9 +50,12 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp constructor( builder: TemplateBuilderService, + route: ActivatedRoute, + templateService: TemplateService, + snackBar: MatSnackBar, private dialog: MatDialog ) { - super(builder); + super(builder,route,templateService,snackBar); } ngOnDestroy(): void { @@ -104,6 +64,7 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp ngOnInit(): void { this.onInit(); + this.presets = this.templateService.getApiV2TemplateTypePresetsList().pipe(shareReplay(1)) } onChange() { diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index 9dc540051..aca8316ac 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -1,7 +1,15 @@ import { Component, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; import { TemplateBuilderService } from "../../template-builder.service"; -import { ApiEvaluationTemplate, ApiHint, ApiTarget, ApiTaskGroup, ApiTaskTemplate, ApiTaskType } from "../../../../../../openapi"; +import { + ApiEvaluationTemplate, + ApiHint, + ApiTarget, + ApiTaskGroup, + ApiTaskTemplate, + ApiTaskType, + TemplateService +} from "../../../../../../openapi"; import { MatTable } from "@angular/material/table"; import { Observable, Subscription } from "rxjs"; import { SelectionModel } from "@angular/cdk/collections"; @@ -11,6 +19,8 @@ import { ConfirmationDialogComponentData } from "../../../../shared/confirmation-dialog/confirmation-dialog.component"; import { MatDialog } from "@angular/material/dialog"; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; export interface TaskTemplateEditorLauncher { editTask(taskType: ApiTaskType, taskGroup: ApiTaskGroup, task?: ApiTaskTemplate); @@ -40,8 +50,12 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent private selectedTaskSub: Subscription; - constructor(builder: TemplateBuilderService, private dialog: MatDialog) { - super(builder); + constructor(builder: TemplateBuilderService, + route: ActivatedRoute, + templateService: TemplateService, + snackBar: MatSnackBar, + private dialog: MatDialog) { + super(builder,route,templateService,snackBar); } ngOnInit(): void { diff --git a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts index 8736767d0..b06fd1b30 100644 --- a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts +++ b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; import { TemplateBuilderService } from "../../template-builder.service"; -import { ApiTaskType, ApiTeam, ApiTeamGroup } from "../../../../../../openapi"; +import { ApiTaskType, ApiTeam, ApiTeamGroup, TemplateService } from "../../../../../../openapi"; import { Observable } from "rxjs"; import { filter, map } from "rxjs/operators"; import { MatDialog } from "@angular/material/dialog"; @@ -12,6 +12,8 @@ import { ActionableDynamicTableColumnType } from "../../../../shared/actionable-dynamic-table/actionable-dynamic-table.component"; import { TeamgroupsDialogComponent } from "../teamgroups-dialog/teamgroups-dialog.component"; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector: 'app-teamgroups-list', @@ -34,9 +36,12 @@ export class TeamgroupsListComponent extends AbstractTemplateBuilderComponent im constructor( builderService: TemplateBuilderService, + route: ActivatedRoute, + templateService: TemplateService, + snackBar: MatSnackBar, private dialog: MatDialog ){ - super(builderService); + super(builderService, route, templateService, snackBar); } ngOnInit() { diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts index eecf1eb4e..6910a4f52 100644 --- a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.component"; -import { ApiTeam, UserService } from "../../../../../../openapi"; +import { ApiTeam, TemplateService, UserService } from "../../../../../../openapi"; import { MatTable } from "@angular/material/table"; import { Observable } from "rxjs"; import { TemplateBuilderService } from "../../template-builder.service"; @@ -8,6 +8,8 @@ import { MatDialog } from "@angular/material/dialog"; import { AppConfig } from "../../../../app.config"; import { filter, map } from "rxjs/operators"; import { TeamBuilderDialogComponent } from "../team-builder-dialog/team-builder-dialog.component"; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector: 'app-teams-list', @@ -24,11 +26,14 @@ export class TeamsListComponent extends AbstractTemplateBuilderComponent impleme teams: Observable = new Observable((o) => o.next([])); constructor( builderService: TemplateBuilderService, + route: ActivatedRoute, + templateService: TemplateService, + snackBar: MatSnackBar, private userService: UserService, private dialog: MatDialog, private config: AppConfig ) { - super(builderService); + super(builderService, route, templateService, snackBar); } ngOnInit(): void { diff --git a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts index 6c49aa6cb..7b6026534 100644 --- a/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts +++ b/frontend/src/app/template/template-builder/components/template-information/template-information.component.ts @@ -4,6 +4,8 @@ import {FormControl, FormGroup, UntypedFormControl, UntypedFormGroup, Validators import { Observable, Subscription } from "rxjs"; import { ApiMediaCollection, CollectionService, TemplateService } from "../../../../../../openapi"; import { TemplateBuilderService } from "../../template-builder.service"; +import { ActivatedRoute } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector: "app-template-information", @@ -28,11 +30,13 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone mediaCollectionSource: Observable; constructor( - private templateService: TemplateService, + templateService: TemplateService, private collectionService: CollectionService, - builder: TemplateBuilderService + builder: TemplateBuilderService, + route: ActivatedRoute, + snackBar: MatSnackBar, ) { - super(builder); + super(builder, route, templateService, snackBar); } ngAfterViewInit(): void { @@ -41,6 +45,15 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone ngOnInit(): void { this.onInit(); + this.routeSub = this.route.params.subscribe( (p) => { + this.templateService.getApiV2TemplateByTemplateId(p.templateId).subscribe((t) => { + /* initialise from route */ + this.builderService.initialise(t); + }, + (r) => { + this.snackBar.open(`Error: ${r?.error?.description}`, null,{duration: 5000}); + }); + }); this.changeSub = this.form.valueChanges.subscribe((value) => { let isDirty = false; if (value.name !== this.builderService.getTemplate().name) { @@ -105,7 +118,8 @@ export class TemplateInformationComponent extends AbstractTemplateBuilderCompone } onChange() { - if (this.builderService.getTemplate()) { + if (this.builderService.getTemplate() && this.getTemplateId() === this.builderService.getTemplate().id) { + console.log("Change", this.builderService.getTemplate(), this.form, this.initOngoing) if (this.form.get("name").value !== this.builderService.getTemplate().name) { this.form.get("name").setValue(this.builderService.getTemplate().name, { emitEvent: !this.initOngoing }); } diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index c605e0b8a..95590d592 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -43,7 +43,7 @@

Edit evaluation template {{(builderService.templateAsObservable() | async)?. -
+
diff --git a/frontend/src/app/template/template-builder/template-builder.component.scss b/frontend/src/app/template/template-builder/template-builder.component.scss index f580801ff..b30329d18 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.scss +++ b/frontend/src/app/template/template-builder/template-builder.component.scss @@ -35,6 +35,18 @@ } } +.tb-container-4col-left { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: auto; + grid-template-areas: "left left left right"; + column-gap: 1em; + + > * { + min-width: 0; + } +} + .tb-container-3col-right { display: grid; grid-template-columns: 1fr 1fr 1fr; diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index b6983243b..ef2c7c2f4 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -22,20 +22,20 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i @ViewChild('taskTemplateEditor', {static: true}) taskEditor: TaskTemplateEditorComponent; - routeSub: Subscription; + changeSub: Subscription; isSaving=false; constructor( - private templateService: TemplateService, + templateService: TemplateService, private userService: UserService, private downloadService: DownloadService, - private route: ActivatedRoute, + route: ActivatedRoute, private router: Router, - private snackBar: MatSnackBar, + snackBar: MatSnackBar, public builderService: TemplateBuilderService ) { - super(builderService); + super(builderService, route, templateService, snackBar); } canDeactivate(nextState?: RouterStateSnapshot): Observable | Promise | boolean { @@ -49,15 +49,8 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i } ngOnInit(): void { - this.routeSub = this.route.params.subscribe( (p) => { - this.templateService.getApiV2TemplateByTemplateId(p.templateId).subscribe((t) => { - /* initialise from route */ - this.builderService.initialise(t); - }, - (r) => { - this.snackBar.open(`Error: ${r?.error?.description}`, null,{duration: 5000}); - }); - }); + this.onInit(); + } fileProvider = () => { diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index df30af855..a5765a3bf 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -98,8 +98,8 @@ export class TemplateBuilderService { public initialise(template: ApiEvaluationTemplate){ this.unmarkDirty(); - this.templateSubject.next(template); console.log("BuilderService.init", template); + this.templateSubject.next(template); } public getTemplate(){ From 247431bfee7ea9f7fd5b597cba205df33266062b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 25 Jul 2023 17:37:23 +0200 Subject: [PATCH 378/498] Moved logic from UpdateEvaluationTemplateHandler into utility class to be reusable in CLI --- .../UpdateEvaluationTemplateHandler.kt | 185 +------------ .../kotlin/dev/dres/utilities/TemplateUtil.kt | 261 ++++++++++++++++++ 2 files changed, 266 insertions(+), 180 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index d183c80e9..2240ae93a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -15,6 +15,7 @@ import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.media.DbMediaItem +import dev.dres.utilities.TemplateUtil import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -74,188 +75,12 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C throw ErrorStatusException(409, "Evaluation template ${apiValue.id} has been modified in the meantime. Reload and try again!", ctx) } - /* Update core information. */ - existing.name = apiValue.name - existing.description = apiValue.description - existing.modified = DateTime.now() - - /* Update task type information. */ - val taskTypes = apiValue.taskTypes.map { it.name }.toTypedArray() - existing.taskTypes.removeAll( - DbTaskType.query(DbTaskType::evaluation eq existing and not(DbTaskType::name.containsIn(*taskTypes))) - ) - for (apiTaskType in apiValue.taskTypes) { - val taskType = DbTaskType.findOrNew(DbTaskType.query((DbTaskType::name eq apiTaskType.name) and (DbTaskType::evaluation eq existing))) { - this.name = apiTaskType.name - } - taskType.duration = apiTaskType.duration - taskType.score = apiTaskType.scoreOption.toDb() - taskType.target = apiTaskType.targetOption.toDb() - taskType.hints.clear() - taskType.hints.addAll(apiTaskType.hintOptions.map { it.toDb() }) - taskType.submission.clear() - taskType.submission.addAll(apiTaskType.submissionOptions.map { it.toDb() }) - taskType.options.clear() - taskType.options.addAll(apiTaskType.taskOptions.map { it.toDb() }) - taskType.configurations.clear() - taskType.configurations.addAll(apiTaskType.configuration.entries.map { - DbConfiguredOption.new { - this.key = it.key - this.value = it.value - } - }) - - /* Establish relationship if entry is new. */ - if (taskType.isNew) { - existing.taskTypes.add(taskType) - } + try { + TemplateUtil.updateDbTemplate(existing, apiValue) + } catch (e: IllegalArgumentException) { + throw ErrorStatusException(404, e.message ?: "", ctx) } - /* Update task group information. */ - val taskGroups = apiValue.taskGroups.map { it.name }.toTypedArray() - existing.taskGroups.removeAll( - DbTaskGroup.query(DbTaskGroup::evaluation eq existing and not(DbTaskGroup::name.containsIn(*taskGroups))) - ) - for (apiTaskGroup in apiValue.taskGroups) { - val taskGroup = DbTaskGroup.findOrNew(DbTaskGroup.query((DbTaskGroup::name eq apiTaskGroup.name) and (DbTaskGroup::evaluation eq existing))) { - this.name = apiTaskGroup.name - } - - /* Update task type if it has changed. */ - if (taskGroup.getSafe(DbTaskGroup::type)?.name != apiTaskGroup.name) { - taskGroup.type = DbTaskType.query((DbTaskType::name eq apiTaskGroup.type) and (DbTaskType::evaluation eq existing)).firstOrNull() - ?: throw ErrorStatusException(404, "Unknown task group ${apiTaskGroup.type} for evaluation ${apiValue.id}.", ctx) - } - - /* Establish relationship if entry is new. */ - if (taskGroup.isNew) { - existing.taskGroups.add(taskGroup) - } - } - - /* Update task information: Remove deleted tasks. */ - val taskIds = apiValue.tasks.mapNotNull { it.id }.toTypedArray() - existing.tasks.removeAll(DbTaskTemplate.query(DbTaskTemplate::evaluation eq existing and not(DbTaskTemplate::id.containsIn(*taskIds)))) - - /* Update task information: Remaining tasks. */ - for (apiTask in apiValue.tasks) { - val task = if (apiTask.id != null) { - existing.tasks.filter { it.id eq apiTask.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown task ${apiTask.id} for evaluation ${apiValue.id}.", ctx) - } else { - val t = DbTaskTemplate.new() - existing.tasks.add(t) - t - } - - /* Update parameters that do no require lookup. */ - task.name = apiTask.name - task.duration = apiTask.duration - task.comment = apiTask.comment - - /* Conditional updating of parameters that do!. */ - if (task.isNew || task.collection.id != apiTask.collectionId) { - task.collection = DbMediaCollection.query(DbMediaCollection::id eq apiTask.collectionId).first() - } - - if (task.isNew || task.taskGroup.name != apiTask.taskGroup) { - task.taskGroup = DbTaskGroup.query(DbTaskGroup::name eq apiTask.taskGroup).first() - } - - /* Update task targets. */ - task.targets.clear() - for (target in apiTask.targets) { - task.targets.add(DbTaskTemplateTarget.new { - this.type = target.type.toDb() - this.start = target.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - this.end = target.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - when (target.type) { - ApiTargetType.TEXT -> this.text = target.target - else -> this.item = target.target?.let { DbMediaItem.query(DbMediaItem::id eq it).firstOrNull() } - } - }) - } - - /* Update task hints. */ - task.hints.clear() - for (hint in apiTask.hints) { - task.hints.add(DbHint.new { - this.type = hint.type.toDb() - this.item = hint.mediaItem?.let { DbMediaItem.query(DbMediaItem::id eq hint.mediaItem).firstOrNull() } - this.text = hint.description - this.path = hint.path - this.start = hint.start - this.end = hint.end - this.temporalRangeStart = hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - this.temporalRangeEnd = hint.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() - }) - } - } - - /* Update team information. */ - val teamIds = apiValue.teams.map { it.id }.toTypedArray() - existing.teams.removeAll(DbTeam.query(DbTeam::evaluation eq existing and not(DbTeam::id.containsIn(*teamIds)))) - for (apiTeam in apiValue.teams) { - val team = if (apiTeam.id != null) { - existing.teams.filter { it.id eq apiTeam.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team ${apiTeam.id} for evaluation ${apiValue.id}.", ctx) - } else { - val t = DbTeam.new() - existing.teams.add(t) /* Establish new relationship. */ - t - } - - team.name = apiTeam.name ?: throw ErrorStatusException(404, "Team name must be specified.", ctx) - team.color = apiTeam.color ?: throw ErrorStatusException(404, "Team colour must be specified.", ctx) - - /* Process logo data. */ - val logoData = apiTeam.logoStream() - if (logoData != null) { - team.logo = logoData - } - - /* Make association with users. */ - val userIds = apiTeam.users.map { it.id }.toTypedArray() - team.users.removeAll(DbUser.query(not(DbUser::id.containsIn(*userIds)))) - for (userId in userIds) { - val user = DbUser.filter { it.id eq userId }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown user $userId for evaluation ${apiValue.id}.", ctx) - if (!team.users.contains(user)) { - team.users.add(user) - } - } - } - - /* Update teamGroup information */ - val teamGroupIds = apiValue.teamGroups.map { it.id }.toTypedArray() - existing.teamGroups.removeAll(DbTeamGroup.query(DbTeamGroup::evaluation eq existing and not(DbTeamGroup::id.containsIn(*teamGroupIds)))) - - for (apiTeamGroup in apiValue.teamGroups) { - val teamGroup = if (apiTeamGroup.id != null) { - existing.teamGroups.filter { it.id eq apiTeamGroup.id }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown team group ${apiTeamGroup.id} for evaluation ${apiValue.id}.", ctx) - } else { - val tg = DbTeamGroup.new() - existing.teamGroups.add(tg) - tg - } - teamGroup.name = apiTeamGroup.name ?: throw ErrorStatusException(404, "Team group name must be specified.", ctx) - teamGroup.defaultAggregator = apiTeamGroup.aggregation.toDb() - - teamGroup.teams.clear() - teamGroup.teams.addAll(DbTeam.query(DbTeam::id.containsIn(*apiTeamGroup.teams.map { it.id }.toTypedArray()))) - - /* Establish relationship if entry is new. */ - if (teamGroup.isNew) { - existing.teamGroups.add(teamGroup) - } - } - - /* Update judge information */ - val judgeIds = apiValue.judges.toTypedArray() - existing.judges.removeAll(DbUser.query(not(DbUser::id.containsIn(*judgeIds)))) - for (userId in judgeIds) { - val user = DbUser.filter { it.id eq userId }.firstOrNull() ?: throw ErrorStatusException(404, "Unknown user $userId for evaluation ${apiValue.id}.", ctx) - if (!existing.judges.contains(user)) { - existing.judges.add(user) - } - } } return SuccessStatus("Competition with ID ${apiValue.id} was updated successfully.") } diff --git a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt new file mode 100644 index 000000000..a262f54e4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt @@ -0,0 +1,261 @@ +package dev.dres.utilities + +import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.api.rest.types.template.tasks.ApiTargetType +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.task.* +import dev.dres.data.model.template.task.options.DbConfiguredOption +import dev.dres.data.model.template.team.DbTeam +import dev.dres.data.model.template.team.DbTeamGroup +import kotlinx.dnq.creator.findOrNew +import kotlinx.dnq.query.* +import kotlinx.dnq.util.getSafe +import org.joda.time.DateTime + +object TemplateUtil { + + /** + * Writes the state of an [ApiEvaluationTemplate] into an existing [DbEvaluationTemplate]. + * Requires a transaction context. + */ + @Throws(IllegalArgumentException::class) + fun updateDbTemplate(dbEvaluationTemplate: DbEvaluationTemplate, apiEvaluationTemplate: ApiEvaluationTemplate) { + + + /* Update core information. */ + dbEvaluationTemplate.name = apiEvaluationTemplate.name + dbEvaluationTemplate.description = apiEvaluationTemplate.description + dbEvaluationTemplate.modified = DateTime.now() + + /* Update task type information. */ + val taskTypes = apiEvaluationTemplate.taskTypes.map { it.name }.toTypedArray() + dbEvaluationTemplate.taskTypes.removeAll( + DbTaskType.query(DbTaskType::evaluation eq dbEvaluationTemplate and not(DbTaskType::name.containsIn(*taskTypes))) + ) + for (apiTaskType in apiEvaluationTemplate.taskTypes) { + val taskType = + DbTaskType.findOrNew(DbTaskType.query((DbTaskType::name eq apiTaskType.name) and (DbTaskType::evaluation eq dbEvaluationTemplate))) { + this.name = apiTaskType.name + } + taskType.duration = apiTaskType.duration + taskType.score = apiTaskType.scoreOption.toDb() + taskType.target = apiTaskType.targetOption.toDb() + taskType.hints.clear() + taskType.hints.addAll(apiTaskType.hintOptions.map { it.toDb() }) + taskType.submission.clear() + taskType.submission.addAll(apiTaskType.submissionOptions.map { it.toDb() }) + taskType.options.clear() + taskType.options.addAll(apiTaskType.taskOptions.map { it.toDb() }) + taskType.configurations.clear() + taskType.configurations.addAll(apiTaskType.configuration.entries.map { + DbConfiguredOption.new { + this.key = it.key + this.value = it.value + } + }) + + /* Establish relationship if entry is new. */ + if (taskType.isNew) { + dbEvaluationTemplate.taskTypes.add(taskType) + } + } + + /* Update task group information. */ + val taskGroups = apiEvaluationTemplate.taskGroups.map { it.name }.toTypedArray() + dbEvaluationTemplate.taskGroups.removeAll( + DbTaskGroup.query(DbTaskGroup::evaluation eq dbEvaluationTemplate and not(DbTaskGroup::name.containsIn(*taskGroups))) + ) + for (apiTaskGroup in apiEvaluationTemplate.taskGroups) { + val taskGroup = + DbTaskGroup.findOrNew(DbTaskGroup.query((DbTaskGroup::name eq apiTaskGroup.name) and (DbTaskGroup::evaluation eq dbEvaluationTemplate))) { + this.name = apiTaskGroup.name + } + + /* Update task type if it has changed. */ + if (taskGroup.getSafe(DbTaskGroup::type)?.name != apiTaskGroup.name) { + taskGroup.type = + DbTaskType.query((DbTaskType::name eq apiTaskGroup.type) and (DbTaskType::evaluation eq dbEvaluationTemplate)) + .firstOrNull() + ?: throw IllegalArgumentException("Unknown task group ${apiTaskGroup.type} for evaluation ${apiEvaluationTemplate.id}.") + } + + /* Establish relationship if entry is new. */ + if (taskGroup.isNew) { + dbEvaluationTemplate.taskGroups.add(taskGroup) + } + } + + /* Update task information: Remove deleted tasks. */ + val taskIds = apiEvaluationTemplate.tasks.mapNotNull { it.id }.toTypedArray() + dbEvaluationTemplate.tasks.removeAll( + DbTaskTemplate.query( + DbTaskTemplate::evaluation eq dbEvaluationTemplate and not( + DbTaskTemplate::id.containsIn(*taskIds) + ) + ) + ) + + /* Update task information: Remaining tasks. */ + for (apiTask in apiEvaluationTemplate.tasks) { + val task = if (apiTask.id != null) { + dbEvaluationTemplate.tasks.filter { it.id eq apiTask.id }.firstOrNull() + ?: throw IllegalArgumentException("Unknown task ${apiTask.id} for evaluation ${apiEvaluationTemplate.id}.") + } else { + val t = DbTaskTemplate.new() + dbEvaluationTemplate.tasks.add(t) + t + } + + /* Update parameters that do no require lookup. */ + task.name = apiTask.name + task.duration = apiTask.duration + task.comment = apiTask.comment + + /* Conditional updating of parameters that do!. */ + if (task.isNew || task.collection.id != apiTask.collectionId) { + task.collection = DbMediaCollection.query(DbMediaCollection::id eq apiTask.collectionId).first() + } + + if (task.isNew || task.taskGroup.name != apiTask.taskGroup) { + task.taskGroup = DbTaskGroup.query(DbTaskGroup::name eq apiTask.taskGroup).first() + } + + /* Update task targets. */ + task.targets.clear() + for (target in apiTask.targets) { + task.targets.add(DbTaskTemplateTarget.new { + this.type = target.type.toDb() + this.start = target.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + this.end = target.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + when (target.type) { + ApiTargetType.TEXT -> this.text = target.target + else -> this.item = + target.target?.let { DbMediaItem.query(DbMediaItem::id eq it).firstOrNull() } + } + }) + } + + /* Update task hints. */ + task.hints.clear() + for (hint in apiTask.hints) { + task.hints.add(DbHint.new { + this.type = hint.type.toDb() + this.item = + hint.mediaItem?.let { DbMediaItem.query(DbMediaItem::id eq hint.mediaItem).firstOrNull() } + this.text = hint.description + this.path = hint.path + this.start = hint.start + this.end = hint.end + this.temporalRangeStart = hint.range?.start?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + this.temporalRangeEnd = hint.range?.end?.toTemporalPoint(item?.fps ?: 0.0f)?.toMilliseconds() + }) + } + } + + /* Update team information. */ + val teamIds = apiEvaluationTemplate.teams.map { it.id }.toTypedArray() + dbEvaluationTemplate.teams.removeAll( + DbTeam.query( + DbTeam::evaluation eq dbEvaluationTemplate and not( + DbTeam::id.containsIn( + *teamIds + ) + ) + ) + ) + for (apiTeam in apiEvaluationTemplate.teams) { + val team = if (apiTeam.id != null) { + dbEvaluationTemplate.teams.filter { it.id eq apiTeam.id }.firstOrNull() + ?: throw IllegalArgumentException("Unknown team ${apiTeam.id} for evaluation ${apiEvaluationTemplate.id}.") + } else { + val t = DbTeam.new() + dbEvaluationTemplate.teams.add(t) /* Establish new relationship. */ + t + } + + team.name = apiTeam.name ?: throw IllegalArgumentException("Team name must be specified.") + team.color = apiTeam.color ?: throw IllegalArgumentException("Team colour must be specified.") + + /* Process logo data. */ + val logoData = apiTeam.logoStream() + if (logoData != null) { + team.logo = logoData + } + + /* Make association with users. */ + val userIds = apiTeam.users.map { it.id }.toTypedArray() + team.users.removeAll(DbUser.query(not(DbUser::id.containsIn(*userIds)))) + for (userId in userIds) { + val user = DbUser.filter { it.id eq userId }.firstOrNull() + ?: throw IllegalArgumentException("Unknown user $userId for evaluation ${apiEvaluationTemplate.id}.") + if (!team.users.contains(user)) { + team.users.add(user) + } + } + } + + /* Update teamGroup information */ + val teamGroupIds = apiEvaluationTemplate.teamGroups.map { it.id }.toTypedArray() + dbEvaluationTemplate.teamGroups.removeAll( + DbTeamGroup.query( + DbTeamGroup::evaluation eq dbEvaluationTemplate and not( + DbTeamGroup::id.containsIn(*teamGroupIds) + ) + ) + ) + + for (apiTeamGroup in apiEvaluationTemplate.teamGroups) { + val teamGroup = if (apiTeamGroup.id != null) { + dbEvaluationTemplate.teamGroups.filter { it.id eq apiTeamGroup.id }.firstOrNull() + ?: throw IllegalArgumentException("Unknown team group ${apiTeamGroup.id} for evaluation ${apiEvaluationTemplate.id}.") + } else { + val tg = DbTeamGroup.new() + dbEvaluationTemplate.teamGroups.add(tg) + tg + } + teamGroup.name = apiTeamGroup.name ?: throw IllegalArgumentException("Team group name must be specified.") + teamGroup.defaultAggregator = apiTeamGroup.aggregation.toDb() + + teamGroup.teams.clear() + teamGroup.teams.addAll(DbTeam.query(DbTeam::id.containsIn(*apiTeamGroup.teams.map { it.id } + .toTypedArray()))) + + /* Establish relationship if entry is new. */ + if (teamGroup.isNew) { + dbEvaluationTemplate.teamGroups.add(teamGroup) + } + } + + /* Update judge information */ + val judgeIds = apiEvaluationTemplate.judges.toTypedArray() + dbEvaluationTemplate.judges.removeAll(DbUser.query(not(DbUser::id.containsIn(*judgeIds)))) + for (userId in judgeIds) { + val user = DbUser.filter { it.id eq userId }.firstOrNull() + ?: throw IllegalArgumentException("Unknown user $userId for evaluation ${apiEvaluationTemplate.id}.") + if (!dbEvaluationTemplate.judges.contains(user)) { + dbEvaluationTemplate.judges.add(user) + } + } + } + + /** + * Creates a copy of an existing [DbEvaluationTemplate] + */ + fun copyTemplate(dbEvaluationTemplate: DbEvaluationTemplate): TemplateId { + + val apiTemplate = dbEvaluationTemplate.toApi() + val copy = apiTemplate.copy(name = "${apiTemplate.name} (copy)") + + val newTemplate = DbEvaluationTemplate.new() + + updateDbTemplate(newTemplate, copy) + + return newTemplate.templateId + + } + +} \ No newline at end of file From 14a69b4b967414203c5340176a38bacae3c44f6c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 25 Jul 2023 18:24:17 +0200 Subject: [PATCH 379/498] #415 Added LSC templates, Legacy AVS scorer --- .../template/ListTaskTypePresetsHandler.kt | 14 +++++++++---- .../template/tasks/options/ApiScoreOption.kt | 6 ++++-- .../run/InteractiveAsynchronousEvaluation.kt | 2 ++ .../run/InteractiveSynchronousEvaluation.kt | 4 +++- .../model/run/NonInteractiveEvaluation.kt | 8 +++----- .../template/task/options/DbScoreOption.kt | 1 + ...json => 10_Textual-Known-Item-Search.json} | 0 ....json => 20_Visual-Known-Item-Search.json} | 0 ...earch.json => 30_Ad-hoc-Video-Search.json} | 0 .../40_LSC-Known-Item-Search.json | 20 +++++++++++++++++++ .../50_LSC-Ad-hoc-Search.json | 16 +++++++++++++++ .../dres-type-presets/60_LSC-Q&A-Text.json | 20 +++++++++++++++++++ doc/oas-client.json | 2 +- doc/oas.json | 4 ++-- 14 files changed, 82 insertions(+), 15 deletions(-) rename backend/src/main/resources/dres-type-presets/{Textual-Known-Item-Search.json => 10_Textual-Known-Item-Search.json} (100%) rename backend/src/main/resources/dres-type-presets/{Visual-Known-Item-Search.json => 20_Visual-Known-Item-Search.json} (100%) rename backend/src/main/resources/dres-type-presets/{Ad-hoc-Video-Search.json => 30_Ad-hoc-Video-Search.json} (100%) create mode 100644 backend/src/main/resources/dres-type-presets/40_LSC-Known-Item-Search.json create mode 100644 backend/src/main/resources/dres-type-presets/50_LSC-Ad-hoc-Search.json create mode 100644 backend/src/main/resources/dres-type-presets/60_LSC-Q&A-Text.json diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt index 7f964c284..344cdaf4a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt @@ -21,6 +21,12 @@ import kotlin.streams.toList /** * A [GetRestHandler] that can be used to list [ApiTaskType] presets stored on disk. + * There are two locations that are checked: + * 1. The "shipped with DRES" _internal_ presets (`src/main/resources/dres-type-presets`) + * 2. Custom _external_ presets (`$DATA_FOLDER/type-presets`) + * + * Both lists are lexicographically sorted and in case two presets have the same name, the external + * one overrides the internal preset. This way instance-specific defaults can be set. * * @author Loris Sauter * @version 1.0.0 @@ -33,7 +39,7 @@ class ListTaskTypePresetsHandler : AccessManagedRestHandler, GetRestHandler = setOf(ApiRole.ADMIN) @OpenApi( - summary = "Lists the task type presets available.", + summary = "Lists the task type presets available. Both, shipped with DRES and custom ones.", path = "/api/v2/template/type-presets/list", operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Template"], @@ -57,7 +63,7 @@ class ListTaskTypePresetsHandler : AccessManagedRestHandler, GetRestHandler DbScoreOption.KIS AVS -> DbScoreOption.AVS + LEGACY_AVS -> DbScoreOption.LEGACY_AVS } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index aa1cea152..5e19b36a0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -18,6 +18,7 @@ import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer +import dev.dres.run.score.scorer.LegacyAvsTaskScorer import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchFilter import dev.dres.run.transformer.SubmissionTransformer @@ -198,6 +199,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description).asSequence().map { it.key to it.value }.toMap() ) DbScoreOption.AVS -> AvsTaskScorer(this) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index b65998dfa..fef59cc6a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -19,6 +19,7 @@ import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.AvsTaskScorer import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer +import dev.dres.run.score.scorer.LegacyAvsTaskScorer import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchFilter import dev.dres.run.transformer.SubmissionTransformer @@ -181,9 +182,10 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description).asSequence().map { it.key to it.value }.toMap() ) DbScoreOption.AVS -> AvsTaskScorer(this) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } ) } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 6f231f24a..bbb8825ce 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -14,10 +14,7 @@ import dev.dres.run.filter.SubmissionFilterAggregator import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard -import dev.dres.run.score.scorer.AvsTaskScorer -import dev.dres.run.score.scorer.CachingTaskScorer -import dev.dres.run.score.scorer.KisTaskScorer -import dev.dres.run.score.scorer.TaskScorer +import dev.dres.run.score.scorer.* import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchFilter import dev.dres.run.transformer.SubmissionTransformer @@ -75,6 +72,7 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev when(val scoreOption = this.template.taskGroup.type.score) { DbScoreOption.KIS -> throw IllegalStateException("KIS task scorer is not applicable to non-interactive evaluations") DbScoreOption.AVS -> AvsTaskScorer(this) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } ) @@ -113,4 +111,4 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev /** */ override val duration: Long = 0 } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt index d06cc785f..dfb9b09b6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbScoreOption.kt @@ -16,6 +16,7 @@ class DbScoreOption(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { val KIS by enumField { description = "KIS" } val AVS by enumField { description = "AVS" } + val LEGACY_AVS by enumField {description = "LEGACY_AVS"} } /** Name / description of the [DbScoreOption]. */ diff --git a/backend/src/main/resources/dres-type-presets/Textual-Known-Item-Search.json b/backend/src/main/resources/dres-type-presets/10_Textual-Known-Item-Search.json similarity index 100% rename from backend/src/main/resources/dres-type-presets/Textual-Known-Item-Search.json rename to backend/src/main/resources/dres-type-presets/10_Textual-Known-Item-Search.json diff --git a/backend/src/main/resources/dres-type-presets/Visual-Known-Item-Search.json b/backend/src/main/resources/dres-type-presets/20_Visual-Known-Item-Search.json similarity index 100% rename from backend/src/main/resources/dres-type-presets/Visual-Known-Item-Search.json rename to backend/src/main/resources/dres-type-presets/20_Visual-Known-Item-Search.json diff --git a/backend/src/main/resources/dres-type-presets/Ad-hoc-Video-Search.json b/backend/src/main/resources/dres-type-presets/30_Ad-hoc-Video-Search.json similarity index 100% rename from backend/src/main/resources/dres-type-presets/Ad-hoc-Video-Search.json rename to backend/src/main/resources/dres-type-presets/30_Ad-hoc-Video-Search.json diff --git a/backend/src/main/resources/dres-type-presets/40_LSC-Known-Item-Search.json b/backend/src/main/resources/dres-type-presets/40_LSC-Known-Item-Search.json new file mode 100644 index 000000000..67b8de43d --- /dev/null +++ b/backend/src/main/resources/dres-type-presets/40_LSC-Known-Item-Search.json @@ -0,0 +1,20 @@ +{ + "name": "LSC Known-Item Search", + "duration": 300, + "targetOption": "SINGLE_MEDIA_ITEM", + "hintOptions": [ + "TEXT" + ], + "submissionOptions": [ + "NO_DUPLICATES", + "LIMIT_CORRECT_PER_TEAM", + "ITEM_SUBMISSION" + ], + "taskOptions": [ + "HIDDEN_RESULTS" + ], + "scoreOption": "KIS", + "configuration": { + "LIMIT_CORRECT_PER_TEAM.limit": "1" + } +} diff --git a/backend/src/main/resources/dres-type-presets/50_LSC-Ad-hoc-Search.json b/backend/src/main/resources/dres-type-presets/50_LSC-Ad-hoc-Search.json new file mode 100644 index 000000000..0c64c4b3e --- /dev/null +++ b/backend/src/main/resources/dres-type-presets/50_LSC-Ad-hoc-Search.json @@ -0,0 +1,16 @@ +{ + "name": "LSC Ad-hoc Search", + "duration": 180, + "targetOption": "JUDGEMENT", + "hintOptions": [ + "TEXT" + ], + "submissionOptions": [ + "NO_DUPLICATES", + "ITEM_SUBMISSION" + ], + "taskOptions": [], + "scoreOption": "AVS", + "configuration": { + } +} diff --git a/backend/src/main/resources/dres-type-presets/60_LSC-Q&A-Text.json b/backend/src/main/resources/dres-type-presets/60_LSC-Q&A-Text.json new file mode 100644 index 000000000..5411b0620 --- /dev/null +++ b/backend/src/main/resources/dres-type-presets/60_LSC-Q&A-Text.json @@ -0,0 +1,20 @@ +{ + "name": "LSC Question & Answer Text", + "duration": 300, + "targetOption": "JUDGEMENT", + "hintOptions": [ + "TEXT" + ], + "submissionOptions": [ + "NO_DUPLICATES", + "LIMIT_CORRECT_PER_TEAM", + "TEXTUAL_SUBMISSION" + ], + "taskOptions": [ + "HIDDEN_RESULTS" + ], + "scoreOption": "KIS", + "configuration": { + "LIMIT_CORRECT_PER_TEAM.limit": "1" + } +} diff --git a/doc/oas-client.json b/doc/oas-client.json index b586a4dc0..275f06c58 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2749,7 +2749,7 @@ }, "ApiScoreOption" : { "type" : "string", - "enum" : [ "KIS", "AVS" ] + "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] }, "ApiSubmissionOption" : { "type" : "string", diff --git a/doc/oas.json b/doc/oas.json index be64dfd34..847eb085f 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4430,7 +4430,7 @@ "/api/v2/template/type-presets/list" : { "get" : { "tags" : [ "Template" ], - "summary" : "Lists the task type presets available.", + "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", "operationId" : "getApiV2TemplateTypePresetsList", "parameters" : [ ], "responses" : { @@ -6319,7 +6319,7 @@ }, "ApiScoreOption" : { "type" : "string", - "enum" : [ "KIS", "AVS" ] + "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] }, "ApiSubmissionOption" : { "type" : "string", From 62527b597deb212a2b8b5db737616bbbb5a2039b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Tue, 25 Jul 2023 20:41:14 +0200 Subject: [PATCH 380/498] Fixed some CLI template commands closes #323 --- .../dres/api/cli/EvaluationTemplateCommand.kt | 128 +++++++++++------- .../types/template/tasks/ApiTaskTemplate.kt | 2 +- .../kotlin/dev/dres/utilities/TemplateUtil.kt | 10 +- 3 files changed, 87 insertions(+), 53 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt index f3d912036..2c5af8ffe 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt @@ -11,9 +11,10 @@ import com.github.ajalt.clikt.parameters.options.validate import com.github.ajalt.clikt.parameters.types.path import com.jakewharton.picnic.table import dev.dres.DRES -import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.mgmt.cache.CacheManager +import dev.dres.utilities.TemplateUtil import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.joda.time.DateTime @@ -29,7 +30,8 @@ import java.util.concurrent.TimeUnit * @author Ralph Gasser * @version 2.0.0 */ -class EvaluationTemplateCommand(private val store: TransientEntityStore, private val cache: CacheManager) : NoOpCliktCommand(name = "template") { +class EvaluationTemplateCommand(private val store: TransientEntityStore, private val cache: CacheManager) : + NoOpCliktCommand(name = "template") { init { this.subcommands( @@ -53,7 +55,8 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private ) } - abstract inner class AbstractEvaluationCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { + abstract inner class AbstractEvaluationCommand(name: String, help: String, printHelpOnEmptyArgs: Boolean = true) : + CliktCommand(name = name, help = help, printHelpOnEmptyArgs = printHelpOnEmptyArgs) { protected val id: String? by option("-i", "--id") protected val name: String? by option("-t", "--template") } @@ -68,10 +71,10 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private .validate { require(it.isNotEmpty()) { "Template description must be non empty." } } private val description: String by option("-d", "--description", help = "Description of the new Template") - .required() - .validate { require(it.isNotEmpty()) { "Template description must be non empty." } } + .required() + .validate { require(it.isNotEmpty()) { "Template description must be non empty." } } - override fun run() { + override fun run() { val newCompetition = this@EvaluationTemplateCommand.store.transactional { DbEvaluationTemplate.new { this.name = this@Create.name @@ -90,7 +93,9 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private inner class Delete : AbstractEvaluationCommand(name = "delete", help = "Deletes a template") { override fun run() { this@EvaluationTemplateCommand.store.transactional { - val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() + val competition = + DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)) + .firstOrNull() if (competition == null) { println("Could not find template to delete.") return@transactional @@ -107,13 +112,16 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private inner class Copy : AbstractEvaluationCommand(name = "copy", help = "Copies a Template") { override fun run() { this@EvaluationTemplateCommand.store.transactional { - val competition = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)).firstOrNull() - if (competition == null) { + val evaluationTemplate = + DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)) + .firstOrNull() + if (evaluationTemplate == null) { println("Could not find template to copy.") return@transactional } - /* TODO: Copy competition. */ + TemplateUtil.copyTemplate(evaluationTemplate) + println("template copied") } //println("Successfully copied template.") } @@ -132,10 +140,10 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private paddingRight = 1 } header { - row("name", "id", "# teams", "# tasks", "description", ) + row("name", "id", "# teams", "# tasks", "description") } body { - DbEvaluationTemplate.all().asSequence().forEach { c -> + DbEvaluationTemplate.all().filter { it.instance eq false }.asSequence().forEach { c -> row(c.name, c.id, c.teams.size(), c.tasks.size(), c.description).also { no++ } } } @@ -178,7 +186,10 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private /** * [CliktCommand] to prepare a specific [DbEvaluationTemplate]. */ - inner class Prepare : AbstractEvaluationCommand(name = "prepare", help = "Checks the used media items and generates precomputed previews.") { + inner class Prepare : AbstractEvaluationCommand( + name = "prepare", + help = "Checks the used media items and generates precomputed previews." + ) { override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { val competition = DbEvaluationTemplate.query( @@ -194,7 +205,7 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private val videos = competition.getAllVideos().toList() val await = videos.mapNotNull { source -> - when(source) { + when (source) { is DbEvaluationTemplate.VideoSource.ItemSource -> { val item = source.item val path = item.pathToOriginal() @@ -204,8 +215,13 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private } println("Rendering ${item.name}$ at ${source.range}") - this@EvaluationTemplateCommand.cache.asyncPreviewVideo(item, source.range.start.toMilliseconds(), source.range.end.toMilliseconds()) + this@EvaluationTemplateCommand.cache.asyncPreviewVideo( + item, + source.range.start.toMilliseconds(), + source.range.end.toMilliseconds() + ) } + is DbEvaluationTemplate.VideoSource.PathSource -> { val path = DRES.EXTERNAL_ROOT.resolve(source.path) if (!Files.exists(path)) { @@ -214,7 +230,11 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private } println("Rendering ${path.fileName}$ at ${source.range}") - this@EvaluationTemplateCommand.cache.asyncPreviewVideo(path, source.range.start.toMilliseconds(), source.range.end.toMilliseconds()) + this@EvaluationTemplateCommand.cache.asyncPreviewVideo( + path, + source.range.start.toMilliseconds(), + source.range.end.toMilliseconds() + ) } } } @@ -245,17 +265,22 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private inner class Export : AbstractEvaluationCommand(name = "export", help = "Exports a template as JSON.") { /** Path to the file that should be created .*/ - private val path: Path by option("-o", "--out", help = "The destination file for the template.").path().required() + private val path: Path by option("-o", "--out", help = "The destination file for the template.").path() + .required() /** Flag indicating whether export should be pretty printed.*/ - private val pretty: Boolean by option("-p", "--pretty", help = "Flag indicating whether exported JSON should be pretty printed.").flag("-u", "--ugly", default = true) + private val pretty: Boolean by option( + "-p", + "--pretty", + help = "Flag indicating whether exported JSON should be pretty printed." + ).flag("-u", "--ugly", default = true) override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { - val competition = DbEvaluationTemplate.query( + val template = DbEvaluationTemplate.query( (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) ).firstOrNull() - if (competition == null) { + if (template == null) { println("Could not find specified template.") return@transactional } @@ -267,56 +292,57 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private } else { mapper.writer() } - writer.writeValue(it, competition) + writer.writeValue(it, template.toApi()) } - println("Successfully wrote template '${competition.name}' (ID = ${competition.id}) to $path.") + println("Successfully wrote template '${template.name}' (ID = ${template.id}) to $path.") } } /** - * Imports a competition from a JSON file. + * Imports a template from a JSON file. */ inner class Import : CliktCommand(name = "import", help = "Imports a template from JSON.") { - /** Flag indicating whether a new competition should be created.*/ - private val new: Boolean by option("-n", "--new", help = "Flag indicating whether a new template should be created.").flag("-u", "--update", default = true) - /** Path to the file that should be imported.*/ private val path: Path by option("-i", "--in", help = "The file to import the template from.").path().required() - override fun run() { - /* TODO: Probably won't work this way. */ - /* Read competition from file */ - /*val reader = jacksonObjectMapper().readerFor(CompetitionDescription::class.java) - val competition = try { + override fun run() = this@EvaluationTemplateCommand.store.transactional { + + /* Read template from file */ + val reader = jacksonObjectMapper().readerFor(ApiEvaluationTemplate::class.java) + val template = try { Files.newBufferedReader(this.path).use { val tree = reader.readTree(it) - if (tree.get("id") != null && (tree.get("description") == null || tree.get("description").isNull || tree.get("description").isTextual)) { - reader.readValue(tree) - } else if (tree.get("id") != null && tree.get("description") != null && tree.get("description").isObject) { - reader.readValue(tree["description"]) - } else { - null - } + reader.readValue(tree) } } catch (e: Throwable) { - println("Could not import competition from $path: ${e.message}.") - return + println("Could not import template from $path: ${e.message}.") + return@transactional } - /* Create/update competition. */ - if (competition != null) { - if (this.new) { - val id = this@CompetitionCommand.competitions.append(competition) - println("Successfully imported new competition '${competition.name}' (ID = $id) from $path.") - } else { - this@CompetitionCommand.competitions.update(competition) - println("Successfully updated competition '${competition.name}' (ID = ${competition.id}) from $path.") - } + /* Create/update template. */ + if (template != null) { + + val target = DbEvaluationTemplate.new() + + val import = //null out ids to force new task creation + template.copy( + tasks = template.tasks.map { + it.copy(id = null) + }, + teams = template.teams.map { + it.copy(id = null) + } + ) + + + TemplateUtil.updateDbTemplate(target, import) + println("template imported") + } else { - println("Could not import competition from $path: Unknown format.") - }*/ + println("Could not import template from $path: Unknown format.") + } } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt index c525844cf..213f1efee 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt @@ -11,7 +11,7 @@ import dev.dres.data.model.template.TemplateId * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class ApiTaskTemplate( +data class ApiTaskTemplate( val id: TemplateId? = null, val name: String, val taskGroup: String, diff --git a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt index a262f54e4..1a74bbac4 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt @@ -248,7 +248,15 @@ object TemplateUtil { fun copyTemplate(dbEvaluationTemplate: DbEvaluationTemplate): TemplateId { val apiTemplate = dbEvaluationTemplate.toApi() - val copy = apiTemplate.copy(name = "${apiTemplate.name} (copy)") + val copy = apiTemplate.copy( + name = "${apiTemplate.name} (copy)", + tasks = apiTemplate.tasks.map { + it.copy(id = null) + }, + teams = apiTemplate.teams.map { + it.copy(id = null) + } + ) val newTemplate = DbEvaluationTemplate.new() From f6a7bf4c4c1c620ffd4ea9394dd8ab69fba32b56 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 26 Jul 2023 16:21:25 +0200 Subject: [PATCH 381/498] Added frontend support for #410 Changes in task order appear to be currently ignored by update mechanism in backend --- frontend/src/app/app.module.ts | 4 ++ ...n-media-item-image-form-field.component.ts | 6 --- .../task-templates-list.component.html | 41 ++++++++-------- .../task-templates-list.component.ts | 49 +++++++++++-------- .../template-builder-components.module.ts | 47 +++++++++--------- 5 files changed, 78 insertions(+), 69 deletions(-) diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index ab91b16bd..76c248137 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -26,6 +26,8 @@ import { CompetitionBuilderModule } from './competition/competition-builder/comp import { TemplateModule } from './template/template.module'; import { EvaluationModule } from './evaluation/evaluation.module'; import { ErrorModule } from "./error/error.module"; +import {DragDropModule} from '@angular/cdk/drag-drop'; +import {MatTableModule} from '@angular/material/table'; /** * Method used to load application config. @@ -50,6 +52,8 @@ export function initializeApp(appConfig: AppConfig) { MatMenuModule, MatTooltipModule, HttpClientModule, + DragDropModule, + MatTableModule, /* Our own modules. */ SharedModule, diff --git a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts index e3990cde4..b7a803bba 100644 --- a/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts +++ b/frontend/src/app/template/template-builder/components/query-description-media-item-image-form-field/query-description-media-item-image-form-field.component.ts @@ -1,10 +1,4 @@ import { Component, Injector, Input } from "@angular/core"; -import { UntypedFormControl } from "@angular/forms"; -import { - CompetitionFormBuilder -} from "../../../../competition/competition-builder/competition-builder-task-dialog/competition-form.builder"; -import { ApiMediaItem } from "../../../../../../openapi"; -import { FormatMediaItemPipe, MediaItemDisplayOptions } from "../../../../services/pipes/format-media-item.pipe"; import { QueryDescriptionMediaItemFormFieldComponent } from "../query-description-media-item-form-field/query-description-media-item-form-field.component"; diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html index 9dec9d155..4ccdc6b05 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html @@ -12,7 +12,7 @@

Tasks

Add task
- +
@@ -46,27 +46,28 @@

Tasks

- - + + + + + + + + + + + + + + + + + + - + +
Name {{ task.name }}
diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts index aca8316ac..f876097d1 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.ts @@ -21,6 +21,7 @@ import { import { MatDialog } from "@angular/material/dialog"; import { ActivatedRoute } from "@angular/router"; import { MatSnackBar } from "@angular/material/snack-bar"; +import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop'; export interface TaskTemplateEditorLauncher { editTask(taskType: ApiTaskType, taskGroup: ApiTaskGroup, task?: ApiTaskTemplate); @@ -102,27 +103,27 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent this.selection.toggle(task); } - public moveTaskUp(task: ApiTaskTemplate) { - const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); - if (oldIndex > 0) { - const buffer = this.builderService.getTemplate().tasks[oldIndex - 1]; - this.builderService.getTemplate().tasks[oldIndex - 1] = task; - this.builderService.getTemplate().tasks[oldIndex] = buffer; - this.builderService.update(); - this.taskTable.renderRows(); - } - } - - public moveTaskDown(task: ApiTaskTemplate) { - const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); - if (oldIndex < this.builderService.getTemplate().tasks.length - 1) { - const buffer = this.builderService.getTemplate().tasks[oldIndex + 1]; - this.builderService.getTemplate().tasks[oldIndex + 1] = task; - this.builderService.getTemplate().tasks[oldIndex] = buffer; - this.builderService.update(); - this.taskTable.renderRows(); - } - } + // public moveTaskUp(task: ApiTaskTemplate) { + // const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); + // if (oldIndex > 0) { + // const buffer = this.builderService.getTemplate().tasks[oldIndex - 1]; + // this.builderService.getTemplate().tasks[oldIndex - 1] = task; + // this.builderService.getTemplate().tasks[oldIndex] = buffer; + // this.builderService.update(); + // this.taskTable.renderRows(); + // } + // } + // + // public moveTaskDown(task: ApiTaskTemplate) { + // const oldIndex = this.builderService.getTemplate().tasks.indexOf(task); + // if (oldIndex < this.builderService.getTemplate().tasks.length - 1) { + // const buffer = this.builderService.getTemplate().tasks[oldIndex + 1]; + // this.builderService.getTemplate().tasks[oldIndex + 1] = task; + // this.builderService.getTemplate().tasks[oldIndex] = buffer; + // this.builderService.update(); + // this.taskTable.renderRows(); + // } + // } public tasksLength() { return this.builderService.getTemplate().tasks.length; @@ -146,4 +147,10 @@ export class TaskTemplatesListComponent extends AbstractTemplateBuilderComponent onChange() { this.taskTable?.renderRows(); } + + public dropTable(event: CdkDragDrop) { + moveItemInArray(this.builderService.getTemplate().tasks, event.previousIndex, event.currentIndex); + this.builderService.update(); + this.taskTable.renderRows(); + } } diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index e7be43508..2814be9f0 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -46,6 +46,7 @@ import { TeamBuilderDialogComponent } from './team-builder-dialog/team-builder-d import { MatChipsModule } from "@angular/material/chips"; import { MatDialogModule } from "@angular/material/dialog"; import { ColorPickerModule } from "ngx-color-picker"; +import {CdkDrag, CdkDropList} from '@angular/cdk/drag-drop'; @NgModule({ @@ -67,28 +68,30 @@ import { ColorPickerModule } from "ngx-color-picker"; QueryDescriptionExternalImageFormFieldComponent, TeamBuilderDialogComponent ], - imports: [ - CommonModule, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatTooltipModule, - MatIconModule, - MatMenuModule, - MatAutocompleteModule, - MatTableModule, - MatListModule, - SharedModule, - MatSelectModule, - MatButtonToggleModule, - MatGridListModule, - CompetitionBuilderModule, - NgOptimizedImage, - MatChipsModule, - MatDialogModule, - ColorPickerModule - ], + imports: [ + CommonModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatMenuModule, + MatAutocompleteModule, + MatTableModule, + MatListModule, + SharedModule, + MatSelectModule, + MatButtonToggleModule, + MatGridListModule, + CompetitionBuilderModule, + NgOptimizedImage, + MatChipsModule, + MatDialogModule, + ColorPickerModule, + CdkDropList, + CdkDrag + ], exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, From 7c25e6566dec3748cdf579f5ceb8a48008722410 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 17:05:03 +0200 Subject: [PATCH 382/498] #415: Media collection improvements and config updates --- backend/src/main/kotlin/dev/dres/DRES.kt | 25 +- .../dres/api/cli/MediaCollectionCommand.kt | 231 +++++++++--------- .../dev/dres/data/model/config/CacheConfig.kt | 10 +- .../dev/dres/data/model/config/Config.kt | 20 +- .../dev/dres/mgmt/cache/CacheManager.kt | 4 +- .../run/eventstream/EventStreamProcessor.kt | 7 +- config.json | 7 +- .../collection-viewer.component.html | 13 +- .../collection-viewer.component.ts | 17 +- .../template-builder.component.ts | 10 +- .../admin-user-list.component.html | 16 +- .../admin-user-list.component.ts | 3 +- frontend/src/app/user/user.module.ts | 2 +- 13 files changed, 221 insertions(+), 144 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 615f0ce9c..6b4d5e103 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -29,6 +29,7 @@ import java.io.File import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.absolute +import kotlin.system.exitProcess /** @@ -50,12 +51,12 @@ object DRES { var DATA_ROOT: Path = APPLICATION_ROOT /** Path to the directory that contains the external items. */ - val EXTERNAL_ROOT: Path - get() = DATA_ROOT.resolve("external") + lateinit var EXTERNAL_ROOT: Path + internal set /** Path to the directory that contains task type presets. */ - val TASK_TYPE_PRESETS_EXTERNAL_LOCATION: Path - get() = DATA_ROOT.resolve("type-presets") + lateinit var TASK_TYPE_PRESETS_EXTERNAL_LOCATION: Path + internal set /** Path to the classpath directory that contains task type presets shipped with DRES. */ val TASK_TYPE_PRESETS_LOCATION = "dres-type-presets" @@ -71,14 +72,22 @@ object DRES { @JvmStatic fun main(args: Array) { CONFIG = if (args.isNotEmpty()) { - val configPath = Paths.get(args[0]) - val config = Config.read(configPath) - DATA_ROOT = configPath.absolute().parent - config + try{ + val configPath = Paths.get(args[0]) + val config = Config.read(configPath) + DATA_ROOT = configPath.absolute().parent + config + }catch(e: Exception){ + println("ERROR: The config at ${Paths.get(args[0])} could not be loaded. See stacktrace for more information.") + exitProcess(11) + } } else { Config() } + EXTERNAL_ROOT = CONFIG.externalMediaLocation + TASK_TYPE_PRESETS_EXTERNAL_LOCATION = CONFIG.presetsLocation + println("Starting DRES (application: $APPLICATION_ROOT, data: $DATA_ROOT)") println("Initializing...") diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 151f1c337..5fc8cc287 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -39,6 +39,7 @@ import kotlin.streams.asSequence * * @author Luca Rossetto * @author Ralph Gasser + * @author Loris Sauter * @version 2.0.0 */ class MediaCollectionCommand(private val store: TransientEntityStore, private val config: Config) : @@ -50,17 +51,17 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va init { this.subcommands( Create(), - Delete(), - Update(), + Delete(store), + Update(store), List(), - Show(), - Check(), - Scan(), - AddItem(), - DeleteItem(), - Export(), - Import(), - ImportSegments() + Show(store), + Check(store), + Scan(store), + AddItem(store), + DeleteItem(store), + Export(store), + Import(store), + ImportSegments(store) ) } @@ -85,7 +86,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * */ - abstract inner class AbstractCollectionCommand(name: String, help: String) : + abstract inner class AbstractCollectionCommand(protected val store: TransientEntityStore, name: String, help: String) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = true) { /** The [CollectionId] of the [DbMediaCollection] affected by this [AbstractCollectionCommand]. */ @@ -146,7 +147,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Delete : AbstractCollectionCommand("delete", help = "Deletes a media collection.") { + inner class Delete(store: TransientEntityStore) : AbstractCollectionCommand(store, "delete", help = "Deletes a media collection.") { override fun run() { this@MediaCollectionCommand.store.transactional { val collection = this.getCollection() @@ -163,7 +164,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Update : AbstractCollectionCommand(name = "update", help = "Updates an existing Collection") { + inner class Update(store: TransientEntityStore) : AbstractCollectionCommand(store, name = "update", help = "Updates an existing Collection") { /** The new name for the [DbMediaCollection]. */ private val newName: String? by option("-n", "--name", help = "The new name of the collection") @@ -239,7 +240,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to show a [DbMediaCollection]'s [DbMediaItem]s in detail. */ - inner class Show : AbstractCollectionCommand("show", help = "Lists the content of a media collection.") { + inner class Show (store: TransientEntityStore) : AbstractCollectionCommand(store,"show", help = "Lists the content of a media collection.") { /** The property of the [DbMediaItem]s to sort by. */ private val sort by option( @@ -304,7 +305,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ - inner class Check : AbstractCollectionCommand( + inner class Check (store: TransientEntityStore) : AbstractCollectionCommand(store, "check", help = "Checks if all the files in a media collection are present and accessible." ) { @@ -337,7 +338,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ - inner class Scan : AbstractCollectionCommand("scan", help = "Scans a collection directory and adds found items") { + inner class Scan(store: TransientEntityStore) : AbstractCollectionCommand( + store, + "scan", + help = "Scans a collection directory and adds found items" + ) { /** The file suffices that should be considered as images. */ @@ -365,116 +370,124 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va override fun run() { - /* Sanity cehck. */ - if (imageTypes.isEmpty() && videoTypes.isEmpty()) { - println("No file types specified.") - return - } + this.store.transactional { - /* Find media collection. */ - val collection = this.getCollection() - if (collection == null) { - println("Collection not found.") - return - } + /* Sanity check. */ + if (imageTypes.isEmpty() && videoTypes.isEmpty()) { + println("No file types specified.") + return@transactional + } - val base = Paths.get(collection.path) - if (!Files.exists(base)) { - println("Failed to scan collection; '${collection.path}' does not exist.") - return - } + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return@transactional + } - if (!Files.isReadable(base)) { - println("Failed to scan collection; '${collection.path}' is not readable.") - return - } + val base = Paths.get(collection.path) + if (!Files.exists(base)) { + println("Failed to scan collection; '${collection.path}' does not exist.") + return@transactional + } - if (!Files.isDirectory(base)) { - println("Failed to scan collection; '${collection.path}' is no directory.") - return - } + if (!Files.isReadable(base)) { + println("Failed to scan collection; '${collection.path}' is not readable.") + return@transactional + } - /* Now scan directory. */ - val issues = mutableMapOf() - var fileCounter = 0 - Files.walk(base).filter { - Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) - }.asSequence().chunked(transactionChunkSize).forEach { list -> - - this@MediaCollectionCommand.store.transactional { - - list.forEach { - val relativePath = it.relativeTo(base) - val exists = - DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty - if (!exists) { - try { - when (it.extension.lowercase()) { - in this.imageTypes -> { - println("Found image $it; analyzing...") - collection.items.add(DbMediaItem.new { - this.type = DbMediaType.IMAGE - this.name = it.fileName.nameWithoutExtension - this.location = relativePath.toString() - }) - } + if (!Files.isDirectory(base)) { + println("Failed to scan collection; '${collection.path}' is no directory.") + return@transactional + } - in videoTypes -> { - println("Found video $it; analyzing...") - val result = this.analyze(it).streams.first() - val fps = (result.rFrameRate ?: result.avgFrameRate!!).toFloat() - val duration = result.getDuration(TimeUnit.MILLISECONDS).let { duration -> - if (duration != null) { - duration - } else { - println("Cannot read duration from file, counting frames") - val analysis = this.analyze(it, countFrames = true) - val frames = analysis.streams.first().nbReadFrames - println("Counted $frames frames") - ((frames * 1000) / fps).toLong() - } + /* Now scan directory. */ + val issues = mutableMapOf() + var fileCounter = 0 + Files.walk(base).filter { + Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) + }.asSequence().chunked(transactionChunkSize).forEach { list -> + + this@MediaCollectionCommand.store.transactional { + + list.forEach { + val relativePath = it.relativeTo(base) + val exists = + DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty + if (!exists) { + try { + when (it.extension.lowercase()) { + in this.imageTypes -> { + println("Found image $it; analyzing...") + collection.items.add(DbMediaItem.new { + this.type = DbMediaType.IMAGE + this.name = it.fileName.nameWithoutExtension + this.location = relativePath.toString() + }) } - println("Found frame rate to be $fps frames per seconds and duration $duration ms") - collection.items.add(DbMediaItem.new { - this.type = DbMediaType.VIDEO - this.name = it.fileName.nameWithoutExtension - this.location = relativePath.toString() - this.durationMs = duration - this.fps = fps - }) + in videoTypes -> { + println("Found video $it; analyzing...") + val result = this.analyze(it).streams.first() + val fps = (result.rFrameRate + ?: result.avgFrameRate!!).toFloat() + val duration = result.getDuration(TimeUnit.MILLISECONDS) + .let { duration -> + if (duration != null) { + duration + } else { + println("Cannot read duration from file, counting frames") + val analysis = + this.analyze(it, countFrames = true) + val frames = + analysis.streams.first().nbReadFrames + println("Counted $frames frames") + ((frames * 1000) / fps).toLong() + } + } + + println("Found frame rate to be $fps frames per seconds and duration $duration ms") + collection.items.add(DbMediaItem.new { + this.type = DbMediaType.VIDEO + this.name = it.fileName.nameWithoutExtension + this.location = relativePath.toString() + this.durationMs = duration + this.fps = fps + }) + } } + } catch (e: Throwable) { + this@MediaCollectionCommand.logger.error( + this@MediaCollectionCommand.logMarker, + "An error occurred with $it. Noting and skipping..." + ) + println("An error occurred with $it. Noting and skipping...") + issues[it] = e.stackTraceToString() } - } catch (e: Throwable) { - this@MediaCollectionCommand.logger.error( - this@MediaCollectionCommand.logMarker, - "An error occurred with $it. Noting and skipping..." - ) - println("An error occurred with $it. Noting and skipping...") - issues[it] = e.stackTraceToString() } - } - ++fileCounter + ++fileCounter + } } - } + } + if (issues.isNotEmpty()) { + val file = + File("issues-scan-${collection.name}-${System.currentTimeMillis()}.json") + println("There have been ${issues.size} issues while scanning. You might want to check them at ${file.path}") + val om = jacksonObjectMapper() + om.writeValue(file, issues) + println("done") + } + println("\nAdded $fileCounter elements to collection") } - if (issues.isNotEmpty()) { - val file = File("issues-scan-${collection.name}-${System.currentTimeMillis()}.json") - println("There have been ${issues.size} issues while scanning. You might want to check them at ${file.path}") - val om = jacksonObjectMapper() - om.writeValue(file, issues) - println("done") - } - println("\nAdded $fileCounter elements to collection") } } /** * [CliktCommand] to delete [DbMediaItem]s. */ - inner class DeleteItem : AbstractCollectionCommand("deleteItem", help = "Deletes media item(s).") { + inner class DeleteItem (store: TransientEntityStore) : AbstractCollectionCommand(store,"deleteItem", help = "Deletes media item(s).") { /** The item ID matching the name of the [DbMediaItem] to delete. */ private val itemId: MediaId? by option("-ii", "--itemId", help = "ID of the media item.") @@ -524,7 +537,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to delete [DbMediaItem]s. */ - inner class AddItem : AbstractCollectionCommand(name = "add", help = "Adds a media item to a media collection.") { + inner class AddItem (store: TransientEntityStore) : AbstractCollectionCommand(store,name = "add", help = "Adds a media item to a media collection.") { /** The [ApiMediaType] of the new [DbMediaItem]. */ private val type: ApiMediaType by option( @@ -576,7 +589,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to export a [DbMediaCollection]. */ - inner class Export : AbstractCollectionCommand("export", help = "Exports a media collection into a CSV file.") { + inner class Export (store: TransientEntityStore) : AbstractCollectionCommand(store,"export", help = "Exports a media collection into a CSV file.") { /** The output path for the export.. */ private val output: Path by option( @@ -618,7 +631,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to import a [DbMediaCollection]. */ - inner class Import : AbstractCollectionCommand("import", help = "Imports a media collection from a CSV file.") { + inner class Import (store: TransientEntityStore) : AbstractCollectionCommand(store,"import", help = "Imports a media collection from a CSV file.") { /** [Path] to the input file. */ private val input: Path by option( @@ -674,7 +687,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va * * Uses the VBS format. */ - inner class ImportSegments : AbstractCollectionCommand( + inner class ImportSegments(store: TransientEntityStore) : AbstractCollectionCommand(store, "importSegments", "Imports the Segment information for the Items in a Collection from a CSV file" ) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt b/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt index fe9e0ab19..c595b2cdb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt @@ -10,7 +10,8 @@ import java.nio.file.Paths * Configuration related to the handling of the preview cache by DRES. * * @author Ralph Gasser - * @version 1.0.0 + * @author Loris Sauter + * @version 1.1.0 */ @JsonIgnoreProperties(ignoreUnknown = true) data class CacheConfig( @@ -34,6 +35,10 @@ data class CacheConfig( /** The maximum bounding box for a preview video. Defaults to 480p. */ val previewVideoMaxSize: Int = 480, + + /** The location of the cache, relative to APPLICATION_ROOT */ + val cachePath: Path = DRES.DATA_ROOT.resolve("cache"), + ) { /** @@ -47,4 +52,5 @@ data class CacheConfig( Files.isDirectory(Paths.get("ffmpeg")) -> Paths.get("ffmpeg") else -> throw IllegalStateException("Could not find valid FFmpeg binary path.") } -} \ No newline at end of file + +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index 636a6f5e2..84e3a4f28 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -2,18 +2,33 @@ package dev.dres.data.model.config import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.ObjectMapper +import dev.dres.DRES import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption @JsonIgnoreProperties(ignoreUnknown = true) data class Config( + /** HTTP Port */ val httpPort: Int = 8080, + /** HTTPS Port, only used when [enableSsl] is set to `true` */ val httpsPort: Int = 8443, + /** Whether to use secure connection or not. Requires keystore and keystore password */ val enableSsl: Boolean = true, + /** The path to the keystore file */ val keystorePath: String = "keystore.jks", + /** Keystore password */ val keystorePassword: String = "password", - val cache: CacheConfig = CacheConfig() + /** The [CacheConfig] */ + val cache: CacheConfig = CacheConfig(), + /** Folder that contains external media. Defaults to `$APPLICATION_ROOT/external` */ + val externalMediaLocation : Path = DRES.APPLICATION_ROOT.resolve("external"), + /** Folder that contains task type rpesets. Defaults to `$APPLICATION_ROOT/type-presets` */ + val presetsLocation: Path = DRES.APPLICATION_ROOT.resolve("type-presets"), + /** Folder for event data. Defaults to `$DATA_ROOT/external` */ + val eventsPath: Path = DRES.DATA_ROOT.resolve("events"), + /** Event buffer retention time, defaults to 1 minute */ + val eventBufferRetentionTime: Int = 60_000, ) { companion object{ @@ -27,4 +42,5 @@ data class Config( ObjectMapper().readValue(it, Config::class.java) } } -} \ No newline at end of file + +} diff --git a/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt index 9206a59f6..196836ecf 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt @@ -43,7 +43,7 @@ class CacheManager(private val config: Config, private val store: TransientEntit private val ffmpegBin = this.config.cache.ffmpegPath() /** The [Path] to the cache location. */ - private val cacheLocation = DRES.DATA_ROOT.resolve("cache") + private val cacheLocation = DRES.CONFIG.cache.cachePath /** A [ConcurrentHashMap] of all [Path]s* that are currently being calculated. */ private val inTransit = ConcurrentHashMap>() @@ -383,4 +383,4 @@ class CacheManager(private val config: Config, private val store: TransientEntit this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt index 12b67be81..522befcb0 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt @@ -1,6 +1,7 @@ package dev.dres.run.eventstream import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.dres.DRES import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write import org.slf4j.LoggerFactory @@ -23,10 +24,10 @@ object EventStreamProcessor { private val eventQueue = ConcurrentLinkedQueue() private val eventHandlers = mutableListOf() private val handlerLock = StampedLock() - private val eventSink = PrintWriter(File("events/${System.currentTimeMillis()}.txt").also { it.parentFile.mkdirs() }) + private val eventSink = PrintWriter(DRES.CONFIG.eventsPath.resolve("${System.currentTimeMillis()}.txt").toFile().also { it.parentFile.mkdirs() }) private val eventBuffer = mutableListOf() - private val eventBufferRetentionTime = 60_000 //TODO make configurable + private val eventBufferRetentionTime = DRES.CONFIG.eventBufferRetentionTime private val eventBufferLock = StampedLock() val recentEvents: List @@ -102,4 +103,4 @@ object EventStreamProcessor { active = false } -} \ No newline at end of file +} diff --git a/config.json b/config.json index 3f6989591..08542dd35 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,5 @@ { - "dataPath" : "/dres-data/data", - "cachePath" : "/dres-data/cache", - "externalPath": "/dres-data/external", + "dataPath" : "./data", + "externalPath": "./external", "enableSsl" : false -} \ No newline at end of file +} diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.html b/frontend/src/app/collection/collection-viewer/collection-viewer.component.html index 584af1d82..7592c3485 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.html +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.html @@ -24,7 +24,13 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect Base Path: {{ (collection | async)?.collection?.basePath }} -
+
+ + + Filter + + + @@ -82,13 +88,13 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect -

Duration [ms]Duration [ms] {{ row?.durationMs }} FPSFPS {{ row?.fps }}
+ diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index 45e45b930..760f44078 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -20,13 +20,16 @@ import {ApiMediaItem, ApiPopulatedMediaCollection, CollectionService} from '../. styleUrls: ['./collection-viewer.component.scss'], }) export class CollectionViewerComponent implements AfterViewInit, OnDestroy { + + public isLoading = true; + displayedColumns = ['actions', 'id', 'name', 'location', 'type', 'durationMs', 'fps']; /** Material Table UI element for sorting. */ @ViewChild(MatSort) sort: MatSort; /** Material Table UI element for pagination. */ - @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + @ViewChild(MatPaginator) paginator: MatPaginator; /** Data source for Material tabl.e */ dataSource = new MatTableDataSource(); @@ -63,6 +66,8 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { /* Initialize sorting and pagination. */ this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; + /* Custom filter: on ID, Name and Location */ + this.dataSource.filterPredicate = (data: ApiMediaItem, value: string) => data.mediaItemId.includes(value) || data.name.includes(value) || data.location.includes(value) /* * Initialize subscription for collection data. @@ -88,9 +93,19 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { ); this.subscription = this.collection.subscribe((s: ApiPopulatedMediaCollection) => { this.dataSource.data = s.items; + this.isLoading = false; }); } + applyFilter(event: Event){ + const filterValue = (event.target as HTMLInputElement).value; + this.dataSource.filter = filterValue.trim().toLowerCase(); + + if(this.dataSource.paginator){ + this.dataSource.paginator.firstPage(); + } + } + /** * House keeping; clean up subscriptions. */ diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index ef2c7c2f4..4dca67b58 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -50,7 +50,15 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i ngOnInit(): void { this.onInit(); - + this.routeSub = this.route.params.subscribe( (p) => { + this.templateService.getApiV2TemplateByTemplateId(p.templateId).subscribe((t) => { + /* initialise from route */ + this.builderService.initialise(t); + }, + (r) => { + this.snackBar.open(`Error: ${r?.error?.description}`, null,{duration: 5000}); + }); + }); } fileProvider = () => { diff --git a/frontend/src/app/user/admin-user-list/admin-user-list.component.html b/frontend/src/app/user/admin-user-list/admin-user-list.component.html index 1241947a3..b1d7a7265 100644 --- a/frontend/src/app/user/admin-user-list/admin-user-list.component.html +++ b/frontend/src/app/user/admin-user-list/admin-user-list.component.html @@ -2,9 +2,9 @@

Users

-
- - Quick filter +
+ + Filter
@@ -32,8 +33,9 @@
-
+
([]); - isFilterInputActive = false; + + shouldDisplayFilter = false; filterValue = ''; constructor( diff --git a/frontend/src/app/user/user.module.ts b/frontend/src/app/user/user.module.ts index 574ed6dee..c22792e87 100644 --- a/frontend/src/app/user/user.module.ts +++ b/frontend/src/app/user/user.module.ts @@ -39,7 +39,7 @@ import { MatSortModule } from '@angular/material/sort'; ClipboardModule, SharedModule, MatSortModule, - FormsModule + FormsModule, ], exports: [ LoginComponent From 135e7099df688ca39ecb87936ab6dd7644baf3a3 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 26 Jul 2023 17:11:49 +0200 Subject: [PATCH 383/498] Added explicit ordering to task templates --- .../main/kotlin/dev/dres/api/cli/EvaluationCommand.kt | 3 ++- .../dev/dres/api/cli/EvaluationTemplateCommand.kt | 11 ++++++----- .../api/rest/types/evaluation/ApiEvaluationInfo.kt | 4 +++- .../model/run/InteractiveSynchronousEvaluation.kt | 2 +- .../dres/data/model/template/DbEvaluationTemplate.kt | 2 +- .../dres/data/model/template/task/DbTaskTemplate.kt | 4 ++++ .../dev/dres/run/InteractiveAsynchronousRunManager.kt | 4 ++-- .../main/kotlin/dev/dres/utilities/TemplateUtil.kt | 5 ++++- 8 files changed, 23 insertions(+), 12 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index 014cdffe7..7668b5bff 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -14,6 +14,7 @@ import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.* import dev.dres.utilities.extensions.toDateString import jetbrains.exodus.database.TransientEntityStore @@ -191,7 +192,7 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom println() println("All Tasks:") - it.template.tasks.asSequence().forEach { task -> + it.template.tasks.sortedBy(DbTaskTemplate::idx).asSequence().forEach { task -> println(task) } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt index 2c5af8ffe..6e9e347d0 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt @@ -13,6 +13,7 @@ import com.jakewharton.picnic.table import dev.dres.DRES import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.mgmt.cache.CacheManager import dev.dres.utilities.TemplateUtil import jetbrains.exodus.database.TransientEntityStore @@ -157,24 +158,24 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private */ inner class Show : AbstractEvaluationCommand(name = "show", help = "Shows details of a Template") { override fun run() = this@EvaluationTemplateCommand.store.transactional(true) { - val competition = DbEvaluationTemplate.query( + val template = DbEvaluationTemplate.query( (DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name) ).firstOrNull() - if (competition == null) { + if (template == null) { println("Could not find specified template.") return@transactional } - println("${competition.name}: ${competition.description}") + println("${template.name}: ${template.description}") println("Teams:") - competition.teams.asSequence().forEach(::println) + template.teams.asSequence().forEach(::println) println() println("Tasks:") - competition.tasks.asSequence().forEach { _ -> + template.tasks.sortedBy(DbTaskTemplate::idx).asSequence().forEach { _ -> /* TODO: it.printOverview(System.out) */ println() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt index 1f7bccfa7..ec8a27d2b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -2,11 +2,13 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunProperties +import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import dev.dres.run.NonInteractiveRunManager import dev.dres.run.RunManager import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.sortedBy /** * Contains the basic and most importantly static information about a [InteractiveSynchronousEvaluation] and the @@ -39,6 +41,6 @@ data class ApiEvaluationInfo( }, manager.runProperties, manager.template.teams.asSequence().map { team -> ApiTeamInfo(team) }.toList(), - manager.template.tasks.asSequence().map { task -> ApiTaskTemplateInfo(task) }.toList() + manager.template.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { task -> ApiTaskTemplateInfo(task) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index fef59cc6a..e588d451a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -48,7 +48,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu override val tasks = LinkedList() /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ - private val templates = this.description.tasks.asSequence().map { it.templateId }.toList() + private val templates = this.description.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { it.templateId }.toList() /** Returns the last [TaskRun]. */ val currentTask: AbstractInteractiveTask? diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt index 249f1b898..4d1950b0c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/DbEvaluationTemplate.kt @@ -91,7 +91,7 @@ class DbEvaluationTemplate(entity: Entity) : PersistentEntity(entity), Evaluatio teamGroups = this.teamGroups.asSequence().map { it.toApi() }.toList(), teams = this.teams.asSequence().map { it.toApi() }.toList(), judges = this.judges.asSequence().map { it.id }.toList(), - tasks = this.tasks.asSequence().map { it.toApi() }.toList(), + tasks = this.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { it.toApi() }.toList(), ) /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 3b83616bb..e343af698 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -56,6 +56,9 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { /** Optional comment to be used by evaluation administrators */ var comment by xdStringProp() + /** Sort key for task order within template */ + var idx by xdIntProp() + /** * Creates a [DbTaskTemplate] instance and returns it. * @@ -71,6 +74,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { this.duration = this@DbTaskTemplate.duration this.taskGroup = forEvaluation.taskGroups.query(DbTaskGroup::name eq this@DbTaskTemplate.taskGroup.name).first() this.comment = this@DbTaskTemplate.comment + this.idx = this@DbTaskTemplate.idx } /* Copy task targets. */ diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 7dd0e7c78..6206cccee 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -239,7 +239,7 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.template.tasks.indexOf(this.currentTaskTemplate(context)) - 1 + val newIndex = this.template.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) - 1 return try { this.goTo(context, newIndex) true @@ -260,7 +260,7 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.template.tasks.indexOf(this.currentTaskTemplate(context)) + 1 + val newIndex = this.template.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) + 1 return try { this.goTo(context, newIndex) true diff --git a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt b/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt index 1a74bbac4..c7aa23df9 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt @@ -100,7 +100,7 @@ object TemplateUtil { ) /* Update task information: Remaining tasks. */ - for (apiTask in apiEvaluationTemplate.tasks) { + apiEvaluationTemplate.tasks.forEachIndexed { idx, apiTask -> val task = if (apiTask.id != null) { dbEvaluationTemplate.tasks.filter { it.id eq apiTask.id }.firstOrNull() ?: throw IllegalArgumentException("Unknown task ${apiTask.id} for evaluation ${apiEvaluationTemplate.id}.") @@ -114,6 +114,7 @@ object TemplateUtil { task.name = apiTask.name task.duration = apiTask.duration task.comment = apiTask.comment + task.idx = idx /* Conditional updating of parameters that do!. */ if (task.isNew || task.collection.id != apiTask.collectionId) { @@ -156,6 +157,8 @@ object TemplateUtil { } } + + /* Update team information. */ val teamIds = apiEvaluationTemplate.teams.map { it.id }.toTypedArray() dbEvaluationTemplate.teams.removeAll( From 3d78ac51415583efac5ac7d4cdbe01773159b21d Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 17:23:00 +0200 Subject: [PATCH 384/498] #415: Removed textual submission render --- frontend/src/app/viewer/teams-viewer.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/viewer/teams-viewer.component.html b/frontend/src/app/viewer/teams-viewer.component.html index b4be32e93..1ed5f35b1 100644 --- a/frontend/src/app/viewer/teams-viewer.component.html +++ b/frontend/src/app/viewer/teams-viewer.component.html @@ -37,7 +37,7 @@

{{ team.name }}

?

-

{{ answer.previewText }}

+ A texticon, the actual submission text is displayed via tooltip
From 8e35fe405c8391d6a91c2e3232c5b81f218bfed6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Wed, 26 Jul 2023 19:10:31 +0200 Subject: [PATCH 385/498] Refactored submission subsystem and related filtering, validation and scoring mechanisms. Addresses issues described in #420. --- backend/build.gradle | 5 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 2 +- .../handler/download/ScoreDownloadHandler.kt | 3 +- .../evaluation/admin/AdjustDurationHandler.kt | 4 +- .../evaluation/admin/ForceViewerHandler.kt | 3 +- .../evaluation/admin/ListPastTaskHandler.kt | 3 +- .../admin/ListSubmissionsHandler.kt | 4 +- .../evaluation/admin/NextTaskHandler.kt | 3 +- .../admin/OverrideAnswerSetVerdictHandler.kt | 6 +- .../evaluation/admin/PreviousTaskHandler.kt | 3 +- .../admin/StartEvaluationHandler.kt | 3 +- .../evaluation/admin/StartTaskHandler.kt | 3 +- .../evaluation/admin/StopEvaluationHandler.kt | 3 +- .../evaluation/admin/StopTaskHandler.kt | 3 +- .../evaluation/admin/SwitchTaskHandler.kt | 3 +- .../client/ClientTaskInfoHandler.kt | 3 +- .../scores/CurrentTaskScoreHandler.kt | 4 +- .../scores/HistoryTaskScoreHandler.kt | 3 +- .../scores/TeamGroupScoreHandler.kt | 3 +- .../viewer/GetCurrentTaskHandler.kt | 3 +- .../viewer/GetEvaluationStateHandler.kt | 3 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 6 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 7 +- .../viewer/GetSubmissionInfoHandler.kt | 7 +- .../evaluation/viewer/GetTaskHintHandler.kt | 8 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 4 +- .../viewer/ListEvaluationStatesHandler.kt | 3 +- .../judgement/DequeueJudgementHandler.kt | 16 +- .../handler/judgement/DequeueVoteHandler.kt | 4 +- .../handler/judgement/PostJudgementHandler.kt | 2 +- .../rest/handler/judgement/PostVoteHandler.kt | 7 +- .../submission/LegacySubmissionHandler.kt | 310 ++++++------------ .../handler/submission/SubmissionHandler.kt | 218 ++---------- .../api/rest/types/evaluation/ApiAnswer.kt | 43 --- .../api/rest/types/evaluation/ApiAnswerSet.kt | 63 ---- .../rest/types/evaluation/ApiClientAnswer.kt | 18 - .../types/evaluation/ApiClientAnswerSet.kt | 8 - .../types/evaluation/ApiClientSubmission.kt | 6 - .../ApiOverrideAnswerSetVerdictDto.kt | 2 + .../rest/types/evaluation/ApiSubmission.kt | 52 --- .../types/evaluation/ApiSubmissionInfo.kt | 1 + .../dres/api/rest/types/evaluation/ApiTask.kt | 1 + .../types/evaluation/submission/ApiAnswer.kt | 29 ++ .../evaluation/submission/ApiAnswerSet.kt | 15 + .../{ => submission}/ApiAnswerType.kt | 3 +- .../evaluation/submission/ApiClientAnswer.kt | 72 ++++ .../submission/ApiClientAnswerRangeType.kt | 13 + .../submission/ApiClientAnswerSet.kt | 40 +++ .../submission/ApiClientSubmission.kt | 55 ++++ .../evaluation/submission/ApiSubmission.kt | 23 ++ .../{ => submission}/ApiVerdictStatus.kt | 2 +- .../api/rest/types/judgement/ApiJudgement.kt | 2 +- .../types/judgement/ApiJudgementRequest.kt | 4 +- .../dres/api/rest/types/judgement/ApiVote.kt | 2 +- .../status/SuccessfulSubmissionsStatus.kt | 2 +- .../dres/data/model/media/DbMediaSegment.kt | 7 +- .../dev/dres/data/model/run/AbstractTask.kt | 4 +- .../kotlin/dev/dres/data/model/run/DbTask.kt | 14 +- .../run/InteractiveAsynchronousEvaluation.kt | 22 +- .../run/InteractiveSynchronousEvaluation.kt | 22 +- .../model/run/NonInteractiveEvaluation.kt | 24 +- .../dres/data/model/run/RunActionContext.kt | 32 +- .../dev/dres/data/model/submissions/Answer.kt | 23 +- .../dres/data/model/submissions/AnswerSet.kt | 48 ++- .../dres/data/model/submissions/AnswerType.kt | 2 +- .../dres/data/model/submissions/DbAnswer.kt | 11 +- .../data/model/submissions/DbAnswerSet.kt | 33 +- .../data/model/submissions/DbAnswerType.kt | 1 - .../data/model/submissions/DbSubmission.kt | 24 +- .../data/model/submissions/DbVerdictStatus.kt | 2 +- .../dres/data/model/submissions/Submission.kt | 26 +- .../data/model/submissions/VerdictStatus.kt | 2 +- .../task/options/DbSubmissionOption.kt | 10 +- .../run/InteractiveAsynchronousRunManager.kt | 120 ++++--- .../dev/dres/run/InteractiveRunManager.kt | 31 +- .../run/InteractiveSynchronousRunManager.kt | 131 +++++--- .../dev/dres/run/NonInteractiveRunManager.kt | 23 +- .../main/kotlin/dev/dres/run/RunManager.kt | 10 +- .../dev/dres/run/audit/DbAuditLogger.kt | 13 +- .../dres/run/filter/AllSubmissionFilter.kt | 18 - .../dres/run/filter/CorrectPerTeamFilter.kt | 37 --- .../run/filter/CorrectPerTeamItemFilter.kt | 28 -- .../run/filter/CorrectPerTeamMemberFilter.kt | 36 -- .../run/filter/DuplicateSubmissionFilter.kt | 68 ++-- .../dres/run/filter/ItemSubmissionFilter.kt | 20 -- .../run/filter/MaximumCorrectPerTeamFilter.kt | 55 ++++ .../MaximumCorrectPerTeamMemberFilter.kt | 58 ++++ .../run/filter/MaximumTotalPerTeamFilter.kt | 51 ++- .../run/filter/MaximumWrongPerTeamFilter.kt | 54 ++- .../dev/dres/run/filter/SubmissionFilter.kt | 35 -- .../run/filter/SubmissionFilterAggregator.kt | 16 - .../dres/run/filter/SubmissionRateFilter.kt | 68 ++-- .../run/filter/SubmissionRejectedException.kt | 5 +- .../run/filter/TemporalSubmissionFilter.kt | 22 -- .../run/filter/TextualSubmissionFilter.kt | 22 -- .../run/filter/ValidItemSubmissionFilter.kt | 34 ++ .../filter/ValidTemporalSubmissionFilter.kt | 35 ++ .../filter/ValidTextualSubmissionFilter.kt | 32 ++ .../filter/basics/AbstractSubmissionFilter.kt | 30 ++ .../basics/AcceptAllSubmissionFilter.kt | 29 ++ .../basics/AcceptNoneSubmissionFilter.kt | 29 ++ .../basics/CombiningSubmissionFilter.kt | 34 ++ .../run/filter/basics/SubmissionFilter.kt | 24 ++ .../IdentitySubmissionTransformer.kt | 7 - .../transformer/MapToSegmentTransformer.kt | 67 ++-- .../transformer/SubmissionTaskMatchFilter.kt | 16 - .../SubmissionTaskMatchTransformer.kt | 28 ++ .../run/transformer/SubmissionTransformer.kt | 10 - .../SubmissionTransformerAggregator.kt | 16 - .../basics/CombiningSubmissionTransformer.kt | 24 ++ .../basics/IdentitySubmissionTransformer.kt | 19 ++ .../basics/SubmissionTransformer.kt | 22 ++ .../run/updatables/EndOnSubmitUpdatable.kt | 2 +- .../updatables/ProlongOnSubmitUpdatable.kt | 11 +- .../validation/ChainedAnswerSetValidator.kt | 21 +- .../MediaItemsAnswerSetValidator.kt | 40 ++- .../TemporalContainmentAnswerSetValidator.kt | 44 +-- .../TemporalOverlapAnswerSetValidator.kt | 43 ++- .../run/validation/TextAnswerSetValidator.kt | 54 ++- .../interfaces/AnswerSetValidator.kt | 22 +- .../interfaces/JudgementValidator.kt | 19 +- .../validation/interfaces/VoteValidator.kt | 29 +- .../judged/BasicJudgementValidator.kt | 164 ++++----- .../validation/judged/BasicVoteValidator.kt | 91 +++-- .../dres/run/validation/judged/ItemRange.kt | 22 +- .../utilities/extensions/ContextExtensions.kt | 5 +- .../run/score/scorer/AvsTaskScorerTest.kt | 1 + .../run/score/scorer/KisTaskScorerTest.kt | 2 +- .../score/scorer/LegacyAvsTaskScorerTest.kt | 2 +- ..._2c07d0ad-4e7d-498a-8ae2-e95274b8ad03.json | 1 + 130 files changed, 1770 insertions(+), 1605 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/{ => submission}/ApiAnswerType.kt (85%) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt rename backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/{ => submission}/ApiVerdictStatus.kt (92%) delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamMemberFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/ValidTextualSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/basics/AbstractSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptAllSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptNoneSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/basics/CombiningSubmissionFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/filter/basics/SubmissionFilter.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchTransformer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/basics/CombiningSubmissionTransformer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/basics/IdentitySubmissionTransformer.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/transformer/basics/SubmissionTransformer.kt create mode 100644 run_dump_2c07d0ad-4e7d-498a-8ae2-e95274b8ad03.json diff --git a/backend/build.gradle b/backend/build.gradle index 48aa86dd8..f80af0362 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -38,12 +38,11 @@ repositories { compileKotlin { kotlinOptions.jvmTarget = "11" - kotlinOptions { - freeCompilerArgs = ["-Xinline-classes"] - } + compilerOptions.freeCompilerArgs.add("-Xcontext-receivers") } compileTestKotlin { kotlinOptions.jvmTarget = "11" + compilerOptions.freeCompilerArgs.add("-Xcontext-receivers") } dependencies { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 9e6db296c..36ca3f3a4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -138,7 +138,7 @@ object RestApi { // Submission LegacySubmissionHandler(store, cache), - SubmissionHandler(store, config), + SubmissionHandler(), // Log QueryLogHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index 316c64407..ac373825f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.handler.download import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.RunManager import dev.dres.utilities.extensions.eligibleManagerForId import io.javalin.http.Context @@ -39,7 +40,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle ) override fun doGet(ctx: Context): String = this.store.transactional(true) { val manager = ctx.eligibleManagerForId() - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() /* Update response header. */ ctx.contentType("text/csv") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index dc22ff89e..754d75062 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -46,8 +47,9 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val evaluationId = ctx.evaluationId() val duration = ctx.pathParamMap()["duration"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'duration' is missing!'", ctx) val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val rac = ctx.runActionContext() + return this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { evaluationManager.adjustDuration(rac, duration) DbAuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", DbAuditLogSource.REST, ctx.sessionToken()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index fc4452195..9d8920950 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -42,9 +43,9 @@ class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHa val evaluationId = ctx.evaluationId() val viewerId = ctx.pathParamMap()["viewerId"] ?: throw ErrorStatusException(404, "Parameter 'viewerId' is missing!'", ctx) val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + val rac = ctx.runActionContext() return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) try { if (evaluationManager.overrideReadyState(rac, viewerId)) { SuccessStatus("State for viewer $viewerId (evaluation '$evaluationId') forced successfully.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index 963b8d3aa..fb3be8f23 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -38,8 +39,8 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH override fun doGet(ctx: Context): List { val evaluationId = ctx.evaluationId() val runManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val rac = ctx.runActionContext() return this.store.transactional (true) { - val rac = RunActionContext.runActionContext(ctx, runManager) runManager.tasks(rac).filter { it.hasEnded }.map { ApiTaskTemplateInfo(it.template) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index 774a61eee..847eb3187 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.evaluation.ApiSubmissionInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -39,8 +40,9 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm val evaluationId = ctx.evaluationId() val templateId = ctx.pathParamMap()["templateId"] ?: throw ErrorStatusException(404, "Parameter 'templateId' is missing!'", ctx) val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val rac = ctx.runActionContext() return this.store.transactional(true) { - evaluationManager.tasks(RunActionContext.runActionContext(ctx, evaluationManager)).filter { it.template.templateId == templateId }.map { + evaluationManager.tasks(rac).filter { it.template.templateId == templateId }.map { ApiSubmissionInfo(evaluationId, it.taskId, it.getSubmissions().map { sub -> sub.toApi() }.toList()) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index ac0716fe1..c48aa5675 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -9,6 +9,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -52,7 +53,7 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl synchronousAdminCheck(evaluationManager, ctx) return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() if (evaluationManager is InteractiveAsynchronousRunManager && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) && evaluationManager.currentTask(rac)?.status !in setOf(DbTaskStatus.ENDED, DbTaskStatus.IGNORED)) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt index b221cbbc6..c7875f09c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt @@ -2,12 +2,12 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.evaluation.ApiOverrideAnswerSetVerdictDto -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId @@ -68,7 +68,7 @@ class OverrideAnswerSetVerdictHandler(store: TransientEntityStore): AbstractEval } return this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() val dbSubmission = evaluationManager.allSubmissions(rac).find { submission -> submission.answerSets.filter { it.id eq answerSetId }.any() } ?: throw ErrorStatusException(404, "No AnswerSet with Id '$answerSetId' found.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index f70421fdc..43e54d66c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -39,7 +40,7 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH val evaluationId = ctx.evaluationId() val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() try { if (evaluationManager.previous(rac)) { SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index 8f21d3478..6152d31f0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -43,7 +44,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() try { evaluationManager.start(rac) DbAuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template, DbAuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 0acc11657..a818683c1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -52,7 +53,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand synchronousAdminCheck(evaluationManager, ctx) return this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() try { evaluationManager.startTask(rac) DbAuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.taskId, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index 17cd57c93..0de206062 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -44,7 +45,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) return this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() try { evaluationManager.end(rac) DbAuditLogger.evaluationEnd(evaluationManager.id, DbAuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 6887df5b1..1c5df1258 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.DbEvaluation import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.audit.DbAuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken @@ -42,7 +43,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl val evaluationId = ctx.evaluationId() val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() try { val task = evaluationManager.currentTaskTemplate(rac) evaluationManager.abortTask(rac) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index e32285077..f3bd02a3e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -42,7 +43,7 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan val idx = ctx.pathParamMap()["idx"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'idx' is missing!'", ctx) val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, evaluationManager) + val rac = ctx.runActionContext() try { evaluationManager.goTo(rac, idx) SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index 38b30e1c6..e7a234e39 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.* import io.javalin.http.Context import io.javalin.openapi.* @@ -39,7 +40,7 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie ) override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional(true) { val run = ctx.eligibleManagerForId() - val rac = RunActionContext.runActionContext(ctx, run) + val rac = ctx.runActionContext() if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) ApiTaskTemplateInfo(task.template) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index b98f0db35..5429c6b03 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -8,8 +8,8 @@ import dev.dres.api.rest.types.evaluation.scores.ApiScore import dev.dres.api.rest.types.evaluation.scores.ApiScoreOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.score.scoreboard.ScoreOverview import io.javalin.http.Context @@ -52,7 +52,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle } - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException( 404, "No active task run in evaluation ${ctx.evaluationId()}.", diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index 0767ad850..528c8a045 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -10,6 +10,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import dev.dres.run.score.scoreboard.ScoreOverview import io.javalin.http.Context @@ -54,7 +55,7 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle return this.store.transactional(true) { val manager = ctx.eligibleManagerForId() - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run ${manager.id}.", ctx) val scores = scorer.scoreMapFromCache() ApiScoreOverview("task", diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index 544431bf7..7e300e1de 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.evaluation.scores.ApiTeamGroupValue import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* @@ -43,7 +44,7 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( } return this.store.transactional(true) { - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() /* TODO: Not suite sure where the teamGroupAggregator got lost.*/ //val aggregators = manager.currentTask(rac)?.teamGroupAggregators ?: throw ErrorStatusException(404, "No active task in evaluation ${ctx.evaluationId()}.", ctx) //val teamGroups = manager.template.teamsGroups.toList() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index 71e852c36..bb97ae3f9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -9,6 +9,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.InteractiveRunManager import io.javalin.http.Context @@ -51,7 +52,7 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index 98ccf04a6..80e7b02c8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* @@ -39,7 +40,7 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() ApiEvaluationState(manager, rac) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index 3b38420bc..823667494 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -4,10 +4,10 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.isParticipant -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import io.javalin.http.Context @@ -41,7 +41,7 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua override fun doGet(ctx: Context): List { val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) return this.store.transactional (true) { - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index 2aab1e60a..a9b902b2f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -2,12 +2,11 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId -import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.isParticipant -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import io.javalin.http.Context @@ -39,7 +38,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval ) override fun doGet(ctx: Context): List = this.store.transactional (true) { val manager = ctx.eligibleManagerForId() - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index 08744cf9a..8ed9dcdc3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -2,12 +2,11 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId -import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.isParticipant -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager @@ -40,7 +39,7 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV ) override fun doGet(ctx: Context): List = this.store.transactional (true) { val manager = ctx.eligibleManagerForId() - val rac = RunActionContext.runActionContext(ctx, manager) + val rac = ctx.runActionContext() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 2607ed9bc..9e97e7e7f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -8,8 +8,8 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.DbHint import dev.dres.data.model.template.task.DbHintType import dev.dres.data.model.template.task.DbTaskTemplate @@ -60,14 +60,14 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiHintContent { - val taskId = - ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val rac = ctx.runActionContext() + return this.store.transactional(true) { val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } - val rac = RunActionContext.runActionContext(ctx, manager) val currentTaskDescription = manager.currentTaskTemplate(rac) val template = if (currentTaskDescription.id == taskId) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index d89e15422..b5b0ea570 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -11,6 +11,7 @@ import dev.dres.api.rest.types.task.ApiContentType import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.DbHint import dev.dres.data.model.template.task.DbTargetType import dev.dres.data.model.template.task.DbTaskTemplate @@ -57,12 +58,13 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val cache: Cache ) override fun doGet(ctx: Context): ApiTargetContent { val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val rac = ctx.runActionContext() + return this.store.transactional (true) { val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } - val rac = RunActionContext.runActionContext(ctx, manager) /* Test for correct state. */ var task = manager.currentTask(rac) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt index 958fa250d..2edcf3a3e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -28,7 +29,7 @@ class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluati ) override fun doGet(ctx: Context): List = this.store.transactional(true) { this.getRelevantManagers(ctx).map { - val rac = RunActionContext.runActionContext(ctx, it) + val rac = ctx.runActionContext() ApiEvaluationState(it, rac) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index ea45b5753..f5d6601e0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -1,22 +1,15 @@ package dev.dres.api.rest.handler.judgement import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.api.rest.types.evaluation.ApiAnswer import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.submissions.Answer -import dev.dres.data.model.submissions.AnswerType -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbAnswerType import dev.dres.run.RunManager import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.firstOrNull /** * A [GetRestHandler] to dequeue the next [ApiJudgementRequest] that is ready for judgement. @@ -54,12 +47,9 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa val request = this.store.transactional(false) { val evaluationManager = ctx.eligibleManagerForId() checkEligibility(ctx, evaluationManager) - val validator = evaluationManager.judgementValidators.find { it.hasOpen } - ?: return@transactional null - val next = validator?.next(ctx.sessionToken()!!) - ?: /* No submission awaiting judgement */ - return@transactional null - val taskDescription = next.second.task().template.textualDescription() + val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: return@transactional null + val next = validator.next() ?: return@transactional null + val taskDescription = next.second.task.template.textualDescription() return@transactional ApiJudgementRequest( token = next.first, validator = validator.id, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 46cc0b36c..23af070fb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -46,10 +46,10 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( val evaluationManager = ctx.eligibleManagerForId() val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: return@transactional null - val next = validator?.next(ctx.sessionToken()!!) + val next = validator.next() ?: /* No submission awaiting judgement */ return@transactional null - val taskDescription = next.second.task().template.textualDescription() + val taskDescription = next.second.task.template.textualDescription() return@transactional ApiJudgementRequest( token = next.first, validator = validator.id, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 94f7575e0..9a4ae3b89 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -58,7 +58,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) try { - validator.judge(judgement.token, VerdictStatus.fromApi(judgement.verdict)) + validator.judge(judgement.token, judgement.verdict.toDb()) } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index 0ca3ab6f2..fbe77f09f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -49,11 +49,10 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto this.store.transactional { val evaluationManager = ctx.eligibleManagerForId() - val validator = evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } // Get first active vote validator + val validator = evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } as? VoteValidator // Get first active vote validator ?: throw ErrorStatusException(404, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx) - validator as VoteValidator - validator.vote(VerdictStatus.fromApi(vote.verdict)) + validator.vote(vote.verdict.toDb()) } - return SuccessStatus("vote received") + return SuccessStatus("Vote received.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index f4ce6175c..235b1a4b8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.evaluation.* +import dev.dres.api.rest.types.evaluation.submission.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus @@ -16,6 +17,7 @@ import dev.dres.data.model.media.* import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.* import dev.dres.mgmt.cache.CacheManager import dev.dres.run.InteractiveRunManager @@ -115,250 +117,130 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { - val (s, r) = this.store.transactional { - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( - 401, - "Authorization required.", - ctx - ) - val run = getEligibleRunManager(userId, ctx) - val time = System.currentTimeMillis() - val submission = toSubmission(userId, run, time, ctx) - val rac = RunActionContext.runActionContext(ctx, run) - - try { - run.postSubmission(rac, submission) - } catch (e: SubmissionRejectedException) { - throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) - } catch (e: IllegalRunStateException) { - logger.info("Submission was received while run manager was not accepting submissions.") - throw ErrorStatusException( - 400, - "Run manager is in wrong state and cannot accept any more submission.", - ctx - ) - } catch (e: IllegalTeamIdException) { - logger.info("Submission with unknown team id '${rac.teamId}' was received.") - throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) - } - - DbAuditLogger.submission(submission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) - if (run.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS)) { //pre-generate preview - generatePreview(submission.answerSets().first()) - } - - val result = DbSubmission.filter { it.id eq submission.submissionId }.firstOrNull()?.answerSets?.first()?.status?.let { VerdictStatus.fromDb(it) } ?: VerdictStatus.INDETERMINATE - - submission to result + /* Obtain run action context and parse submission. */ + val rac = ctx.runActionContext() + val run = getEligibleRunManager(rac, ctx) + val submission = toSubmission(rac, run, ctx) + + /* Post submission. */ + try { + run.postSubmission(rac, submission) + } catch (e: SubmissionRejectedException) { + throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) + } catch (e: IllegalRunStateException) { + logger.info("Submission was received while run manager was not accepting submissions.") + throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + } catch (e: IllegalTeamIdException) { + logger.info("Submission with unknown team id '${submission.teamId}' was received.") + throw ErrorStatusException(400, "Run manager does not know the given teamId ${submission.teamId}.", ctx) } - logger.info("Submission ${s.submissionId} received status $r.") - - return when (r) { - VerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") - VerdictStatus.WRONG -> SuccessfulSubmissionsStatus( - ApiVerdictStatus.WRONG, - "Submission incorrect! Try again" - ) - - VerdictStatus.INDETERMINATE -> { - ctx.status(202) /* HTTP Accepted. */ - SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") + /* Lookup verdict for submission and return it. */ + return this.store.transactional(true) { + when (DbSubmission.filter { it.id eq submission.submissionId }.firstOrNull()?.answerSets?.first()?.status) { + DbVerdictStatus.CORRECT -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct!") + DbVerdictStatus.WRONG -> SuccessfulSubmissionsStatus(ApiVerdictStatus.WRONG, "Submission incorrect! Try again") + DbVerdictStatus.INDETERMINATE -> { + ctx.status(202) /* HTTP Accepted. */ + SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received. Waiting for verdict!") + } + DbVerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus(ApiVerdictStatus.UNDECIDABLE, "Submission undecidable. Try again!") + else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } - - VerdictStatus.UNDECIDABLE -> SuccessfulSubmissionsStatus( - ApiVerdictStatus.UNDECIDABLE, - "Submission undecidable. Try again!" - ) - - else -> throw ErrorStatusException(500, "Unsupported submission status. This is very unusual!", ctx) } } /** * Returns the [InteractiveRunManager] that is eligible for the given [UserId] and [Context] + * + * @param rac The [RunActionContext] used for the lookup. + * @param ctx The current [Context]. */ - private fun getEligibleRunManager(userId: UserId, ctx: Context): InteractiveRunManager { - val managers = - AccessManager.getRunManagerForUser(userId).filterIsInstance(InteractiveRunManager::class.java).filter { - val rac = RunActionContext.runActionContext(ctx, it) - it.currentTask(rac)?.isRunning == true - } - if (managers.isEmpty()) throw ErrorStatusException( - 404, - "There is currently no eligible competition with an active task.", - ctx - ) - if (managers.size > 1) throw ErrorStatusException( - 409, - "More than one possible competition found: ${managers.joinToString { it.template.name }}", - ctx - ) + private fun getEligibleRunManager(rac: RunActionContext, ctx: Context): InteractiveRunManager { + val managers = AccessManager.getRunManagerForUser(rac.userId).filterIsInstance(InteractiveRunManager::class.java).filter { + it.currentTask(rac)?.isRunning == true + } + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) return managers.first() } /** - * Converts the user request tu a [DbSubmission]. + * Converts the user request tu a [ApiClientSubmission]. * - * Creates the associated database entry. Requires an ongoing transaction. - * - * @param userId The [UserId] of the user who triggered the [DbSubmission]. + * @param rac The [RunActionContext] used for the conversion. * @param runManager The [InteractiveRunManager] - * @param submissionTime Time of the submission. * @param ctx The HTTP [Context] */ - private fun toSubmission( - userId: UserId, - runManager: InteractiveRunManager, - submissionTime: Long, - ctx: Context - ): ApiSubmission { + private fun toSubmission(rac: RunActionContext, runManager: InteractiveRunManager, ctx: Context): ApiClientSubmission { val map = ctx.queryParamMap() + return this.store.transactional(true) { + /* If text is supplied, it supersedes other parameters */ + val textParam = map[PARAMETER_NAME_TEXT]?.first() + val itemParam = map[PARAMETER_NAME_ITEM]?.first() + + val answer = if (textParam != null) { + ApiClientAnswer(text = textParam) + } else if (itemParam != null) { + val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ + val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) + val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)).firstOrNull() + ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) + val range: Pair? = when { + map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { + val shot = map[PARAMETER_NAME_SHOT]?.first()!! + val time = item.segments.filter { it.name eq shot }.firstOrNull()?.range?.toMilliseconds()//TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) + ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) + time.first to time.second + } - /* Find team that the user belongs to. */ - val user = DbUser.query(DbUser::id eq userId).firstOrNull() - ?: throw ErrorStatusException(404, "No user with ID '$userId' could be found.", ctx) - val team = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull() - ?: throw ErrorStatusException(404, "No team for user '$userId' could be found.", ctx) - val rac = RunActionContext.runActionContext(ctx, runManager) - - val answerSets = mutableListOf() - - /* Create new submission. */ - val submission = ApiSubmission( - teamId = team.teamId, - memberId = user.userId, - teamName = team.name, - memberName = user.username, - answers = answerSets, - evaluationId = runManager.id - ) - - /* If text is supplied, it supersedes other parameters */ - val textParam = map[PARAMETER_NAME_TEXT]?.first() - val itemParam = map[PARAMETER_NAME_ITEM]?.first() - val currentTaskId = runManager.currentTask(rac)?.taskId - val task = DbTask.query(DbTask::id eq currentTaskId).firstOrNull() ?: throw ErrorStatusException( - 404, - "No active task for ID '$currentTaskId' could be found.", - ctx - ) - - - val answer = if (textParam != null) { - ApiAnswer( - type = ApiAnswerType.TEXT, - text = textParam, - item = null, - start = null, - end = null - ) - } else if (itemParam != null) { - val collection = - runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ - val mapToSegment = - runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) - val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)) - .firstOrNull() - ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) - val range: Pair? = when { - map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { - val shot = map[PARAMETER_NAME_SHOT]?.first()!! - val time = item.segments.filter { it.name eq shot }.firstOrNull()?.range?.toMilliseconds()//TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) - ?: throw ErrorStatusException( - 400, - "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", - ctx + map.containsKey(PARAMETER_NAME_FRAME) && item.type == DbMediaType.VIDEO -> { + val fps = item.fps + ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") + val time = TemporalPoint.Frame( + map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() + ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), + fps ) - time.first to time.second - } - - map.containsKey(PARAMETER_NAME_FRAME) && item.type == DbMediaType.VIDEO -> { - val fps = item.fps - ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") - val time = TemporalPoint.Frame( - map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() - ?: throw ErrorStatusException( - 400, - "Parameter '$PARAMETER_NAME_FRAME' must be a number.", - ctx - ), - fps - ) - if (mapToSegment) { - DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) - ?: throw ErrorStatusException( - 400, - "No matching segments found for item '${item.name}'.", - ctx - ) - } else { - val ms = time.toMilliseconds() - ms to ms + if (mapToSegment) { + DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) + ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + } else { + val ms = time.toMilliseconds() + ms to ms + } } - } - map.containsKey(PARAMETER_NAME_TIMECODE) -> { - val fps = item.fps - ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") - val time = - TemporalPoint.Millisecond(TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, fps) - ?: throw ErrorStatusException( - 400, - "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", - ctx - ) - ) - if (mapToSegment) { - DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) - ?: throw ErrorStatusException( - 400, - "No matching segments found for item '${item.name}'.", - ctx + map.containsKey(PARAMETER_NAME_TIMECODE) -> { + val fps = item.fps ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") + val time = + TemporalPoint.Millisecond(TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, fps) + ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) ) - } else { - val ms = time.toMilliseconds() - ms to ms + if (mapToSegment) { + DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) + ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + } else { + val ms = time.toMilliseconds() + ms to ms + } } + else -> null } - else -> null - } - - /* Assign information to submission. */ - if (range != null) { - ApiAnswer( - type = ApiAnswerType.TEMPORAL, - item = item.toApi(), - start = range.first, - end = range.second, - text = null - ) + /* Assign information to submission. */ + if (range != null) { + ApiClientAnswer(itemName = itemParam, start = range.first, end = range.second) + } else { + ApiClientAnswer(itemName = itemParam) + } } else { - ApiAnswer( - type = ApiAnswerType.ITEM, - item = item.toApi(), - start = null, - end = null, - text = null - ) + throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) } - } else { - throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) + /* Generate and return ApiClientSubmission. */ + ApiClientSubmission(listOf(ApiClientAnswerSet(answers = listOf(answer)))) } - - /* Create AnswerSet. */ - answerSets.add( - ApiAnswerSet( - id = UUID.randomUUID().toString(), - status = ApiVerdictStatus.INDETERMINATE, - taskId = task.taskId, - answers = listOf(answer) - ) - ) - - return submission } /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 21b02c524..576b64590 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -3,39 +3,23 @@ package dev.dres.api.rest.handler.submission import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler -import dev.dres.api.rest.types.evaluation.* +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.config.Config import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.media.DbMediaCollection -import dev.dres.data.model.run.NonInteractiveEvaluation -import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.interfaces.EvaluationRun -import dev.dres.data.model.submissions.AnswerType -import dev.dres.run.InteractiveAsynchronousRunManager -import dev.dres.run.InteractiveRunManager -import dev.dres.run.RunManager -import dev.dres.run.audit.DbAuditLogger +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException -import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.first -import kotlinx.dnq.query.firstOrNull import org.slf4j.LoggerFactory -class SubmissionHandler(private val store: TransientEntityStore, private val config: Config) : - PostRestHandler, AccessManagedRestHandler { +class SubmissionHandler: PostRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.PARTICIPANT) @@ -46,13 +30,13 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con private val logger = LoggerFactory.getLogger(this.javaClass) @OpenApi( - summary = "Endpoint to accept submissions", + summary = "Endpoint to accept submissions.", path = "/api/v2/submit/{evaluationId}", methods = [HttpMethod.POST], operationId = OpenApiOperation.AUTO_GENERATE, requestBody = OpenApiRequestBody([OpenApiContent(ApiClientSubmission::class)]), pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("evaluationId", String::class, "The ID of the evaluation the submission belongs to.", required = true), ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -64,176 +48,34 @@ class SubmissionHandler(private val store: TransientEntityStore, private val con tags = ["Submission"] ) override fun doPost(ctx: Context): SuccessStatus { - - return this.store.transactional { - - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( - 401, - "Authorization required.", - ctx - ) - val evaluationId = ctx.pathParam("evaluationId") - val runManager = - AccessManager.getRunManagerForUser(userId).find { it.id == evaluationId } ?: throw ErrorStatusException( - 404, - "Evaluation with id '$evaluationId' not found.", - ctx - ) - - val rac = RunActionContext.runActionContext(ctx, runManager) - - val apiClientSubmission = try { - ctx.bodyAsClass() - } catch (e: BadRequestResponse) { - throw ErrorStatusException(400, "Invalid submission, cannot parse: ${e.message}", ctx) - } - - val apiSubmission = try { - transformClientSubmission(apiClientSubmission, runManager, rac) - } catch (e: Exception) { - throw ErrorStatusException(400, "Invalid submission: ${e.message}", ctx) - } - - try { - runManager.postSubmission(rac, apiSubmission) - } catch (e: SubmissionRejectedException) { - throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) - } catch (e: IllegalRunStateException) { - logger.info("Submission was received while run manager was not accepting submissions.") - throw ErrorStatusException( - 400, - "Run manager is in wrong state and cannot accept any more submission.", - ctx - ) - } catch (e: IllegalTeamIdException) { - logger.info("Submission with unknown team id '${rac.teamId}' was received.") - throw ErrorStatusException(400, "Run manager does not know the given teamId ${rac.teamId}.", ctx) - } - - DbAuditLogger.submission(apiSubmission, DbAuditLogSource.REST, ctx.sessionToken(), ctx.req().remoteAddr) - - - return@transactional SuccessStatus("Submission received") - - + /* Obtain run action context and parse submission. */ + val rac = ctx.runActionContext() + val apiClientSubmission = try { + ctx.bodyAsClass() + } catch (e: BadRequestResponse) { + throw ErrorStatusException(400, "Invalid submission, cannot parse: ${e.message}", ctx) } - } - - private fun transformClientSubmission( - apiClientSubmission: ApiClientSubmission, - runManager: RunManager, - rac: RunActionContext - ): ApiSubmission { - - if (rac.userId == null || rac.teamId == null) { - throw Exception("Invalid association between user and evaluation") + /* Obtain run action context and run manager. */ + val runManager = AccessManager.getRunManagerForUser(rac.userId).find { it.id == rac.evaluationId } + ?: throw ErrorStatusException(404, "Evaluation with ID '${rac.evaluationId}' could not be found.", ctx) + + /* Post submission. */ + try { + runManager.postSubmission(rac, apiClientSubmission) + } catch (e: SubmissionRejectedException) { + throw ErrorStatusException(412, e.message ?: "Submission rejected by submission filter.", ctx) + } catch (e: IllegalRunStateException) { + logger.info("Submission was received while run manager was not accepting submissions.") + throw ErrorStatusException(400, "Run manager is in wrong state and cannot accept any more submission.", ctx) + } catch (e: IllegalTeamIdException) { + logger.info("Submission with unknown team id '${apiClientSubmission.teamId}' was received.") + throw ErrorStatusException(400, "Run manager does not know the given teamId ${apiClientSubmission.teamId}.", ctx) + } finally { + DbAuditLogSource } - val evaluationRun = runManager.evaluation - - val errorBuffer = StringBuffer() - - val answerSets = apiClientSubmission.answerSets.mapNotNull mapClientAnswerSet@{ clientAnswerSet -> - - val task = if (runManager is InteractiveRunManager) { - val currentTask = runManager.currentTask(rac) //only accept submissions for current task in interactive runs - if (currentTask?.isRunning != true) { - throw Exception("No active task") - } - currentTask - } else { - evaluationRun.tasks.find { it.template.name == clientAnswerSet.taskName } //look up task by name - } - - if (task == null) { - if (runManager is InteractiveRunManager) { - errorBuffer.append("No active task\n") - } else { - errorBuffer.append("task '${clientAnswerSet.taskName}' not found\n") - } - return@mapClientAnswerSet null - } - - val answers = clientAnswerSet.answers.mapNotNull mapClientAnswers@{ clientAnswer -> - - val item = if (clientAnswer.itemName != null) { - val collection = if (clientAnswer.itemCollectionName != null) { - DbMediaCollection.filter { it.name eq clientAnswer.itemCollectionName }.firstOrNull() - } else { - task.template.collection - } - collection?.items?.filter { it.name eq clientAnswer.itemName }?.firstOrNull() - } else { - null - }?.toApi() - - return@mapClientAnswers when (clientAnswer.type()) { - AnswerType.ITEM -> { - if (item == null) { - errorBuffer.append("item for answer $clientAnswer not found") - return@mapClientAnswers null - } - ApiAnswer(type = ApiAnswerType.ITEM, item = item, start = null, end = null, text = null) - } - - AnswerType.TEMPORAL -> { - if (item == null) { - errorBuffer.append("item for answer $clientAnswer not found") - return@mapClientAnswers null - } - ApiAnswer( - type = ApiAnswerType.TEMPORAL, - item = item, - start = clientAnswer.start, - end = clientAnswer.end, - text = null - ) - } - - AnswerType.TEXT -> ApiAnswer( - type = ApiAnswerType.TEXT, - text = clientAnswer.text, - item = null, - start = null, - end = null - ) - - null -> { - errorBuffer.append("could not determine AnswerType of answer $clientAnswer\n") - null - } - } - - } - - ApiAnswerSet( - status = ApiVerdictStatus.INDETERMINATE, - taskId = task.taskId, - answers = answers - ) - - } - - if (errorBuffer.isNotBlank()) { - throw Exception(errorBuffer.toString()) - } - - if (answerSets.isEmpty()) { - throw Exception("Submission does not contain any AnswerSets") - } - - val userName = DbUser.filter { it.id eq rac.userId }.first().username - val teamName = evaluationRun.description.teams.filter { it.id eq rac.teamId }.first().name - - return ApiSubmission( - teamId = rac.teamId, - memberId = rac.userId, - teamName = teamName, - memberName = userName, - answers = answerSets, - evaluationId = evaluationRun.id - ) - + /* Return status. */ + return SuccessStatus("Submission received.") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt deleted file mode 100644 index 79cf0a1f7..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswer.kt +++ /dev/null @@ -1,43 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.api.rest.types.collection.ApiMediaItem -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.Answer -import dev.dres.data.model.submissions.AnswerType -import dev.dres.data.model.submissions.DbAnswer -import io.javalin.openapi.OpenApiContentProperty -import io.javalin.openapi.OpenApiIgnore -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.firstOrNull - -data class ApiAnswer( - val type: ApiAnswerType, - override val item: ApiMediaItem?, - override val text: String?, - - override val start: Long?, // ms - override val end: Long? // ms - ) : Answer { - - override val temporalRange: TemporalRange? - @OpenApiIgnore get() = super.temporalRange // Do not leak temporal range into api - - /** - * Creates a new [DbAnswer] for this [ApiAnswer]. Requires an ongoing transaction. - * - * @return [DbAnswer] - */ - fun toNewDb(): DbAnswer { - return DbAnswer.new { - this.type = this@ApiAnswer.type.toDb() - this.item = DbMediaItem.filter { it.id eq this@ApiAnswer.item?.mediaItemId }.firstOrNull() - this.text = this@ApiAnswer.text - this.start = this@ApiAnswer.start - this.end = this@ApiAnswer.end - } - } - - override fun type(): AnswerType = AnswerType.fromApi(this.type) -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt deleted file mode 100644 index 04a3e4e6c..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerSet.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import com.fasterxml.jackson.annotation.JsonIgnore -import dev.dres.data.model.run.DbTask -import dev.dres.data.model.run.Task -import dev.dres.data.model.run.TaskId -import dev.dres.data.model.submissions.* -import io.javalin.openapi.OpenApi -import io.javalin.openapi.OpenApiIgnore -import kotlinx.dnq.query.addAll -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.first -import kotlinx.dnq.query.firstOrNull -import java.util.* - -/** - * The RESTful API equivalent for the type of [ApiAnswerSet]. - * - * @see ApiAnswerSet - * @author Ralph Gasser - * @version 1.0.0 - */ -data class ApiAnswerSet( - override val id: AnswerSetId = UUID.randomUUID().toString(), - var status: ApiVerdictStatus, - override val taskId: TaskId, - val answers: List -) : AnswerSet { - - @JsonIgnore - @get: OpenApiIgnore - override lateinit var submission: ApiSubmission - internal set - - override fun task(): Task = DbTask.filter { - it.id eq this@ApiAnswerSet.taskId - }.firstOrNull() ?: throw IllegalStateException("The specified task ${this.taskId} does not exist in the database. This is a programmer's error!") - - override fun answers(): Sequence = answers.asSequence() - override fun status(): VerdictStatus = VerdictStatus.fromApi(this.status) - - override fun status(status: VerdictStatus) { - this.status = status.toApi() - } - - /** - * Creates a new [DbAnswerSet] for this [ApiAnswerSet]. Requires an ongoing transaction. - * - * @return [DbAnswerSet] - */ - fun toNewDb(): DbAnswerSet { - return DbAnswerSet.new { - this.id = this@ApiAnswerSet.id - this.status = this@ApiAnswerSet.status.toDb() - this.task = DbTask.filter { it.id eq this@ApiAnswerSet.taskId }.first() - this.answers.addAll( - this@ApiAnswerSet.answers.map { - it.toNewDb() - } - ) - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt deleted file mode 100644 index 4f7e1483e..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.data.model.submissions.AnswerType - -data class ApiClientAnswer( - val text: String?, - val itemName: String?, - val itemCollectionName: String?, - val start: Long?, // ms - val end: Long? // ms -) { - fun type() : AnswerType? = when { - this.text != null -> AnswerType.TEXT - this.itemName == null -> null - this.start != null && this.end != null -> AnswerType.TEMPORAL - else -> AnswerType.ITEM - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt deleted file mode 100644 index a67dcbe74..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientAnswerSet.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -data class ApiClientAnswerSet( - val taskName: String? = null, - val answers: List -) { - -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt deleted file mode 100644 index 2c58d8938..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiClientSubmission.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -class ApiClientSubmission( - val answerSets: List -) { -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiOverrideAnswerSetVerdictDto.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiOverrideAnswerSetVerdictDto.kt index b8bd83a05..a0b7e0c45 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiOverrideAnswerSetVerdictDto.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiOverrideAnswerSetVerdictDto.kt @@ -1,5 +1,7 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus + /** * Data transfer object for overriding a [ApiAnswerSet]'s verdict. * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt deleted file mode 100644 index 33b3e963a..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmission.kt +++ /dev/null @@ -1,52 +0,0 @@ -package dev.dres.api.rest.types.evaluation - -import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.submissions.* -import dev.dres.data.model.template.team.DbTeam -import kotlinx.dnq.query.addAll -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.first -import java.util.UUID - -/** - * The RESTful API equivalent of a [DbSubmission]. - * - * @author Luca Rossetto - * @author Ralph Gasser - * @version 2.0.0 - */ -data class ApiSubmission( - override val submissionId: SubmissionId = UUID.randomUUID().toString(), //TODO is there a use case where this needs to be settable via an API request? - override val teamId: String, - override val memberId: String, - val teamName: String, - val memberName: String, - val answers: List, - override val timestamp: Long = System.currentTimeMillis(), - override val evaluationId: EvaluationId -) : Submission { - - init { - answers.forEach { - it.submission = this - } - } - - override fun answerSets(): Sequence = this.answers.asSequence() - - /** - * Creates a new [DbSubmission] for this [ApiSubmission]. Requires an ongoing transaction. - * - * @return [DbSubmission] - */ - fun toNewDb(): DbSubmission = DbSubmission.new { - this.id = this@ApiSubmission.submissionId - this.timestamp = this@ApiSubmission.timestamp - this.team = DbTeam.filter { it.id eq this@ApiSubmission.teamId }.first() - this.user = DbUser.filter { it.id eq this@ApiSubmission.memberId }.first() - this.answerSets.addAll( - this@ApiSubmission.answers.map { it.toNewDb() } - ) - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt index 824603629..6a1c80558 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiSubmissionInfo.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.TemplateId diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index 291dcb96b..6f6e7603f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.TemplateId diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt new file mode 100644 index 000000000..54e9b900a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt @@ -0,0 +1,29 @@ +package dev.dres.api.rest.types.evaluation.submission + +import dev.dres.api.rest.types.collection.ApiMediaItem + +/** + * The RESTful API equivalent for the type of an answer as submitted by the DRES endpoint. + * + * There is an inherent asymmetry between the answers received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). + * + * @see ApiAnswerSet + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiAnswer( + /** The [ApiAnswerType] of this [ApiAnswer]. */ + val type: ApiAnswerType, + + /** For [ApiAnswer]s of type [ApiAnswerType.ITEM] or [ApiAnswerType.TEMPORAL]: The [ApiMediaItem] that is part of the [ApiAnswer]. */ + val item: ApiMediaItem?, + + /** For [ApiAnswer]s of type [ApiAnswerType.TEXT]: The text that is part of this [ApiAnswer]. */ + val text: String?, + + /** For [ApiAnswer]s of type [ApiAnswerType.TEMPORAL]: Start of the segment in question in milliseconds that is part of this [ApiAnswer]. */ + val start: Long? = null, + + /** For [ApiAnswer]s of type [ApiAnswerType.TEMPORAL]: Start of the segment in question in milliseconds that is part of this [ApiAnswer]. */ + val end: Long? = null +) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt new file mode 100644 index 000000000..5c1edc46b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt @@ -0,0 +1,15 @@ +package dev.dres.api.rest.types.evaluation.submission + +import dev.dres.data.model.run.TaskId +import dev.dres.data.model.submissions.* + +/** + * The RESTful API equivalent for the type of an answer set as submitted by the DRES endpoint. + * + * There is an inherent asymmetry between the answers sets received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). + * + * @see ApiAnswerSet + * @author Ralph Gasser + * @version 1.0.0 + */ +data class ApiAnswerSet(val id: AnswerSetId, val status: ApiVerdictStatus, val taskId: TaskId, val answers: List) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerType.kt similarity index 85% rename from backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerType.kt index ff7d45796..45b89662b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiAnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerType.kt @@ -1,6 +1,5 @@ -package dev.dres.api.rest.types.evaluation +package dev.dres.api.rest.types.evaluation.submission -import dev.dres.data.model.submissions.AnswerType import dev.dres.data.model.submissions.DbAnswerType /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt new file mode 100644 index 000000000..02aa9520b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt @@ -0,0 +1,72 @@ +package dev.dres.api.rest.types.evaluation.submission + +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.MediaItemId +import dev.dres.data.model.submissions.DbAnswer +import dev.dres.data.model.submissions.DbAnswerType +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.singleOrNull + +/** + * The RESTful API equivalent of an answer as provided by a client of the submission API endpoints. + * + * There is an inherent asymmetry between the answers received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). + + * @author Luca Rossetto + * @version 1.0.0 + */ +data class ApiClientAnswer( + /** The text that is part of this [ApiClientAnswer]. */ + val text: String? = null, + + /** The name of the media item that is part of the answer. */ + val itemName: String? = null, + + /** The name of the collection the media item belongs to. */ + val itemCollectionName: String? = null, + + /** For temporal [ApiClientAnswer]s: Start of the segment in question in milliseconds. */ + val start: Long? = null, + + /** For temporal [ApiClientAnswer]s: End of the segment in question in milliseconds. */ + val end: Long? = null, +) { + + /** The [MediaItemId] associated with the [ApiClientAnswer]. Is usually added as contextual information by the receiving endpoint. */ + val mediaItemId: MediaItemId? = null + + /** + * Creates a new [DbAnswer] for this [ApiAnswer]. Requires an ongoing transaction. + * + * @return [DbAnswer] + */ + fun toNewDb(): DbAnswer = DbAnswer.new { + this.type = this@ApiClientAnswer.tryDetermineType() + this.item = when { + this@ApiClientAnswer.mediaItemId != null -> + DbMediaItem.filter { it.id eq mediaItemId }.singleOrNull() + ?: throw IllegalArgumentException("Could not find media item with ID ${this@ApiClientAnswer.mediaItemId}.") + this@ApiClientAnswer.itemName != null && this@ApiClientAnswer.itemCollectionName != null -> + DbMediaItem.filter { (it.name eq itemName) and (it.collection.name eq this@ApiClientAnswer.itemCollectionName) }.singleOrNull() + ?: throw IllegalArgumentException("Could not find media item with name '${this@ApiClientAnswer.itemName}' in collection '${this@ApiClientAnswer.itemCollectionName}'.") + this@ApiClientAnswer.itemName != null -> DbMediaItem.filter { it.name eq itemName}.singleOrNull() + ?: throw IllegalArgumentException("Could not find media item with name '${this@ApiClientAnswer.itemName}'. Maybe collection name is required.") + else -> null + } + this.text = this@ApiClientAnswer.text + this.start = this@ApiClientAnswer.start + this.end = this@ApiClientAnswer.end + } + + /** + * Tries to determine the type of [ApiAnswer]. + * + * @return The [DbAnswerType] for this [ApiClientAnswer]. + */ + fun tryDetermineType() = when { + this.itemName != null && this.start != null && this.end != null -> DbAnswerType.TEMPORAL + this.itemName != null -> DbAnswerType.ITEM + this.text != null -> DbAnswerType.TEXT + else -> throw IllegalArgumentException("Could not determine answer type for provided answer.") + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt new file mode 100644 index 000000000..e27e642be --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt @@ -0,0 +1,13 @@ +package dev.dres.api.rest.types.evaluation.submission + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +enum class ApiClientAnswerRangeType { + SHOT, + FRAME, + TIMECODE, + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt new file mode 100644 index 000000000..ada2f82f4 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt @@ -0,0 +1,40 @@ +package dev.dres.api.rest.types.evaluation.submission + +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.submissions.AnswerSetId +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus +import kotlinx.dnq.query.addAll +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first +import java.util.* + +/** + * The RESTful API equivalent of an answer set as provided by a client of the submission API endpoints. + * + * There is an inherent asymmetry between the answers sets received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). + * + * @author Luca Rossetto + * @version 1.0.0 + */ +data class ApiClientAnswerSet(var taskId: String? = null, val taskName: String? = null, val answers: List) { + + /** The [AnswerSetId] of this [ApiClientAnswerSet]. Typically generated by the receiving endpoint. */ + var answerSetId: AnswerSetId = UUID.randomUUID().toString() + + /** + * Creates a new [DbAnswerSet] for this [ApiClientAnswerSet]. + * + * Requires an ongoing transaction. + * + * @return [DbAnswerSet] + */ + fun toNewDb(): DbAnswerSet = DbAnswerSet.new { + this.id = this@ApiClientAnswerSet.answerSetId + this.status = DbVerdictStatus.INDETERMINATE + this.task = DbTask.filter { it.id eq this@ApiClientAnswerSet.taskId }.first() + this.answers.addAll( + this@ApiClientAnswerSet.answers.map { it.toNewDb() } + ) + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt new file mode 100644 index 000000000..e5d1de378 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt @@ -0,0 +1,55 @@ +package dev.dres.api.rest.types.evaluation.submission + +import dev.dres.data.model.admin.DbUser +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.SubmissionId +import dev.dres.data.model.template.team.DbTeam +import dev.dres.data.model.template.team.TeamId +import dev.dres.run.exceptions.IllegalTeamIdException +import kotlinx.dnq.query.addAll +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.single +import kotlinx.dnq.query.singleOrNull +import java.util.UUID + +/** + * The RESTful API equivalent of a submission as provided by a client of the submission API endpoints. + * + * There is an inherent asymmetry between the submissions received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). + * + * @author Luca Rossetto + * @version 1.0.0 + */ + class ApiClientSubmission(val answerSets: List) { + + /** The [UserId] associated with the submission. Is usually added as contextual information by the receiving endpoint. */ + var userId: UserId? = null + + /** The [TeamId] associated with the submission. Is usually added as contextual information by the receiving endpoint. */ + var teamId: TeamId? = null + + /** The [SubmissionId] of this [ApiClientSubmission]. Typically generated by the receiving endpoint. */ + val submissionId: SubmissionId = UUID.randomUUID().toString() + + /** The timestamp at which this [ApiClientSubmission] was received. Typically generated by the receiving endpoint.*/ + val timestamp: Long = System.currentTimeMillis() + + /** + * Creates a new [DbSubmission] for this [ApiClientSubmission]. + * + * Requires an ongoing transaction. + * + * @return [DbSubmission] + */ + fun toNewDb(): DbSubmission = DbSubmission.new { + this.id = this@ApiClientSubmission.submissionId + this.timestamp = this@ApiClientSubmission.timestamp + this.team = this@ApiClientSubmission.teamId?.let { teamId -> DbTeam.filter { it.id eq teamId }.singleOrNull() } + ?: throw IllegalArgumentException("Failed to lookup specified team ID ${this@ApiClientSubmission.teamId}.") + + this.user = this@ApiClientSubmission.userId?.let { userId -> DbUser.filter { it.id eq userId }.singleOrNull() } + ?: throw IllegalArgumentException("Failed to lookup specified user ID ${this@ApiClientSubmission.userId}.") + this.answerSets.addAll(this@ApiClientSubmission.answerSets.map { it.toNewDb() }) + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt new file mode 100644 index 000000000..2a1e83fb3 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt @@ -0,0 +1,23 @@ +package dev.dres.api.rest.types.evaluation.submission + + +import dev.dres.data.model.submissions.* + +/** + * The RESTful API equivalent of a submission as returned by the DRES endpoint. + * + * There is an inherent asymmetry between the submissions received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ +data class ApiSubmission( + val submissionId: SubmissionId, + val teamId: String, + val memberId: String, + val teamName: String, + val memberName: String, + val timestamp: Long, + val answers: List = emptyList() + ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiVerdictStatus.kt similarity index 92% rename from backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt rename to backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiVerdictStatus.kt index 70f8a2ab8..0c03ae901 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiVerdictStatus.kt @@ -1,4 +1,4 @@ -package dev.dres.api.rest.types.evaluation +package dev.dres.api.rest.types.evaluation.submission import dev.dres.data.model.submissions.DbVerdictStatus diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt index 3860e4eac..361d3774c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgement.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.judgement -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus /** * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt index fdde008e5..2cd6c7efe 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiJudgementRequest.kt @@ -1,8 +1,6 @@ package dev.dres.api.rest.types.judgement -import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.api.rest.types.evaluation.ApiAnswer -import dev.dres.api.rest.types.evaluation.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet /** * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt index 7482179dc..19b378be3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/judgement/ApiVote.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.judgement -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus /** * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt index 56ae54c46..ac78a7a21 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/SuccessfulSubmissionsStatus.kt @@ -1,5 +1,5 @@ package dev.dres.api.rest.types.status -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus data class SuccessfulSubmissionsStatus(val submission: ApiVerdictStatus, val description: String) : AbstractStatus(status = true) diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt index f2a547409..433a5f550 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaSegment.kt @@ -24,16 +24,21 @@ class DbMediaSegment(entity: Entity) : PersistentEntity(entity) { listOf(DbMediaSegment::name, DbMediaSegment::item) ) + /** + * + */ fun findContaining(itemId: MediaItemId, time: TemporalPoint): DbMediaSegment? { val item = DbMediaItem.filter { it.mediaItemId eq itemId }.firstOrNull() ?: return null return findContaining(item, time) } + /** + * + */ fun findContaining(item: DbMediaItem, time: TemporalPoint): DbMediaSegment? { val ms = time.toMilliseconds().toInt() return item.segments.filter { (ms ge it.start) and (it.end ge ms) }.firstOrNull() } - } /** The name of this [DbMediaSegment]. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 34caceb10..fd5d7135b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -9,8 +9,8 @@ import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.team.TeamAggregatorImpl import dev.dres.data.model.template.team.TeamGroupId import dev.dres.data.model.template.team.TeamId -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.transformer.SubmissionTransformer +import dev.dres.run.filter.basics.SubmissionFilter +import dev.dres.run.transformer.basics.SubmissionTransformer import dev.dres.run.validation.interfaces.AnswerSetValidator import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index c5bc0cc37..c5b70370c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -21,28 +21,28 @@ import java.lang.IllegalStateException * @author Ralph Gasser * @version 1.0.0 */ -class DbTask(entity: Entity) : PersistentEntity(entity), Task { +class DbTask(entity: Entity) : PersistentEntity(entity) { companion object : XdNaturalEntityType() /** The [TaskId] of this [DbTask]. */ - override var taskId: TaskId + var taskId: TaskId get() = this.id set(value) { this.id = value } /** The [DbEvaluation] this [DbTask] belongs to. */ - override var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) + var evaluation: DbEvaluation by xdParent(DbEvaluation::tasks) /** The [DbTaskStatus] of this [DbTask]. */ var status by xdLink1(DbTaskStatus) /** Timestamp of when this [DbEvaluation] started. */ - override var started by xdNullableLongProp() + var started by xdNullableLongProp() /** Timestamp of when this [DbEvaluation] ended. */ - override var ended by xdNullableLongProp() + var ended by xdNullableLongProp() /** The [DbTaskTemplate] this [DbTask] is an instance of. */ - override var template by xdLink1(DbTaskTemplate) + var template by xdLink1(DbTaskTemplate) /** Link to a [DbTeam] this [DbTask] was created for. Can be NULL!*/ var team by xdLink0_1(DbTeam) @@ -50,8 +50,6 @@ class DbTask(entity: Entity) : PersistentEntity(entity), Task { /** List of [DbSubmission]s received by this [DbTask]. */ val answerSets by xdLink0_N(DbAnswerSet::task) - override fun answerSets(): Sequence = answerSets.asSequence() //TODO can this be sorted by submission timestamp? - /** * Converts this [DbTask] to a RESTful API representation [ApiTask]. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 5e19b36a0..a4984950b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -9,9 +9,9 @@ import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.exceptions.IllegalTeamIdException -import dev.dres.run.filter.AllSubmissionFilter -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.filter.SubmissionFilterAggregator +import dev.dres.run.filter.basics.AcceptAllSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import dev.dres.run.filter.basics.CombiningSubmissionFilter import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard @@ -20,9 +20,9 @@ import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer import dev.dres.run.score.scorer.LegacyAvsTaskScorer import dev.dres.run.transformer.MapToSegmentTransformer -import dev.dres.run.transformer.SubmissionTaskMatchFilter -import dev.dres.run.transformer.SubmissionTransformer -import dev.dres.run.transformer.SubmissionTransformerAggregator +import dev.dres.run.transformer.SubmissionTaskMatchTransformer +import dev.dres.run.transformer.basics.SubmissionTransformer +import dev.dres.run.transformer.basics.CombiningSubmissionTransformer import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -169,9 +169,9 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval /* Initialize submission filter. */ if (this.template.taskGroup.type.submission.isEmpty) { - this.filter = AllSubmissionFilter + this.filter = AcceptAllSubmissionFilter } else { - this.filter = SubmissionFilterAggregator( + this.filter = CombiningSubmissionFilter( this.template.taskGroup.type.submission.asSequence().map { option -> val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) .asSequence().map { it.key to it.value }.toMap() @@ -181,14 +181,14 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval } this.transformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { - SubmissionTransformerAggregator( + CombiningSubmissionTransformer( listOf( - SubmissionTaskMatchFilter(this.taskId), + SubmissionTaskMatchTransformer(this.taskId), MapToSegmentTransformer() ) ) } else { - SubmissionTaskMatchFilter(this.taskId) + SubmissionTaskMatchTransformer(this.taskId) } /* Initialize task scorer. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index e588d451a..fbe2a2cb4 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -10,9 +10,9 @@ import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.TeamId -import dev.dres.run.filter.AllSubmissionFilter -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.filter.SubmissionFilterAggregator +import dev.dres.run.filter.basics.AcceptAllSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import dev.dres.run.filter.basics.CombiningSubmissionFilter import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard @@ -21,9 +21,9 @@ import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.score.scorer.KisTaskScorer import dev.dres.run.score.scorer.LegacyAvsTaskScorer import dev.dres.run.transformer.MapToSegmentTransformer -import dev.dres.run.transformer.SubmissionTaskMatchFilter -import dev.dres.run.transformer.SubmissionTransformer -import dev.dres.run.transformer.SubmissionTransformerAggregator +import dev.dres.run.transformer.SubmissionTaskMatchTransformer +import dev.dres.run.transformer.basics.SubmissionTransformer +import dev.dres.run.transformer.basics.CombiningSubmissionTransformer import kotlinx.dnq.query.* import java.lang.IndexOutOfBoundsException import java.util.LinkedList @@ -152,9 +152,9 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /* Initialize submission filter. */ if (this.template.taskGroup.type.submission.isEmpty) { - this.filter = AllSubmissionFilter + this.filter = AcceptAllSubmissionFilter } else { - this.filter = SubmissionFilterAggregator( + this.filter = CombiningSubmissionFilter( this.template.taskGroup.type.submission.asSequence().map { option -> val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) .asSequence().map { it.key to it.value }.toMap() @@ -164,14 +164,14 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu } val mapToSegment = this.template.taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) this.transformer = if (mapToSegment) { - SubmissionTransformerAggregator( + CombiningSubmissionTransformer( listOf( - SubmissionTaskMatchFilter(this.taskId), + SubmissionTaskMatchTransformer(this.taskId), MapToSegmentTransformer() ) ) } else { - SubmissionTaskMatchFilter(this.taskId) + SubmissionTaskMatchTransformer(this.taskId) } /* Initialize task scorer. */ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index bbb8825ce..96ef703c9 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -8,17 +8,19 @@ import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.TeamId -import dev.dres.run.filter.AllSubmissionFilter -import dev.dres.run.filter.SubmissionFilter -import dev.dres.run.filter.SubmissionFilterAggregator +import dev.dres.run.filter.basics.AcceptAllSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import dev.dres.run.filter.basics.CombiningSubmissionFilter import dev.dres.run.score.scoreboard.MaxNormalizingScoreBoard import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.score.scoreboard.SumAggregateScoreBoard import dev.dres.run.score.scorer.* +import dev.dres.run.score.scorer.AvsTaskScorer +import dev.dres.run.score.scorer.CachingTaskScorer import dev.dres.run.transformer.MapToSegmentTransformer -import dev.dres.run.transformer.SubmissionTaskMatchFilter -import dev.dres.run.transformer.SubmissionTransformer -import dev.dres.run.transformer.SubmissionTransformerAggregator +import dev.dres.run.transformer.SubmissionTaskMatchTransformer +import dev.dres.run.transformer.basics.SubmissionTransformer +import dev.dres.run.transformer.basics.CombiningSubmissionTransformer import kotlinx.dnq.query.* @@ -78,14 +80,14 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev ) override val transformer: SubmissionTransformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { - SubmissionTransformerAggregator( + CombiningSubmissionTransformer( listOf( - SubmissionTaskMatchFilter(this.taskId), + SubmissionTaskMatchTransformer(this.taskId), MapToSegmentTransformer() ) ) } else { - SubmissionTaskMatchFilter(this.taskId) + SubmissionTaskMatchTransformer(this.taskId) } /** The [SubmissionFilter] instance used by this [NITaskRun]. */ @@ -93,9 +95,9 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev init{ if (this.template.taskGroup.type.submission.isEmpty) { - this.filter = AllSubmissionFilter + this.filter = AcceptAllSubmissionFilter } else { - this.filter = SubmissionFilterAggregator( + this.filter = CombiningSubmissionFilter( this.template.taskGroup.type.submission.asSequence().map { option -> val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) .asSequence().map { it.key to it.value }.toMap() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 050f65104..87f41db11 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -2,44 +2,42 @@ package dev.dres.data.model.run import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.admin.DbRole -import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context -import kotlinx.dnq.query.* /** * The [RunActionContext] captures and encapsulates information usually required during the interaction with a [RunManager]. * It exposes information available to the OpenAPI facility (e.g., through session management) to the [RunManager]. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 */ -data class RunActionContext(val userId: UserId?, val teamId: TeamId?, val roles: Set) { +data class RunActionContext(val userId: UserId, var evaluationId: EvaluationId?, val roles: Set) { /** True if the user associated with this [RunActionContext] acts as [DbRole.ADMIN]*/ val isAdmin: Boolean - get() = this.roles.contains(DbRole.ADMIN) + get() = this.roles.contains(ApiRole.ADMIN) companion object { /** A static [RunActionContext] used for internal invocations by DRES. Always acts as an implicit [DbRole.ADMIN]. */ - val INTERNAL = RunActionContext(null, null, setOf(DbRole.ADMIN)) + val INTERNAL = RunActionContext("SYSTEM",null, setOf(ApiRole.ADMIN)) /** - * Constructs a [RunActionContext] from a [Context] and a [RunManager]. + * Constructs a [RunActionContext] from this [Context]. * - * @param ctx The Javalin [Context] to construct the [RunActionContext] from. - * @param runManager The [RunManager] to construct the [RunActionContext] for. + * @return ctx [RunActionContext] */ - fun runActionContext(ctx: Context, runManager: RunManager) : RunActionContext { - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(403, "Unauthorized user.", ctx) - val roles = AccessManager.rolesOfSession(ctx.sessionToken()).mapNotNull { it.toDb() }.toSet() - val user = DbUser.query(DbUser::id eq userId).firstOrNull() ?: throw ErrorStatusException(500, "Specified user does not exist in database. This is a programmer's error!", ctx) - val teamId = runManager.template.teams.filter { it.users.contains(user) }.firstOrNull()?.teamId - return RunActionContext(userId, teamId, roles) - } + fun Context.runActionContext() = RunActionContext( + AccessManager.userIdForSession(this.sessionToken()) ?: throw ErrorStatusException(403, "Unauthorized user.", this), + this.pathParamMap()["evaluationId"], + AccessManager.rolesOfSession(this.sessionToken()).toSet() + ) } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt index 81b0a056f..06fa90259 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Answer.kt @@ -3,7 +3,15 @@ package dev.dres.data.model.submissions import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.media.time.TemporalRange - +/** + * A [Answer] as issued by a DRES user as part of a [AnswerSet]. + * + * This abstraction is mainly required to enable testability of implementations. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ interface Answer { val item: MediaItem? @@ -14,20 +22,13 @@ interface Answer { /** Returns the [TemporalRange] for this [DbAnswerSet]. */ val temporalRange: TemporalRange? get() { - val start = this.start ?: return null val end = this.end ?: return null - return TemporalRange(TemporalPoint.Millisecond(start), TemporalPoint.Millisecond(end)) } + /** + * + */ fun type(): AnswerType - - infix fun eq(answer: Answer) : Boolean = - answer.type() == this.type() && - answer.item?.mediaItemId == this.item?.mediaItemId && - //answer.item?.collection?.id == this.item?.collection?.id && - answer.start == this.start && - answer.end == this.end && - answer.text == this.text } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt index f975865c4..68c1d3b92 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerSet.kt @@ -1,39 +1,37 @@ package dev.dres.data.model.submissions -import dev.dres.data.model.run.Task import dev.dres.data.model.run.TaskId typealias AnswerSetId = String + +/** + * An [AnswerSet] as issued by a DRES user as part of a [Submission]. + * + * This abstraction is mainly required to enable testability of implementations. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ interface AnswerSet { + /** The ID of this [AnswerSet]. */ val id : AnswerSetId - val taskId: TaskId - val submission: Submission - - fun task(): Task - fun answers() : Sequence + /** The ID of the task this [AnswerSet] belongs to. */ + val taskId: TaskId - fun status() : VerdictStatus - fun status(status: VerdictStatus) + /** The [Submission] this [AnswerSet] belongs to. */ + val submission: Submission /** - * checks if the answers of a given [AnswerSet] have the same content as + * The [VerdictStatus] of this [AnswerSet]. + * + * @return The [VerdictStatus] of this [AnswerSet]. */ - infix fun equivalent(answerSet: AnswerSet): Boolean { + fun status(): VerdictStatus - if (this.answers().count() != answerSet.answers().count()) { - return false - } - - val tmp = this.answers().toMutableList() - - //pairwise comparison - answerSet.answers().forEach { answer -> - //this assumes that there are no duplicates within an AnswerSet - tmp.removeIf { it eq answer } - } - - return tmp.isEmpty() - - } + /** + * Returns a [Sequence] of [Answer]s for this [AnswerSet]. + */ + fun answers() : Sequence } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt index ee0ccc928..64672f46d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/AnswerType.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiAnswerType +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerType enum class AnswerType { ITEM, TEMPORAL, TEXT; diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt index 0c7741739..e37c5b3ba 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswer.kt @@ -1,10 +1,8 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiAnswer +import dev.dres.api.rest.types.evaluation.submission.ApiAnswer import dev.dres.data.model.PersistentEntity import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.time.TemporalPoint -import dev.dres.data.model.media.time.TemporalRange import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.simple.requireIf @@ -29,7 +27,12 @@ class DbAnswer(entity: Entity) : PersistentEntity(entity), Answer { /** The text submitted. Only for [DbAnswerType.TEXT] . */ override var text by xdStringProp { requireIf { this.type == DbAnswerType.TEXT } } - override fun type(): AnswerType = AnswerType.fromDb(this.type) + /** + * Implementation of the [Answer] interface: Returns the [AnswerType] representation of this [DbAnswer]'s [DbAnswerType]. + * + * @return [AnswerType] of this [DbAnswer]. + */ + override fun type(): AnswerType = AnswerType.valueOf(this.type.description) /** * Converts this [DbAnswer] to a RESTful API representation [ApiAnswer]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index f864ca8e6..6e40c7e91 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -1,9 +1,8 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet import dev.dres.data.model.PersistentEntity import dev.dres.data.model.run.DbTask -import dev.dres.data.model.run.Task import dev.dres.data.model.run.TaskId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -19,6 +18,8 @@ import kotlinx.dnq.query.asSequence class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { companion object : XdNaturalEntityType() + + /** The [DbSubmission] this [DbAnswerSet] belongs to. */ override var submission: DbSubmission by xdParent(DbSubmission::answerSets) @@ -28,18 +29,26 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { /** The [DbTask] this [DbAnswerSet] belongs to. */ var task: DbTask by xdLink1(DbTask::answerSets) - override fun task(): Task = task - override val taskId: TaskId - get() = task.taskId - + /** The [DbAnswer]s that belong to this [DbAnswerSet]. */ val answers by xdChildren1_N(DbAnswer::answerSet) - override fun answers(): Sequence = answers.asSequence() - override fun status(): VerdictStatus = VerdictStatus.fromDb(status) + /** Implementation of the [AnswerSet] interface: The ID of the [DbTask] this [DbAnswerSet] is associated with. */ + override val taskId: TaskId + get() = this.task.id + + /** + * Implementation of the [AnswerSet] interface: Returns the [VerdictStatus] representation of this [DbAnswerSet]'s [DbVerdictStatus]. + * + * @return [VerdictStatus] of this [DbAnswerSet]. + */ + override fun status(): VerdictStatus = VerdictStatus.valueOf(this.status.description) - override fun status(status: VerdictStatus) { - this.status = status.toDb() - } + /** + * Implementation of the [AnswerSet] interface: Returns a [Sequence] of [Answer]s. + * + * @return [Sequence] of [Answer]s. + */ + override fun answers(): Sequence = this.answers.asSequence() /** * Converts this [DbAnswerSet] to a RESTful API representation [ApiAnswerSet]. @@ -52,7 +61,7 @@ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { fun toApi(blind: Boolean = false): ApiAnswerSet = ApiAnswerSet( id = this.id, status = this.status.toApi(), - taskId = this.taskId, + taskId = this.task.id, answers = this.answers.asSequence().map { it.toApi(blind) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt index 5749cbbd4..796d3763f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerType.kt @@ -1,6 +1,5 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiAnswerType import dev.dres.data.model.admin.DbRole import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 6a1dbe534..5253d3751 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId @@ -10,7 +10,6 @@ import dev.dres.data.model.template.team.TeamId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.first import kotlinx.dnq.simple.min /** @@ -35,21 +34,27 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { /** The [DbTeam] that submitted this [DbSubmission] */ var team by xdLink1(DbTeam) + + /** Implementation of the [Submission] interface: Returns the [UserId] of this [DbSubmission]. */ + override val memberId: UserId + get() = this.user.id + + /** Implementation of the [Submission] interface: Returns the [TeamId] of this [DbSubmission]. */ override val teamId: TeamId get() = this.team.teamId /** The [DbUser] that submitted this [DbSubmission] */ var user by xdLink1(DbUser) - override val memberId: UserId - get() = this.user.userId - override val evaluationId: EvaluationId - get() = this.answerSets.first().task.evaluation.evaluationId - /** The [DbAnswerSet]s that make-up this [DbSubmission]. For batched submissions, more than one verdict can be possible. */ val answerSets by xdChildren1_N(DbAnswerSet::submission) - override fun answerSets(): Sequence = answerSets.asSequence() + /** + * Returns a [Sequence] of the [AnswerSet]s contained in this [DbSubmission]. + * + * @return [Sequence] of [AnswerSet]. + */ + override fun answerSets(): Sequence = this.answerSets.asSequence() /** * Converts this [DbSubmission] to a RESTful API representation [ApiSubmission]. @@ -66,7 +71,6 @@ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { memberId = this.user.id, memberName = this.user.username, timestamp = this.timestamp, - answers = this.answerSets.asSequence().map { it.toApi(blind) }.toList(), - evaluationId = this.evaluationId + answers = this.answerSets.asSequence().map { it.toApi(blind) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt index 6a2ca7850..bbd1f9f72 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.data.model.admin.DbRole import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.XdEnumEntity diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 9cd7949fe..325046551 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -1,18 +1,34 @@ package dev.dres.data.model.submissions import dev.dres.data.model.admin.UserId -import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.team.TeamId typealias SubmissionId = String +/** + * A [Submission] as issued by a DRES user. + * + * This abstraction is mainly required to enable testability of implementations. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ interface Submission { - + /** The ID of this [Submission]. */ val submissionId: SubmissionId - val timestamp: Long - val teamId: TeamId + + /** The ID of the user who issued this [Submission]. */ val memberId: UserId - val evaluationId: EvaluationId + /** The ID of the team, the user belongs to. */ + val teamId: TeamId + + /** The timestamp of this [Submission]. */ + val timestamp: Long + + /** + * Returns a [Sequence] of [AnswerSet]s. + */ fun answerSets(): Sequence } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt index 21ed06c12..8654dd029 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/VerdictStatus.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.submissions -import dev.dres.api.rest.types.evaluation.ApiVerdictStatus +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus enum class VerdictStatus { CORRECT, WRONG, INDETERMINATE, UNDECIDABLE; diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt index 6fe1d885b..3e51be4ad 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/options/DbSubmissionOption.kt @@ -37,13 +37,13 @@ class DbSubmissionOption(entity: Entity) : XdEnumEntity(entity) { */ fun newFilter(parameters: Map) = when (this) { NO_DUPLICATES -> DuplicateSubmissionFilter() - LIMIT_CORRECT_PER_TEAM -> CorrectPerTeamFilter(parameters) + LIMIT_CORRECT_PER_TEAM -> MaximumCorrectPerTeamFilter(parameters) LIMIT_WRONG_PER_TEAM -> MaximumWrongPerTeamFilter(parameters) LIMIT_TOTAL_PER_TEAM -> MaximumTotalPerTeamFilter(parameters) - LIMIT_CORRECT_PER_MEMBER -> CorrectPerTeamMemberFilter(parameters) - TEMPORAL_SUBMISSION -> TemporalSubmissionFilter() - TEXTUAL_SUBMISSION -> TextualSubmissionFilter() - ITEM_SUBMISSION -> ItemSubmissionFilter() + LIMIT_CORRECT_PER_MEMBER -> MaximumCorrectPerTeamMemberFilter(parameters) + TEMPORAL_SUBMISSION -> ValidTemporalSubmissionFilter() + TEXTUAL_SUBMISSION -> ValidTextualSubmissionFilter() + ITEM_SUBMISSION -> ValidItemSubmissionFilter() MINIMUM_TIME_GAP -> SubmissionRateFilter(parameters) else -> throw IllegalStateException("The task filter option ${this.description} is currently not supported.") } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 6206cccee..69b84c1e9 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -2,11 +2,13 @@ package dev.dres.run import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType +import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.admin.DbRole import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.template.DbEvaluationTemplate @@ -145,12 +147,11 @@ class InteractiveAsynchronousRunManager( this.evaluation.tasks.forEach { task -> task.getSubmissions().forEach { sub -> this.scoresUpdatable.enqueue(task) - sub.answerSets().filter { v -> v.status() == VerdictStatus.INDETERMINATE }.forEach { - task.validator.validate(it) + for (answerSet in sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }) { + task.validator.validate(answerSet) } } } - } /** @@ -222,8 +223,7 @@ class InteractiveAsynchronousRunManager( * @return The [DbTaskTemplate] for the given team. */ override fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate { - require(context.teamId != null) { "TeamId missing from RunActionContext, which is required for interaction with InteractiveAsynchronousRunManager." } - return this.evaluation.currentTaskDescription(context.teamId) + return this.evaluation.currentTaskDescription(context.teamId()) } /** @@ -281,32 +281,30 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun goTo(context: RunActionContext, index: Int) = this.stateLock.write { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE)//, RunManagerStatus.TASK_ENDED) - require(!teamHasRunningTask(context.teamId)) { "Cannot change task while task is active" } + val teamId = context.teamId() + checkTeamStatus(teamId, RunManagerStatus.ACTIVE)//, RunManagerStatus.TASK_ENDED) + require(!teamHasRunningTask(teamId)) { "Cannot change task while task is active" } val idx = (index + this.template.tasks.size()) % this.template.tasks.size() /* Update active task. */ //this.run.navigationMap[context.teamId] = this.description.tasks[index] - this.evaluation.goTo(context.teamId, idx) + this.evaluation.goTo(teamId, idx) //FIXME since task run and competition run states are separated, this is not actually a state change - this.statusMap[context.teamId] = RunManagerStatus.ACTIVE + this.statusMap[teamId] = RunManagerStatus.ACTIVE /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( - context.teamId, + teamId, ServerMessage( this.id, ServerMessageType.COMPETITION_UPDATE, - this.evaluation.currentTaskForTeam(context.teamId)?.taskId + this.evaluation.currentTaskForTeam(teamId)?.taskId ) ) LOGGER.info("SynchronousRunManager ${this.id} set to task $idx") - - } /** @@ -321,27 +319,24 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] or [currentTask] is not set. */ override fun startTask(context: RunActionContext) = this.stateLock.write { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - checkTeamStatus(context.teamId, RunManagerStatus.ACTIVE) + val teamId = context.teamId() + checkTeamStatus(teamId, RunManagerStatus.ACTIVE) /* Create task and update status. */ - val currentTaskTemplate = this.evaluation.currentTaskDescription(context.teamId) + val currentTaskTemplate = this.evaluation.currentTaskDescription(teamId) /* Check for duplicate task runs */ - if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasksForTeam(context.teamId) + if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasksForTeam(teamId) .any { it.template.id == currentTaskTemplate.id } ) { throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used") } - val currentTaskRun = this.evaluation.IATaskRun(currentTaskTemplate, context.teamId) + val currentTaskRun = this.evaluation.IATaskRun(currentTaskTemplate, teamId) currentTaskRun.prepare() /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - context.teamId, - ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId) - ) + RunExecutor.broadcastWsMessage(teamId, ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId)) LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") } @@ -358,19 +353,16 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ override fun abortTask(context: RunActionContext) = this.stateLock.write { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } + val teamId = context.teamId() + require(teamHasRunningTask(teamId)) { "No running task for Team ${teamId}" } /* End TaskRun and update status. */ val currentTask = this.currentTask(context) - ?: throw IllegalStateException("Could not find active task for team ${context.teamId} despite status of the team being ${this.statusMap[context.teamId]}. This is a programmer's error!") + ?: throw IllegalStateException("Could not find active task for team ${teamId} despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") currentTask.end() /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - context.teamId, - ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId) - ) + RunExecutor.broadcastWsMessage(teamId, ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId)) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } @@ -384,7 +376,7 @@ class InteractiveAsynchronousRunManager( * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } + val teamId = context.teamId() val currentTaskRun = this.currentTask(context) return if (currentTaskRun?.isRunning == true) { @@ -422,8 +414,8 @@ class InteractiveAsynchronousRunManager( */ override fun taskCount(context: RunActionContext): Int { if (context.isAdmin) return this.evaluation.tasks.size - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.evaluation.tasksForTeam(context.teamId).size + val teamId = context.teamId() + return this.evaluation.tasksForTeam(teamId).size } /** @@ -436,8 +428,8 @@ class InteractiveAsynchronousRunManager( */ override fun tasks(context: RunActionContext): List { if (context.isAdmin) return this.evaluation.tasks - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.evaluation.tasksForTeam(context.teamId) + val teamId = context.teamId() + return this.evaluation.tasksForTeam(teamId) } /** @@ -455,11 +447,10 @@ class InteractiveAsynchronousRunManager( * @param context The [RunActionContext] used for the invocation. * @return [InteractiveAsynchronousEvaluation.IATaskRun] that is currently active or null, if no such task is active. */ - override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = - this.stateLock.read { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - return this.evaluation.currentTaskForTeam(context.teamId) - } + override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = this.stateLock.read { + val teamId = context.teamId() + return this.evaluation.currentTaskForTeam(teamId) + } /** * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. @@ -486,11 +477,10 @@ class InteractiveAsynchronousRunManager( * */ override fun adjustDuration(context: RunActionContext, s: Int): Long { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } + val teamId = context.teamId() + require(teamHasRunningTask(teamId)) { "No running task for Team $teamId." } - val currentTaskRun = this.currentTask(context) - ?: throw IllegalStateException("No active TaskRun found. This is a serious error!") + val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("No active TaskRun found. This is a serious error!") val newDuration = currentTaskRun.duration + s check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration @@ -522,8 +512,10 @@ class InteractiveAsynchronousRunManager( * @return [DbVerdictStatus] of the [DbSubmission] * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ - override fun postSubmission(context: RunActionContext, submission: ApiSubmission) = this.stateLock.read { - require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } + override fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) = this.stateLock.read { + TODO("Not yet implemented") + + /* require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } require( submission.answerSets().count() == 1 @@ -564,8 +556,7 @@ class InteractiveAsynchronousRunManager( RunExecutor.broadcastWsMessage( context.teamId, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId) - ) - + ) */ } override fun reScore(taskId: TaskId) { @@ -589,26 +580,21 @@ class InteractiveAsynchronousRunManager( * * @return Whether the update was successful or not */ - override fun updateSubmission( - context: RunActionContext, - submissionId: SubmissionId, - submissionStatus: DbVerdictStatus - ): Boolean = this.stateLock.read { + override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: ApiVerdictStatus): Boolean = this.stateLock.read { + val teamId = context.teamId() val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false val task = this.taskForId(context, answerSet.task.id) ?: return false /* Actual update - currently, only status update is allowed */ - if (answerSet.status != submissionStatus) { - answerSet.status = submissionStatus + val dbStatus = submissionStatus.toDb() + if (answerSet.status != dbStatus) { + answerSet.status = dbStatus /* Enqueue submission for post-processing. */ this.scoresUpdatable.enqueue(task) /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - context.teamId!!, - ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId) - ) + RunExecutor.broadcastWsMessage(teamId, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) return true } @@ -713,7 +699,7 @@ class InteractiveAsynchronousRunManager( val taskStatus = this.evaluation.currentTaskForTeam(teamId)?.status?.toApi() for (updatable in this.updatables) { if (updatable.shouldBeUpdated(runStatus, taskStatus)) { - updatable.update(runStatus, taskStatus, RunActionContext(null, teamId, setOf(DbRole.ADMIN))) + updatable.update(runStatus, taskStatus, RunActionContext("SYSTEM", teamId, setOf(ApiRole.ADMIN))) } } } @@ -804,4 +790,16 @@ class InteractiveAsynchronousRunManager( */ private fun teamHasPreparingTask(teamId: TeamId) = this.evaluation.currentTaskForTeam(teamId)?.status == DbTaskStatus.PREPARING + + /** + * Convenience method: Tries to find a matching [TeamId] in the context of this [InteractiveAsynchronousEvaluation] + * for the user associated with the current [RunActionContext]. + * + * @return [TeamId] + */ + private fun RunActionContext.teamId(): TeamId { + val userId = this.userId + return this@InteractiveAsynchronousRunManager.template.teams.filter { it.users.filter { it.id eq userId }.isNotEmpty() }.firstOrNull()?.teamId + ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") + } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index b59198007..1515f215b 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -1,12 +1,13 @@ package dev.dres.run +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.SubmissionId import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard @@ -146,6 +147,17 @@ interface InteractiveRunManager : RunManager { */ fun taskForId(context: RunActionContext, taskId: EvaluationId): TaskRun? + /** + * Invoked by an external caller to update an existing submission by its [SubmissionId] with a new [ApiVerdictStatus]. + * + * @param context The [RunActionContext] used for the invocation. + * @param submissionId The [SubmissionId] of the submission to update. + * @param submissionStatus The new [ApiVerdictStatus] + * + * @return Whether the update was successful or not. + */ + fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: ApiVerdictStatus): Boolean + /** * List of all [DbSubmission]s for this [InteractiveRunManager], irrespective of the [DbTask] it belongs to. * @@ -170,21 +182,4 @@ interface InteractiveRunManager : RunManager { * @return true on success, false otherwise */ fun overrideReadyState(context: RunActionContext, viewerId: String): Boolean - - /** - * Invoked by an external caller to update an existing [DbSubmission] by its [DbSubmission.uid] with a new [DbVerdictStatus]. - * [DbSubmission]s usually cause updates to the internal state and/or the [Scoreboard] of this [InteractiveRunManager]. - * - * This method will not throw an exception and instead returns false if a [DbSubmission] was - * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke - * this method again. - * - * @param context The [RunActionContext] used for the invocation - * @param submissionId The [EvaluationId] of the [DbSubmission] to update. - * @param submissionStatus The new [DbVerdictStatus] - * - * @return Whether the update was successful or not - * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. - */ - fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: DbVerdictStatus): Boolean } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 118ab2775..e8cae1974 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -2,11 +2,14 @@ package dev.dres.run import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType +import dev.dres.data.model.admin.DbUser import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId @@ -18,6 +21,7 @@ import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.task.options.Defaults.SCOREBOARD_UPDATE_INTERVAL_DEFAULT import dev.dres.data.model.template.task.options.Defaults.VIEWER_TIMEOUT_DEFAULT +import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT import dev.dres.run.audit.DbAuditLogger import dev.dres.run.eventstream.EventStreamProcessor @@ -30,6 +34,8 @@ import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.ReadyLatch import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* +import kotlinx.dnq.query.FilteringContext.eq +import kotlinx.dnq.query.FilteringContext.isNotEmpty import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.concurrent.locks.ReentrantReadWriteLock @@ -275,6 +281,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return [DbTask] or null */ override fun currentTask(context: RunActionContext) = this.stateLock.read { + this.evaluation.currentTask when (this.evaluation.currentTask?.status) { DbTaskStatus.PREPARING, DbTaskStatus.RUNNING, @@ -439,37 +446,47 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * - * @param context The [RunActionContext] used for the invocation + * @param context The [RunActionContext] used for the invocation. * @param submission [ApiSubmission] that should be registered. */ - override fun postSubmission(context: RunActionContext, submission: ApiSubmission) = this.stateLock.read { - /* Register submission. */ - val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager, despite correct status. This is a programmer's error!") + override fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) = this.stateLock.read { + + /* Phase 1: Basic lookups required for validation (read-only). */ + val task = this.store.transactional(true) { + val task = this.currentTask(context) ?: throw IllegalStateException("Could not find ongoing task in run manager.") + check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running." } + + /* Update submission with contextual information. */ + submission.userId = context.userId + submission.teamId = context.teamId() + submission.answerSets.forEach { + it.taskId = task.taskId /* All answers are explicitly associated with the running task. */ + } - /* Sanity check. */ - check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } - check(this.template.teams.asSequence().filter { it.teamId == submission.teamId }.any()) { "Team ${submission.teamId} does not exists for evaluation run ${this.name}. This is a programmer's error!" } + /* Run submission through all the filters. */ + task.filter.acceptOrThrow(submission) + task + } - /* Check if ApiSubmission meets formal requirements. */ - task.filter.acceptOrThrow(submission) + /* Phase 2: Create DbSubmission, apply transformers and validate it. */ + this.store.transactional { + /* Convert submission to database representation. */ + val db = submission.toNewDb() - /* Apply transformations to submissions */ - val transformedSubmission = task.transformer.transform(submission) + /* Apply transformer(s) to submission. */ + task.transformer.transform(db) - /* Check if there are answers left after transformation */ - if (transformedSubmission.answers.isEmpty()) { - throw IllegalStateException("Submission contains no valid answer sets") - } + /* Check if there are answers left after transformation */ + if (db.answerSets.isEmpty) { + throw IllegalStateException("Submission contains no valid answer sets.") + } - /* At this point, the submission is considered valid and is persisted */ - /* Validator is applied to each answer set */ - transformedSubmission.answerSets().forEach { - task.validator.validate(it) + /* Apply validators to each answer set. */ + db.answerSets.asSequence().forEach { + task.validator.validate(it) + } } - /* Persist the submission. */ - transformedSubmission.toNewDb() - /* Enqueue submission for post-processing. */ this.scoresUpdatable.enqueue(task) @@ -477,43 +494,45 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) } - override fun reScore(taskId: TaskId) { - val task = evaluation.tasks.find { it.taskId == taskId } - if (task != null) { - this.scoresUpdatable.enqueue(task) - } - } - /** * Processes incoming [DbSubmission]s. If a [DbTask] is running then that [DbSubmission] will usually * be associated with that [DbTask]. * - * This method will not throw an exception and instead return false if a [DbSubmission] was - * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke - * this method again. - * - * @param context The [RunActionContext] used for the invocation + * @param context The [RunActionContext] used for the invocation. * @param submissionId The [EvaluationId] of the [DbSubmission] to update. - * @param submissionStatus The new [DbVerdictStatus] * @return True on success, false otherwise. */ - override fun updateSubmission(context: RunActionContext, submissionId: EvaluationId, submissionStatus: DbVerdictStatus): Boolean = this.stateLock.read { - val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false - val task = this.taskForId(context, answerSet.task.id) ?: return false + override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: ApiVerdictStatus): Boolean = this.stateLock.read { + val (taskId, status) = this.store.transactional { + val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() + ?: throw IllegalArgumentException("Could not find submission with ID ${submissionId}.") + + /* Actual update - currently, only status update is allowed */ + val newStatus = submissionStatus.toDb() + if (answerSet.status != newStatus) { + answerSet.status = newStatus + answerSet.task.id to true + } else { + answerSet.task.id to false + } + } - /* Actual update - currently, only status update is allowed */ - if (answerSet.status != submissionStatus) { - answerSet.status = submissionStatus + /** Broadcast information about change to submission. */ + if (status) { + RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_UPDATED, taskId)) + } - /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(task) + return status + } - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(context.teamId!!, ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId)) - return true + /** + * + */ + override fun reScore(taskId: TaskId) { + val task = evaluation.tasks.find { it.taskId == taskId } + if (task != null) { + this.scoresUpdatable.enqueue(task) } - - return false } /** @@ -668,4 +687,18 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private fun assureNoRunningTask() { if (this.evaluation.tasks.any { it.status == DbTaskStatus.RUNNING }) throw IllegalStateException("Task is already running!") } + + /** + * Convenience method: Tries to find a matching [TeamId] in the context of this [InteractiveSynchronousRunManager] + * for the user associated with the current [RunActionContext]. + * + * @return [TeamId] + */ + private fun RunActionContext.teamId(): TeamId { + val userId = this.userId + val user = DbUser.filter { u -> u.id eq userId }.singleOrNull() + ?: throw IllegalArgumentException("Could not find user with ID ${userId}.") + return this@InteractiveSynchronousRunManager.template.teams.filter { t -> t.users.contains(user) }.singleOrNull()?.teamId + ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 576eebfd1..8193d69cb 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -1,14 +1,14 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.data.model.run.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.submissions.* -import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.SubmissionRejectedException import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.ScoreboardsUpdatable @@ -16,8 +16,6 @@ import dev.dres.run.updatables.ScoresUpdatable import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore import org.slf4j.LoggerFactory -import java.util.concurrent.LinkedBlockingQueue -import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -59,8 +57,7 @@ class NonInteractiveRunManager( private val scoresUpdatable = ScoresUpdatable(this) /** The internal [ScoreboardsUpdatable] instance for this [InteractiveSynchronousRunManager]. */ - private val scoreboardsUpdatable = - ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) //TODO requires some changes + private val scoreboardsUpdatable = ScoreboardsUpdatable(this, SCOREBOARD_UPDATE_INTERVAL_MS) //TODO requires some changes /** The [List] of [Scoreboard]s maintained by this [NonInteractiveRunManager]. */ override val scoreboards: List @@ -159,9 +156,15 @@ class NonInteractiveRunManager( private val taskMap = this.evaluation.tasks.associateBy { it.taskId } - override fun postSubmission(context: RunActionContext, submission: ApiSubmission) { + /** + * + */ + override fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) { - val submissionByTask = + + TODO("Not yet implemented") + + /*val submissionByTask = submission.answers.groupBy { it.taskId }.mapValues { submission.copy(answers = it.value) } if (submissionByTask.keys.any { !taskMap.containsKey(it) }) { @@ -211,9 +214,7 @@ class NonInteractiveRunManager( throw SubmissionRejectedException(submission, errorBuffer.toString()) } - } - - TODO("Not yet implemented") + } */ } override fun reScore(taskId: TaskId) { diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 965b6557c..9db25a8df 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -1,7 +1,8 @@ package dev.dres.run import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId @@ -112,13 +113,12 @@ interface RunManager : Runnable { * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke * this method again. * - * - * @param context The [RunActionContext] used for the invocation - * @param submission The [ApiSubmission] to be posted. + * @param context: The [RunActionContext] + * @param submission The [ApiClientSubmission] to be posted. * * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.RUNNING_TASK]. */ - fun postSubmission(context: RunActionContext, submission: ApiSubmission) + fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) /** * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. diff --git a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt index 6c534d7e7..3ec9aa31b 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt @@ -11,6 +11,7 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator +import kotlinx.dnq.query.first /** * Audit logging instance of DRES. Requires one-time initialisation @@ -123,16 +124,16 @@ object DbAuditLogger { * @param sessionToken The identifier of the user session. * @param address The IP address of the submitter. */ - fun submission(submission: Submission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) { - DbAuditLogEntry.new { + fun submission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) { + val entry = DbAuditLogEntry.new { this.type = DbAuditLogType.SUBMISSION this.source = api this.submissionId = submission.submissionId - this.evaluationId = submission.evaluationId + this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId this.session = sessionToken this.address = address } - EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", submission.evaluationId, null, submission)) + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", entry.evaluationId!!, null, submission)) } /** @@ -163,8 +164,8 @@ object DbAuditLogger { this.source = DbAuditLogSource.INTERNAL this.submissionId = answerSet.submission.submissionId this.evaluationId = answerSet.task.evaluation.evaluationId - this.taskId = answerSet.taskId - this.description = "Token: $token, Validator: ${validator.id}, AnswerSet: ${answerSet.id}, Verdict: ${answerSet.status()}" + this.taskId = answerSet.task.taskId + this.description = "Token: $token, Validator: ${validator.id}, AnswerSet: ${answerSet.id}, Verdict: ${answerSet.status.description}" } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt deleted file mode 100644 index fd4171ef3..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/AllSubmissionFilter.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission - - -/** - * A [SubmissionFilter] that lets all [DbSubmission] pass. - * - * @author Ralph Gasser - * @version 1.0 - */ -object AllSubmissionFilter : SubmissionFilter { - override val reason = "" //will never be relevant - - override fun test(t: ApiSubmission): Boolean = true -} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt deleted file mode 100644 index 317cf1d52..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamFilter.kt +++ /dev/null @@ -1,37 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.template.task.options.DbSubmissionOption -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.size - -/** - * A [SubmissionFilter] that filters correct [DbSubmission]s if the number of correct [DbSubmission] for the team exceed the limit. - * - * @author Ralph Gasser - * @author Luca Rossetto - * @author Loris Sauter - * @version 1.2.0 - */ -class CorrectPerTeamFilter(private val limit: Int = 1) : SubmissionFilter { - - companion object { - val PARAMETER_KEY_LIMIT = "${DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description}.limit" - } - - override val reason = "Maximum number of correct submissions ($limit) exceeded for the team." - - constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "1").toIntOrNull() ?: 1) - override fun test(submission: ApiSubmission): Boolean { - return submission.answerSets().all { answer -> - answer.task().answerSets().filter { - (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId - }.count() < limit - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt deleted file mode 100644 index 010b75b36..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamItemFilter.kt +++ /dev/null @@ -1,28 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.size - - -class CorrectPerTeamItemFilter(private val limit: Int = 1) : SubmissionFilter { - - constructor(parameters: Map) : this(parameters.getOrDefault("limit", "1").toIntOrNull() ?: 1) - - override val reason: String = "Maximum number of correct submissions ($limit) exceeded for this item." - - override fun test(submission: ApiSubmission): Boolean { - val submittedItems = submission.answerSets().flatMap { it.answers() }.mapNotNull { it.item }.toSet() - return submission.answerSets().all { answerSet -> - answerSet.task().answerSets().filter { taskAnswerSets -> - (taskAnswerSets.status() == VerdictStatus.CORRECT) && taskAnswerSets.submission.teamId == submission.teamId && taskAnswerSets.answers().any { it.item in submittedItems } - }.count() < this.limit - } - } - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt deleted file mode 100644 index cd5150a1c..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/CorrectPerTeamMemberFilter.kt +++ /dev/null @@ -1,36 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus -import dev.dres.data.model.template.task.options.DbSubmissionOption -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.size - -/** - * A [SubmissionFilter] that filters correct [DbSubmission]s if the number of correct [DbSubmission] for the team member exceeds the limit. - * - * @author Ralph Gasser - * @author Luca Rossetto - * @author Loris Sauter - * @version 1.2.0 - */ -class CorrectPerTeamMemberFilter(private val limit: Int = 1) : SubmissionFilter { - companion object { - val PARAMETER_KEY_LIMIT = "${DbSubmissionOption.LIMIT_CORRECT_PER_MEMBER.description}.limit" - } - - constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "1").toIntOrNull() ?: 1) - - override val reason = "Maximum number of correct submissions ($limit) exceeded for the team member." - override fun test(submission: ApiSubmission): Boolean { - return submission.answerSets().all { answer -> - answer.task().answerSets().filter { - (it.status() == VerdictStatus.CORRECT) && it.submission.teamId == submission.teamId && it.submission.memberId == submission.memberId - }.count() < limit - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index d09a73ee8..d33f1ad3d 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -1,11 +1,14 @@ package dev.dres.run.filter -import dev.dres.api.rest.types.evaluation.ApiSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.isEmpty +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import kotlinx.dnq.query.* /** @@ -14,26 +17,43 @@ import kotlinx.dnq.query.isEmpty * @author Luca Rossetto * @version 1.1.0 */ -class DuplicateSubmissionFilter : SubmissionFilter { - - override val reason = "Duplicate submission received." - - override fun test(submission: ApiSubmission): Boolean = - - submission.answers.groupBy { it.taskId }.all { - - val task = it.value.firstOrNull()?.task() ?: return@all true - - val presentSubmissions = task.answerSets().filter { it.submission.teamId == submission.teamId } - - presentSubmissions.forEach { presentAnswerSet -> - if (it.value.any { it equivalent presentAnswerSet }) { //any overlap in answerSets - return@all false +class DuplicateSubmissionFilter : AbstractSubmissionFilter("Duplicate submission received.") { + + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + require(t.teamId != null) { "Submission ${t.submissionId} is not associated with a team. This is a programmer's error!" } + for ((taskId, answerSets) in t.answerSets.groupBy { it.taskId }) { + val existingAnswerSets = DbAnswerSet.filter { (it.taskId eq taskId) and (it.submission.team.id eq t.teamId!!) } + for (answerSet in existingAnswerSets) { + if (answerSets.any { isEquivalent(answerSet, it) }) { + return false } } - - true } - - + return true + } + + /** + * Checks a [DbAnswerSet] and an [ApiClientAnswerSet] for equivalence. + * + * @param a The [DbAnswerSet]. + * @param b The [ApiClientAnswerSet] + * @return True, if compared sets are equivalents and thus duplicates. + */ + private fun isEquivalent(a: DbAnswerSet, b: ApiClientAnswerSet): Boolean { + for (answer in a.answers) { + if (b.answers.find { it.text == answer.text && it.start == answer.start && it.end == answer.end && it.itemName == answer.item?.name } == null) { + return false + } + } + return true + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt deleted file mode 100644 index 40c38efcf..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/ItemSubmissionFilter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.AnswerType -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerType -import dev.dres.data.model.submissions.Submission -import kotlinx.dnq.query.asSequence - -/** - * A [SubmissionFilter] that filters temporal submissions. - * - * @author Luca Rossetto - * @version 1.1.0 - */ -class ItemSubmissionFilter : SubmissionFilter { - override val reason = "Submission does include temporal information, but whole item was expected" - override fun test(submission: ApiSubmission): Boolean - = submission.answerSets().any { it.answers().any { it.type() == AnswerType.ITEM } } -} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamFilter.kt new file mode 100644 index 000000000..98cf4d708 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamFilter.kt @@ -0,0 +1,55 @@ +package dev.dres.run.filter + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.options.DbSubmissionOption +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import kotlinx.dnq.query.* + +/** + * A [SubmissionFilter] that checks, if the maximum number of correct submissions has not been exceeded for the submitting team and task. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class MaximumCorrectPerTeamFilter(private val limit: Int = PARAMETER_KEY_LIMIT_DEFAULT) : AbstractSubmissionFilter("Maximum number of correct submissions ($limit) exceeded for the team.") { + + companion object { + /** The name for the limit parameter. */ + val PARAMETER_KEY_LIMIT = "${DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description}.limit" + + /** The default value for the limit parameter. */ + const val PARAMETER_KEY_LIMIT_DEFAULT = 1 + } + + /** + * Constructor using the parameters map. + * + * @param parameters The parameters map. + */ + constructor(parameters: Map) : this(parameters[PARAMETER_KEY_LIMIT]?.toIntOrNull() ?: PARAMETER_KEY_LIMIT_DEFAULT) + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + require(t.teamId != null) { "Submission ${t.submissionId} is not associated with a team. This is a programmer's error!" } + for (answerSet in t.answerSets) { + require(answerSet.taskId != null) { "Answer for submission ${t.submissionId} is not associated with a task." } + val task = DbTask.filter { it.id eq answerSet.taskId }.singleOrNull() ?: throw IllegalStateException("The specified task ${answerSet.taskId} does not exist in the database.") + if (task.answerSets.filter { (it.status eq DbVerdictStatus.CORRECT) and (it.submission.team.id eq t.teamId) }.size() >= this.limit) { + return false + } + } + return true + } +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamMemberFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamMemberFilter.kt new file mode 100644 index 000000000..5c17c1b11 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumCorrectPerTeamMemberFilter.kt @@ -0,0 +1,58 @@ +package dev.dres.run.filter + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.DbTask +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.template.task.options.DbSubmissionOption +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.singleOrNull +import kotlinx.dnq.query.size + +/** + * A [SubmissionFilter] that checks that the maximum number of correct submissions has not been exceeded for the submitting team member and task. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class MaximumCorrectPerTeamMemberFilter(private val limit: Int = PARAMETER_KEY_LIMIT_DEFAULT) : AbstractSubmissionFilter("Maximum number of correct submissions ($limit) exceeded for the team member.") { + companion object { + /** The name for the limit parameter. */ + val PARAMETER_KEY_LIMIT = "${DbSubmissionOption.LIMIT_CORRECT_PER_MEMBER.description}.limit" + + /** The default value for the limit parameter. */ + const val PARAMETER_KEY_LIMIT_DEFAULT = 1 + } + + /** + * Constructor using the parameters map. + * + * @param parameters The parameters map. + */ + constructor(parameters: Map) : this(parameters[PARAMETER_KEY_LIMIT]?.toIntOrNull() ?: PARAMETER_KEY_LIMIT_DEFAULT) + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + require(t.teamId != null) { "Submission ${t.submissionId} is not associated with a team. This is a programmer's error!" } + require(t.userId != null) { "Submission ${t.submissionId} is not associated with a user. This is a programmer's error!" } + + for (answerSet in t.answerSets) { + require(answerSet.taskId != null) { "Answer for submission ${t.submissionId} is not associated with a task." } + val task = DbTask.filter { it.id eq answerSet.taskId }.singleOrNull() ?: throw IllegalStateException("The specified task ${answerSet.taskId} does not exist in the database.") + if (task.answerSets.filter { (it.status eq DbVerdictStatus.CORRECT) and (it.submission.team.id eq t.teamId) and (it.submission.user.id eq t.userId) }.size() >= this.limit) { + return false + } + } + return true + } +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt index 557875047..391146040 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumTotalPerTeamFilter.kt @@ -1,27 +1,56 @@ package dev.dres.run.filter -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.DbTask import dev.dres.data.model.template.task.options.DbSubmissionOption -import kotlinx.dnq.query.asSequence +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter import kotlinx.dnq.query.filter +import kotlinx.dnq.query.singleOrNull import kotlinx.dnq.query.size -class MaximumTotalPerTeamFilter(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { +/** + * A [SubmissionFilter] that checks that the maximum number of submissions has not been exceeded for the submitting team and task. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class MaximumTotalPerTeamFilter(private val limit: Int = PARAMETER_KEY_LIMIT_DEFAULT) : AbstractSubmissionFilter("Maximum number of submissions ($limit) exceeded for the team.") { companion object { val PARAMETER_KEY_LIMIT = "${DbSubmissionOption.LIMIT_TOTAL_PER_TEAM.description}.limit" - } - constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) + /** The default value for the limit parameter. */ + const val PARAMETER_KEY_LIMIT_DEFAULT = 10 + } - override val reason = "Maximum total number of submissions ($max) exceeded for the team" + /** + * Constructor using the parameters map. + * + * @param parameters The parameters map. + */ + constructor(parameters: Map) : this(parameters[PARAMETER_KEY_LIMIT]?.toIntOrNull() ?: PARAMETER_KEY_LIMIT_DEFAULT) - override fun test(submission: ApiSubmission): Boolean { - return submission.answerSets().all { answerSet -> - answerSet.task().answerSets().filter { it.submission.teamId == submission.teamId }.count() < max + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + require(t.teamId != null) { "Submission ${t.submissionId} is not associated with a team. This is a programmer's error!" } + for (answerSet in t.answerSets) { + require(answerSet.taskId != null) { "Answer for submission ${t.submissionId} is not associated with a task." } + val task = DbTask.filter { it.id eq answerSet.taskId }.singleOrNull() ?: throw IllegalStateException("The specified task ${answerSet.taskId} does not exist in the database.") + if (task.answerSets.filter { (it.submission.team.id eq t.teamId) }.size() >= this.limit) { + return false + } } + return true } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt index 57267df29..3c8a25137 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/MaximumWrongPerTeamFilter.kt @@ -1,33 +1,61 @@ package dev.dres.run.filter -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.DbTask import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.Submission -import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.task.options.DbSubmissionOption -import kotlinx.dnq.query.asSequence +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter import kotlinx.dnq.query.filter +import kotlinx.dnq.query.singleOrNull import kotlinx.dnq.query.size /** + * A [SubmissionFilter] that checks that the maximum number of wrong submissions has not been exceeded for the submitting team and task. * * @author Ralph Gasser - * @version 1.0 + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 */ -class MaximumWrongPerTeamFilter(private val max: Int = Int.MAX_VALUE) : SubmissionFilter { +class MaximumWrongPerTeamFilter(private val limit: Int = PARAMETER_KEY_LIMIT_DEFAULT) : AbstractSubmissionFilter("Maximum number of incorrect submissions ($limit) exceeded for the team.") { + + init { + require(limit > 0) { "Value of limit must be greater than zero for MaximumWrongPerTeamFilter." } + } companion object { + /** The name for the limit parameter. */ val PARAMETER_KEY_LIMIT = "${DbSubmissionOption.LIMIT_WRONG_PER_TEAM.description}.limit" - } - constructor(parameters: Map) : this(parameters.getOrDefault(PARAMETER_KEY_LIMIT, "${Int.MAX_VALUE}").toIntOrNull() ?: Int.MAX_VALUE) + /** The default value for the limit parameter. */ + const val PARAMETER_KEY_LIMIT_DEFAULT = 3 + } - override val reason = "Maximum number of wrong submissions ($max) exceeded for the team" + /** + * Constructor using the parameters map. + * + * @param parameters The parameters map. + */ + constructor(parameters: Map) : this(parameters[PARAMETER_KEY_LIMIT]?.toIntOrNull() ?: PARAMETER_KEY_LIMIT_DEFAULT) - override fun test(submission: ApiSubmission): Boolean { - return submission.answerSets().all { answerSet -> - answerSet.task().answerSets().filter { (it.submission.teamId == submission.teamId) and (it.status() == VerdictStatus.WRONG) }.count() < max + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + require(t.teamId != null) { "Submission ${t.submissionId} is not associated with a team." } + for (answerSet in t.answerSets) { + require(answerSet.taskId != null) { "Answer for submission ${t.submissionId} is not associated with a task." } + val task = DbTask.filter { it.id eq answerSet.taskId }.singleOrNull() ?: throw IllegalStateException("The specified task ${answerSet.taskId} does not exist in the database.") + if (task.answerSets.filter { (it.status eq DbVerdictStatus.WRONG) and (it.submission.team.id eq t.teamId) }.size() >= this.limit) { + return false + } } + return true } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt deleted file mode 100644 index 1994cae0a..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilter.kt +++ /dev/null @@ -1,35 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission -import org.slf4j.LoggerFactory -import java.util.function.Predicate - -/** - * A [Predicate] that can be used to filter [Submission]'s prior to them being processed - * by the [Submission] evaluation pipeline. - * - * @author Ralph Gasser - * @version 1.1.0 - */ -interface SubmissionFilter : Predicate { - companion object { - private val LOGGER = LoggerFactory.getLogger(this::class.java) - } - - val reason: String - - /** - * Tests the given [DbSubmission] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. - * - * @param submission The [DbSubmission] to check. - * @throws SubmissionRejectedException on failure - */ - fun acceptOrThrow(submission: ApiSubmission) { - if (!this.test(submission)) { - LOGGER.info("Submission $${submission.submissionId} was rejected by filter: $reason") - throw SubmissionRejectedException(submission, reason) - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt deleted file mode 100644 index cd87e1e95..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionFilterAggregator.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission - -class SubmissionFilterAggregator(private val filters: List) : SubmissionFilter { - - override val reason = "" //will never be relevant - - override fun acceptOrThrow(submission: ApiSubmission) { - for (filter in filters) { - filter.acceptOrThrow(submission) - } - } - - override fun test(t: ApiSubmission): Boolean = filters.all { it.test(t) } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt index 934f0c803..a88113878 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRateFilter.kt @@ -1,33 +1,53 @@ package dev.dres.run.filter -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.Submission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.task.options.DbSubmissionOption - -class SubmissionRateFilter(private val minDelayMS: Int = 500) : SubmissionFilter { +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter +import java.util.concurrent.ConcurrentHashMap + +/** + * A [SubmissionFilter] that checks that the specified submission rate is not exceeded. + * + * @author Ralph Gasser + * @author Luca Rossetto + * @author Loris Sauter + * @version 2.0.0 + */ +class SubmissionRateFilter(private val minDelayMs: Int = PARAMETER_KEY_DELAY_DEFAULT) : AbstractSubmissionFilter("Not enough time has passed since last submission. Delay needs to be at least $minDelayMs ms.") { companion object { + /** The name for the delay parameter. */ val PARAMETER_KEY_DELAY = "${DbSubmissionOption.MINIMUM_TIME_GAP.description}.delay" - } - - constructor(parameters: Map) : this( - parameters.getOrDefault(PARAMETER_KEY_DELAY, "500").toIntOrNull() ?: 500 - ) - - override val reason = "Not enough time has passed since last submission, gap needs to be at least $minDelayMS ms" - - override fun test(submission: ApiSubmission): Boolean = - - submission.answers.groupBy { it.taskId }.all { - val task = it.value.firstOrNull()?.task() ?: return@all true - - val mostRecentSubmissionTime = task.answerSets().filter { it.submission.teamId == submission.teamId } - .maxOfOrNull { it.submission.timestamp } ?: return@all true - - return (submission.timestamp - mostRecentSubmissionTime) >= minDelayMS - - } + /** The default value for the limit parameter. */ + const val PARAMETER_KEY_DELAY_DEFAULT = 500 + } + /** + * Constructor using the parameters map. + * + * @param parameters The parameters map. + */ + constructor(parameters: Map) : this(parameters[PARAMETER_KEY_DELAY]?.toIntOrNull() ?: PARAMETER_KEY_DELAY_DEFAULT) + + /** Internal map of [UserId] to submission timestamp. */ + private val submissions = ConcurrentHashMap() + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + require(t.userId != null) { "Submission ${t.submissionId} is not associated with a user." } + val lastSubmission = this.submissions[t.userId] + val currentSubmission = System.currentTimeMillis() + this.submissions[t.userId!!] = currentSubmission + return lastSubmission == null || (currentSubmission - lastSubmission <= minDelayMs) + } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt index 091be13fc..64cb8736a 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/SubmissionRejectedException.kt @@ -1,7 +1,6 @@ package dev.dres.run.filter -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.DbSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.data.model.submissions.Submission /** @@ -10,4 +9,4 @@ import dev.dres.data.model.submissions.Submission * @author Ralph Gasser * @version 1.0.0 */ -class SubmissionRejectedException(s: ApiSubmission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file +class SubmissionRejectedException(s: ApiClientSubmission, reason: String) : Throwable("Submission ${s.submissionId} was rejected by filter: $reason") \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt deleted file mode 100644 index cc4d3307a..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/TemporalSubmissionFilter.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.AnswerType -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerType -import dev.dres.data.model.submissions.Submission -import kotlinx.dnq.query.asSequence - - -/** - * A [SubmissionFilter} that checks if a [DbSubmission] contains temporal information. - * - * @author Luca Rossetto - * @version 1.1.0 - */ -class TemporalSubmissionFilter : SubmissionFilter { - override val reason = "Submission does not include temporal information." - - override fun test(submission: ApiSubmission): Boolean - = submission.answerSets().all { set -> set.answers().all { it.type() == AnswerType.TEMPORAL && it.start != null && it.end != null } } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt deleted file mode 100644 index 24d46dbf6..000000000 --- a/backend/src/main/kotlin/dev/dres/run/filter/TextualSubmissionFilter.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dev.dres.run.filter - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.AnswerType -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.DbAnswerType -import dev.dres.data.model.submissions.Submission -import kotlinx.dnq.query.asSequence - -/** - * A [SubmissionFilter} that checks if a [DbSubmission] contains text information. - * - * @author Luca Rossetto - * @version 1.1.0 - */ -class TextualSubmissionFilter : SubmissionFilter { - - override val reason = "Submission does not include textual information (or is an empty submission)" - - override fun test(submission: ApiSubmission): Boolean - = submission.answerSets().all { set -> set.answers().all { it.text != null && it.type() == AnswerType.TEXT } } -} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt new file mode 100644 index 000000000..6ffbec776 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt @@ -0,0 +1,34 @@ +package dev.dres.run.filter + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter + +/** + * A [SubmissionFilter] that filters for pure media item submissions (no temporal information, no text). + * + * @author Luca Rossetto + * @version 2.0.0 + */ +class ValidItemSubmissionFilter: AbstractSubmissionFilter("Submission does include temporal information, but whole item was expected.") { + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + for (answerSet in t.answerSets) { + for (answer in answerSet.answers) { + if (answer.itemName == null) return false /* Check that a media item has been specified. */ + if (answer.start != null) return false /* Check no start timestamp is contained. */ + if (answer.end != null) return false /* Check no end timestamp is contained. */ + if (answer.text != null) return false /* Check no text is contained. */ + } + } + return true + } +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt new file mode 100644 index 000000000..870a55879 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt @@ -0,0 +1,35 @@ +package dev.dres.run.filter + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter + + +/** + * A [SubmissionFilter] that checks if a [ApiClientSubmission] contains temporal information. + * + * @author Luca Rossetto + * @version 2.0.0 + */ +class ValidTemporalSubmissionFilter : AbstractSubmissionFilter("Submission does include non-temporal information.") { + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + for (answerSet in t.answerSets) { + for (answer in answerSet.answers) { + if (answer.itemName == null) return false /* Check that either a media item or a text has been specified. */ + if (answer.start == null) return false /* Check that start timestamp is contained. */ + if (answer.end == null) return false /* Check that end timestamp is contained. */ + if (answer.start > answer.end) return false /* Check that start precedes end timestamp. */ + } + } + return true + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ValidTextualSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ValidTextualSubmissionFilter.kt new file mode 100644 index 000000000..bd6bfa4ae --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/ValidTextualSubmissionFilter.kt @@ -0,0 +1,32 @@ +package dev.dres.run.filter + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.run.filter.basics.AbstractSubmissionFilter +import dev.dres.run.filter.basics.SubmissionFilter + +/** + * A [SubmissionFilter} that checks if a [DbSubmission] contains text information. + * + * @author Luca Rossetto + * @version 1.1.0 + */ +class ValidTextualSubmissionFilter : AbstractSubmissionFilter("Submission does not include textual information (or is an empty submission)") { + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * Requires an ongoing transaction! + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean { + for (answerSet in t.answerSets) { + for (answer in answerSet.answers) { + if (answer.text == null) return false /* Check that start precedes end timestamp. */ + } + } + return true + } +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/basics/AbstractSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/basics/AbstractSubmissionFilter.kt new file mode 100644 index 000000000..f55ce7ba6 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/basics/AbstractSubmissionFilter.kt @@ -0,0 +1,30 @@ +package dev.dres.run.filter.basics + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.AbstractTask +import dev.dres.run.filter.SubmissionRejectedException +import org.slf4j.LoggerFactory + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractSubmissionFilter(private val reason: String): SubmissionFilter { + companion object { + private val LOGGER = LoggerFactory.getLogger(SubmissionFilter::class.java) + } + + /** + * Tests the given [ApiClientSubmission] for the provided [AbstractTask] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. + * + * @param submission The [ApiClientSubmission] to check. + * @throws SubmissionRejectedException on failure + */ + final override fun acceptOrThrow(submission: ApiClientSubmission) { + if (!this.test(submission)) { + LOGGER.info("Submission ${submission.submissionId} was rejected by filter: $reason") + throw SubmissionRejectedException(submission, this.reason) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptAllSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptAllSubmissionFilter.kt new file mode 100644 index 000000000..5cce2e4ed --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptAllSubmissionFilter.kt @@ -0,0 +1,29 @@ +package dev.dres.run.filter.basics + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission + + +/** + * A [SubmissionFilter] that lets all [ApiClientSubmission] pass. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +object AcceptAllSubmissionFilter : SubmissionFilter { + /** + * Tests the given [ApiClientSubmission] with this [AcceptAllSubmissionFilter]. + * + * @param submission The [ApiClientSubmission] to check. + */ + override fun acceptOrThrow(submission: ApiClientSubmission) { + /* No op. */ + } + + /** + * Evaluates this [SubmissionFilter] on the given argument. Always returns true. + * + * @param submission The input argument. + * @return True + */ + override fun test(submission: ApiClientSubmission): Boolean = true +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptNoneSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptNoneSubmissionFilter.kt new file mode 100644 index 000000000..44bf1f851 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/basics/AcceptNoneSubmissionFilter.kt @@ -0,0 +1,29 @@ +package dev.dres.run.filter.basics + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.run.filter.SubmissionRejectedException + +/** + * A [SubmissionFilter] that lets no [ApiClientSubmission] pass. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +object AcceptNoneSubmissionFilter: SubmissionFilter { + /** + * Tests the given [ApiClientSubmission] with this [AcceptAllSubmissionFilter]. + * + * @param submission The [ApiClientSubmission] to check. + */ + override fun acceptOrThrow(submission: ApiClientSubmission) { + throw SubmissionRejectedException(submission, "No submission are accepted!") + } + + /** + * Evaluates this [SubmissionFilter] on the given argument. Always returns true. + * + * @param submission The input argument. + * @return True + */ + override fun test(submission: ApiClientSubmission): Boolean = false +} diff --git a/backend/src/main/kotlin/dev/dres/run/filter/basics/CombiningSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/basics/CombiningSubmissionFilter.kt new file mode 100644 index 000000000..7d43c434a --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/basics/CombiningSubmissionFilter.kt @@ -0,0 +1,34 @@ +package dev.dres.run.filter.basics + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.AbstractTask +import dev.dres.run.filter.SubmissionRejectedException + +/** + * A [SubmissionFilter] that combines multiple [SubmissionFilter]s. + * + * @author Luca Rossetto + * @version 2.0.0 + */ +class CombiningSubmissionFilter(private val filters: List) : SubmissionFilter { + + /** + * Tests the given [ApiClientSubmission] for the provided [AbstractTask] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. + * + * @param submission The [ApiClientSubmission] to check. + * @throws SubmissionRejectedException on failure + */ + override fun acceptOrThrow(submission: ApiClientSubmission) { + for (filter in filters) { + filter.acceptOrThrow(submission) + } + } + + /** + * Tests the given [ApiClientSubmission] with this [SubmissionFilter] return true, if test succeeds. + * + * @param t The [ApiClientSubmission] to check. + * @return True on success, false otherwise. + */ + override fun test(t: ApiClientSubmission): Boolean = this.filters.all { it.test(t) } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/filter/basics/SubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/basics/SubmissionFilter.kt new file mode 100644 index 000000000..03ebe0564 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/filter/basics/SubmissionFilter.kt @@ -0,0 +1,24 @@ +package dev.dres.run.filter.basics + +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.data.model.run.AbstractTask +import dev.dres.data.model.submissions.Submission +import dev.dres.run.filter.SubmissionRejectedException +import java.util.function.Predicate + +/** + * A [Predicate] that can be used to filter [Submission]'s prior to them being processed + * by the [Submission] evaluation pipeline. + * + * @author Ralph Gasser + * @version 2.0.0 + */ +interface SubmissionFilter : Predicate { + /** + * Tests the given [ApiClientSubmission] for the provided [AbstractTask] with this [SubmissionFilter] and throws a [SubmissionRejectedException], if the test fails. + * + * @param submission The [ApiClientSubmission] to check. + * @throws SubmissionRejectedException on failure + */ + fun acceptOrThrow(submission: ApiClientSubmission) +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt deleted file mode 100644 index 37ab89326..000000000 --- a/backend/src/main/kotlin/dev/dres/run/transformer/IdentitySubmissionTransformer.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.dres.run.transformer - -import dev.dres.api.rest.types.evaluation.ApiSubmission - -object IdentitySubmissionTransformer : SubmissionTransformer { - override fun transform(submission: ApiSubmission): ApiSubmission = submission -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt index 92a2c3acf..4e2ab7de3 100644 --- a/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt +++ b/backend/src/main/kotlin/dev/dres/run/transformer/MapToSegmentTransformer.kt @@ -1,36 +1,49 @@ package dev.dres.run.transformer -import dev.dres.api.rest.types.evaluation.ApiAnswer -import dev.dres.api.rest.types.evaluation.ApiAnswerType -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.DbMediaSegment import dev.dres.data.model.media.time.TemporalPoint -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.query.query - +import dev.dres.data.model.submissions.DbAnswer +import dev.dres.data.model.submissions.DbAnswerType +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.run.transformer.basics.SubmissionTransformer +import kotlinx.dnq.query.iterator + +/** + * A [SubmissionTransformer] that maps temporal [DbSubmission] + * + * @author Luca Rossetto + */ class MapToSegmentTransformer : SubmissionTransformer { - override fun transform(submission: ApiSubmission): ApiSubmission = submission.copy( - answers = submission.answers.map { apiAnswerSet -> - apiAnswerSet.copy( - answers = apiAnswerSet.answers.map { mapAnswer(it) } - ) - } - ) - - private fun mapAnswer(answer: ApiAnswer) : ApiAnswer { - if (answer.type != ApiAnswerType.TEMPORAL) { - return answer + /** + * Apply this [MapToSegmentTransformer] to the provided [DbSubmission]. Transformation happens in place. + * + * Requires an ongoing transaction. + * + * @param submission [DbSubmission] to transform. + */ + override fun transform(submission: DbSubmission) { + for (answerSet in submission.answerSets) { + for (answer in answerSet.answers) { + if (answer.type == DbAnswerType.TEMPORAL) { + transformAnswer(answer) + } + } } + } - val item = DbMediaItem.query(DbMediaItem::id eq answer.item?.mediaItemId).firstOrNull() ?: throw IllegalStateException("MediaItem with id ${answer.item?.mediaItemId} not found") - - + /** + * Apples transformation to an individual [DbAnswer]. + * + * @param answer The [DbAnswer] to transform. + */ + private fun transformAnswer(answer: DbAnswer) { + /* Extract item and find start and end segment. */ + val item = answer.item ?: throw IllegalStateException("Media item not specified for answer.") val startSegment = answer.start?.let { DbMediaSegment.findContaining(item, TemporalPoint.Millisecond(it)) } val endSegment = answer.end?.let { DbMediaSegment.findContaining(item, TemporalPoint.Millisecond(it)) } + /* Calculate bounds. */ val bounds = when{ startSegment != null && endSegment == null -> startSegment startSegment == null && endSegment != null -> endSegment @@ -39,12 +52,8 @@ class MapToSegmentTransformer : SubmissionTransformer { else -> throw IllegalStateException("Cannot map answer time to segment, range does not fall within one segment") }.range.toMilliseconds() - return answer.copy( - start = bounds.first, - end = bounds.second - ) - - + /* Adjust start and end timestamp. */ + answer.start = bounds.first + answer.end = bounds.second } - } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt deleted file mode 100644 index 094ffa1d0..000000000 --- a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchFilter.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.run.transformer - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.run.TaskId - -/** - * Removes all [AnswerSet]s from a [Submission] that do not match a specified [Task] - */ - -class SubmissionTaskMatchFilter(private val taskId: TaskId) : SubmissionTransformer { - override fun transform(submission: ApiSubmission): ApiSubmission = - submission.copy( - answers = submission.answers.filter { it.taskId == taskId } - ) - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchTransformer.kt new file mode 100644 index 000000000..fcc7d8f44 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTaskMatchTransformer.kt @@ -0,0 +1,28 @@ +package dev.dres.run.transformer + +import dev.dres.data.model.run.TaskId +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.run.transformer.basics.SubmissionTransformer +import kotlinx.dnq.query.iterator + +/** + * A [SubmissionTransformer] that removes all [AnswerSet]s from a [Submission] that do not match a specified [TaskId] + * + * @author Luca Rossetto + */ +class SubmissionTaskMatchTransformer(private val taskId: TaskId) : SubmissionTransformer { + /** + * Apply this [SubmissionTaskMatchTransformer] to the provided [DbSubmission]. Transformation happens in place. + * + * Requires an ongoing transaction. + * + * @param submission [DbSubmission] to transform. + */ + override fun transform(submission: DbSubmission) { + for (answerSet in submission.answerSets) { + if (answerSet.task.id != this.taskId) { + answerSet.delete() + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt deleted file mode 100644 index d25d1bac8..000000000 --- a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.dres.run.transformer - -import dev.dres.api.rest.types.evaluation.ApiSubmission -import dev.dres.data.model.submissions.Submission - -interface SubmissionTransformer { - - fun transform(submission: ApiSubmission): ApiSubmission - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt b/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt deleted file mode 100644 index 1a613b10b..000000000 --- a/backend/src/main/kotlin/dev/dres/run/transformer/SubmissionTransformerAggregator.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.run.transformer - -import dev.dres.api.rest.types.evaluation.ApiSubmission - -class SubmissionTransformerAggregator(private val transformers: List) : SubmissionTransformer { - - override fun transform(submission: ApiSubmission): ApiSubmission { - - var transformed = submission - for (transformer in transformers) { - transformed = transformer.transform(transformed) - } - return transformed - - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/basics/CombiningSubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/basics/CombiningSubmissionTransformer.kt new file mode 100644 index 000000000..bb1c19c84 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/basics/CombiningSubmissionTransformer.kt @@ -0,0 +1,24 @@ +package dev.dres.run.transformer.basics + +import dev.dres.data.model.submissions.DbSubmission + +/** + * A [SubmissionTransformer] for [DbSubmission]s that combines multiple [SubmissionTransformer]. + * + * @author Luca Rossetto + */ +class CombiningSubmissionTransformer(private val transformers: List) : SubmissionTransformer { + + /** + * Apply this [CombiningSubmissionTransformer] to the provided [DbSubmission]. Transformation happens in place. + * + * Requires an ongoing transaction if nested [SubmissionTransformer]s require an ongoing transaction. + * + * @param submission [DbSubmission] to transform. + */ + override fun transform(submission: DbSubmission) { + for (transformer in transformers) { + transformer.transform(submission) + } + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/basics/IdentitySubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/basics/IdentitySubmissionTransformer.kt new file mode 100644 index 000000000..7b3fba335 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/basics/IdentitySubmissionTransformer.kt @@ -0,0 +1,19 @@ +package dev.dres.run.transformer.basics + +import dev.dres.data.model.submissions.DbSubmission + +/** + * A [SubmissionTransformer] that does not make any changes. + * + * @author Luca Rossetto + */ +object IdentitySubmissionTransformer : SubmissionTransformer { + /** + * Apply this [IdentitySubmissionTransformer] to the provided [DbSubmission]. + * + * @param submission [DbSubmission] to transform. + */ + override fun transform(submission: DbSubmission) { + /* No op. */ + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/transformer/basics/SubmissionTransformer.kt b/backend/src/main/kotlin/dev/dres/run/transformer/basics/SubmissionTransformer.kt new file mode 100644 index 000000000..dc86fcf9e --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/transformer/basics/SubmissionTransformer.kt @@ -0,0 +1,22 @@ +package dev.dres.run.transformer.basics + +import dev.dres.data.model.submissions.DbSubmission + + +/** + * A transformer for [DbSubmission]s. + * + * @author Luca Rossetto + */ +interface SubmissionTransformer { + + /** + * Apply this [SubmissionTransformer] to the provided [DbSubmission]. Transformation happens in place. + * + * Usually requires an ongoing transaction. + * + * @param submission [DbSubmission] to transform. + */ + fun transform(submission: DbSubmission) + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt index d03b60fad..aecaf7b5e 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt @@ -50,7 +50,7 @@ class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private v for (s in submissions) { for (a in s.answerSets.toList()) { if (a.status == DbVerdictStatus.CORRECT) { - teams[s.teamId] = teams[s.teamId]!! + 1 + teams[s.team.id] = teams[s.team.id]!! + 1 } } } diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt index 7b05f4a30..342e31026 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt @@ -3,16 +3,15 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.evaluation.ApiTaskStatus import dev.dres.data.model.run.RunActionContext +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.task.options.Defaults import dev.dres.data.model.template.task.options.Parameters import dev.dres.run.InteractiveRunManager import dev.dres.run.RunManagerStatus -import kotlinx.dnq.query.any -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.* /** * An [Updatable] that takes care of prolonging a task if a last-minute [DbSubmission] was received. @@ -49,8 +48,8 @@ class ProlongOnSubmitUpdatable(private val manager: InteractiveRunManager): Upda }.firstOrNull()?.value?.toBooleanStrictOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT /* Apply prolongation if necessary. */ - val submission: DbSubmission? = this.manager.currentSubmissions(context).lastOrNull() - if (submission == null || (correctOnly && submission.answerSets().all { it.status() != VerdictStatus.CORRECT })) { + val lastSubmission: DbSubmission? = DbAnswerSet.filter { it.task.id eq currentTask.taskId }.mapDistinct { it.submission }.lastOrNull() + if (lastSubmission == null || (correctOnly && lastSubmission.answerSets.asSequence().all { it.status != DbVerdictStatus.CORRECT })) { return } val timeLeft = Math.floorDiv(this.manager.timeLeft(context), 1000) diff --git a/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt index 07bb181db..7ce972ac1 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/ChainedAnswerSetValidator.kt @@ -1,8 +1,6 @@ package dev.dres.run.validation -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.AnswerSetValidator /** @@ -10,12 +8,12 @@ import dev.dres.run.validation.interfaces.AnswerSetValidator * * @author Luca Rossetto * @author Ralph Gasser - * @version 1.1.0 + * @version 2.0.0 */ -class ChainedAnswerSetValidator(private val firstValidator: AnswerSetValidator, private val continueStates: Set, private val secondValidator: AnswerSetValidator) : AnswerSetValidator { +class ChainedAnswerSetValidator(private val firstValidator: AnswerSetValidator, private val continueStates: Set, private val secondValidator: AnswerSetValidator) : AnswerSetValidator { companion object{ - fun of(continueStates: Set, vararg validator: AnswerSetValidator) : ChainedAnswerSetValidator { + fun of(continueStates: Set, vararg validator: AnswerSetValidator) : ChainedAnswerSetValidator { return when { validator.size < 2 -> throw IllegalArgumentException("Chain needs at least two validators") validator.size == 2 -> ChainedAnswerSetValidator(validator[0], continueStates, validator[1]) @@ -24,6 +22,7 @@ class ChainedAnswerSetValidator(private val firstValidator: AnswerSetValidator, } } + /** */ override val deferring: Boolean get() = this.secondValidator.deferring @@ -32,13 +31,15 @@ class ChainedAnswerSetValidator(private val firstValidator: AnswerSetValidator, } /** - * Validates a [DbSubmission] based on two [AnswerSetValidator]s. + * Validates the [DbAnswerSet] and updates its [DbVerdictStatus]. * - * @param submission The [DbSubmission] to validate. + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. */ - override fun validate(answerSet: AnswerSet) { + override fun validate(answerSet: DbAnswerSet) { this.firstValidator.validate(answerSet) - if (this.continueStates.contains(answerSet.status())) { + if (this.continueStates.contains(answerSet.status)) { this.secondValidator.validate(answerSet) } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt index 805bb0909..3bdea63dd 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/MediaItemsAnswerSetValidator.kt @@ -1,31 +1,53 @@ package dev.dres.run.validation import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.media.MediaItemId +import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.AnswerSetValidator +import kotlinx.dnq.query.iterator /** * A [AnswerSetValidator] that checks if the items specified by a [Submission] match the items in the provided set. * * @author Luca Rossetto - * @version 1.0.1 + * @author Ralph Gasser + * @version 2.0.0 */ class MediaItemsAnswerSetValidator(items: Set) : AnswerSetValidator { /** This type of [AnswerSetValidator] can be executed directly.*/ override val deferring = false + /** List of [MediaItemId]s that are considered valid. */ private val itemIds = items.map { it.mediaItemId } - override fun validate(answerSet: AnswerSet) { + /** + * Validates the [DbAnswerSet] and updates its [DbVerdictStatus]. + * + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. + */ + override fun validate(answerSet: DbAnswerSet) { + /* Basically, we assume that the DBAnswerSet is wrong. */ + answerSet.status = DbVerdictStatus.WRONG - if (answerSet.answers().any { it.item == null || it.item!!.mediaItemId !in this.itemIds }) { - answerSet.status(VerdictStatus.WRONG) - } else { - answerSet.status(VerdictStatus.CORRECT) + /* Now we check all the answers. */ + for (answer in answerSet.answers) { + /* Perform sanity checks. */ + val item = answer.item + if (answer.type != DbAnswerType.ITEM || item == null) { + answerSet.status = DbVerdictStatus.WRONG + return + } + + /* Perform item validation. */ + if (item.id !in this.itemIds) { + return + } } + /* If code reaches this point, the [DbAnswerSet] is correct. */ + answerSet.status = DbVerdictStatus.CORRECT } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt index 492e82dc7..9ca058e1e 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt @@ -2,6 +2,7 @@ package dev.dres.run.validation import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.AnswerSetValidator +import kotlinx.dnq.query.iterator /** * A [AnswerSetValidator] class that checks, if a submission is correct based on the target segment and the @@ -9,47 +10,48 @@ import dev.dres.run.validation.interfaces.AnswerSetValidator * * @author Luca Rossetto * @author Ralph Gasser - * @version 1.1.0 + * @version 2.0.0 */ class TemporalContainmentAnswerSetValidator(private val targetSegment: TransientMediaSegment) : AnswerSetValidator { override val deferring: Boolean get() = false - override fun validate(answerSet: AnswerSet) { + /** + * Validates the [DbAnswerSet] and updates its [DBVerdictStatus]. + * + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. + */ + override fun validate(answerSet: DbAnswerSet) { - answerSet.answers().forEach { answer -> + /* Basically, we assume that the DBAnswerSet is wrong. */ + answerSet.status = DbVerdictStatus.WRONG + /* Now we check all the answers. */ + for (answer in answerSet.answers) { /* Perform sanity checks. */ - if (answer.type() != AnswerType.TEMPORAL) { - answerSet.status(VerdictStatus.WRONG) - return@forEach - } - + val item = answer.item val start = answer.start val end = answer.end - val item = answer.item - if (item == null || start == null || end == null || start > end) { - answerSet.status(VerdictStatus.WRONG) - return@forEach - + if (answer.type != DbAnswerType.TEMPORAL || item == null || start == null || end == null || start > end) { + return } /* Perform item validation. */ - if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { - answerSet.status(VerdictStatus.WRONG) - return@forEach + if (item.mediaItemId != this.targetSegment.first.mediaItemId) { + return } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() - if (outer.first <= start && outer.second >= end) { - answerSet.status(VerdictStatus.CORRECT) - } else { - answerSet.status(VerdictStatus.WRONG) + if (outer.first > start || outer.second < end) { + return } - } + /* If code reaches this point, the [DbAnswerSet] is correct. */ + answerSet.status = DbVerdictStatus.CORRECT } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt index 557e48f55..08d7fec3a 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt @@ -1,10 +1,10 @@ package dev.dres.run.validation -import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.MediaItem import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.AnswerSetValidator +import kotlinx.dnq.query.iterator /** */ typealias TransientMediaSegment = Pair @@ -15,51 +15,46 @@ typealias TransientMediaSegment = Pair * * @author Luca Rossetto * @author Ralph Gasser - * @version 1.1.0 + * @version 2.0.0 */ class TemporalOverlapAnswerSetValidator(private val targetSegment: TransientMediaSegment) : AnswerSetValidator { override val deferring: Boolean = false /** - * Validates a [DbSubmission] based on the target segment and the temporal overlap of the - * [DbSubmission] with the [DbTaskTemplate]. + * Validates the [DbAnswerSet] and updates its [DbVerdictStatus]. * - * @param submission The [DbSubmission] to validate. + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. */ - override fun validate(answerSet: AnswerSet) { - - answerSet.answers().forEach { answer -> + override fun validate(answerSet: DbAnswerSet) { + /* Basically, we assume that the DBAnswerSet is wrong. */ + answerSet.status = DbVerdictStatus.WRONG + /* Now we check all the answers. */ + for (answer in answerSet.answers) { /* Perform sanity checks. */ - if (answer.type() != AnswerType.TEMPORAL) { - answerSet.status(VerdictStatus.WRONG) - return@forEach - } - val start = answer.start val end = answer.end val item = answer.item - if (item == null || start == null || end == null || start > end) { - answerSet.status(VerdictStatus.WRONG) - return@forEach + if (answer.type != DbAnswerType.TEMPORAL || item == null || start == null || end == null || start > end) { + return } /* Perform item validation. */ - if (answer.item?.mediaItemId != this.targetSegment.first.mediaItemId) { - answerSet.status(VerdictStatus.WRONG) - return@forEach + if (item.id != this.targetSegment.first.mediaItemId) { + return } /* Perform temporal validation. */ val outer = this.targetSegment.second.toMilliseconds() - if ((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) { - answerSet.status(VerdictStatus.CORRECT) - } else { - answerSet.status(VerdictStatus.WRONG) + if (!((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end))) { + return } } - + /* If code reaches this point, the [DbAnswerSet] is correct. */ + answerSet.status = DbVerdictStatus.CORRECT } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt index e235b8187..129f09684 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TextAnswerSetValidator.kt @@ -2,13 +2,14 @@ package dev.dres.run.validation import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.AnswerSetValidator +import kotlinx.dnq.query.iterator /** - * A [AnswerSetValidator] class that valiadates textual submissions based on [Regex]. + * A [AnswerSetValidator] class that validates textual submissions based on [Regex]. * * @author Luca Rossetto * @author Ralph Gasser - * @version 1.1.0 + * @version 2.0.0 */ class TextAnswerSetValidator(targets: List) : AnswerSetValidator { @@ -26,44 +27,37 @@ class TextAnswerSetValidator(targets: List) : AnswerSetValidator { */ private val regex = targets.map { when { - it.startsWith("\\") && it.endsWith("\\") -> { - Regex(it.substring(1, it.length - 1), RegexOption.CANON_EQ) - } - - it.startsWith("\\") && it.endsWith("\\i") -> { - Regex(it.substring(1, it.length - 2), setOf(RegexOption.CANON_EQ, RegexOption.IGNORE_CASE)) - } - - else -> { - Regex(it, setOf(RegexOption.CANON_EQ, RegexOption.LITERAL)) - } + it.startsWith("\\") && it.endsWith("\\") -> Regex(it.substring(1, it.length - 1), RegexOption.CANON_EQ) + it.startsWith("\\") && it.endsWith("\\i") ->Regex(it.substring(1, it.length - 2), setOf(RegexOption.CANON_EQ, RegexOption.IGNORE_CASE)) + else -> Regex(it, setOf(RegexOption.CANON_EQ, RegexOption.LITERAL)) } } - override fun validate(answerSet: AnswerSet) { - - answerSet.answers().forEach { answer -> + /** + * Validates the [DbAnswerSet] and updates its [DBVerdictStatus]. + * + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. + */ + override fun validate(answerSet: DbAnswerSet) { + /* Basically, we assume that the DBAnswerSet is wrong. */ + answerSet.status = DbVerdictStatus.WRONG + /* Now we check all the answers. */ + for (answer in answerSet.answers) { /* Perform sanity checks. */ - if (answer.type() != AnswerType.TEXT) { - answerSet.status(VerdictStatus.WRONG) - return@forEach - } - - /* Perform text validation. */ val text = answer.text - if (text == null) { - answerSet.status(VerdictStatus.WRONG) - return@forEach + if (answer.type != DbAnswerType.TEXT || text == null) { + return } - if (regex.any { it matches text }) { - answerSet.status(VerdictStatus.CORRECT) - } else { - answerSet.status(VerdictStatus.WRONG) - return@forEach + if (!regex.any { it matches text }) { + return } } + /* If code reaches this point, the [DbAnswerSet] is correct. */ + answerSet.status = DbVerdictStatus.CORRECT } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt index 2ff0c5f13..17c5fb791 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/AnswerSetValidator.kt @@ -1,25 +1,27 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.AnswerSet +import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission /** * A validator class that checks, if a [DbSubmission] is correct. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 */ interface AnswerSetValidator { /** - * Validates the [AnswerSet] and updates its [VerdictStatus]. - * - * @param answerSet The [AnswerSet] to validate. + * Indicates whether this [AnswerSetValidator] needs to defer the validation to some later point in time or changes the status of a submission immediately. */ - fun validate(answerSet: AnswerSet) + val deferring: Boolean /** - * Indicates whether this [AnswerSetValidator] needs to defer the validation to some later point in time - * or changes the status of a submission immediately + * Validates the [DbAnswerSet] and updates its [DBVerdictStatus]. + * + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. */ - val deferring: Boolean + fun validate(answerSet: DbAnswerSet) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index a49a6a6d3..2b64d371c 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -7,8 +7,9 @@ import dev.dres.data.model.submissions.* * * This kind of [AnswerSetValidator] is inherently asynchronous. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.1.0 + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 */ interface JudgementValidator { /** unique id to identify the [JudgementValidator]*/ @@ -22,23 +23,23 @@ interface JudgementValidator { /** Returns true, if this [JudgementValidator] has open [DbSubmission]s. */ val hasOpen: Boolean - get() = open > 0 + get() = this.open > 0 /** - * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]' - * internal queue. If such an element exists, then the [DbSubmission] is returned alongside a - * unique token, that can be used to update the [DbSubmission]'s [DbVerdictStatus]. + * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]'s internal queue. + * + * If such an element exists, then the [DbSubmission] is returned alongside a unique token, that can be used to update the [DbSubmission]'s [DbVerdictStatus]. * * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. */ - fun next(queue: String): Pair? + fun next(): Pair? /** * Places a verdict for the [Submission] identified by the given token. * * @param token The token used to identify the [Submission]. - * @param verdict The verdict of the judge. + * @param verdict The [DbVerdictStatus] assigned by the judge. */ - fun judge(token: String, verdict: VerdictStatus) + fun judge(token: String, verdict: DbVerdictStatus) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index fbe8833d4..4e729492b 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,30 +1,35 @@ package dev.dres.run.validation.interfaces -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.DbAnswerSet +import dev.dres.data.model.submissions.DbVerdictStatus /** + * A [JudgementValidator] that can hand-off undecidable verdict to a public vote. * + * This kind of [AnswerSetValidator] is inherently asynchronous. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 */ interface VoteValidator : JudgementValidator { - - /** - * Indicates that this validator is currently active and accepting votes. - */ + /** Indicates that this validator is currently active and accepting votes. */ val isActive: Boolean - /** - * Current distribution of votes - */ + /** Current distribution of votes */ val voteCount: Map /** - * Places a verdict for the currently active Submission + * Places a verdict for the currently active vote. + * + * @param verdict The [DbVerdictStatus] of the vote. */ - fun vote(status: VerdictStatus) + fun vote(verdict: DbVerdictStatus) /** + * Returns the [DbAnswerSet] this [VoteValidator] is currently accepting votes for. * + * @return [DbAnswerSet] or null, if no vote is ongoing. */ - fun nextSubmissionToVoteOn() : AnswerSet? + fun current() : DbAnswerSet? } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index 9e264f744..b16911645 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,15 +1,11 @@ package dev.dres.run.validation.judged import dev.dres.data.model.submissions.* -import dev.dres.run.RunExecutor import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.AnswerSetValidator -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.query.query +import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -18,17 +14,13 @@ import kotlin.concurrent.read import kotlin.concurrent.write /** - * A validator class that checks, if a submission is correct based on a manual judgement by a user. - * + * An implementation of the [JudgementValidator] that checks, if a submission is correct based on a manual judgement by a user. * * @author Luca Rossetto * @author Ralph Gasser - * @version 1.1.0 + * @version 2.0.0 */ -open class BasicJudgementValidator( - knownCorrectRanges: Collection = emptyList(), - knownWrongRanges: Collection = emptyList() -) : AnswerSetValidator, JudgementValidator { +open class BasicJudgementValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList()) : AnswerSetValidator, JudgementValidator { companion object { private val counter = AtomicInteger() @@ -44,138 +36,131 @@ open class BasicJudgementValidator( /** Internal lock on relevant data structures. */ private val updateLock = ReentrantReadWriteLock() - /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ - private val queue: Queue = LinkedList() + /** Internal queue that keeps track of all the [AnswerSetId]s and associated [ItemRange]s that require judgement. */ + private val queue: Queue> = LinkedList() - /** Internal queue that keeps track of all the [DbAnswerSet]s in need of judgement. */ - private val queuedItemRanges: MutableMap> = HashMap() + /** Internal map of all [AnswerSetId]s and associated [ItemRange]s that have been retrieved by a judge and are pending a verdict. */ + private val waiting = HashMap>() - /** Internal map of all [AnswerSetId]s that have been retrieved by a judge and are pending a verdict. */ - private val waiting = HashMap() + /** Internal queue that keeps track of all the pending [ItemRange]s in need of judgement. */ + private val queuedItemRanges: MutableMap> = HashMap() /** Helper structure to keep track when a request needs to be re-scheduled */ private val timeouts = mutableListOf>() - /** Internal map of already judged [DbSubmission]s, independent of their source. */ - private val cache: MutableMap = ConcurrentHashMap() + /** Internal map of known [ItemRange]s to associated [DbVerdictStatus]. */ + private val cache: MutableMap = ConcurrentHashMap() init { - knownCorrectRanges.forEach { cache[it] = VerdictStatus.CORRECT } - knownWrongRanges.forEach { cache[it] = VerdictStatus.WRONG } - } - - private fun checkTimeOuts() = updateLock.write { - val now = System.currentTimeMillis() - val due = timeouts.filter { it.first <= now } - due.forEach { - val submission = waiting.remove(it.second) - if (submission != null) { - queue.offer(submission) - } - } - timeouts.removeAll(due) + knownCorrectRanges.forEach { this.cache[it] = DbVerdictStatus.CORRECT } + knownWrongRanges.forEach { this.cache[it] = DbVerdictStatus.WRONG } } /** Returns the number of [DbSubmission]s that are currently pending a judgement. */ override val pending: Int get() = updateLock.read { this.queue.size + this.waiting.size } + /** Returns the number of [DbAnswerSet]s pending judgement. */ override val open: Int - get() = updateLock.read { + get() = this.updateLock.read { checkTimeOuts() return this.queue.size } + /** True, if there are [DbAnswerSet]s pending judgement. */ override val hasOpen: Boolean get() = updateLock.read { checkTimeOuts() return this.queue.isNotEmpty() } - override fun validate(answerSet: AnswerSet) = this.updateLock.read { - + /** + * Validates the [DbAnswerSet]. For the [BasicJudgementValidator] this means that the [DbAnswerSet] is enqueued for judgement. + * + * Usually requires an ongoing transaction. + * + * @param answerSet The [DbAnswerSet] to validate. + */ + override fun validate(answerSet: DbAnswerSet) = this.updateLock.read { //only validate submissions which are not already validated - if (answerSet.status() != VerdictStatus.INDETERMINATE) { + if (answerSet.status != DbVerdictStatus.INDETERMINATE) { return@read } //check cache first - val itemRange = ItemRange(answerSet.answers().first()) //TODO reason about semantics + val itemRange = ItemRange(answerSet.answers.first()) //TODO reason about semantics val cachedStatus = this.cache[itemRange] if (cachedStatus != null) { - answerSet.status(cachedStatus) - } else if (itemRange !in queuedItemRanges.keys) { - updateLock.write { - this.queue.offer(answerSet.id) - answerSet.status(VerdictStatus.INDETERMINATE) - this.queuedItemRanges[itemRange] = mutableListOf(answerSet) + answerSet.status = cachedStatus + } else if (itemRange !in this.queuedItemRanges.keys) { + this.updateLock.write { + this.queue.offer(answerSet.id to itemRange) + this.queuedItemRanges[itemRange] = mutableListOf(answerSet.id) } } else { this.updateLock.write { - this.queuedItemRanges[itemRange]!!.add(answerSet) + this.queuedItemRanges[itemRange]!!.add(answerSet.id) } } - } /** - * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]' - * internal queue. If such an element exists, then the [DbSubmission] is returned alongside a - * unique token, that can be used to update the [DbSubmission]'s [DbVerdictStatus]. + * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]'s internal queue. + * + * If such an element exists, then the [DbAnswerSet] is returned alongside a unique token, that can be used to update + * the [DbAnswerSet]'s [DbVerdictStatus]. * - * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. + * @return [Pair] containing a string token and the [DbSubmission] that should be judged. Can be null! */ - override fun next(queue: String): Pair? = updateLock.write { + override fun next(): Pair? = this.updateLock.write { checkTimeOuts() - val nextAnswerSetId = this.queue.poll() ?: return@write null - val next = DbAnswerSet.query(DbAnswerSet::id eq nextAnswerSetId).firstOrNull() ?: return@write null + val next = this.queue.poll() ?: return@write null + val answerSet = DbAnswerSet.query(DbAnswerSet::id eq next.first).singleOrNull() ?: return@write null val token = UUID.randomUUID().toString() - this.waiting[token] = nextAnswerSetId + this.waiting[token] = next this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) - DbAuditLogger.prepareJudgement(next, this, token) - Pair(token, next) - + DbAuditLogger.prepareJudgement(answerSet, this, token) + token to answerSet } /** * Places a verdict for the [DbSubmission] identified by the given token. * + * Requires an ongoing transaction! + * * @param token The token used to identify the [DbSubmission]. * @param verdict The verdict of the judge. */ - override fun judge(token: String, verdict: VerdictStatus) { - processSubmission(token, verdict).status(verdict) + override fun judge(token: String, verdict: DbVerdictStatus) = this.updateLock.write { + this.judgeInternal(token, verdict) + Unit } /** + * Internal implementation for re-use of the judgement logic for re-use. * + * @param token The token used to identify the [DbSubmission]. + * @param verdict The verdict of the judge. */ - fun processSubmission(token: String, status: VerdictStatus): DbAnswerSet = this.updateLock.write { - - val nextAnswerSetId = this.waiting[token] + protected fun judgeInternal(token: String, verdict: DbVerdictStatus): AnswerSetId { + val next = this.waiting.remove(token) ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged - val answerSet = DbAnswerSet.query(DbAnswerSet::id eq nextAnswerSetId).firstOrNull() ?: throw IllegalStateException("DbAnswerSet with id '$nextAnswerSetId' not found") - - val itemRange = ItemRange(answerSet.answers().first()) //TODO reason about semantics - - //add to cache - this.cache[itemRange] = status - - //remove from waiting map - this.waiting.remove(token) - - //remove from queue set - val otherSubmissions = this.queuedItemRanges.remove(itemRange) - otherSubmissions?.forEach { it.status(status) } - - //trigger score update - RunExecutor.managerForId(answerSet.task.evaluation.evaluationId)?.reScore(answerSet.taskId) - - - return@write answerSet + /* Remove from queue set. */ + val otherSubmissions = this.queuedItemRanges.remove(next.second) ?: emptyList() + for ((i, answerSetId) in (otherSubmissions + next.first).withIndex()) { + val answerSet = DbAnswerSet.query(DbAnswerSet::id eq answerSetId).singleOrNull() + if (answerSet != null) { + answerSet.status = verdict + if (i == 0) { + this.cache[ItemRange(answerSet.answers.first())] = verdict //TODO reason about semantics + } + } + } + return next.first } + /** * Clears this [JudgementValidator] and all the associated queues and maps. */ @@ -183,4 +168,19 @@ open class BasicJudgementValidator( this.waiting.clear() this.queue.clear() } + + /** + * + */ + private fun checkTimeOuts() = this.updateLock.write { + val now = System.currentTimeMillis() + val due = timeouts.filter { it.first <= now } + due.forEach { + val submission = waiting.remove(it.second) + if (submission != null) { + queue.offer(submission) + } + } + timeouts.removeAll(due) + } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index 8d8fd977c..c8c36607b 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -1,15 +1,22 @@ package dev.dres.run.validation.judged -import dev.dres.data.model.submissions.AnswerSet -import dev.dres.data.model.submissions.DbAnswerSet -import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.submissions.VerdictStatus +import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.VoteValidator +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.singleOrNull import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read import kotlin.concurrent.write +/** + * An implementation of the [VoteValidator] that checks, if a submission is correct based on a manual judgement by a user followed by a public vote. + * + * @author Luca Rossetto + * @author Ralph Gasser + * @version 2.0.0 + */ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), private val minimumVotes: Int = defaultMinimimVotes, private val voteDifference: Int = defaultVoteDifference) : BasicJudgementValidator(knownCorrectRanges, knownWrongRanges), VoteValidator { constructor(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), parameters: Map): this( @@ -27,32 +34,77 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() private val defaultVoteDifference = 1 } - private val submissionQueue = ConcurrentLinkedQueue() - private val voteCountMap = ConcurrentHashMap() + /** Internal queue of [AnswerSetId]s that pend voting. */ + private val submissionQueue = ConcurrentLinkedQueue() + + /** Internal map that counts votes for [DbVerdictStatus] for the current vote. */ + private val voteCountMap = ConcurrentHashMap() + + /** Internal lock that mediates access to this [BasicVoteValidator]. */ private val updateLock = ReentrantReadWriteLock() override val isActive: Boolean - get() = submissionQueue.isNotEmpty() + get() = this.updateLock.read { this.submissionQueue.isNotEmpty() } override val voteCount: Map - get() = voteCountMap.mapKeys { it.toString() } + get() = this.updateLock.read { this.voteCountMap.mapKeys { it.toString() } } - override fun vote(status: VerdictStatus) = updateLock.write { - if (status == VerdictStatus.INDETERMINATE || status == VerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does + /** + * Places a vote for the current [DbAnswerSet]. + * + * Requires an ongoing transaction! + * + * @param verdict The [DbVerdictStatus] of the vote. + */ + override fun vote(verdict: DbVerdictStatus) = this.updateLock.write { + if (verdict == DbVerdictStatus.INDETERMINATE || verdict == DbVerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does return@write } - val verdict = this.submissionQueue.firstOrNull() ?: return@write - this.voteCountMap[status] = 1 + this.voteCountMap.getOrDefault(status, 0) + val answerSet = this.current() ?: return@write + this.voteCountMap[verdict] = 1 + this.voteCountMap.getOrDefault(verdict, 0) if (enoughVotes()){ val finalVerdict = this.voteCountMap.entries.maxByOrNull { it.value }!!.key - verdict.status(finalVerdict) + answerSet.status = finalVerdict this.submissionQueue.poll() this.voteCountMap.clear() } } + /** + * Dequeues the next [DbAnswerSet] to vote for. + * + * Requires an ongoing transaction. + * + * @return [DbAnswerSet] that requires voting. + */ + override fun current(): DbAnswerSet? = this.updateLock.read { + val answerSetId = this.submissionQueue.firstOrNull() + return DbAnswerSet.filter { it.id eq answerSetId }.singleOrNull() + } + + /** + * Places a verdict for the [DbSubmission] identified by the given token. Inherits basic logic from parent class + * but siphons undecidable entries to voting subsystem. + * + * Requires an ongoing transaction! + * + * @param token The token used to identify the [DbSubmission]. + * @param verdict The verdict of the judge. + */ + override fun judge(token: String, verdict: DbVerdictStatus) = this.updateLock.write { + val next = this.judgeInternal(token, verdict) + if (verdict == DbVerdictStatus.UNDECIDABLE) { + this.submissionQueue.add(next) + } + } + + /** + * Checks if enough votes have been gathered for the current round. + * + * @return True if enough votes have been gathered, false otherwise. + */ private fun enoughVotes() : Boolean { val sum = voteCountMap.values.sum() if (sum < minimumVotes) return false @@ -60,17 +112,4 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() val others = sum - max return max - others >= voteDifference } - - override fun nextSubmissionToVoteOn() = submissionQueue.firstOrNull() //TODO maybe add timeout mechanism? - - //siphon of undecidable submission from logic of super class - override fun judge(token: String, status: VerdictStatus) { - val verdict = super.processSubmission(token, status) - when (status){ - VerdictStatus.CORRECT, - VerdictStatus.WRONG -> verdict.status(status) - VerdictStatus.INDETERMINATE -> {} - VerdictStatus.UNDECIDABLE -> this.submissionQueue.add(verdict) - } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt index 4d31c2b20..f08627966 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/ItemRange.kt @@ -12,7 +12,7 @@ import dev.dres.data.model.submissions.DbAnswerType * @author Luca Rossetto * @version 2.0.0 */ -data class ItemRange(val element: String, val start: Long, val end: Long){ +data class ItemRange(val element: String, val start: Long, val end: Long) { constructor(item: DbMediaItem): this(item.id, 0, 0) constructor(item: DbMediaItem, start: Long, end: Long): this(item.id, start, end) constructor(answer: Answer): this(when (answer.type()){ @@ -20,24 +20,4 @@ data class ItemRange(val element: String, val start: Long, val end: Long){ AnswerType.TEMPORAL -> answer.item!!.mediaItemId AnswerType.TEXT -> answer.text!! }, answer.start ?: 0, answer.end ?: 0) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ItemRange - - if (element != other.element) return false - if (start != other.start) return false - if (end != other.end) return false - - return true - } - - override fun hashCode(): Int { - var result = element.hashCode() - result = 31 * result + start.hashCode() - result = 31 * result + end.hashCode() - return result - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 41ebcb5f7..ad3b60f53 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -7,14 +7,12 @@ import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.util.MimeTypeHelper import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.run.InteractiveRunManager import dev.dres.run.RunExecutor import dev.dres.run.RunManager import dev.dres.run.RunManagerStatus import io.javalin.http.Context import kotlinx.dnq.query.filter import kotlinx.dnq.query.flatMapDistinct -import kotlinx.dnq.query.isEmpty import kotlinx.dnq.query.isNotEmpty import java.io.File import java.nio.file.Files @@ -102,8 +100,7 @@ fun Context.userId(): UserId = AccessManager.userIdForSession(this.sessionToken( * * @return [EvaluationId] */ -fun Context.evaluationId(): EvaluationId - = this.pathParamMap()["evaluationId"] ?: throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", this) +fun Context.evaluationId(): EvaluationId = this.pathParamMap()["evaluationId"] ?: throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", this) /** diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 7990c17dd..d002227ef 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -4,6 +4,7 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.api.rest.types.evaluation.* +import dev.dres.api.rest.types.evaluation.submission.* import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 253a7e19e..fbb9d9b45 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -2,7 +2,7 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.api.rest.types.evaluation.* +import dev.dres.api.rest.types.evaluation.submission.* import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index 5681e6f2b..247f4a57e 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -2,7 +2,7 @@ package dres.run.score.scorer import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.ApiMediaType -import dev.dres.api.rest.types.evaluation.* +import dev.dres.api.rest.types.evaluation.submission.* import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable diff --git a/run_dump_2c07d0ad-4e7d-498a-8ae2-e95274b8ad03.json b/run_dump_2c07d0ad-4e7d-498a-8ae2-e95274b8ad03.json new file mode 100644 index 000000000..552d01923 --- /dev/null +++ b/run_dump_2c07d0ad-4e7d-498a-8ae2-e95274b8ad03.json @@ -0,0 +1 @@ +{"id":"2c07d0ad-4e7d-498a-8ae2-e95274b8ad03","name":"The Rabbit Competition 01","description":{"entity":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle":{},"queryEngine":{"store":{"entityLifecycle" \ No newline at end of file From f0325fabb460f22ca50b814f70ed8b8595e161fd Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 26 Jul 2023 20:28:59 +0200 Subject: [PATCH 386/498] Various minor fixes --- .../dres/api/cli/MediaCollectionCommand.kt | 223 +++--- .../types/evaluation/submission/ApiAnswer.kt | 20 +- .../evaluation/submission/ApiAnswerSet.kt | 22 +- .../submission/ApiClientAnswerRangeType.kt | 13 - .../evaluation/submission/ApiSubmission.kt | 17 +- .../run/score/scorer/AvsTaskScorerTest.kt | 308 ++++++-- .../run/score/scorer/KisTaskScorerTest.kt | 30 +- .../score/scorer/LegacyAvsTaskScorerTest.kt | 696 ++++++++++++++++-- 8 files changed, 1060 insertions(+), 269 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 5fc8cc287..d1a5ecafd 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -86,7 +86,11 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * */ - abstract inner class AbstractCollectionCommand(protected val store: TransientEntityStore, name: String, help: String) : + abstract inner class AbstractCollectionCommand( + protected val store: TransientEntityStore, + name: String, + help: String + ) : CliktCommand(name = name, help = help, printHelpOnEmptyArgs = true) { /** The [CollectionId] of the [DbMediaCollection] affected by this [AbstractCollectionCommand]. */ @@ -147,7 +151,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Delete(store: TransientEntityStore) : AbstractCollectionCommand(store, "delete", help = "Deletes a media collection.") { + inner class Delete(store: TransientEntityStore) : + AbstractCollectionCommand(store, "delete", help = "Deletes a media collection.") { override fun run() { this@MediaCollectionCommand.store.transactional { val collection = this.getCollection() @@ -164,7 +169,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Update(store: TransientEntityStore) : AbstractCollectionCommand(store, name = "update", help = "Updates an existing Collection") { + inner class Update(store: TransientEntityStore) : + AbstractCollectionCommand(store, name = "update", help = "Updates an existing Collection") { /** The new name for the [DbMediaCollection]. */ private val newName: String? by option("-n", "--name", help = "The new name of the collection") @@ -240,7 +246,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to show a [DbMediaCollection]'s [DbMediaItem]s in detail. */ - inner class Show (store: TransientEntityStore) : AbstractCollectionCommand(store,"show", help = "Lists the content of a media collection.") { + inner class Show(store: TransientEntityStore) : + AbstractCollectionCommand(store, "show", help = "Lists the content of a media collection.") { /** The property of the [DbMediaItem]s to sort by. */ private val sort by option( @@ -305,7 +312,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ - inner class Check (store: TransientEntityStore) : AbstractCollectionCommand(store, + inner class Check(store: TransientEntityStore) : AbstractCollectionCommand( + store, "check", help = "Checks if all the files in a media collection are present and accessible." ) { @@ -370,124 +378,125 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va override fun run() { - this.store.transactional { - /* Sanity check. */ - if (imageTypes.isEmpty() && videoTypes.isEmpty()) { - println("No file types specified.") - return@transactional - } - /* Find media collection. */ - val collection = this.getCollection() - if (collection == null) { - println("Collection not found.") - return@transactional - } - - val base = Paths.get(collection.path) - if (!Files.exists(base)) { - println("Failed to scan collection; '${collection.path}' does not exist.") - return@transactional - } + /* Sanity check. */ + if (imageTypes.isEmpty() && videoTypes.isEmpty()) { + println("No file types specified.") + return + } - if (!Files.isReadable(base)) { - println("Failed to scan collection; '${collection.path}' is not readable.") - return@transactional - } + /* Find media collection. */ + val collection = this.getCollection() + if (collection == null) { + println("Collection not found.") + return + } - if (!Files.isDirectory(base)) { - println("Failed to scan collection; '${collection.path}' is no directory.") - return@transactional - } + val base = this.store.transactional(true) { Paths.get(collection.path) } + if (!Files.exists(base)) { + println("Failed to scan collection; '${collection.path}' does not exist.") + return + } - /* Now scan directory. */ - val issues = mutableMapOf() - var fileCounter = 0 - Files.walk(base).filter { - Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) - }.asSequence().chunked(transactionChunkSize).forEach { list -> + if (!Files.isReadable(base)) { + println("Failed to scan collection; '${collection.path}' is not readable.") + return + } - this@MediaCollectionCommand.store.transactional { + if (!Files.isDirectory(base)) { + println("Failed to scan collection; '${collection.path}' is no directory.") + return + } - list.forEach { - val relativePath = it.relativeTo(base) - val exists = - DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty - if (!exists) { - try { - when (it.extension.lowercase()) { - in this.imageTypes -> { - println("Found image $it; analyzing...") - collection.items.add(DbMediaItem.new { - this.type = DbMediaType.IMAGE - this.name = it.fileName.nameWithoutExtension - this.location = relativePath.toString() - }) - } + /* Now scan directory. */ + val issues = mutableMapOf() + var fileCounter = 0 + Files.walk(base).filter { + Files.isRegularFile(it) && (it.extension in imageTypes || it.extension in videoTypes) + }.asSequence().chunked(transactionChunkSize).forEach { list -> + + this@MediaCollectionCommand.store.transactional { + + list.forEach { + val relativePath = it.relativeTo(base) + val exists = + DbMediaItem.query((DbMediaItem::collection eq collection) and (DbMediaItem::location eq relativePath.toString())).isNotEmpty + if (!exists) { + try { + when (it.extension.lowercase()) { + in this.imageTypes -> { + println("Found image $it; analyzing...") + collection.items.add(DbMediaItem.new { + this.type = DbMediaType.IMAGE + this.name = it.fileName.nameWithoutExtension + this.location = relativePath.toString() + }) + } - in videoTypes -> { - println("Found video $it; analyzing...") - val result = this.analyze(it).streams.first() - val fps = (result.rFrameRate - ?: result.avgFrameRate!!).toFloat() - val duration = result.getDuration(TimeUnit.MILLISECONDS) - .let { duration -> - if (duration != null) { - duration - } else { - println("Cannot read duration from file, counting frames") - val analysis = - this.analyze(it, countFrames = true) - val frames = - analysis.streams.first().nbReadFrames - println("Counted $frames frames") - ((frames * 1000) / fps).toLong() - } + in videoTypes -> { + println("Found video $it; analyzing...") + val result = this.analyze(it).streams.first() + val fps = (result.rFrameRate + ?: result.avgFrameRate!!).toFloat() + val duration = result.getDuration(TimeUnit.MILLISECONDS) + .let { duration -> + if (duration != null) { + duration + } else { + println("Cannot read duration from file, counting frames") + val analysis = + this.analyze(it, countFrames = true) + val frames = + analysis.streams.first().nbReadFrames + println("Counted $frames frames") + ((frames * 1000) / fps).toLong() } - - println("Found frame rate to be $fps frames per seconds and duration $duration ms") - collection.items.add(DbMediaItem.new { - this.type = DbMediaType.VIDEO - this.name = it.fileName.nameWithoutExtension - this.location = relativePath.toString() - this.durationMs = duration - this.fps = fps - }) - } + } + + println("Found frame rate to be $fps frames per seconds and duration $duration ms") + collection.items.add(DbMediaItem.new { + this.type = DbMediaType.VIDEO + this.name = it.fileName.nameWithoutExtension + this.location = relativePath.toString() + this.durationMs = duration + this.fps = fps + }) } - } catch (e: Throwable) { - this@MediaCollectionCommand.logger.error( - this@MediaCollectionCommand.logMarker, - "An error occurred with $it. Noting and skipping..." - ) - println("An error occurred with $it. Noting and skipping...") - issues[it] = e.stackTraceToString() } + } catch (e: Throwable) { + this@MediaCollectionCommand.logger.error( + this@MediaCollectionCommand.logMarker, + "An error occurred with $it. Noting and skipping..." + ) + println("An error occurred with $it. Noting and skipping...") + issues[it] = e.stackTraceToString() } - ++fileCounter - } - } + ++fileCounter + } } - if (issues.isNotEmpty()) { - val file = - File("issues-scan-${collection.name}-${System.currentTimeMillis()}.json") - println("There have been ${issues.size} issues while scanning. You might want to check them at ${file.path}") - val om = jacksonObjectMapper() - om.writeValue(file, issues) - println("done") - } - println("\nAdded $fileCounter elements to collection") + + } + if (issues.isNotEmpty()) { + val file = + File("issues-scan-${collection.name}-${System.currentTimeMillis()}.json") + println("There have been ${issues.size} issues while scanning. You might want to check them at ${file.path}") + val om = jacksonObjectMapper() + om.writeValue(file, issues) + println("done") } + println("\nAdded $fileCounter elements to collection") } + } /** * [CliktCommand] to delete [DbMediaItem]s. */ - inner class DeleteItem (store: TransientEntityStore) : AbstractCollectionCommand(store,"deleteItem", help = "Deletes media item(s).") { + inner class DeleteItem(store: TransientEntityStore) : + AbstractCollectionCommand(store, "deleteItem", help = "Deletes media item(s).") { /** The item ID matching the name of the [DbMediaItem] to delete. */ private val itemId: MediaId? by option("-ii", "--itemId", help = "ID of the media item.") @@ -537,7 +546,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to delete [DbMediaItem]s. */ - inner class AddItem (store: TransientEntityStore) : AbstractCollectionCommand(store,name = "add", help = "Adds a media item to a media collection.") { + inner class AddItem(store: TransientEntityStore) : + AbstractCollectionCommand(store, name = "add", help = "Adds a media item to a media collection.") { /** The [ApiMediaType] of the new [DbMediaItem]. */ private val type: ApiMediaType by option( @@ -589,7 +599,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to export a [DbMediaCollection]. */ - inner class Export (store: TransientEntityStore) : AbstractCollectionCommand(store,"export", help = "Exports a media collection into a CSV file.") { + inner class Export(store: TransientEntityStore) : + AbstractCollectionCommand(store, "export", help = "Exports a media collection into a CSV file.") { /** The output path for the export.. */ private val output: Path by option( @@ -631,7 +642,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to import a [DbMediaCollection]. */ - inner class Import (store: TransientEntityStore) : AbstractCollectionCommand(store,"import", help = "Imports a media collection from a CSV file.") { + inner class Import(store: TransientEntityStore) : + AbstractCollectionCommand(store, "import", help = "Imports a media collection from a CSV file.") { /** [Path] to the input file. */ private val input: Path by option( @@ -687,7 +699,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va * * Uses the VBS format. */ - inner class ImportSegments(store: TransientEntityStore) : AbstractCollectionCommand(store, + inner class ImportSegments(store: TransientEntityStore) : AbstractCollectionCommand( + store, "importSegments", "Imports the Segment information for the Items in a Collection from a CSV file" ) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt index 54e9b900a..d2078dac4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswer.kt @@ -1,9 +1,11 @@ package dev.dres.api.rest.types.evaluation.submission import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.data.model.submissions.Answer +import dev.dres.data.model.submissions.AnswerType /** - * The RESTful API equivalent for the type of an answer as submitted by the DRES endpoint. + * The RESTful API equivalent for the type of answer as submitted by the DRES endpoint. * * There is an inherent asymmetry between the answers received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). * @@ -16,14 +18,20 @@ data class ApiAnswer( val type: ApiAnswerType, /** For [ApiAnswer]s of type [ApiAnswerType.ITEM] or [ApiAnswerType.TEMPORAL]: The [ApiMediaItem] that is part of the [ApiAnswer]. */ - val item: ApiMediaItem?, + override val item: ApiMediaItem?, /** For [ApiAnswer]s of type [ApiAnswerType.TEXT]: The text that is part of this [ApiAnswer]. */ - val text: String?, + override val text: String?, /** For [ApiAnswer]s of type [ApiAnswerType.TEMPORAL]: Start of the segment in question in milliseconds that is part of this [ApiAnswer]. */ - val start: Long? = null, + override val start: Long? = null, /** For [ApiAnswer]s of type [ApiAnswerType.TEMPORAL]: Start of the segment in question in milliseconds that is part of this [ApiAnswer]. */ - val end: Long? = null -) + override val end: Long? = null +) : Answer { + override fun type(): AnswerType = when(type) { + ApiAnswerType.TEMPORAL -> AnswerType.TEMPORAL + ApiAnswerType.ITEM -> AnswerType.ITEM + ApiAnswerType.TEXT -> AnswerType.TEXT + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt index 5c1edc46b..b6e53a76a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation.submission +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.run.TaskId import dev.dres.data.model.submissions.* @@ -12,4 +13,23 @@ import dev.dres.data.model.submissions.* * @author Ralph Gasser * @version 1.0.0 */ -data class ApiAnswerSet(val id: AnswerSetId, val status: ApiVerdictStatus, val taskId: TaskId, val answers: List) +data class ApiAnswerSet( + override val id: AnswerSetId, + val status: ApiVerdictStatus, + override val taskId: TaskId, + val answers: List +) : AnswerSet { + + @get:JsonIgnore + override lateinit var submission: Submission + internal set + + override fun status(): VerdictStatus = when(this.status) { + ApiVerdictStatus.CORRECT -> VerdictStatus.CORRECT + ApiVerdictStatus.WRONG -> VerdictStatus.WRONG + ApiVerdictStatus.INDETERMINATE -> VerdictStatus.INDETERMINATE + ApiVerdictStatus.UNDECIDABLE -> VerdictStatus.UNDECIDABLE + } + + override fun answers(): Sequence = answers.asSequence() +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt deleted file mode 100644 index e27e642be..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerRangeType.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.dres.api.rest.types.evaluation.submission - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -enum class ApiClientAnswerRangeType { - SHOT, - FRAME, - TIMECODE, - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt index 2a1e83fb3..90a6011ef 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiSubmission.kt @@ -13,11 +13,18 @@ import dev.dres.data.model.submissions.* * @version 2.0.0 */ data class ApiSubmission( - val submissionId: SubmissionId, - val teamId: String, - val memberId: String, + override val submissionId: SubmissionId, + override val teamId: String, + override val memberId: String, val teamName: String, val memberName: String, - val timestamp: Long, + override val timestamp: Long, val answers: List = emptyList() - ) \ No newline at end of file + ) : Submission { + + init { + answers.forEach { it.submission = this } + } + override fun answerSets(): Sequence = answers.asSequence() + +} \ No newline at end of file diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index d002227ef..1204b2031 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -23,47 +23,59 @@ class AvsTaskScorerTest { private val dummyVideoItems: List private val maxPointsPerTask = 1000.0 private val penalty = 0.2 - private val scoreable = object: Scoreable { + private val scoreable = object : Scoreable { override val taskId: TaskId = "task1" override val teams: List = this@AvsTaskScorerTest.teams override val duration: Long = this@AvsTaskScorerTest.defaultTaskDuration override val started: Long = this@AvsTaskScorerTest.taskStartTime override val ended: Long? = null } + init { val collectionId = "testCollection" val list = mutableListOf() - for (i in 1..10){ - list.add(ApiMediaItem("video$i", "video $i", ApiMediaType.VIDEO, collectionId, "videos/$i", 10 * 60 * 1000 * i.toLong(), 24f)) + for (i in 1..10) { + list.add( + ApiMediaItem( + "video$i", + "video $i", + ApiMediaType.VIDEO, + collectionId, + "videos/$i", + 10 * 60 * 1000 * i.toLong(), + 24f + ) + ) } dummyVideoItems = list } - private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long, taskId: String) = listOf( - ApiAnswerSet( - "dummyId", - status, - taskId, - listOf( - ApiAnswer( - ApiAnswerType.TEMPORAL, - item, - null, - start, - end + private fun answerSets(status: ApiVerdictStatus, item: ApiMediaItem, start: Long, end: Long, taskId: String) = + listOf( + ApiAnswerSet( + "dummyId", + status, + taskId, + listOf( + ApiAnswer( + ApiAnswerType.TEMPORAL, + item, + null, + start, + end + ) ) ) ) - ) @BeforeEach - fun setup(){ + fun setup() { this.scorer = AvsTaskScorer(this.scoreable, penaltyConstant = penalty, maxPointsPerTask) } @Test @DisplayName("Three teams all without a submission. Expected score: 0.0") - fun noSubmissions(){ + fun noSubmissions() { val scores = this.scorer.scoreMap(emptySequence()) Assertions.assertEquals(0.0, scores[teams[0]]) Assertions.assertEquals(0.0, scores[teams[1]]) @@ -72,9 +84,17 @@ class AvsTaskScorerTest { @Test @DisplayName("Team One with a single correct submission. Expected score: 1000 (maxPointsPerTask)") - fun onlyTeamOneWithAllEqualsOneCorrect(){ + fun onlyTeamOneWithAllEqualsOneCorrect() { val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 1000, "task2" ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2") + ), ) val scores = this.scorer.scoreMap(subs) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -84,11 +104,35 @@ class AvsTaskScorerTest { @Test @DisplayName("All teams with exact same, correct submission. Expected score: 1000 each") - fun allTeamsWithAllEqualsOneCorrect(){ + fun allTeamsWithAllEqualsOneCorrect() { val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 1000, "task2"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 2000, "task2"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2"), taskStartTime + 3000, "task2") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2") + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task2") + ) ) val scores = this.scorer.scoreMap(subs) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -98,50 +142,218 @@ class AvsTaskScorerTest { @Test @DisplayName("Team One with 2 / 2 correct videos, Team Two with 1 / 2 correct videos, Team Three without submission") - fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo(){ + fun teamsWithVariousSubmissionsTwoOfTwoAndOneOfTwoAndNoneOfTwo() { val subs = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStartTime + 1000, "task3"), - ApiSubmission(teams[0], teams[0], "user1", "team2", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000, "task3"), taskStartTime + 2000, "task3"), - ApiSubmission(teams[1], teams[1], "user2", "team3", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3"), taskStartTime + 3000, "task3"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3") + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team2", + "user1", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 10_000, 20_000, "task3") + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team3", + "user2", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task3") + ), ) val scores = this.scorer.scoreMap(subs) Assertions.assertEquals(maxPointsPerTask, scores[teams[0]]) - Assertions.assertEquals(maxPointsPerTask/2.0, scores[teams[1]]) + Assertions.assertEquals(maxPointsPerTask / 2.0, scores[teams[1]]) Assertions.assertEquals(0.0, scores[teams[2]]) } @Test @DisplayName("Team One with 3/3 correct videos. Team Two with 2/3 correct (and one on the second attempt), Team Three with Brute Force (0 wrong, 1 wrong and 2 wrong") - fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce(){ + fun teamsWithVariousSubmissionsTeamOneAllTeamTwoOneWrongTeamThreeBruteForce() { val subs = sequenceOf( /* Team One: All correct */ - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStartTime + 1000, "task4"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000, "task4"), taskStartTime + 2000, "task4"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStartTime + 3000, "task4"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4") + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 20_000, 30_000, "task4") + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4") + ), /* Team Two: One correct, One correct with one wrong */ - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStartTime + 1000, "task4"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStartTime + 2000, "task4"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStartTime + 3000, "task4"), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4") + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4") + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4") + ), /* Team Three: Brute Force: (correct/wrong): v1: (1/0), v2: (1/1), v3: (1/2), v4: (0/3), v5: (0/3)*/ /* v1 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4"), taskStartTime + 1000, "task4"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000, "task4") + ), /* v2 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), taskStartTime + 2000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4"), taskStartTime + 3000, "task4"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[1], 10_000, 20_000, "task4"), + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 30_000, 40_000, "task4") + ), /* v3 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000, "task4"), taskStartTime + 4000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000, "task4"), taskStartTime + 5000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4"), taskStartTime + 6000, "task4"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 10_000, 20_000, "task4") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 5000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 20_000, 30_000, "task4") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 6000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000, "task4") + ), /* v4 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000, "task4"), taskStartTime + 7000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000, "task4"), taskStartTime + 8000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000, "task4"), taskStartTime + 9000, "task4"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 7000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000, "task4") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 8000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 20_000, 30_000, "task4") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 9000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 30_000, 40_000, "task4") + ), /* v5 */ - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000, "task4"), taskStartTime + 10000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000, "task4"), taskStartTime + 11000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000, "task4"), taskStartTime + 12000, "task4"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 10000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 10_000, 20_000, "task4") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 11000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 20_000, 30_000, "task4") + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 12000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[4], 30_000, 40_000, "task4") + ), ) val scores = this.scorer.scoreMap(subs) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index fbb9d9b45..665bfeb4c 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -75,9 +75,9 @@ class KisTaskScorerTest { @Test fun allWrong() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", wrongAnswer, taskStartTime + 1000, "task2"), - ApiSubmission(teams[1], teams[1], "user2", "team1", "user2", wrongAnswer, taskStartTime + 2000, "task2"), - ApiSubmission(teams[2], teams[2], "user3", "team1", "user3", wrongAnswer, taskStartTime + 3000, "task2") + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", taskStartTime + 1000, wrongAnswer), + ApiSubmission(teams[1], teams[1], "user2", "team1", "user2", taskStartTime + 2000, wrongAnswer), + ApiSubmission(teams[2], teams[2], "user3", "team1", "user3", taskStartTime + 3000, wrongAnswer) ) val scores = this.scorer.scoreMap(submissions) assertEquals(0.0, scores[teams[0]]) @@ -88,7 +88,7 @@ class KisTaskScorerTest { @Test fun immediatelyRight() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime, "task3"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", taskStartTime, correctAnswer), ) val scores = this.scorer.scoreMap(submissions) assertEquals(maxPointsPerTask, scores[teams[0]]) @@ -99,7 +99,7 @@ class KisTaskScorerTest { @Test fun rightAtTheEnd() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000), "task4"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", taskStartTime + (defaultTaskDuration * 1000), correctAnswer), ) val scores = this.scorer.scoreMap(submissions) assertEquals(maxPointsAtTaskEnd, scores[teams[0]]) @@ -108,7 +108,7 @@ class KisTaskScorerTest { @Test fun rightInTheMiddle() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task5"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", taskStartTime + (defaultTaskDuration * 1000 / 2), correctAnswer), ) val scores = this.scorer.scoreMap(submissions) assertEquals(maxPointsAtTaskEnd + (maxPointsPerTask - maxPointsAtTaskEnd) / 2, scores[teams[0]]) @@ -119,19 +119,19 @@ class KisTaskScorerTest { val submissions = sequenceOf( //incorrect submissions - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", wrongAnswer, taskStartTime + 1, "task6"), + ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", taskStartTime + 1, wrongAnswer), - ApiSubmission(teams[1], teams[1], "user2", "team2","user2", wrongAnswer, taskStartTime + 2, "task6"), - ApiSubmission(teams[1], teams[1], "user2", "team2","user2", wrongAnswer, taskStartTime + 3, "task6"), + ApiSubmission(teams[1], teams[1], "user2", "team2","user2", taskStartTime + 2, wrongAnswer), + ApiSubmission(teams[1], teams[1], "user2", "team2","user2", taskStartTime + 3, wrongAnswer), - ApiSubmission(teams[2], teams[2], "user3", "team3","user3", wrongAnswer, taskStartTime + 4, "task6"), - ApiSubmission(teams[2], teams[2], "user3", "team3","user3", wrongAnswer, taskStartTime + 5, "task6"), - ApiSubmission(teams[2], teams[2], "user3", "team3","user3", wrongAnswer, taskStartTime + 6, "task6"), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", taskStartTime + 4, wrongAnswer), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", taskStartTime + 5, wrongAnswer), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", taskStartTime + 6, wrongAnswer), //correct submissions at 1/2 the task time - ApiSubmission(teams[0], teams[0], "user1", "team1","user1", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), - ApiSubmission(teams[1], teams[1], "user2", "team2","user2", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), - ApiSubmission(teams[2], teams[2], "user3", "team3","user3", correctAnswer, taskStartTime + (defaultTaskDuration * 1000 / 2), "task6"), + ApiSubmission(teams[0], teams[0], "user1", "team1","user1", taskStartTime + (defaultTaskDuration * 1000 / 2), correctAnswer), + ApiSubmission(teams[1], teams[1], "user2", "team2","user2", taskStartTime + (defaultTaskDuration * 1000 / 2), correctAnswer), + ApiSubmission(teams[2], teams[2], "user3", "team3","user3", taskStartTime + (defaultTaskDuration * 1000 / 2), correctAnswer), ) val scores = this.scorer.scoreMap(submissions) diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index 247f4a57e..413716e56 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -17,10 +17,10 @@ class LegacyAvsTaskScorerTest { private lateinit var scorer: LegacyAvsTaskScorer private val teams = listOf("team-1", "team-2", "team-3") private val defaultTaskDuration = 5 * 60L - private val taskStartTime = System.currentTimeMillis() - 100_000 + private val taskStartTime = System.currentTimeMillis() - 100_000 private val dummyImageItems: List private val dummyVideoItems: List - private val scoreable = object: Scoreable { + private val scoreable = object : Scoreable { override val taskId: TaskId = "task1" override val teams: List = this@LegacyAvsTaskScorerTest.teams override val duration: Long = this@LegacyAvsTaskScorerTest.defaultTaskDuration @@ -99,9 +99,33 @@ class LegacyAvsTaskScorerTest { @Test fun allWrongSameImage() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000, "task2"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 2000, "task2"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 3000, "task2") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]) + ) ) val scores = this.scorer.scoreMap(submissions) assertEquals(0.0, scores[teams[0]]) @@ -112,9 +136,33 @@ class LegacyAvsTaskScorerTest { @Test fun allWrongDifferentImages() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]), taskStartTime + 1000, "task3"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[1]), taskStartTime + 2000, "task3"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[2]), taskStartTime + 3000, "task3") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[0]) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[1]) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[2]), + ) ) val scores = this.scorer.scoreMap(submissions) assertEquals(0.0, scores[teams[0]]) @@ -125,9 +173,33 @@ class LegacyAvsTaskScorerTest { @Test fun allSameImageAllCorrect() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task4"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 2000, "task4"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 3000, "task4") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]) + ) ) val scores = this.scorer.scoreMap(submissions) assertEquals(100.0, scores[teams[0]]) @@ -138,9 +210,33 @@ class LegacyAvsTaskScorerTest { @Test fun allDifferentImageAllCorrect() { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task5"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000, "task5"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000, "task5") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]) + ) ) val scores = this.scorer.scoreMap(submissions) assertEquals(100.0 / 3.0, scores[teams[0]]!!, 0.001) @@ -159,19 +255,91 @@ class LegacyAvsTaskScorerTest { */ //3 out of 4 correct - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task6"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]), taskStartTime + 2000, "task6"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]), taskStartTime + 3000, "task6"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[3]), taskStartTime + 4000, "task6"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[1]) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[2]) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[3]) + ), //1 out of 3 correct - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]), taskStartTime + 1000, "task6"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[4]), taskStartTime + 2000, "task6"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000, "task6"), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[0]) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[4]) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]) + ), //1 out of 2 correct - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[3]), taskStartTime + 2000, "task6"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]), taskStartTime + 3000, "task6"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyImageItems[3]) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.WRONG, dummyImageItems[5]) + ), ) @@ -212,9 +380,33 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task7"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 2000, "task7"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 3000, "task7") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ) ) val scores = this.scorer.scoreMap(submissions) @@ -229,9 +421,33 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task8"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 30_000, 40_000), taskStartTime + 2000, "task8"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 50_000, 60_000), taskStartTime + 3000, "task8") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 30_000, 40_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 50_000, 60_000) + ) ) val scores = this.scorer.scoreMap(submissions) @@ -246,9 +462,33 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task9"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000), taskStartTime + 2000, "task9"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[3], 50_000, 60_000), taskStartTime + 3000, "task9") + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 30_000, 40_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[3], 50_000, 60_000) + ) ) val scores = this.scorer.scoreMap(submissions) @@ -263,17 +503,89 @@ class LegacyAvsTaskScorerTest { val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task10"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task10"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task10"), - - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task10"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task10"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task10"), - - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task10"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000, "task10"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000, "task10"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000) + ), + + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000) + ), + + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000) + ), ) val scores = this.scorer.scoreMap(submissions) @@ -287,23 +599,119 @@ class LegacyAvsTaskScorerTest { val taskStartTime = System.currentTimeMillis() - 100_000 val submissions = sequenceOf( - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task11"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task11"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task11"), - - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task11"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 2000, "task11"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 3000, "task11"), - - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task11"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000), taskStartTime + 1000, "task11"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000), taskStartTime + 1000, "task11"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000) + ), + + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000) + ), + + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 11_000, 21_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 12_000, 22_000) + ), + + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000) + ), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 1000, "task11"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 2000, "task11"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 20_000), taskStartTime + 3000, "task11"), - - ) + ) val scores = this.scorer.scoreMap(submissions) assertEquals(85.71428571428571, scores[teams[0]]!!, 0.001) @@ -318,47 +726,183 @@ class LegacyAvsTaskScorerTest { val submissions = sequenceOf( //team 1 //gets merged to one - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task12"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000, "task12"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000, "task12"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000) + ), //plus 2 independent correct ones - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000, "task12"), - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 3_000, 4_000), taskStartTime + 5000, "task12"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000) + ), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 5000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[2], 3_000, 4_000) + ), //and an incorrect one directly next to it - ApiSubmission(teams[0], teams[0], "user1", "team1", "user1", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 4_001, 5_000), taskStartTime + 6000, "task12"), + ApiSubmission( + teams[0], + teams[0], + "user1", + "team1", + "user1", + taskStartTime + 6000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[2], 4_001, 5_000) + ), //c = 5, q(c) = 3, i = 1 //team 2 //the center part is missing, so it's counted as two in the quantization - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task12"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000, "task12"), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000) + ), //another correct one, same as team 1 - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000, "task12"), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000) + ), //and two wrong ones - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 6000, "task12"), - ApiSubmission(teams[1], teams[1], "user2", "team2", "user2", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 6000, "task12"), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 6000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000) + ), + ApiSubmission( + teams[1], + teams[1], + "user2", + "team2", + "user2", + taskStartTime + 6000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000) + ), //c = 3, q(c) = 3, i = 2 //team 3 - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000), taskStartTime + 1000, "task12"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000), taskStartTime + 2000, "task12"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000), taskStartTime + 3000, "task12"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 1000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 10_000, 20_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 2000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 20_001, 25_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 3000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[0], 25_001, 30_000) + ), //another correct one, same as team 1 - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000), taskStartTime + 4000, "task12"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.CORRECT, dummyVideoItems[1], 5_000, 6_000) + ), //and two wrong ones - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000), taskStartTime + 4000, "task12"), - ApiSubmission(teams[2], teams[2], "user3", "team3", "user3", answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000), taskStartTime + 4000, "task12"), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 0, 5_000) + ), + ApiSubmission( + teams[2], + teams[2], + "user3", + "team3", + "user3", + taskStartTime + 4000, + answerSets(ApiVerdictStatus.WRONG, dummyVideoItems[3], 10_000, 15_000) + ), //c = 4, q(c) = 2, i = 2 From 56e717c8d7284fa4d9c1d387cf8068719e2724d5 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Wed, 26 Jul 2023 20:46:40 +0200 Subject: [PATCH 387/498] Minor OpenApi fixes --- .../evaluation/submission/ApiAnswerSet.kt | 4 + .../submission/ApiClientSubmission.kt | 12 + doc/oas-client.json | 317 ++++++++++-------- doc/oas.json | 317 ++++++++++-------- 4 files changed, 364 insertions(+), 286 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt index b6e53a76a..33e446591 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt @@ -3,6 +3,7 @@ package dev.dres.api.rest.types.evaluation.submission import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.run.TaskId import dev.dres.data.model.submissions.* +import io.javalin.openapi.OpenApiIgnore /** * The RESTful API equivalent for the type of an answer set as submitted by the DRES endpoint. @@ -21,7 +22,10 @@ data class ApiAnswerSet( ) : AnswerSet { @get:JsonIgnore + @get:OpenApiIgnore override lateinit var submission: Submission + @JsonIgnore + @OpenApiIgnore internal set override fun status(): VerdictStatus = when(this.status) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt index e5d1de378..5a300cbb5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientSubmission.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation.submission +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId import dev.dres.data.model.submissions.DbSubmission @@ -7,6 +8,7 @@ import dev.dres.data.model.submissions.SubmissionId import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import dev.dres.run.exceptions.IllegalTeamIdException +import io.javalin.openapi.OpenApiIgnore import kotlinx.dnq.query.addAll import kotlinx.dnq.query.filter import kotlinx.dnq.query.single @@ -24,15 +26,25 @@ import java.util.UUID class ApiClientSubmission(val answerSets: List) { /** The [UserId] associated with the submission. Is usually added as contextual information by the receiving endpoint. */ + @JsonIgnore + @get:OpenApiIgnore + @set:OpenApiIgnore var userId: UserId? = null /** The [TeamId] associated with the submission. Is usually added as contextual information by the receiving endpoint. */ + @JsonIgnore + @get:OpenApiIgnore + @set:OpenApiIgnore var teamId: TeamId? = null /** The [SubmissionId] of this [ApiClientSubmission]. Typically generated by the receiving endpoint. */ + @JsonIgnore + @get:OpenApiIgnore val submissionId: SubmissionId = UUID.randomUUID().toString() /** The timestamp at which this [ApiClientSubmission] was received. Typically generated by the receiving endpoint.*/ + @JsonIgnore + @get:OpenApiIgnore val timestamp: Long = System.currentTimeMillis() /** diff --git a/doc/oas-client.json b/doc/oas-client.json index 275f06c58..4215d7c8b 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1225,12 +1225,12 @@ "/api/v2/submit/{evaluationId}" : { "post" : { "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", + "summary" : "Endpoint to accept submissions.", "operationId" : "postApiV2SubmitByEvaluationId", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "The ID of the evaluation the submission belongs to.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -1799,108 +1799,6 @@ "type" : "string", "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "id", "status", "taskId", "answers" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiClientAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "text" : { - "type" : "string" - }, - "itemName" : { - "type" : "string" - }, - "itemCollectionName" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - } - }, - "ApiClientAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswer" - } - } - }, - "required" : [ "answers" ] - }, - "ApiClientSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "answerSets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswerSet" - } - } - }, - "required" : [ "answerSets" ] - }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -1999,41 +1897,6 @@ }, "required" : [ "verdict" ] }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "evaluationId" : { - "type" : "string" - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] - }, "ApiSubmissionInfo" : { "type" : "object", "additionalProperties" : false, @@ -2149,10 +2012,6 @@ }, "required" : [ "teamId", "tasks" ] }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, "ApiViewerInfo" : { "type" : "object", "additionalProperties" : false, @@ -2253,6 +2112,156 @@ }, "required" : [ "name", "value" ] }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + } + }, + "required" : [ "id", "status", "taskId", "answers" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "itemName" : { + "type" : "string" + }, + "itemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "mediaItemId" : { + "type" : "string" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + }, + "answerSetId" : { + "type" : "string" + } + }, + "required" : [ "answers", "answerSetId" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, "ApiJudgement" : { "type" : "object", "additionalProperties" : false, @@ -2959,6 +2968,28 @@ }, "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, "RunProperties" : { "type" : "object", "additionalProperties" : false, diff --git a/doc/oas.json b/doc/oas.json index 847eb085f..ae118faba 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4062,12 +4062,12 @@ "/api/v2/submit/{evaluationId}" : { "post" : { "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", + "summary" : "Endpoint to accept submissions.", "operationId" : "postApiV2SubmitByEvaluationId", "parameters" : [ { "name" : "evaluationId", "in" : "path", - "description" : "The evaluation ID.", + "description" : "The ID of the evaluation the submission belongs to.", "required" : true, "deprecated" : false, "allowEmptyValue" : false, @@ -5369,108 +5369,6 @@ "type" : "string", "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "id", "status", "taskId", "answers" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiClientAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "text" : { - "type" : "string" - }, - "itemName" : { - "type" : "string" - }, - "itemCollectionName" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - } - }, - "ApiClientAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswer" - } - } - }, - "required" : [ "answers" ] - }, - "ApiClientSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "answerSets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswerSet" - } - } - }, - "required" : [ "answerSets" ] - }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -5569,41 +5467,6 @@ }, "required" : [ "verdict" ] }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "evaluationId" : { - "type" : "string" - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "answers", "timestamp", "evaluationId" ] - }, "ApiSubmissionInfo" : { "type" : "object", "additionalProperties" : false, @@ -5719,10 +5582,6 @@ }, "required" : [ "teamId", "tasks" ] }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, "ApiViewerInfo" : { "type" : "object", "additionalProperties" : false, @@ -5823,6 +5682,156 @@ }, "required" : [ "name", "value" ] }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + } + }, + "required" : [ "id", "status", "taskId", "answers" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "itemName" : { + "type" : "string" + }, + "itemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "mediaItemId" : { + "type" : "string" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + }, + "answerSetId" : { + "type" : "string" + } + }, + "required" : [ "answers", "answerSetId" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, "ApiJudgement" : { "type" : "object", "additionalProperties" : false, @@ -6529,6 +6538,28 @@ }, "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, "RunProperties" : { "type" : "object", "additionalProperties" : false, From 73d79b02492d829f2a28deea89a29bd89e36c5c8 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 21:32:49 +0200 Subject: [PATCH 388/498] Datapath config-configurable and scan command fix --- backend/src/main/kotlin/dev/dres/DRES.kt | 10 ++++++---- .../kotlin/dev/dres/api/cli/MediaCollectionCommand.kt | 10 +++++++--- .../kotlin/dev/dres/data/model/config/CacheConfig.kt | 2 +- .../main/kotlin/dev/dres/data/model/config/Config.kt | 6 ++++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 6b4d5e103..3d33efd71 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -48,7 +48,8 @@ object DRES { val APPLICATION_ROOT: Path = File(DRES::class.java.protectionDomain.codeSource.location.toURI()).toPath() /** The path to the data folder. Can be different from the application root if provided via command line argument */ - var DATA_ROOT: Path = APPLICATION_ROOT + lateinit var DATA_ROOT: Path + internal set /** Path to the directory that contains the external items. */ lateinit var EXTERNAL_ROOT: Path @@ -59,8 +60,9 @@ object DRES { internal set /** Path to the classpath directory that contains task type presets shipped with DRES. */ - val TASK_TYPE_PRESETS_LOCATION = "dres-type-presets" + const val TASK_TYPE_PRESETS_LOCATION = "dres-type-presets" + /** The config tloaded */ lateinit var CONFIG : Config internal set @@ -75,10 +77,9 @@ object DRES { try{ val configPath = Paths.get(args[0]) val config = Config.read(configPath) - DATA_ROOT = configPath.absolute().parent config }catch(e: Exception){ - println("ERROR: The config at ${Paths.get(args[0])} could not be loaded. See stacktrace for more information.") + println("ERROR: The config at ${Paths.get(args[0]).toAbsolutePath()} could not be loaded. See stacktrace for more information.") exitProcess(11) } } else { @@ -87,6 +88,7 @@ object DRES { EXTERNAL_ROOT = CONFIG.externalMediaLocation TASK_TYPE_PRESETS_EXTERNAL_LOCATION = CONFIG.presetsLocation + DATA_ROOT = CONFIG.dataPath println("Starting DRES (application: $APPLICATION_ROOT, data: $DATA_ROOT)") println("Initializing...") diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index d1a5ecafd..56000bdc3 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -386,14 +386,18 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va return } - /* Find media collection. */ - val collection = this.getCollection() + /* Find media collection and note base path */ + val collectionAndPathTuple = this.store.transactional(true) { + val collection = this.getCollection() + return@transactional collection to collection?.path + } + val collection = collectionAndPathTuple.first if (collection == null) { println("Collection not found.") return } - val base = this.store.transactional(true) { Paths.get(collection.path) } + val base = Paths.get(collectionAndPathTuple.second) if (!Files.exists(base)) { println("Failed to scan collection; '${collection.path}' does not exist.") return diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt b/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt index c595b2cdb..6f0a71eaf 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/CacheConfig.kt @@ -37,7 +37,7 @@ data class CacheConfig( val previewVideoMaxSize: Int = 480, /** The location of the cache, relative to APPLICATION_ROOT */ - val cachePath: Path = DRES.DATA_ROOT.resolve("cache"), + val cachePath: Path = DRES.APPLICATION_ROOT.resolve("cache"), ) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index 84e3a4f28..d7f05ca3f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -21,12 +21,14 @@ data class Config( val keystorePassword: String = "password", /** The [CacheConfig] */ val cache: CacheConfig = CacheConfig(), + /** Folder taht contains the database. Defaults to `$APPLICATION_ROOT/data`*/ + val dataPath: Path = DRES.APPLICATION_ROOT.resolve("data"), /** Folder that contains external media. Defaults to `$APPLICATION_ROOT/external` */ val externalMediaLocation : Path = DRES.APPLICATION_ROOT.resolve("external"), /** Folder that contains task type rpesets. Defaults to `$APPLICATION_ROOT/type-presets` */ val presetsLocation: Path = DRES.APPLICATION_ROOT.resolve("type-presets"), - /** Folder for event data. Defaults to `$DATA_ROOT/external` */ - val eventsPath: Path = DRES.DATA_ROOT.resolve("events"), + /** Folder for event data. Defaults to `$APPLICATION_ROOT/external` */ + val eventsPath: Path = DRES.APPLICATION_ROOT.resolve("events"), /** Event buffer retention time, defaults to 1 minute */ val eventBufferRetentionTime: Int = 60_000, ) { From 4968ef84a8710a0f2afd1c12f7de6bdf9344e1c4 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 21:40:09 +0200 Subject: [PATCH 389/498] Code cleanup and preventing exceptions due to db access outside of transaction --- .../dres/api/cli/MediaCollectionCommand.kt | 71 +++++++++---------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 56000bdc3..12bc2df4a 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -14,11 +14,9 @@ import com.github.kokorin.jaffree.StreamType import com.github.kokorin.jaffree.ffprobe.FFprobe import com.github.kokorin.jaffree.ffprobe.FFprobeResult import com.jakewharton.picnic.table -import dev.dres.DRES import dev.dres.api.rest.types.collection.ApiMediaType import dev.dres.data.model.config.Config import dev.dres.data.model.media.* -import dev.dres.data.model.media.time.TemporalPoint import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.slf4j.LoggerFactory @@ -51,17 +49,17 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va init { this.subcommands( Create(), - Delete(store), - Update(store), + Delete(), + Update(), List(), - Show(store), - Check(store), - Scan(store), - AddItem(store), - DeleteItem(store), - Export(store), - Import(store), - ImportSegments(store) + Show(), + Check(), + Scan(), + AddItem(), + DeleteItem(), + Export(), + Import(), + ImportSegments() ) } @@ -87,7 +85,6 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va * */ abstract inner class AbstractCollectionCommand( - protected val store: TransientEntityStore, name: String, help: String ) : @@ -151,8 +148,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Delete(store: TransientEntityStore) : - AbstractCollectionCommand(store, "delete", help = "Deletes a media collection.") { + inner class Delete : + AbstractCollectionCommand( "delete", help = "Deletes a media collection.") { override fun run() { this@MediaCollectionCommand.store.transactional { val collection = this.getCollection() @@ -169,8 +166,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to create a new [DbMediaCollection]. */ - inner class Update(store: TransientEntityStore) : - AbstractCollectionCommand(store, name = "update", help = "Updates an existing Collection") { + inner class Update : + AbstractCollectionCommand(name = "update", help = "Updates an existing Collection") { /** The new name for the [DbMediaCollection]. */ private val newName: String? by option("-n", "--name", help = "The new name of the collection") @@ -246,8 +243,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to show a [DbMediaCollection]'s [DbMediaItem]s in detail. */ - inner class Show(store: TransientEntityStore) : - AbstractCollectionCommand(store, "show", help = "Lists the content of a media collection.") { + inner class Show : + AbstractCollectionCommand("show", help = "Lists the content of a media collection.") { /** The property of the [DbMediaItem]s to sort by. */ private val sort by option( @@ -312,9 +309,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ - inner class Check(store: TransientEntityStore) : AbstractCollectionCommand( - store, - "check", + inner class Check : AbstractCollectionCommand( + "check", help = "Checks if all the files in a media collection are present and accessible." ) { override fun run() = this@MediaCollectionCommand.store.transactional(true) { @@ -346,8 +342,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to validate a [DbMediaCollection]'s [DbMediaItem]s. */ - inner class Scan(store: TransientEntityStore) : AbstractCollectionCommand( - store, + inner class Scan() : AbstractCollectionCommand( "scan", help = "Scans a collection directory and adds found items" ) { @@ -387,7 +382,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va } /* Find media collection and note base path */ - val collectionAndPathTuple = this.store.transactional(true) { + val collectionAndPathTuple = this@MediaCollectionCommand.store.transactional(true) { val collection = this.getCollection() return@transactional collection to collection?.path } @@ -399,17 +394,17 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va val base = Paths.get(collectionAndPathTuple.second) if (!Files.exists(base)) { - println("Failed to scan collection; '${collection.path}' does not exist.") + println("Failed to scan collection; '$base' does not exist.") return } if (!Files.isReadable(base)) { - println("Failed to scan collection; '${collection.path}' is not readable.") + println("Failed to scan collection; '$base' is not readable.") return } if (!Files.isDirectory(base)) { - println("Failed to scan collection; '${collection.path}' is no directory.") + println("Failed to scan collection; '$base' is no directory.") return } @@ -499,8 +494,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to delete [DbMediaItem]s. */ - inner class DeleteItem(store: TransientEntityStore) : - AbstractCollectionCommand(store, "deleteItem", help = "Deletes media item(s).") { + inner class DeleteItem : + AbstractCollectionCommand( "deleteItem", help = "Deletes media item(s).") { /** The item ID matching the name of the [DbMediaItem] to delete. */ private val itemId: MediaId? by option("-ii", "--itemId", help = "ID of the media item.") @@ -550,8 +545,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to delete [DbMediaItem]s. */ - inner class AddItem(store: TransientEntityStore) : - AbstractCollectionCommand(store, name = "add", help = "Adds a media item to a media collection.") { + inner class AddItem : + AbstractCollectionCommand( name = "add", help = "Adds a media item to a media collection.") { /** The [ApiMediaType] of the new [DbMediaItem]. */ private val type: ApiMediaType by option( @@ -603,8 +598,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to export a [DbMediaCollection]. */ - inner class Export(store: TransientEntityStore) : - AbstractCollectionCommand(store, "export", help = "Exports a media collection into a CSV file.") { + inner class Export : + AbstractCollectionCommand( "export", help = "Exports a media collection into a CSV file.") { /** The output path for the export.. */ private val output: Path by option( @@ -646,8 +641,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /** * [CliktCommand] to import a [DbMediaCollection]. */ - inner class Import(store: TransientEntityStore) : - AbstractCollectionCommand(store, "import", help = "Imports a media collection from a CSV file.") { + inner class Import : + AbstractCollectionCommand( "import", help = "Imports a media collection from a CSV file.") { /** [Path] to the input file. */ private val input: Path by option( @@ -703,8 +698,8 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va * * Uses the VBS format. */ - inner class ImportSegments(store: TransientEntityStore) : AbstractCollectionCommand( - store, + inner class ImportSegments() : AbstractCollectionCommand( + "importSegments", "Imports the Segment information for the Items in a Collection from a CSV file" ) { From 3c4347b4bbdc8f8ca57aac0fbfbc0a147c6ed1e0 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 22:00:34 +0200 Subject: [PATCH 390/498] #415 fixed run admin view team list --- frontend/src/app/run/run-admin-view.component.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 655aa47d0..63d7ddcd2 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -180,16 +180,17 @@ [style.color]="team.color" > {{ team.name }}'s logo -

{{ team.name }}

-

{{ team.users.length }} member(s)

-

+

{{ team.name }}

+

{{ team.users.length }} member(s)

+

• {{ user.username }}

From a71066d490a9184e1d9aace84b6ee86b45161138 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 22:09:05 +0200 Subject: [PATCH 391/498] #415 Fixed admin view active viewer list --- frontend/src/app/run/run-admin-view.component.html | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 63d7ddcd2..ceeb16606 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -68,13 +68,15 @@ -
- {{ viewer.viewersId }} + + {{viewer.ready ? 'link' : 'link_off'}} + +
+ {{ viewer.viewersId }}
-
+
User: {{ viewer.username }}, Address: {{ viewer.host }}
@@ -177,7 +179,6 @@ {{ team.name }}'s logo -

{{ team.name }}

+

{{ team.name }}

{{ team.users.length }} member(s)

From 0c5529f4053a078b5ffe4f680c229fbbf66244ae Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 26 Jul 2023 22:13:28 +0200 Subject: [PATCH 392/498] Cosmetics: Fixed padding for scoreboards --- .../competition-scoreboard-viewer.component.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html index 599c41444..dbaabf3bc 100644 --- a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html +++ b/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html @@ -1,8 +1,10 @@ - - Normalized Competition Scores - Scores of {{ currentTaskGroup | async }} - + + + Normalized Competition Scores + Scores of {{ currentTaskGroup | async }} + + Date: Thu, 27 Jul 2023 09:33:13 +0200 Subject: [PATCH 393/498] Updated the ApiClientAnswer and ApiClientAnswer class w.r.t. to field visibility via OpenAPI. --- .../evaluation/submission/ApiClientAnswer.kt | 24 ++++++++++--------- .../submission/ApiClientAnswerSet.kt | 6 ++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt index 02aa9520b..621ab378d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt @@ -19,11 +19,14 @@ data class ApiClientAnswer( /** The text that is part of this [ApiClientAnswer]. */ val text: String? = null, + /** The [MediaItemId] associated with the [ApiClientAnswer]. Is usually added as contextual information by the receiving endpoint. */ + val mediaItemId: MediaItemId? = null, + /** The name of the media item that is part of the answer. */ - val itemName: String? = null, + val mediaItemName: String? = null, /** The name of the collection the media item belongs to. */ - val itemCollectionName: String? = null, + val mediaItemCollectionName: String? = null, /** For temporal [ApiClientAnswer]s: Start of the segment in question in milliseconds. */ val start: Long? = null, @@ -32,8 +35,7 @@ data class ApiClientAnswer( val end: Long? = null, ) { - /** The [MediaItemId] associated with the [ApiClientAnswer]. Is usually added as contextual information by the receiving endpoint. */ - val mediaItemId: MediaItemId? = null + /** * Creates a new [DbAnswer] for this [ApiAnswer]. Requires an ongoing transaction. @@ -46,11 +48,11 @@ data class ApiClientAnswer( this@ApiClientAnswer.mediaItemId != null -> DbMediaItem.filter { it.id eq mediaItemId }.singleOrNull() ?: throw IllegalArgumentException("Could not find media item with ID ${this@ApiClientAnswer.mediaItemId}.") - this@ApiClientAnswer.itemName != null && this@ApiClientAnswer.itemCollectionName != null -> - DbMediaItem.filter { (it.name eq itemName) and (it.collection.name eq this@ApiClientAnswer.itemCollectionName) }.singleOrNull() - ?: throw IllegalArgumentException("Could not find media item with name '${this@ApiClientAnswer.itemName}' in collection '${this@ApiClientAnswer.itemCollectionName}'.") - this@ApiClientAnswer.itemName != null -> DbMediaItem.filter { it.name eq itemName}.singleOrNull() - ?: throw IllegalArgumentException("Could not find media item with name '${this@ApiClientAnswer.itemName}'. Maybe collection name is required.") + this@ApiClientAnswer.mediaItemName != null && this@ApiClientAnswer.mediaItemCollectionName != null -> + DbMediaItem.filter { (it.name eq mediaItemName) and (it.collection.name eq this@ApiClientAnswer.mediaItemCollectionName) }.singleOrNull() + ?: throw IllegalArgumentException("Could not find media item with name '${this@ApiClientAnswer.mediaItemName}' in collection '${this@ApiClientAnswer.mediaItemCollectionName}'.") + this@ApiClientAnswer.mediaItemName != null -> DbMediaItem.filter { it.name eq mediaItemName}.singleOrNull() + ?: throw IllegalArgumentException("Could not find media item with name '${this@ApiClientAnswer.mediaItemName}'. Maybe collection name is required.") else -> null } this.text = this@ApiClientAnswer.text @@ -64,8 +66,8 @@ data class ApiClientAnswer( * @return The [DbAnswerType] for this [ApiClientAnswer]. */ fun tryDetermineType() = when { - this.itemName != null && this.start != null && this.end != null -> DbAnswerType.TEMPORAL - this.itemName != null -> DbAnswerType.ITEM + this.mediaItemName != null && this.start != null && this.end != null -> DbAnswerType.TEMPORAL + this.mediaItemName != null -> DbAnswerType.ITEM this.text != null -> DbAnswerType.TEXT else -> throw IllegalArgumentException("Could not determine answer type for provided answer.") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt index ada2f82f4..8210ddb97 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswerSet.kt @@ -1,9 +1,11 @@ package dev.dres.api.rest.types.evaluation.submission +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.run.DbTask import dev.dres.data.model.submissions.AnswerSetId import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus +import io.javalin.openapi.OpenApiIgnore import kotlinx.dnq.query.addAll import kotlinx.dnq.query.filter import kotlinx.dnq.query.first @@ -20,7 +22,9 @@ import java.util.* data class ApiClientAnswerSet(var taskId: String? = null, val taskName: String? = null, val answers: List) { /** The [AnswerSetId] of this [ApiClientAnswerSet]. Typically generated by the receiving endpoint. */ - var answerSetId: AnswerSetId = UUID.randomUUID().toString() + @JsonIgnore + @get:OpenApiIgnore + val answerSetId: AnswerSetId = UUID.randomUUID().toString() /** * Creates a new [DbAnswerSet] for this [ApiClientAnswerSet]. From 97429ec71bb0a184dbf7ee711d7778319a7c3e02 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 27 Jul 2023 09:33:31 +0200 Subject: [PATCH 394/498] Fixed the LegacySubmissionHandler. --- .../submission/LegacySubmissionHandler.kt | 126 +++++++++--------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 235b1a4b8..a56e74bd5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -4,34 +4,27 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.evaluation.* import dev.dres.api.rest.types.evaluation.submission.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus -import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.media.* import dev.dres.data.model.media.time.TemporalPoint import dev.dres.data.model.run.RunActionContext -import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.* import dev.dres.mgmt.cache.CacheManager import dev.dres.run.InteractiveRunManager -import dev.dres.run.audit.DbAuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException -import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.slf4j.LoggerFactory -import java.util.* /** * An [GetRestHandler] used to process [DbSubmission]s. @@ -119,8 +112,10 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v override fun doGet(ctx: Context): SuccessfulSubmissionsStatus { /* Obtain run action context and parse submission. */ val rac = ctx.runActionContext() - val run = getEligibleRunManager(rac, ctx) - val submission = toSubmission(rac, run, ctx) + val (run, submission) = this.store.transactional(true) { + val run = getEligibleRunManager(rac, ctx) + run to toSubmission(rac, run, ctx) + } /* Post submission. */ try { @@ -174,73 +169,72 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v */ private fun toSubmission(rac: RunActionContext, runManager: InteractiveRunManager, ctx: Context): ApiClientSubmission { val map = ctx.queryParamMap() - return this.store.transactional(true) { - /* If text is supplied, it supersedes other parameters */ - val textParam = map[PARAMETER_NAME_TEXT]?.first() - val itemParam = map[PARAMETER_NAME_ITEM]?.first() - val answer = if (textParam != null) { - ApiClientAnswer(text = textParam) - } else if (itemParam != null) { - val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ - val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) - val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)).firstOrNull() - ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) - val range: Pair? = when { - map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { - val shot = map[PARAMETER_NAME_SHOT]?.first()!! - val time = item.segments.filter { it.name eq shot }.firstOrNull()?.range?.toMilliseconds()//TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) - ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) - time.first to time.second - } + /* If text is supplied, it supersedes other parameters */ + val textParam = map[PARAMETER_NAME_TEXT]?.first() + val itemParam = map[PARAMETER_NAME_ITEM]?.first() + + val answer = if (textParam != null) { + ApiClientAnswer(text = textParam) + } else if (itemParam != null) { + val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ + val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) + val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)).firstOrNull() + ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) + val range: Pair? = when { + map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { + val shot = map[PARAMETER_NAME_SHOT]?.first()!! + val time = item.segments.filter { it.name eq shot }.firstOrNull()?.range?.toMilliseconds()//TimeUtil.shotToTime(map[PARAMETER_NAME_SHOT]?.first()!!, item.segments.toList()) + ?: throw ErrorStatusException(400, "Shot '${item.name}.${map[PARAMETER_NAME_SHOT]?.first()!!}' not found.", ctx) + time.first to time.second + } - map.containsKey(PARAMETER_NAME_FRAME) && item.type == DbMediaType.VIDEO -> { - val fps = item.fps - ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") - val time = TemporalPoint.Frame( - map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() - ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), - fps - ) - if (mapToSegment) { - DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) - ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) - } else { - val ms = time.toMilliseconds() - ms to ms - } + map.containsKey(PARAMETER_NAME_FRAME) && item.type == DbMediaType.VIDEO -> { + val fps = item.fps + ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") + val time = TemporalPoint.Frame( + map[PARAMETER_NAME_FRAME]?.first()?.toIntOrNull() + ?: throw ErrorStatusException(400, "Parameter '$PARAMETER_NAME_FRAME' must be a number.", ctx), + fps + ) + if (mapToSegment) { + DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) + ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + } else { + val ms = time.toMilliseconds() + ms to ms } + } - map.containsKey(PARAMETER_NAME_TIMECODE) -> { - val fps = item.fps ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") - val time = - TemporalPoint.Millisecond(TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, fps) - ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) - ) - if (mapToSegment) { - DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) - ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) - } else { - val ms = time.toMilliseconds() - ms to ms - } + map.containsKey(PARAMETER_NAME_TIMECODE) -> { + val fps = item.fps ?: throw IllegalStateException("Missing media item fps information prevented mapping from frame number to milliseconds.") + val time = + TemporalPoint.Millisecond(TemporalPoint.Timecode.timeCodeToMilliseconds(map[PARAMETER_NAME_TIMECODE]?.first()!!, fps) + ?: throw ErrorStatusException(400, "'${map[PARAMETER_NAME_TIMECODE]?.first()!!}' is not a valid time code", ctx) + ) + if (mapToSegment) { + DbMediaSegment.findContaining(item, time)?.range?.toMilliseconds()//TimeUtil.timeToSegment(time, item.segments.toList()) + ?: throw ErrorStatusException(400, "No matching segments found for item '${item.name}'.", ctx) + } else { + val ms = time.toMilliseconds() + ms to ms } - else -> null } + else -> null + } - /* Assign information to submission. */ - if (range != null) { - ApiClientAnswer(itemName = itemParam, start = range.first, end = range.second) - } else { - ApiClientAnswer(itemName = itemParam) - } + /* Assign information to submission. */ + if (range != null) { + ApiClientAnswer(mediaItemName = itemParam, start = range.first, end = range.second) } else { - throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) + ApiClientAnswer(mediaItemName = itemParam) } - - /* Generate and return ApiClientSubmission. */ - ApiClientSubmission(listOf(ApiClientAnswerSet(answers = listOf(answer)))) + } else { + throw ErrorStatusException(404, "Required submission parameters are missing (content not set)!", ctx) } + + /* Generate and return ApiClientSubmission. */ + return ApiClientSubmission(listOf(ApiClientAnswerSet(answers = listOf(answer)))) } /** From 9eb9aaec29ed328f91eca93c2a012ceb4299de7f Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 27 Jul 2023 09:33:45 +0200 Subject: [PATCH 395/498] Refactoring. --- .../kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt | 4 +--- .../kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt | 2 +- .../dev/dres/run/filter/ValidTemporalSubmissionFilter.kt | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt index d33f1ad3d..e7dff629d 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/DuplicateSubmissionFilter.kt @@ -2,8 +2,6 @@ package dev.dres.run.filter import dev.dres.api.rest.types.evaluation.submission.ApiClientAnswerSet import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission -import dev.dres.data.model.run.DbTask -import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.filter.basics.AbstractSubmissionFilter @@ -50,7 +48,7 @@ class DuplicateSubmissionFilter : AbstractSubmissionFilter("Duplicate submission */ private fun isEquivalent(a: DbAnswerSet, b: ApiClientAnswerSet): Boolean { for (answer in a.answers) { - if (b.answers.find { it.text == answer.text && it.start == answer.start && it.end == answer.end && it.itemName == answer.item?.name } == null) { + if (b.answers.find { it.text == answer.text && it.start == answer.start && it.end == answer.end && it.mediaItemName == answer.item?.name } == null) { return false } } diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt index 6ffbec776..e4db895d1 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ValidItemSubmissionFilter.kt @@ -23,7 +23,7 @@ class ValidItemSubmissionFilter: AbstractSubmissionFilter("Submission does inclu override fun test(t: ApiClientSubmission): Boolean { for (answerSet in t.answerSets) { for (answer in answerSet.answers) { - if (answer.itemName == null) return false /* Check that a media item has been specified. */ + if (answer.mediaItemName == null) return false /* Check that a media item has been specified. */ if (answer.start != null) return false /* Check no start timestamp is contained. */ if (answer.end != null) return false /* Check no end timestamp is contained. */ if (answer.text != null) return false /* Check no text is contained. */ diff --git a/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt b/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt index 870a55879..4cce88375 100644 --- a/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt +++ b/backend/src/main/kotlin/dev/dres/run/filter/ValidTemporalSubmissionFilter.kt @@ -24,7 +24,7 @@ class ValidTemporalSubmissionFilter : AbstractSubmissionFilter("Submission does override fun test(t: ApiClientSubmission): Boolean { for (answerSet in t.answerSets) { for (answer in answerSet.answers) { - if (answer.itemName == null) return false /* Check that either a media item or a text has been specified. */ + if (answer.mediaItemName == null) return false /* Check that either a media item or a text has been specified. */ if (answer.start == null) return false /* Check that start timestamp is contained. */ if (answer.end == null) return false /* Check that end timestamp is contained. */ if (answer.start > answer.end) return false /* Check that start precedes end timestamp. */ From 59d4eb61253d887cfbd1747ff4fa06f8e7045ca3 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Thu, 27 Jul 2023 09:34:34 +0200 Subject: [PATCH 396/498] Fixed problem with data root folder. --- backend/src/main/kotlin/dev/dres/DRES.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 3d33efd71..da63fbd6c 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -169,7 +169,7 @@ object DRES { DbAnswerType, DbVerdictStatus, ) - val store = StaticStoreContainer.init(dbFolder = DATA_ROOT.resolve("data").toFile(), entityStoreName = "dres-db") + val store = StaticStoreContainer.init(dbFolder = DATA_ROOT.toFile(), entityStoreName = "dres-db") initMetaData(XdModel.hierarchy, store) return store } From 2bf36f274efa8d360652826c9c6b803e4fd6b833 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 27 Jul 2023 09:53:21 +0200 Subject: [PATCH 397/498] Dependency updates --- backend/build.gradle | 11 ++--- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 48 +++++++++++-------- .../dres/api/cli/MediaCollectionCommand.kt | 4 +- gradle.properties | 12 ++--- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index f80af0362..f8cee49c8 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -46,9 +46,8 @@ compileTestKotlin { } dependencies { - def javalin = '5.6.1' + def javalinOpenapi = '5.6.1' - def log4jVersion = '2.17.1' ///// Frontend files (produced by sub-project). implementation frontendClasspath(project(path: ":frontend", configuration: 'frontendFiles')) @@ -64,12 +63,12 @@ dependencies { implementation group: 'org.jetbrains.xodus', name: 'dnq', version: version_xodus_dnq ////// Javalin - implementation group: 'io.javalin', name: 'javalin', version: javalin + implementation group: 'io.javalin', name: 'javalin', version: version_javalin kapt("io.javalin.community.openapi:openapi-annotation-processor:$javalinOpenapi") implementation group: 'io.javalin.community.openapi', name: 'javalin-openapi-plugin', version: javalinOpenapi implementation group: 'io.javalin.community.openapi', name:'javalin-swagger-plugin', version: javalinOpenapi - implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: javalin + implementation group: 'io.javalin.community.ssl', name: 'ssl-plugin', version: version_javalin ////// Bcrypt implementation group: 'org.mindrot', name: 'jbcrypt', version: version_bcrypt @@ -79,7 +78,7 @@ dependencies { implementation group: 'org.jline', name: 'jline-terminal-jna', version: version_jline3 implementation group: 'org.jline', name: 'jline-reader', version: version_jline3 implementation group: 'org.jline', name: 'jline-builtins', version: version_jline3 - implementation group: 'com.github.ajalt', name: 'clikt', version: version_clikt + implementation group: 'com.github.ajalt.clikt', name: 'clikt', version: version_clikt implementation group: 'com.jakewharton.picnic', name: 'picnic', version: version_picnic ///// Fuel @@ -91,8 +90,6 @@ dependencies { ////// Jaffree ffmpeg wrapper implementation group: 'com.github.kokorin.jaffree', name: 'jaffree', version: version_jaffree - ////// Cache - implementation group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.8.6' ////// Log4J implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: version_log4j diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index 492ce4b7a..ed861aaad 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -1,12 +1,11 @@ package dev.dres.api.cli -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.core.NoOpCliktCommand -import com.github.ajalt.clikt.core.context -import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.output.CliktHelpFormatter +import com.github.ajalt.clikt.core.* import com.github.ajalt.clikt.output.HelpFormatter +import com.github.ajalt.clikt.output.MordantHelpFormatter +import com.github.ajalt.mordant.rendering.Widget +import com.github.ajalt.mordant.table.verticalLayout import dev.dres.data.model.config.Config import dev.dres.mgmt.cache.CacheManager import jetbrains.exodus.database.TransientEntityStore @@ -98,7 +97,8 @@ object Cli { break } if (line.lowercase() == "help") { - println(clikt.getFormattedHelp()) //TODO overwrite with something more useful in a cli context + println(clikt.getFormattedHelp()) + lineReader.printAbove(PROMPT) continue } if (line.isBlank()) { @@ -109,13 +109,14 @@ object Cli { execute(line) } catch (e: Exception) { when (e) { - is com.github.ajalt.clikt.core.NoSuchSubcommand -> println("command not found") - is com.github.ajalt.clikt.core.PrintHelpMessage -> println(e.command.getFormattedHelp()) - is com.github.ajalt.clikt.core.MissingParameter -> println(e.localizedMessage) - is com.github.ajalt.clikt.core.NoSuchOption -> println(e.localizedMessage) - is com.github.ajalt.clikt.core.UsageError -> println("invalid command: ${e.localizedMessage}") + is NoSuchSubcommand -> println("command not found") + is PrintHelpMessage -> println(e.context?.command?.getFormattedHelp()) + is MissingArgument -> println(e.localizedMessage) + is NoSuchOption -> println(e.localizedMessage) + is UsageError -> println("invalid command: ${e.localizedMessage}") else -> e.printStackTrace() } + lineReader.printAbove(PROMPT) } } catch (e: EndOfFileException) { System.err.println("Could not read from terminal due to EOF. If you're running DRES in Docker, try running the container in interactive mode.") @@ -161,7 +162,7 @@ object Cli { class DRESBaseCommand : NoOpCliktCommand(name = "dres") { init { - context { helpFormatter = CliHelpFormatter() } + context { helpFormatter = {CliHelpFormatter(it)} } } } @@ -180,17 +181,26 @@ object Cli { } } - class CliHelpFormatter : CliktHelpFormatter() { - override fun formatHelp( + class CliHelpFormatter(context: Context) : MordantHelpFormatter(context) { + + + + override fun collectHelpParts( + error: UsageError?, prolog: String, epilog: String, parameters: List, - programName: String - ) = buildString { - addOptions(parameters) - addArguments(parameters) - addCommands(parameters) + programName: String, + ): List = buildList { + if (error == null) { + if (prolog.isNotEmpty()) add(renderProlog(prolog)) + if (parameters.isNotEmpty()) add(renderParameters(parameters)) + if (epilog.isNotEmpty()) add(renderEpilog(epilog)) + } else { + add(renderError(parameters, error)) + } } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 12bc2df4a..192bf18ea 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -438,10 +438,10 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va val result = this.analyze(it).streams.first() val fps = (result.rFrameRate ?: result.avgFrameRate!!).toFloat() - val duration = result.getDuration(TimeUnit.MILLISECONDS) + val duration = result.duration .let { duration -> if (duration != null) { - duration + (duration.toDouble() * 1000.0).toLong() } else { println("Cannot read duration from file, counting frames") val analysis = diff --git a/gradle.properties b/gradle.properties index 74aebd7a0..92c5c1829 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,15 +8,15 @@ systemProp.https.protocols=TLSv1.1,TLSv1.2,TLSv1.3 # Dependency versions version_bcrypt=0.4 -version_clikt=2.8.0 -version_fastutil=8.5.9 +version_clikt=4.1.0 +version_fastutil=8.5.12 version_fuel=2.3.1 -version_jaffree=0.11.0 -version_javalin=5.1.2 -version_jline3=3.21.0 +version_jaffree=2022.06.03 +version_javalin=5.6.1 +version_jline3=3.22.0 version_junit=5.9.1 version_kotlin=1.8.10 -version_kotlin_csv=1.6.0 +version_kotlin_csv=1.9.1 version_log4j=2.20.0 version_picnic=0.6.0 version_xodus=2.0.1 From bce89002ebc097e3d2c04c683d86b8e75e6eed0f Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 27 Jul 2023 11:28:24 +0200 Subject: [PATCH 398/498] Simplified AuditLogger See #428 --- backend/src/main/kotlin/dev/dres/DRES.kt | 22 +- .../kotlin/dev/dres/api/cli/AuditCommand.kt | 106 -------- .../src/main/kotlin/dev/dres/api/cli/Cli.kt | 1 - .../main/kotlin/dev/dres/api/rest/RestApi.kt | 8 - .../handler/audit/AbstractAuditLogHandler.kt | 21 -- .../handler/audit/GetAuditLogInfoHandler.kt | 31 --- .../handler/audit/ListAuditLogsHandler.kt | 56 ---- .../audit/ListAuditLogsInRangeHandler.kt | 51 ---- .../evaluation/admin/AdjustDurationHandler.kt | 7 +- .../admin/OverrideAnswerSetVerdictHandler.kt | 6 +- .../admin/StartEvaluationHandler.kt | 7 +- .../evaluation/admin/StartTaskHandler.kt | 7 +- .../evaluation/admin/StopEvaluationHandler.kt | 7 +- .../evaluation/admin/StopTaskHandler.kt | 7 +- .../handler/judgement/PostJudgementHandler.kt | 7 +- .../handler/submission/SubmissionHandler.kt | 6 +- .../api/rest/handler/system/LoginHandler.kt | 6 +- .../api/rest/handler/system/LogoutHandler.kt | 6 +- .../api/rest/types/audit/ApiAuditLogEntry.kt | 22 -- .../api/rest/types/audit/ApiAuditLogSource.kt | 22 -- .../api/rest/types/audit/ApiAuditLogType.kt | 33 --- .../dres/api/rest/types/audit/AuditLogInfo.kt | 7 - .../dres/api/rest/types/audit/AuditLogPage.kt | 16 -- .../dres/data/model/audit/DbAuditLogEntry.kt | 61 ----- .../dres/data/model/audit/DbAuditLogSource.kt | 33 --- .../dres/data/model/audit/DbAuditLogType.kt | 45 --- .../dev/dres/data/model/config/Config.kt | 4 +- .../run/InteractiveAsynchronousRunManager.kt | 9 +- .../run/InteractiveSynchronousRunManager.kt | 8 +- .../dev/dres/run/audit/AuditLogEntry.kt | 97 +++++++ .../dev/dres/run/audit/AuditLogSource.kt | 7 + .../kotlin/dev/dres/run/audit/AuditLogger.kt | 256 ++++++++++++++++++ .../dev/dres/run/audit/DbAuditLogger.kt | 223 --------------- .../run/eventstream/EventStreamProcessor.kt | 3 +- .../dev/dres/run/eventstream/StreamEvent.kt | 13 +- .../judged/BasicJudgementValidator.kt | 4 +- doc/oas-client.json | 80 +----- doc/oas.json | 226 +--------------- 38 files changed, 434 insertions(+), 1097 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt delete mode 100644 backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/audit/AuditLogSource.kt create mode 100644 backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt delete mode 100644 backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index da63fbd6c..368cb239c 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -6,9 +6,6 @@ import dev.dres.api.rest.RestApi import dev.dres.data.model.config.Config import dev.dres.data.model.admin.DbRole import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.audit.DbAuditLogEntry -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.audit.DbAuditLogType import dev.dres.data.model.media.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.* @@ -20,6 +17,7 @@ import dev.dres.data.model.run.* import dev.dres.data.model.submissions.* import dev.dres.mgmt.cache.CacheManager import dev.dres.run.RunExecutor +import dev.dres.run.audit.AuditLogger import dev.dres.run.eventstream.EventStreamProcessor import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.XdModel @@ -28,7 +26,6 @@ import kotlinx.dnq.util.initMetaData import java.io.File import java.nio.file.Path import java.nio.file.Paths -import kotlin.io.path.absolute import kotlin.system.exitProcess @@ -55,6 +52,10 @@ object DRES { lateinit var EXTERNAL_ROOT: Path internal set + /** Path to the directory that contains the audit log. */ + lateinit var AUDIT_LOG_ROOT: Path + internal set + /** Path to the directory that contains task type presets. */ lateinit var TASK_TYPE_PRESETS_EXTERNAL_LOCATION: Path internal set @@ -62,7 +63,7 @@ object DRES { /** Path to the classpath directory that contains task type presets shipped with DRES. */ const val TASK_TYPE_PRESETS_LOCATION = "dres-type-presets" - /** The config tloaded */ + /** The config loaded */ lateinit var CONFIG : Config internal set @@ -89,12 +90,13 @@ object DRES { EXTERNAL_ROOT = CONFIG.externalMediaLocation TASK_TYPE_PRESETS_EXTERNAL_LOCATION = CONFIG.presetsLocation DATA_ROOT = CONFIG.dataPath + AUDIT_LOG_ROOT = CONFIG.auditLocation println("Starting DRES (application: $APPLICATION_ROOT, data: $DATA_ROOT)") println("Initializing...") /* Initialize Xodus based data store. */ - val store = this.prepareDatabase(CONFIG) + val store = this.prepareDatabase() /* Initialize the global Cache Manager. */ val global = CacheManager(CONFIG, store) @@ -122,18 +124,14 @@ object DRES { RestApi.stop() RunExecutor.stop() EventStreamProcessor.stop() + AuditLogger.stop() } /** * Loads and prepares the database. - * - * @param config The [Config] */ - private fun prepareDatabase(config: Config): TransientEntityStore { + private fun prepareDatabase(): TransientEntityStore { XdModel.registerNodes( - DbAuditLogEntry, - DbAuditLogSource, - DbAuditLogType, DbConfiguredOption, DbEvaluation, DbEvaluationStatus, diff --git a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt deleted file mode 100644 index 2f6640fd4..000000000 --- a/backend/src/main/kotlin/dev/dres/api/cli/AuditCommand.kt +++ /dev/null @@ -1,106 +0,0 @@ -package dev.dres.api.cli - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.core.NoOpCliktCommand -import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.parameters.options.convert -import com.github.ajalt.clikt.parameters.options.default -import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.required -import dev.dres.data.model.audit.DbAuditLogEntry -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.* -import org.joda.time.DateTime -import org.joda.time.format.DateTimeFormat -import java.io.File - - -class AuditCommand(private val store: TransientEntityStore) : NoOpCliktCommand(name = "audit") { - - init { - subcommands(Show(), Export()) - } - - inner class Show() : - CliktCommand(name = "show", help = "Shows latest audit log entries", printHelpOnEmptyArgs = false) { - - private val number: Int by option("-n", "--number", help = "Number of audit log entries to show") - .convert { it.toInt() } - .default(10) - - override fun run() { - this@AuditCommand.store.transactional(readonly = true) { - DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = false) - .take(number).asSequence().forEach { entry -> - println("${entry.timestamp}: (${entry.type.description}@${entry.source.description}) user: ${entry.userId}, evaluation: ${entry.evaluationId}, task: ${entry.taskId}, description: ${entry.description}") - } - } - } - } - - inner class Export() : CliktCommand( - name = "export", - help = "Exports audit log entries from within a specified time frame to a file as one JSON object per line", - printHelpOnEmptyArgs = true - ) { - - /** The output path for the export. */ - private val output: File by option( - "-o", - "--output", - help = "Path of the file the media collection should to be exported to." - ).convert { File(it) }.required() - - private val pattern = DateTimeFormat.forPattern("dd.MM.yyyy-HH:mm:ss") - - private val startTimeStamp: DateTime by option( - "--from", - help = "start time stamp from which to export in the format dd.MM.yyyy-HH:mm:ss" - ).convert { - DateTime.parse(it, pattern) - }.default(DateTime(0)) - - private val endTimeStamp: DateTime by option( - "--to", - help = "end time stamp from which to export in the format dd.MM.yyyy-HH:mm:ss" - ).convert { - DateTime.parse(it, pattern) - }.default(DateTime(Long.MAX_VALUE)) - - private val evaluationId: String? by option( - "-e", "--evaluation", - help = "Optional evaluation Id" - ) - - override fun run() { - val writer = output.printWriter(Charsets.UTF_8) - val mapper = jacksonObjectMapper() - - var counter = 0 - - this@AuditCommand.store.transactional(readonly = true) { - val baseQuery = DbAuditLogEntry.query((DbAuditLogEntry::timestamp ge startTimeStamp) and (DbAuditLogEntry::timestamp le endTimeStamp)) - if(evaluationId != null) { - baseQuery.query(DbAuditLogEntry::evaluationId eq evaluationId!!) - } else { - baseQuery - } - .sortedBy(DbAuditLogEntry::timestamp, asc = true) - .asSequence().forEach { - writer.println(mapper.writeValueAsString(it.toApi())) - ++counter - } - } - - writer.flush() - writer.close() - - println("wrote $counter entries to ${output.absolutePath}") - - } - - } - - -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt index ed861aaad..74bf77078 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/Cli.kt @@ -48,7 +48,6 @@ object Cli { EvaluationCommand(store), OpenApiCommand(), ExecutionCommand(), - AuditCommand(store), ConfigCommand() ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 36ca3f3a4..7802e2614 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -1,11 +1,8 @@ package dev.dres.api.rest -import GetAuditLogInfoHandler import GetTaskHintHandler import dev.dres.DRES import dev.dres.api.rest.handler.* -import dev.dres.api.rest.handler.audit.ListAuditLogsHandler -import dev.dres.api.rest.handler.audit.ListAuditLogsInRangeHandler import dev.dres.api.rest.handler.collection.* import dev.dres.api.rest.handler.download.EvaluationDownloadHandler import dev.dres.api.rest.handler.download.EvaluationTemplateDownloadHandler @@ -192,11 +189,6 @@ object RestApi { PostVoteHandler(store), JudgementStatusHandler(store), - // Audit Log - GetAuditLogInfoHandler(store), - ListAuditLogsInRangeHandler(store), - ListAuditLogsHandler(store), - // Status CurrentTimeHandler(), InfoHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt deleted file mode 100644 index fcc345f03..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/AbstractAuditLogHandler.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.dres.api.rest.handler.audit - -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.handler.AccessManagedRestHandler -import dev.dres.api.rest.handler.RestHandler -import dev.dres.api.rest.handler.collection.AbstractCollectionHandler -import io.javalin.security.RouteRole -import jetbrains.exodus.database.TransientEntityStore - -/** - * - * @author Loris Sauter - * @version 2.0.0 - */ -abstract class AbstractAuditLogHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { - /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ - override val permittedRoles: Set = setOf(ApiRole.ADMIN) - - /** All [AbstractCollectionHandler]s are part of the v1 API. */ - override val apiVersion = "v2" -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt deleted file mode 100644 index eeaf44f21..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/GetAuditLogInfoHandler.kt +++ /dev/null @@ -1,31 +0,0 @@ -import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.audit.AbstractAuditLogHandler -import jetbrains.exodus.database.TransientEntityStore -import dev.dres.api.rest.types.audit.AuditLogInfo -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.audit.DbAuditLogEntry -import io.javalin.http.Context -import io.javalin.openapi.* -import kotlinx.dnq.query.lastOrNull -import kotlinx.dnq.query.size -import kotlinx.dnq.query.sortedBy - -class GetAuditLogInfoHandler(store: TransientEntityStore) : AbstractAuditLogHandler(store), GetRestHandler { - - override val route = "audit/info" - - @OpenApi( - summary = "Gives information about the audit log. Namely size and latest timestamp of known entries.", - path = "/api/v2/audit/info", - tags = ["Audit"], - operationId = OpenApiOperation.AUTO_GENERATE, - responses = [ - OpenApiResponse("200", [OpenApiContent(AuditLogInfo::class)], description = "The audit log info."), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user executes the call.") - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): AuditLogInfo = this.store.transactional(true) { - AuditLogInfo(size = DbAuditLogEntry.all().size(), latest = DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, true).lastOrNull()?.timestamp?.millis) - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt deleted file mode 100644 index 0414b351e..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsHandler.kt +++ /dev/null @@ -1,56 +0,0 @@ -package dev.dres.api.rest.handler.audit - -import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.audit.ApiAuditLogEntry -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.audit.DbAuditLogEntry -import dev.dres.utilities.extensions.toPathParamKey -import io.javalin.http.Context -import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.drop -import kotlinx.dnq.query.sortedBy -import kotlinx.dnq.query.take - -/** - * [AbstractAuditLogHandler] to list all [DbAuditLogEntry]. Allows for pagination. - * - * @author Loris Sauter - * @version 1.0.0 - */ -class ListAuditLogsHandler(store: TransientEntityStore) : AbstractAuditLogHandler(store), GetRestHandler> { - override val route = "audit/log/list/limit/${LIMIT_PARAM.toPathParamKey()}/${PAGE_INDEX_PARAM.toPathParamKey()}" - - companion object { - const val LIMIT_PARAM = "limit" - const val PAGE_INDEX_PARAM = "page" - const val DEFAULT_LIMIT = 500 - - const val TYPE_PARAM = "type" // Coupled to 1047, not yet ready - const val RELATION_PARAM = "when" - const val BEFORE_PARAM_KEY = "before" - const val UPTO_PARAM_KEY = "upto" - } - - @OpenApi( - summary = "Lists all audit logs matching the query.", - path = "/api/v2/audit/log/list/limit/{limit}/{page}", - pathParams = [ - OpenApiParam(LIMIT_PARAM, Int::class, "The maximum number of results. Default: 500", required = true), - OpenApiParam(PAGE_INDEX_PARAM, Int::class, "The page index offset, relative to the limit.", required = true) - ], - tags = ["Audit"], - operationId = OpenApiOperation.AUTO_GENERATE, - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { - val limit = (ctx.pathParamMap()[LIMIT_PARAM]?.toIntOrNull() ?: DEFAULT_LIMIT).coerceAtLeast(0) - val index = (ctx.pathParamMap()[PAGE_INDEX_PARAM]?.toIntOrNull() ?: 0).coerceAtLeast(0) - DbAuditLogEntry.all().sortedBy(DbAuditLogEntry::timestamp, asc = false).drop(index * limit).take(limit).asSequence().map { it.toApi() }.toList() - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt deleted file mode 100644 index 428465e8f..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/audit/ListAuditLogsInRangeHandler.kt +++ /dev/null @@ -1,51 +0,0 @@ -package dev.dres.api.rest.handler.audit - -import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.types.audit.ApiAuditLogEntry -import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.audit.DbAuditLogEntry -import io.javalin.http.Context -import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.* -import org.joda.time.DateTime - -/** - * [AbstractAuditLogHandler] to list all [DbAuditLogEntry] between two points in time. - * - * @author Loris Sauter - * @version 1.0.0 - */ -class ListAuditLogsInRangeHandler(store: TransientEntityStore): AbstractAuditLogHandler(store), GetRestHandler> { - - override val route = "audit/log/list/since/{since}/{upto}" - - @OpenApi( - summary = "Lists all audit logs matching the query", - path = "/api/v2/audit/log/list/since/{since}/{upto}", - pathParams = [ - OpenApiParam("since", Long::class, "Timestamp of the earliest audit log to include", required = true), - OpenApiParam("upto", Long::class, "Timestamp of the latest audit log to include.", required = true) - ], - operationId = OpenApiOperation.AUTO_GENERATE, - tags = ["Audit"], - responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)], description = "The audit logs"), - OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)], description = "Whenever a non-admin user starts the call") - ], - methods = [HttpMethod.GET] - ) - override fun doGet(ctx: Context): List { - val since = DateTime(ctx.pathParamMap()["since"]?.toLongOrNull() ?: 0L) - val upto = DateTime(ctx.pathParamMap()["upto"]?.toLongOrNull() ?:Long.MAX_VALUE) - - if (since < upto) throw ErrorStatusException(400, "Since must be smaller or equal to upto.", ctx) - - return this.store.transactional(true) { - DbAuditLogEntry.query((DbAuditLogEntry::timestamp gt since) and (DbAuditLogEntry::timestamp lt upto)).asSequence().map { - it.toApi() - }.toList() - } - } -} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 754d75062..71cb66206 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -4,11 +4,10 @@ import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -52,7 +51,7 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi return this.store.transactional { try { evaluationManager.adjustDuration(rac, duration) - DbAuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", DbAuditLogSource.REST, ctx.sessionToken()) + AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Duration for run $evaluationId was successfully adjusted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt index c7875f09c..e5f4b6044 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt @@ -6,10 +6,10 @@ import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse @@ -77,7 +77,7 @@ class OverrideAnswerSetVerdictHandler(store: TransientEntityStore): AbstractEval val verdictStatus = apiVerdictStatus.toDb() answerSet.status = verdictStatus - DbAuditLogger.overrideVerdict(answerSet, verdictStatus, DbAuditLogSource.REST, ctx.sessionToken()) + AuditLogger.overrideVerdict(answerSet.toApi(), verdictStatus.toApi(), AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Set status of AnswerSet '$answerSetId' to '${apiVerdictStatus.name}'") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index 6152d31f0..45e785eb0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -5,10 +5,9 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -47,7 +46,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd val rac = ctx.runActionContext() try { evaluationManager.start(rac) - DbAuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template, DbAuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) + AuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template.toApi(), AuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index a818683c1..cdcf2ac2e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -5,11 +5,10 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -56,7 +55,7 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand val rac = ctx.runActionContext() try { evaluationManager.startTask(rac) - DbAuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.taskId, evaluationManager.currentTaskTemplate(rac), DbAuditLogSource.REST, ctx.sessionToken()) + AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.taskId, evaluationManager.currentTaskTemplate(rac).toApi(), AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index 0de206062..c896f4c21 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -5,10 +5,9 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -48,7 +47,7 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi val rac = ctx.runActionContext() try { evaluationManager.end(rac) - DbAuditLogger.evaluationEnd(evaluationManager.id, DbAuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) + AuditLogger.evaluationEnd(evaluationManager.id, AuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully stopped.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 1c5df1258..93be1bb2a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -4,11 +4,10 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.evaluationId import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -47,7 +46,7 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl try { val task = evaluationManager.currentTaskTemplate(rac) evaluationManager.abortTask(rac) - DbAuditLogger.taskEnd(evaluationManager.id, task.id, DbAuditLogSource.REST, ctx.sessionToken()) + AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 9a4ae3b89..5409c3dc1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -6,10 +6,9 @@ import dev.dres.api.rest.types.judgement.ApiJudgementRequest import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.RunManager -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.sessionToken @@ -62,7 +61,7 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle } catch (ex: JudgementTimeoutException) { throw ErrorStatusException(408, ex.message!!, ctx) } - DbAuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict.toDb(), DbAuditLogSource.REST, ctx.sessionToken()) + AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict, AuditLogSource.REST, ctx.sessionToken()) } return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 576b64590..61815d995 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -8,11 +8,13 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.RunActionContext.Companion.runActionContext +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.filter.SubmissionRejectedException +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -72,7 +74,7 @@ class SubmissionHandler: PostRestHandler, AccessManagedRestHandle logger.info("Submission with unknown team id '${apiClientSubmission.teamId}' was received.") throw ErrorStatusException(400, "Run manager does not know the given teamId ${apiClientSubmission.teamId}.", ctx) } finally { - DbAuditLogSource + AuditLogger.submission(apiClientSubmission, rac.evaluationId!!, AuditLogSource.REST, ctx.sessionToken(), ctx.ip()) } /* Return status. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index acd2ede79..cdfca3f2c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -7,9 +7,9 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Password -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.mgmt.admin.DbUserManager -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.getOrCreateSessionToken import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -63,7 +63,7 @@ class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostR val sessionToken = ctx.getOrCreateSessionToken() AccessManager.registerUserForSession(sessionToken, user) - DbAuditLogger.login(loginRequest.username, DbAuditLogSource.REST, sessionToken) + AuditLogger.login(loginRequest.username, AuditLogSource.REST, sessionToken) //explicitly set cookie on login ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index 82e414c70..4175a2a50 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -6,8 +6,8 @@ import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* @@ -40,7 +40,7 @@ class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetR val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) return store.transactional { - DbAuditLogger.logout(userId, DbAuditLogSource.REST, ctx.sessionToken()!!) + AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionToken()!!) AccessManager.deregisterUserSession(ctx.sessionToken()!!) SuccessStatus("User '${username}' logged out successfully.") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt deleted file mode 100644 index d5f35bc6e..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogEntry.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dev.dres.api.rest.types.audit - -import dev.dres.data.model.audit.DbAuditLogEntry - -/** - * A RESTful API representation of a [DbAuditLogEntry] - * - * @author Loris Sauter - * @version 1.0.0 - */ -data class ApiAuditLogEntry( - val id: String, - val type: ApiAuditLogType, - val source: ApiAuditLogSource, - val timestamp: Long, - val competitionId: String? = null, - val userId: String? = null, - val submissionId: String? = null, - val session: String? = null, - val address: String? = null, - val description: String? = null -) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt deleted file mode 100644 index 5055fc31f..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogSource.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dev.dres.api.rest.types.audit - -import dev.dres.data.model.audit.DbAuditLogSource - -/** - * @author Ralph Gasser - * @version 1.0.0 - */ -enum class ApiAuditLogSource { - REST, CLI, INTERNAL; - - /** - * Converts this [ApiAuditLogSource] to a RESTful API representation [DbAuditLogSource]. - * - * @return [DbAuditLogSource] - */ - fun toDb(): DbAuditLogSource = when(this) { - REST -> DbAuditLogSource.REST - CLI -> DbAuditLogSource.CLI - INTERNAL -> DbAuditLogSource.INTERNAL - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt deleted file mode 100644 index 24b71c6a5..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/ApiAuditLogType.kt +++ /dev/null @@ -1,33 +0,0 @@ -package dev.dres.api.rest.types.audit - -import dev.dres.data.model.audit.DbAuditLogType - -/** - * @author Ralph Gasser - * @version 1.0.0 - */ -enum class ApiAuditLogType { - COMPETITION_START, COMPETITION_END, TASK_START, TASK_MODIFIED, TASK_END, SUBMISSION, - PREPARE_JUDGEMENT, JUDGEMENT, LOGIN, LOGOUT, SUBMISSION_VALIDATION, SUBMISSION_STATUS_OVERWRITE; - - - /** - * Converts this [ApiAuditLogType] to a RESTful API representation [DbAuditLogType]. - * - * @return [DbAuditLogType] - */ - fun toDb(): DbAuditLogType = when(this) { - COMPETITION_START -> DbAuditLogType.COMPETITION_START - COMPETITION_END -> DbAuditLogType.COMPETITION_END - TASK_START -> DbAuditLogType.TASK_START - TASK_MODIFIED -> DbAuditLogType.TASK_MODIFIED - TASK_END -> DbAuditLogType.TASK_END - SUBMISSION -> DbAuditLogType.SUBMISSION - PREPARE_JUDGEMENT -> DbAuditLogType.PREPARE_JUDGEMENT - JUDGEMENT -> DbAuditLogType.JUDGEMENT - LOGIN -> DbAuditLogType.LOGIN - LOGOUT -> DbAuditLogType.LOGOUT - SUBMISSION_VALIDATION -> DbAuditLogType.SUBMISSION_VALIDATION - SUBMISSION_STATUS_OVERWRITE -> DbAuditLogType.SUBMISSION_STATUS_OVERWRITE - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt deleted file mode 100644 index 063b3b5f4..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.dres.api.rest.types.audit - -data class AuditLogInfo( - val timestamp: Long = System.currentTimeMillis(), - val size: Int, - val latest: Long? = null -) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt deleted file mode 100644 index 7cc1f141b..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/audit/AuditLogPage.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.dres.api.rest.types.audit - - -/** - * A collection of [ApiAuditLogEntry]s with pagination information - * - * @author Loris Sauter - * @version 1.0.0 - */ -data class AuditLogPage( -val index: Int = 0, -val entries: List, -val timestamp: Long = System.currentTimeMillis(), -val oldest: Long -) - diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt deleted file mode 100644 index 718b24c4f..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogEntry.kt +++ /dev/null @@ -1,61 +0,0 @@ -package dev.dres.data.model.audit - -import dev.dres.api.rest.types.audit.* -import dev.dres.data.model.PersistentEntity -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdNaturalEntityType -import kotlinx.dnq.xdLink1 -import kotlinx.dnq.xdRequiredDateTimeProp -import kotlinx.dnq.xdStringProp -import org.joda.time.DateTime - -/** - * - */ -class DbAuditLogEntry(entity: Entity): PersistentEntity(entity) { - companion object : XdNaturalEntityType() - - override fun constructor() { - super.constructor() - this.timestamp = DateTime.now() - } - - /** The type of [DbAuditLogEntry]. */ - var type by xdLink1(DbAuditLogType) - - /** The [DbAuditLogSource] that generated this [DbAuditLogEntry]. */ - var source by xdLink1(DbAuditLogSource) - - /** The timestamp of this [DbAuditLogEntry]. */ - var timestamp by xdRequiredDateTimeProp() - - /** The ID of the evaluation this [DbAuditLogEntry] belongs to. */ - var evaluationId by xdStringProp() - - /** The ID of the task this [DbAuditLogEntry] belongs to. */ - var taskId by xdStringProp() - - /** The ID of the submission this [DbAuditLogEntry] belongs to. Only valid if [type] is equal to [DbAuditLogType.SUBMISSION], [DbAuditLogType.SUBMISSION_VALIDATION] or [DbAuditLogType.SUBMISSION_STATUS_OVERWRITE]. */ - var submissionId by xdStringProp() - - /** The user ID of the user who generated this [DbAuditLogEntry]. Only set if [source] is equal to [DbAuditLogSource.REST]. */ - var userId by xdStringProp() - - /** The session ID of the [DbAuditLogEntry]. Only set if [source] is equal to [DbAuditLogSource.REST]. */ - var session by xdStringProp() - - /** The source address of the [DbAuditLogEntry]. Only set if [source] is equal to [DbAuditLogSource.REST]. */ - var address by xdStringProp() - - /** Descriptive metadata for this [DbAuditLogEntry]. */ - var description by xdStringProp() - - /** - * Converts this [DbAuditLogEntry] to a RESTful API representation [ApiAuditLogEntry]. - * - * This is a convenience method and requires an active transaction context. - * - * @return [ApiAuditLogEntry] - */ - fun toApi(): ApiAuditLogEntry = ApiAuditLogEntry(this.id, this.type.toApi(), this.source.toApi(), this.timestamp.millis, this.evaluationId, this.userId, this.submissionId, this.session, this.address, this.description) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt deleted file mode 100644 index debe5139f..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogSource.kt +++ /dev/null @@ -1,33 +0,0 @@ -package dev.dres.data.model.audit - -import dev.dres.api.rest.types.audit.ApiAuditLogSource -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdEnumEntity -import kotlinx.dnq.XdEnumEntityType -import kotlinx.dnq.xdRequiredStringProp - -/** - * Enumeration of the source of a [DbAuditLogEntry]. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class DbAuditLogSource(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { - val REST by enumField { description = "REST" } - val CLI by enumField { description = "CLI" } - val INTERNAL by enumField { description = "INTERNAL" } - } - - /** Name / description of the [DbAuditLogType]. */ - var description by xdRequiredStringProp(unique = true) - private set - - /** - * Converts this [DbAuditLogSource] to a RESTful API representation [ApiAuditLogSource]. - * - * @return [ApiAuditLogSource] - */ - fun toApi(): ApiAuditLogSource - = ApiAuditLogSource.values().find { it.toDb() == this } ?: throw IllegalStateException("Audit log source ${this.description} is not supported.") -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt b/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt deleted file mode 100644 index 0b5370a46..000000000 --- a/backend/src/main/kotlin/dev/dres/data/model/audit/DbAuditLogType.kt +++ /dev/null @@ -1,45 +0,0 @@ -package dev.dres.data.model.audit - -import dev.dres.api.rest.types.audit.ApiAuditLogSource -import dev.dres.api.rest.types.audit.ApiAuditLogType -import jetbrains.exodus.entitystore.Entity -import kotlinx.dnq.XdEnumEntity -import kotlinx.dnq.XdEnumEntityType -import kotlinx.dnq.xdRequiredStringProp - -/** - * The [DbAuditLogEntry] types currently supported by DRES. - * - * @author Luca Rossetto - * @version 2.0.0 - */ -class DbAuditLogType(entity: Entity) : XdEnumEntity(entity) { - companion object : XdEnumEntityType() { - val COMPETITION_START by enumField { description = "COMPETITION_START" } - val COMPETITION_END by enumField { description = "COMPETITION_END" } - val TASK_START by enumField { description = "TASK_START" } - val TASK_MODIFIED by enumField { description = "TASK_MODIFIED" } - val TASK_END by enumField { description = "TASK_END" } - val SUBMISSION by enumField { description = "SUBMISSION" } - val PREPARE_JUDGEMENT by enumField { description = "PREPARE_JUDGEMENT" } - val JUDGEMENT by enumField { description = "JUDGEMENT" } - val LOGIN by enumField { description = "LOGIN" } - val LOGOUT by enumField { description = "LOGOUT" } - val SUBMISSION_VALIDATION by enumField { description = "SUBMISSION_VALIDATION" } - val SUBMISSION_STATUS_OVERWRITE by enumField { description = "SUBMISSION_STATUS_OVERWRITE" } - } - - /** Name / description of the [DbAuditLogType]. */ - var description by xdRequiredStringProp(unique = true) - private set - - /** - * Converts this [DbAuditLogSource] to a RESTful API representation [ApiAuditLogSource]. - * - * @return [ApiAuditLogSource] - */ - fun toApi(): ApiAuditLogType - = ApiAuditLogType.values().find { it.toDb() == this } ?: throw IllegalStateException("Audit log type ${this.description} is not supported.") - - override fun toString() = this.description -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index d7f05ca3f..db4fd3910 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -28,7 +28,9 @@ data class Config( /** Folder that contains task type rpesets. Defaults to `$APPLICATION_ROOT/type-presets` */ val presetsLocation: Path = DRES.APPLICATION_ROOT.resolve("type-presets"), /** Folder for event data. Defaults to `$APPLICATION_ROOT/external` */ - val eventsPath: Path = DRES.APPLICATION_ROOT.resolve("events"), + val eventsLocation: Path = DRES.APPLICATION_ROOT.resolve("events"), + /** Location for Audit event log. */ + val auditLocation: Path = DRES.APPLICATION_ROOT.resolve("audit"), /** Event buffer retention time, defaults to 1 minute */ val eventBufferRetentionTime: Int = 60_000, ) { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 69b84c1e9..6ca73b8a9 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -9,8 +9,6 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.api.rest.types.users.ApiRole -import dev.dres.data.model.admin.DbRole -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* @@ -20,7 +18,8 @@ import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.task.options.Defaults.SCOREBOARD_UPDATE_INTERVAL_DEFAULT import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException import dev.dres.run.exceptions.IllegalTeamIdException import dev.dres.run.score.ScoreTimePoint @@ -721,7 +720,7 @@ class InteractiveAsynchronousRunManager( ) if (timeLeft <= 0) { task.end() - DbAuditLogger.taskEnd(this.id, task.taskId, DbAuditLogSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, task.taskId, AuditLogSource.INTERNAL, null) /* Enqueue WS message for sending */ RunExecutor.broadcastWsMessage( @@ -735,7 +734,7 @@ class InteractiveAsynchronousRunManager( val task = this.evaluation.currentTaskForTeam(teamId) ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") task.start() - DbAuditLogger.taskStart(this.id, task.teamId, task.template, DbAuditLogSource.REST, null) + AuditLogger.taskStart(this.id, task.teamId, task.template.toApi(), AuditLogSource.REST, null) RunExecutor.broadcastWsMessage( teamId, ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index e8cae1974..9e556b60a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -10,7 +10,6 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.audit.DbAuditLogSource import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun @@ -23,7 +22,8 @@ import dev.dres.data.model.template.task.options.Defaults.SCOREBOARD_UPDATE_INTE import dev.dres.data.model.template.task.options.Defaults.VIEWER_TIMEOUT_DEFAULT import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogSource +import dev.dres.run.audit.AuditLogger import dev.dres.run.eventstream.EventStreamProcessor import dev.dres.run.eventstream.TaskEndEvent import dev.dres.run.exceptions.IllegalRunStateException @@ -619,7 +619,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { this.evaluation.currentTask!!.start() - DbAuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate(), DbAuditLogSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate().toApi(), AuditLogSource.INTERNAL, null) } /* Enqueue WS message for sending */ @@ -633,7 +633,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) if (timeLeft <= 0) { task.end() - DbAuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.taskId, DbAuditLogSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.taskId, AuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.taskId)) /* Enqueue WS message for sending */ diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt new file mode 100644 index 000000000..5e6726bce --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt @@ -0,0 +1,97 @@ +package dev.dres.run.audit + +import com.fasterxml.jackson.annotation.JsonTypeInfo +import dev.dres.api.rest.handler.users.SessionToken +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus +import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.run.validation.interfaces.JudgementValidator + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") +sealed class AuditLogEntry{ + val timestamp: Long = System.currentTimeMillis() +} + +data class EvaluationStartAuditLogEntry( + val evaluationId: EvaluationId, + val description: ApiEvaluationTemplate, + val api: AuditLogSource, + val userId: UserId?, + val session: SessionToken? +): AuditLogEntry() + +data class EvaluationEndAuditLogEntry( + val evaluationId: EvaluationId, + val api: AuditLogSource, + val userId: UserId?, + val session: SessionToken? +) : AuditLogEntry() + +data class TaskStartAuditLogEntry( + val evaluationId: EvaluationId, + val taskId: EvaluationId, + val description: ApiTaskTemplate, + val api: AuditLogSource, + val session: SessionToken? +) : AuditLogEntry() + +data class TaskModifiedAuditLogEntry( + val evaluationId: EvaluationId, + val taskId: EvaluationId, + val modification: String, + val api: AuditLogSource, + val session: String? +) : AuditLogEntry() + +data class TaskEndAuditLogEntry( + val evaluationId: EvaluationId, + val taskId: EvaluationId, + val api: AuditLogSource, + val session: SessionToken? +) : AuditLogEntry() + +data class SubmissionAuditLogEntry( + val submission: ApiClientSubmission, + val evaluationId: EvaluationId, + val api: AuditLogSource, + val sessionToken: SessionToken?, + val address: String +) : AuditLogEntry() + +data class OverrideVerdictAuditLogEntry( + val answerSet: ApiAnswerSet, + val verdict: ApiVerdictStatus, + val api: AuditLogSource, + val sessionToken: SessionToken? +) : AuditLogEntry() + +data class PrepareJudgementAuditLogEntry( + val answerSet: ApiAnswerSet, + val validator: JudgementValidator, + val token: String +) : AuditLogEntry() + +data class JudgementAuditLogEntry( + val evaluationId: EvaluationId, + val validator: JudgementValidator, + val token: String, + val verdict: ApiVerdictStatus, + val api: AuditLogSource, + val sessionToken: SessionToken? +) : AuditLogEntry() + +data class LoginAuditLogEntry( + val userId: UserId, + val api: AuditLogSource, + val sessionToken: SessionToken +) : AuditLogEntry() + +data class LogoutAuditLogEntry( + val userId: UserId, + val api: AuditLogSource, + val sessionToken: SessionToken +) : AuditLogEntry() \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogSource.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogSource.kt new file mode 100644 index 000000000..09bedb4f0 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogSource.kt @@ -0,0 +1,7 @@ +package dev.dres.run.audit + +enum class AuditLogSource { + REST, + CLI, + INTERNAL +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt new file mode 100644 index 000000000..4ae16aadd --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -0,0 +1,256 @@ +package dev.dres.run.audit + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.dres.DRES +import dev.dres.api.rest.handler.users.SessionToken +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus +import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate +import dev.dres.data.model.admin.UserId +import dev.dres.data.model.run.interfaces.EvaluationId + +import dev.dres.run.eventstream.* +import dev.dres.run.validation.interfaces.JudgementValidator +import org.slf4j.LoggerFactory +import org.slf4j.Marker +import org.slf4j.MarkerFactory +import java.io.File +import java.io.FileWriter +import java.io.PrintWriter +import java.nio.file.Files +import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.concurrent.thread + +object AuditLogger { + + const val FLUSH_INTERVAL = 30_000 //flush every 30 seconds + + private val logMarker: Marker = MarkerFactory.getMarker("AUDIT") + + private val logger = LoggerFactory.getLogger(this.javaClass) + + private val queue = ConcurrentLinkedQueue() + + private var active = true + + private val writerThread = thread( + name = "AuditLogHelperThread", + isDaemon = true, + start = false + ) { + val mapper = jacksonObjectMapper() + Files.createDirectories(DRES.AUDIT_LOG_ROOT) + val auditLogFile = DRES.AUDIT_LOG_ROOT.resolve("audit.jsonl").toFile() + val writer = PrintWriter(FileWriter(auditLogFile, Charsets.UTF_8, true)) + var lastFlush = 0L + while (active || queue.isNotEmpty()) { + + try { + + while (queue.isNotEmpty()) { + val logEntry = queue.poll() ?: break + writer.println( + mapper.writeValueAsString(logEntry) + ) + } + + val now = System.currentTimeMillis() + + if (now - lastFlush > FLUSH_INTERVAL) { + writer.flush() + lastFlush = now + } + + } catch (e: Exception) { + e.printStackTrace() + } + + } + + writer.flush() + writer.close() + + } + + init { + writerThread.start() + } + + fun stop() { + active = false + } + + private fun log(entry: AuditLogEntry) { + queue.add(entry) + logger.info(logMarker, "Audit event: $entry") + } + + /** + * Logs the start of a DRES competition. + * + * @param description The [ApiEvaluationTemplate]. + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun evaluationStart( + evaluationId: EvaluationId, + description: ApiEvaluationTemplate, + api: AuditLogSource, + userId: UserId?, + session: SessionToken? + ) { + log(EvaluationStartAuditLogEntry(evaluationId, description, api, userId, session)) + EventStreamProcessor.event(RunStartEvent(evaluationId, description)) + } + + /** + * Logs the end of a DRES competition. + * + * @param evaluationId [EvaluationId] that identifies the competition + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun evaluationEnd(evaluationId: EvaluationId, api: AuditLogSource, userId: UserId?, session: SessionToken?) { + log(EvaluationEndAuditLogEntry(evaluationId, api, userId, session)) + EventStreamProcessor.event(RunEndEvent(evaluationId)) + } + + /** + * Logs the start of a DRES task. + * + * @param evaluationId [EvaluationId] that identifies the competition + * @param taskId [EvaluationId] that identifies the task + * @param description The [ApiTaskTemplate]. + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun taskStart( + evaluationId: EvaluationId, + taskId: EvaluationId, + description: ApiTaskTemplate, + api: AuditLogSource, + session: SessionToken? + ) { + log(TaskStartAuditLogEntry(evaluationId, taskId, description, api, session)) + EventStreamProcessor.event(TaskStartEvent(evaluationId, taskId, description)) + } + + /** + * Logs the start of a DRES task. + * + * @param evaluationId [EvaluationId] that identifies the competition + * @param taskId [EvaluationId] that identifies the task + * @param modification Description of the modification. + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun taskModified( + evaluationId: EvaluationId, + taskId: EvaluationId, + modification: String, + api: AuditLogSource, + session: String? + ) { + log(TaskModifiedAuditLogEntry(evaluationId, taskId, modification, api, session)) + } + + /** + * Logs the end of a DRES task. + * + * @param evaluationId [EvaluationId] that identifies the competition + * @param taskId [EvaluationId] that identifies the task + * @param api The [AuditLogSource] + * @param session The identifier of the user session. + */ + fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionToken?) { + log(TaskEndAuditLogEntry(evaluationId, taskId, api, session)) + EventStreamProcessor.event(TaskEndEvent(evaluationId, taskId)) + } + + /** + * Logs an incoming submission to DRES. + * + * @param submission The [ApiClientSubmission] that was registered. + * @param api The [AuditLogSource] + * @param sessionToken The identifier of the user session. + * @param address The IP address of the submitter. + */ + fun submission( + submission: ApiClientSubmission, + evaluationId: EvaluationId, + api: AuditLogSource, + sessionToken: SessionToken?, + address: String + ) { + log(SubmissionAuditLogEntry(submission, evaluationId, api, sessionToken, address)) + EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", evaluationId, submission)) + } + + /** + * Logs a AnswerSet VerdictStatus override to DRES. + */ + fun overrideVerdict( + answerSet: ApiAnswerSet, + verdict: ApiVerdictStatus, + api: AuditLogSource, + sessionToken: SessionToken? + ) { + log(OverrideVerdictAuditLogEntry(answerSet, verdict, api, sessionToken)) + } + + /** + * Logs a submission override to DRES. + * + * @param answerSet The [ApiAnswerSet] that was overwritten (new snapshot). + * @param validator The [JudgementValidator] instance. + * @param token The token generated by the judgement sub-system + */ + fun prepareJudgement(answerSet: ApiAnswerSet, validator: JudgementValidator, token: String) { + log(PrepareJudgementAuditLogEntry(answerSet, validator, token)) + } + + /** + * Logs a submission override to DRES. + * + * @param evaluationId [EvaluationId] that identifies the competition + * @param validator The [JudgementValidator] instance. + * @param token The token generated by the judgement sub-system + * @param verdict The [ApiVerdictStatus] submitted by the judge. + * @param api The [AuditLogSource] + * @param sessionToken The identifier of the user session. + */ + fun judgement( + evaluationId: EvaluationId, + validator: JudgementValidator, + token: String, + verdict: ApiVerdictStatus, + api: AuditLogSource, + sessionToken: SessionToken? + ) { + log(JudgementAuditLogEntry(evaluationId, validator, token, verdict, api, sessionToken)) + } + + /** + * Logs a user user login event. + * + * @param userId [EvaluationId] of the user who logged out. + * @param api The [AuditLogSource] + * @param sessionToken The [SessionToken] + */ + fun login(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { + log(LoginAuditLogEntry(userId, api, sessionToken)) + } + + /** + * Logs a user logout event. + * + * @param userId [EvaluationId] of the user who logged out. + * @param api The [AuditLogSource] + * @param sessionToken The [SessionToken] + */ + fun logout(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { + + } +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt deleted file mode 100644 index 3ec9aa31b..000000000 --- a/backend/src/main/kotlin/dev/dres/run/audit/DbAuditLogger.kt +++ /dev/null @@ -1,223 +0,0 @@ -package dev.dres.run.audit - -import dev.dres.api.rest.handler.users.SessionToken -import dev.dres.data.model.admin.UserId -import dev.dres.data.model.audit.DbAuditLogEntry -import dev.dres.data.model.audit.DbAuditLogSource -import dev.dres.data.model.audit.DbAuditLogType -import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.submissions.* -import dev.dres.data.model.template.DbEvaluationTemplate -import dev.dres.data.model.template.task.DbTaskTemplate -import dev.dres.run.eventstream.* -import dev.dres.run.validation.interfaces.JudgementValidator -import kotlinx.dnq.query.first - -/** - * Audit logging instance of DRES. Requires one-time initialisation - * - * @author Luca Rossetto - * @version 2.0.0 - */ -object DbAuditLogger { - - /** - * Logs the start of a DRES competition. - * - * @param description The [DbEvaluationTemplate]. - * @param api The [DbAuditLogSource] - * @param session The identifier of the user session. - */ - fun evaluationStart(evaluationId: EvaluationId, description: DbEvaluationTemplate, api: DbAuditLogSource, userId: UserId?, session: SessionToken?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.COMPETITION_START - this.source = api - this.evaluationId = evaluationId - this.userId = userId - this.session = session - } - EventStreamProcessor.event(RunStartEvent(evaluationId, description)) - } - - /** - * Logs the end of a DRES competition. - * - * @param evaluationId [EvaluationId] that identifies the competition - * @param api The [DbAuditLogSource] - * @param session The identifier of the user session. - */ - fun evaluationEnd(evaluationId: EvaluationId, api: DbAuditLogSource, userId: UserId?, session: SessionToken?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.COMPETITION_END - this.source = api - this.evaluationId = evaluationId - this.userId = userId - this.session = session - } - EventStreamProcessor.event(RunEndEvent(evaluationId)) - } - - /** - * Logs the start of a DRES task. - * - * @param evaluationId [EvaluationId] that identifies the competition - * @param taskId [EvaluationId] that identifies the task - * @param description The [DbTaskTemplate]. - * @param api The [DbAuditLogSource] - * @param session The identifier of the user session. - */ - fun taskStart(evaluationId: EvaluationId, taskId: EvaluationId, description: DbTaskTemplate, api: DbAuditLogSource, session: SessionToken?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.TASK_START - this.source = api - this.evaluationId = evaluationId - this.taskId = taskId - this.session = session - } - EventStreamProcessor.event(TaskStartEvent(evaluationId, taskId, description)) - } - - /** - * Logs the start of a DRES task. - * - * @param evaluationId [EvaluationId] that identifies the competition - * @param taskId [EvaluationId] that identifies the task - * @param modification Description of the modification. - * @param api The [DbAuditLogSource] - * @param session The identifier of the user session. - */ - fun taskModified(evaluationId: EvaluationId, taskId: EvaluationId, modification: String, api: DbAuditLogSource, session: String?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.TASK_MODIFIED - this.source = api - this.evaluationId = evaluationId - this.taskId = taskId - this.description = modification - this.session = session - } - } - - /** - * Logs the end of a DRES task. - * - * @param evaluationId [EvaluationId] that identifies the competition - * @param taskId [EvaluationId] that identifies the task - * @param api The [DbAuditLogSource] - * @param session The identifier of the user session. - */ - fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: DbAuditLogSource, session: SessionToken?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.TASK_END - this.source = api - this.evaluationId = evaluationId - this.taskId = taskId - this.session = session - } - EventStreamProcessor.event(TaskEndEvent(evaluationId, taskId)) - } - - /** - * Logs an incoming submission to DRES. - * - * @param submission The [DbSubmission] that was registered. - * @param api The [DbAuditLogSource] - * @param sessionToken The identifier of the user session. - * @param address The IP address of the submitter. - */ - fun submission(submission: DbSubmission, api: DbAuditLogSource, sessionToken: SessionToken?, address: String) { - val entry = DbAuditLogEntry.new { - this.type = DbAuditLogType.SUBMISSION - this.source = api - this.submissionId = submission.submissionId - this.evaluationId = submission.answerSets.first().task.evaluation.evaluationId - this.session = sessionToken - this.address = address - } - EventStreamProcessor.event(SubmissionEvent(sessionToken ?: "na", entry.evaluationId!!, null, submission)) - } - - /** - * Logs a AnswerSet VerdictStatus override to DRES. - */ - fun overrideVerdict(answerSet: DbAnswerSet, verdict: DbVerdictStatus, api: DbAuditLogSource, sessionToken: SessionToken?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.SUBMISSION_STATUS_OVERWRITE - this.source = api - this.submissionId = answerSet.submission.submissionId - this.evaluationId = answerSet.task.evaluation.evaluationId - this.taskId =answerSet.task.id - this.description = "Set verdict of '${answerSet.id}' to ${verdict.description}" - this.session = sessionToken - } - } - - /** - * Logs a submission override to DRES. - * - * @param answerSet The [DbSubmission] that was overwritten (new snapshot). - * @param validator The [JudgementValidator] instance. - * @param token The token generated by the judgement sub-system - */ - fun prepareJudgement(answerSet: DbAnswerSet, validator: JudgementValidator, token: String) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.PREPARE_JUDGEMENT - this.source = DbAuditLogSource.INTERNAL - this.submissionId = answerSet.submission.submissionId - this.evaluationId = answerSet.task.evaluation.evaluationId - this.taskId = answerSet.task.taskId - this.description = "Token: $token, Validator: ${validator.id}, AnswerSet: ${answerSet.id}, Verdict: ${answerSet.status.description}" - } - } - - /** - * Logs a submission override to DRES. - * - * @param evaluationId [EvaluationId] that identifies the competition - * @param validator The [JudgementValidator] instance. - * @param token The token generated by the judgement sub-system - * @param verdict The [DbVerdictStatus] submitted by the judge. - * @param api The [DbAuditLogSource] - * @param sessionToken The identifier of the user session. - */ - fun judgement(evaluationId: EvaluationId, validator: JudgementValidator, token: String, verdict: DbVerdictStatus, api: DbAuditLogSource, sessionToken: SessionToken?) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.JUDGEMENT - this.source = api - this.evaluationId = evaluationId - this.description = "Token: $token, Validator: ${validator.id}, Verdict: ${verdict.description}" - this.session = sessionToken - } - } - - /** - * Logs a user user login event. - * - * @param userId [EvaluationId] of the user who logged out. - * @param api The [DbAuditLogSource] - * @param sessionToken The [SessionToken] - */ - fun login(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.LOGIN - this.source = api - this.userId = userId - this.session = sessionToken - } - } - - /** - * Logs a user logout event. - * - * @param userId [EvaluationId] of the user who logged out. - * @param api The [DbAuditLogSource] - * @param sessionToken The [SessionToken] - */ - fun logout(userId: UserId, api: DbAuditLogSource, sessionToken: SessionToken) { - DbAuditLogEntry.new { - this.type = DbAuditLogType.LOGOUT - this.source = api - this.userId = userId - this.session = sessionToken - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt index 522befcb0..a5364eb47 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt @@ -5,7 +5,6 @@ import dev.dres.DRES import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write import org.slf4j.LoggerFactory -import java.io.File import java.io.PrintWriter import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.locks.StampedLock @@ -24,7 +23,7 @@ object EventStreamProcessor { private val eventQueue = ConcurrentLinkedQueue() private val eventHandlers = mutableListOf() private val handlerLock = StampedLock() - private val eventSink = PrintWriter(DRES.CONFIG.eventsPath.resolve("${System.currentTimeMillis()}.txt").toFile().also { it.parentFile.mkdirs() }) + private val eventSink = PrintWriter(DRES.CONFIG.eventsLocation.resolve("${System.currentTimeMillis()}.txt").toFile().also { it.parentFile.mkdirs() }) private val eventBuffer = mutableListOf() private val eventBufferRetentionTime = DRES.CONFIG.eventBufferRetentionTime diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt index a7141d072..4cc3c7f8e 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/StreamEvent.kt @@ -1,20 +1,21 @@ package dev.dres.run.eventstream import com.fasterxml.jackson.annotation.JsonTypeInfo -import dev.dres.data.model.template.DbEvaluationTemplate -import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.log.QueryEventLog import dev.dres.data.model.log.QueryResultLog import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.submissions.Submission + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class") sealed class StreamEvent(var timeStamp : Long = System.currentTimeMillis(), var session: String? = null) -class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val taskTemplate: DbTaskTemplate) : StreamEvent() +class TaskStartEvent(val runId: EvaluationId, val taskId: EvaluationId, val taskTemplate: ApiTaskTemplate) : StreamEvent() class TaskEndEvent(val runId: EvaluationId, val taskId: EvaluationId) : StreamEvent() -class RunStartEvent(val runId: EvaluationId, val description: DbEvaluationTemplate) : StreamEvent() +class RunStartEvent(val runId: EvaluationId, val description: ApiEvaluationTemplate) : StreamEvent() class RunEndEvent(val runId: EvaluationId) : StreamEvent() -class SubmissionEvent(session: String, val runId: EvaluationId, val taskId: EvaluationId?, val submission : Submission) : StreamEvent(session = session) +class SubmissionEvent(session: String, val runId: EvaluationId, val submission : ApiClientSubmission) : StreamEvent(session = session) class QueryEventLogEvent(session: String?, val runId: EvaluationId, val queryEventLog: QueryEventLog) : StreamEvent(session = session) class QueryResultLogEvent(session: String?, val runId: EvaluationId, val queryResultLog: QueryResultLog) : StreamEvent(session = session) class InvalidRequestEvent(session: String?, val runId: EvaluationId, val requestData: String) : StreamEvent(session = session) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index b16911645..d729dc39b 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,7 +1,7 @@ package dev.dres.run.validation.judged import dev.dres.data.model.submissions.* -import dev.dres.run.audit.DbAuditLogger +import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.AnswerSetValidator @@ -119,7 +119,7 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e val token = UUID.randomUUID().toString() this.waiting[token] = next this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) - DbAuditLogger.prepareJudgement(answerSet, this, token) + AuditLogger.prepareJudgement(answerSet.toApi(), this, token) token to answerSet } diff --git a/doc/oas-client.json b/doc/oas-client.json index 4215d7c8b..33756d0c2 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1616,71 +1616,6 @@ }, "required" : [ "username", "password" ] }, - "ApiAuditLogEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiAuditLogType" - }, - "source" : { - "$ref" : "#/components/schemas/ApiAuditLogSource" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "competitionId" : { - "type" : "string" - }, - "userId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "id", "type", "source", "timestamp" ] - }, - "ApiAuditLogSource" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "ApiAuditLogType" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] - }, - "AuditLogInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "size" : { - "type" : "integer", - "format" : "int32" - }, - "latest" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, "ApiMediaCollection" : { "type" : "object", "additionalProperties" : false, @@ -2172,10 +2107,13 @@ "text" : { "type" : "string" }, - "itemName" : { + "mediaItemId" : { + "type" : "string" + }, + "mediaItemName" : { "type" : "string" }, - "itemCollectionName" : { + "mediaItemCollectionName" : { "type" : "string" }, "start" : { @@ -2185,9 +2123,6 @@ "end" : { "type" : "integer", "format" : "int64" - }, - "mediaItemId" : { - "type" : "string" } } }, @@ -2206,12 +2141,9 @@ "items" : { "$ref" : "#/components/schemas/ApiClientAnswer" } - }, - "answerSetId" : { - "type" : "string" } }, - "required" : [ "answers", "answerSetId" ] + "required" : [ "answers" ] }, "ApiClientSubmission" : { "type" : "object", diff --git a/doc/oas.json b/doc/oas.json index ae118faba..879be6437 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -158,152 +158,6 @@ "security" : [ ] } }, - "/api/v2/audit/info" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Gives information about the audit log. Namely size and latest timestamp of known entries.", - "operationId" : "getApiV2AuditInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "The audit log info.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/AuditLogInfo" - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user executes the call.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/log/list/limit/{limit}/{page}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query.", - "operationId" : "getApiV2AuditLogListLimitByLimitByPage", - "parameters" : [ { - "name" : "limit", - "in" : "path", - "description" : "The maximum number of results. Default: 500", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "page", - "in" : "path", - "description" : "The page index offset, relative to the limit.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAuditLogEntry" - } - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user starts the call", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/audit/log/list/since/{since}/{upto}" : { - "get" : { - "tags" : [ "Audit" ], - "summary" : "Lists all audit logs matching the query", - "operationId" : "getApiV2AuditLogListSinceBySinceByUpto", - "parameters" : [ { - "name" : "since", - "in" : "path", - "description" : "Timestamp of the earliest audit log to include", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "upto", - "in" : "path", - "description" : "Timestamp of the latest audit log to include.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "The audit logs", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAuditLogEntry" - } - } - } - } - }, - "403" : { - "description" : "Whenever a non-admin user starts the call", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, "/api/v2/client/evaluation/currentTask/{evaluationId}" : { "get" : { "tags" : [ "Evaluation Client" ], @@ -5186,71 +5040,6 @@ }, "required" : [ "username", "password" ] }, - "ApiAuditLogEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiAuditLogType" - }, - "source" : { - "$ref" : "#/components/schemas/ApiAuditLogSource" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "competitionId" : { - "type" : "string" - }, - "userId" : { - "type" : "string" - }, - "submissionId" : { - "type" : "string" - }, - "session" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "id", "type", "source", "timestamp" ] - }, - "ApiAuditLogSource" : { - "type" : "string", - "enum" : [ "REST", "CLI", "INTERNAL" ] - }, - "ApiAuditLogType" : { - "type" : "string", - "enum" : [ "COMPETITION_START", "COMPETITION_END", "TASK_START", "TASK_MODIFIED", "TASK_END", "SUBMISSION", "PREPARE_JUDGEMENT", "JUDGEMENT", "LOGIN", "LOGOUT", "SUBMISSION_VALIDATION", "SUBMISSION_STATUS_OVERWRITE" ] - }, - "AuditLogInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "size" : { - "type" : "integer", - "format" : "int32" - }, - "latest" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "size" ] - }, "ApiMediaCollection" : { "type" : "object", "additionalProperties" : false, @@ -5742,10 +5531,13 @@ "text" : { "type" : "string" }, - "itemName" : { + "mediaItemId" : { + "type" : "string" + }, + "mediaItemName" : { "type" : "string" }, - "itemCollectionName" : { + "mediaItemCollectionName" : { "type" : "string" }, "start" : { @@ -5755,9 +5547,6 @@ "end" : { "type" : "integer", "format" : "int64" - }, - "mediaItemId" : { - "type" : "string" } } }, @@ -5776,12 +5565,9 @@ "items" : { "$ref" : "#/components/schemas/ApiClientAnswer" } - }, - "answerSetId" : { - "type" : "string" } }, - "required" : [ "answers", "answerSetId" ] + "required" : [ "answers" ] }, "ApiClientSubmission" : { "type" : "object", From 01179986da376dfe59754d3327caac59e26d0e68 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 27 Jul 2023 12:11:09 +0200 Subject: [PATCH 399/498] Removed audit log related ui components --- frontend/src/app/app-routing.module.ts | 7 -- frontend/src/app/app.component.html | 4 - frontend/src/app/app.module.ts | 2 - .../admin-auditlog-overview.component.html | 52 -------- .../admin-auditlog-overview.component.scss | 0 .../admin-auditlog-overview.component.ts | 119 ------------------ .../auditlog.datasource.ts | 51 -------- frontend/src/app/auditlog/auditlog.module.ts | 25 ---- .../task-templates-list.component.html | 18 --- .../task-templates-list.component.ts | 22 ---- 10 files changed, 300 deletions(-) delete mode 100644 frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html delete mode 100644 frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.scss delete mode 100644 frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts delete mode 100644 frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts delete mode 100644 frontend/src/app/auditlog/auditlog.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 99aa88f81..855d26ff6 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -9,7 +9,6 @@ import { RunListComponent } from './run/run-list.component'; import { RunAdminViewComponent } from './run/run-admin-view.component'; import { CollectionListComponent } from './collection/collection-list/collection-list.component'; import { CollectionViewerComponent } from './collection/collection-viewer/collection-viewer.component'; -import { AdminAuditlogOverviewComponent } from './auditlog/admin-auditlog-overview/admin-auditlog-overview.component'; import { CanDeactivateGuard } from './services/can-deactivate.guard'; import { RunScoreHistoryComponent } from './run/score-history/run-score-history.component'; import { JudgementVotingViewerComponent } from './judgement/judgement-voting-viewer.component'; @@ -113,12 +112,6 @@ const routes: Routes = [ canActivate: [canActivateAdministrator] }, - { - path: 'logs/list', - component: AdminAuditlogOverviewComponent, - canActivate: [canActivateAdministrator] - }, - /* The login + forbidden page is always accessible. */ { path: 'login', component: LoginComponent }, { path: 'forbidden', component: ForbiddenComponent}, diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 47be100fb..c96bd789f 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -33,10 +33,6 @@ perm_media Media Collection Management - -

diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 76c248137..ffb568ea8 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -19,7 +19,6 @@ import { AppConfig } from './app.config'; import { UserModule } from './user/user.module'; import { JudgementModule } from './judgement/judgement.module'; import { AccessRoleService } from './services/session/access-role.service'; -import { AuditlogModule } from './auditlog/auditlog.module'; import { SharedModule } from './shared/shared.module'; import { CollectionModule } from './collection/collection.module'; import { CompetitionBuilderModule } from './competition/competition-builder/competition-builder.module'; @@ -59,7 +58,6 @@ export function initializeApp(appConfig: AppConfig) { SharedModule, ServicesModule, UserModule, - AuditlogModule, CompetitionModule, CompetitionBuilderModule, ViewerModule, diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html deleted file mode 100644 index b82694984..000000000 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.html +++ /dev/null @@ -1,52 +0,0 @@ -
- -

Audit logs

-
-
- -
-
- -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Time - {{ row.timestamp | epoch2date | date: 'dd.MM.yyyy HH:mm:ss' }} - API{{ row.source ? row.source : 'N/A' }}Type{{ row.type }}Details{{ detailsOf(row) }}ID - {{ row.id.substring(0, 8) }} -
-
diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.scss b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts b/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts deleted file mode 100644 index c60e63e24..000000000 --- a/frontend/src/app/auditlog/admin-auditlog-overview/admin-auditlog-overview.component.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { Subscription, timer } from 'rxjs'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; -import { switchMap } from 'rxjs/operators'; -import { AuditlogDatasource } from './auditlog.datasource'; -import {ApiAuditLogEntry, AuditLogInfo, AuditService} from '../../../../openapi'; - -@Component({ - selector: 'app-admin-auditlog-overview', - templateUrl: './admin-auditlog-overview.component.html', - styleUrls: ['./admin-auditlog-overview.component.scss'], -}) -export class AdminAuditlogOverviewComponent implements AfterViewInit, OnDestroy { - displayCols = ['time', 'api', 'type', 'details', 'id']; // TODO clever way to dynamically list things - pollingFrequency = 5000; // every second - - /** Material Table UI reference. */ - @ViewChild('table', { static: true }) table; - - /** Material Table UI element for sorting. */ - @ViewChild(MatSort) sort: MatSort; - - /** Material Table UI element for pagination. */ - @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; - - /** Data source for Material table */ - public dataSource: AuditlogDatasource; - - /** Number of audit log items. */ - public length = 0; - - /** Subscription used for polling audit logs. */ - private pollingSub: Subscription; - - /** Subscription used for pagination. */ - private paginationSub: Subscription; - - constructor(private snackBar: MatSnackBar, private logService: AuditService) { - this.dataSource = new AuditlogDatasource(logService); - } - - /** - * Initialize subscription for loading audit logs. - * - * IMPORTANT: Unsubscribe OnDestroy! - */ - public ngAfterViewInit(): void { - /* Initialize subscription for loading audit logs. */ - this.pollingSub = timer(0, this.pollingFrequency) - .pipe(switchMap((s) => this.logService - .getApiV2AuditInfo())) - .subscribe((i: AuditLogInfo) => { - this.length = i.size; - if (this.paginator.pageIndex === 0) { - /* Only the first page needs refreshing because logs are ordered chronologically. */ - this.dataSource.refresh(this.paginator.pageIndex, this.paginator.pageSize); - } - }); - - /* Initialize subscription for pagination. */ - this.paginationSub = this.paginator.page.subscribe((p) => { - this.dataSource.refresh(this.paginator.pageIndex, this.paginator.pageSize); - }); - } - - /** - * House keeping; clean up subscriptions. - */ - public ngOnDestroy(): void { - this.pollingSub.unsubscribe(); - this.pollingSub = null; - - this.paginationSub.unsubscribe(); - this.paginationSub = null; - } - - resolveAuditLogEntryById(_: number, item: ApiAuditLogEntry) { - return item.id; - } - - public detailsOf(log: ApiAuditLogEntry): string { - // TODO new design probably asks for the description to be printed -- alone? - switch (log.type) { - case 'PREPARE_JUDGEMENT': - return `Judgement preparation in competition ${log?.competitionId} | ${log?.description}.`; - case 'SUBMISSION_VALIDATION': - return `Submission validation in competition ${log?.competitionId} for submission ${log?.submissionId} | ${log?.description}.`; - case 'SUBMISSION_STATUS_OVERWRITE': - return `Submission status overwrite in competition ${log?.competitionId} for submission ${log.submissionId} | ${log?.description}.`; - case 'COMPETITION_START': - return `Competition ${log.competitionId} has been started by user ${log.userId} | ${log?.description}.`; - case 'COMPETITION_END': - return `Competition ${log.competitionId} has been ended by user ${log.userId} | ${log?.description}.`; - case 'TASK_START': - // return `Task ${log.taskId} in competition ${log.competitionId} has been started by user ${log.userId}`; - return `Task *** in competition ${log.competitionId} has been started by user ${log.userId} | ${log?.description}.`; - case 'TASK_MODIFIED': - // return `Task ${log.taskId} in competition ${log.competitionId} has been modified by user ${log.userId}: ${log.modification}`; - return `Task *** in competition ${log.competitionId} has been modified by user ${log.userId}: *** | ${log?.description}.`; - case 'TASK_END': - return `Task *** in competition ${log.competitionId} has been ended by user ${log.userId} | ${log?.description}.`; - case 'SUBMISSION': - return `For task ***, a submission ${log.submissionId} was made in competition ${ - log.competitionId - } - by user ${log.userId} from ${log.address} | ${log?.description}.`.replace('\n', ''); - case 'JUDGEMENT': - return `Judge ${log.userId ? log.userId : ''} published a verdict in competition ${log.competitionId} | ${log?.description}.`.replace('\n', ''); - case 'LOGIN': - return `${log.userId} has logged in using ${log.session} | ${log?.description}.`; - case 'LOGOUT': - return `${log.session} was logged out | ${log?.description}.`; - default: - return JSON.stringify(log); - } - } -} diff --git a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts b/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts deleted file mode 100644 index 848ea57cb..000000000 --- a/frontend/src/app/auditlog/admin-auditlog-overview/auditlog.datasource.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { BehaviorSubject, Observable, of } from 'rxjs'; -import { catchError, finalize, first } from 'rxjs/operators'; -import {ApiAuditLogEntry, AuditService} from '../../../../openapi'; - -/** - * {@link DataSource} implementation of {@link ApiAuditLogEntry}. - */ -export class AuditlogDatasource implements DataSource { - /** {@link BehaviorSubject} used to publish {@link ApiAuditLogEntry} array. */ - private logsSubject = new BehaviorSubject([]); - private loadingSubject = new BehaviorSubject(false); - - constructor(private logService: AuditService) {} - - /** - * Connects this {@link AuditlogDatasource}. - * - * @param collectionViewer The collection viewer - */ - connect(collectionViewer: CollectionViewer): Observable { - return this.logsSubject.asObservable(); - } - - /** - * Disconnects this {@link AuditlogDatasource}. - * - * @param collectionViewer the collection viewer - */ - disconnect(collectionViewer: CollectionViewer): void { - this.logsSubject.complete(); - this.loadingSubject.complete(); - } - - /** - * - * @param pageIndex the page index to request audit logs for - * @param pageSize the page size to limit the number of logs - */ - refresh(pageIndex = 0, pageSize = 100) { - this.loadingSubject.next(true); - this.logService - .getApiV2AuditLogListLimitByLimitByPage(pageSize, pageIndex) - .pipe( - first(), - catchError(() => of([])), - finalize(() => this.loadingSubject.next(false)) - ) - .subscribe((logs) => this.logsSubject.next(logs)); - } -} diff --git a/frontend/src/app/auditlog/auditlog.module.ts b/frontend/src/app/auditlog/auditlog.module.ts deleted file mode 100644 index 86ba76805..000000000 --- a/frontend/src/app/auditlog/auditlog.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { AdminAuditlogOverviewComponent } from './admin-auditlog-overview/admin-auditlog-overview.component'; -import { SharedModule } from '../shared/shared.module'; -import { MatTableModule } from '@angular/material/table'; -import { ServicesModule } from '../services/services.module'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { ClipboardModule } from '@angular/cdk/clipboard'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatSortModule } from '@angular/material/sort'; - -@NgModule({ - declarations: [AdminAuditlogOverviewComponent], - imports: [ - CommonModule, - SharedModule, - MatTableModule, - ServicesModule, - MatTooltipModule, - ClipboardModule, - MatPaginatorModule, - MatSortModule, - ], -}) -export class AuditlogModule {} diff --git a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html index 4ccdc6b05..6ff2533e0 100644 --- a/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html +++ b/frontend/src/app/template/template-builder/components/tasks-list/task-templates-list.component.html @@ -46,24 +46,6 @@

Tasks

- - - - - - - - - - - - - - - - - -

diff --git a/frontend/src/app/run/run-list.component.html b/frontend/src/app/run/run-list.component.html index dc35ea459..ba2f989f0 100644 --- a/frontend/src/app/run/run-list.component.html +++ b/frontend/src/app/run/run-list.component.html @@ -1,4 +1,18 @@ - +
+ +

Ongoing runs

+
+
+ +
+
+ diff --git a/frontend/src/app/run/run-list.component.scss b/frontend/src/app/run/run-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/run/run-list.component.ts b/frontend/src/app/run/run-list.component.ts index 5d0d12807..feb1e7c07 100644 --- a/frontend/src/app/run/run-list.component.ts +++ b/frontend/src/app/run/run-list.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, ViewChild } from "@angular/core"; import { Observable } from 'rxjs'; import { AuthenticationService } from '../services/session/authentication.sevice'; import { map } from 'rxjs/operators'; @@ -6,20 +6,35 @@ import { AccessChecking } from '../model/access-checking.interface'; import { UserGroup } from '../model/user-group.model'; import { AccessRoleService } from '../services/session/access-role.service'; import {ApiRole} from '../../../openapi'; +import { AbstractRunListComponent } from "./abstract-run-list.component"; +import { AdminRunListComponent } from "./admin-run-list.component"; +import { ViewerRunListComponent } from "./viewer-run-list.component"; @Component({ selector: 'app-run-list', templateUrl: './run-list.component.html', + styleUrls: ['./run-list.component.scss'] }) export class RunListComponent implements AccessChecking { currentRole: Observable; adminGroup = AccessRoleService.ADMIN_GROUP; + @ViewChild('runList') adminList: AdminRunListComponent + @ViewChild('viewer') normalList: ViewerRunListComponent + constructor(private authenticationService: AuthenticationService, private accessService: AccessRoleService) { this.currentRole = this.authenticationService.user.pipe(map((u) => u.role)); } + refresh(){ + if(this.adminList instanceof AdminRunListComponent){ + this.adminList?.refresh(); + }else if(this.normalList instanceof ViewerRunListComponent){ + this.normalList?.refresh(); + } + } + hasAccessFor(group: UserGroup): boolean { return this.accessService.accessGranted(group); } diff --git a/frontend/src/app/run/viewer-run-list.component.html b/frontend/src/app/run/viewer-run-list.component.html index 98e34c68a..b54c9dcb6 100644 --- a/frontend/src/app/run/viewer-run-list.component.html +++ b/frontend/src/app/run/viewer-run-list.component.html @@ -1,7 +1,4 @@ -
- -

Ongoing runs

-
+
diff --git a/frontend/src/app/services/navigation/navigation.service.ts b/frontend/src/app/services/navigation/navigation.service.ts index 7b2efe92d..044fcb172 100644 --- a/frontend/src/app/services/navigation/navigation.service.ts +++ b/frontend/src/app/services/navigation/navigation.service.ts @@ -24,7 +24,7 @@ export class NavigationService { * Navigates back in the local application navigation history */ back(forceBack: boolean = false): void { - let destination = 'home'; // Anything that routes to ** + let destination = ''; // Anything that routes to ** if (this.history.length > 1) { this.alternateHistory.push(this.history.pop()); // Current route is put into the alternate history destination = this.history.pop(); // The one before the current one is our next destination diff --git a/frontend/src/app/shared/back-button/back-button.component.scss b/frontend/src/app/shared/back-button/back-button.component.scss index e69de29bb..64a6c57d9 100644 --- a/frontend/src/app/shared/back-button/back-button.component.scss +++ b/frontend/src/app/shared/back-button/back-button.component.scss @@ -0,0 +1,3 @@ +:host { + align-self: center; +} diff --git a/frontend/src/app/user/admin-user-list/admin-user-list.component.html b/frontend/src/app/user/admin-user-list/admin-user-list.component.html index b1d7a7265..231b1c6bb 100644 --- a/frontend/src/app/user/admin-user-list/admin-user-list.component.html +++ b/frontend/src/app/user/admin-user-list/admin-user-list.component.html @@ -1,4 +1,4 @@ -
+

Users

From 5a1a4907ec85b05de275362188c12b906fcb9cd6 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 20:31:19 +0200 Subject: [PATCH 404/498] #415: Re-enabled admins as team members and team-member-input fix --- .../team-builder-dialog.component.html | 4 ++-- .../team-builder-dialog.component.ts | 16 +--------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html index 27839ecac..c211a0d20 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html @@ -25,10 +25,10 @@

Add team

- + {{ user.username }} diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts index 2fb116a66..58444c410 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts @@ -62,7 +62,7 @@ export class TeamBuilderDialogComponent { users: new FormControl(team?.users || []), userInput: new FormControl(''), }); - this.userService.getApiV2UserList().subscribe(value => this.users = value.filter(u => u.role === "PARTICIPANT").filter(user => !this.form.get('users').value.includes(user))); + this.userService.getApiV2UserList().subscribe(value => this.users = value.filter(u => u.role === "PARTICIPANT" || u.role === "ADMIN").filter(user => !this.form.get('users').value.includes(user))); this.availableUsers = this.form.get('userInput').valueChanges.pipe( startWith(''), map(value => this.filterAvailableUsers(value || '')) @@ -73,20 +73,6 @@ export class TeamBuilderDialogComponent { downloadProvider = () => this.asJson(); - /** - * Adds the selected user to the list of users. - * - * @param event @{MatAutocompleteSelectedEvent} - */ - public addUser(event: MatChipInputEvent): void { - const value = (event.value || '').trim() - if(value){ - this.form.get('users').value.push(value); - } - - event.chipInput!.clear(); - this.form.get('userInput').setValue(null, {emit: false}); - } /** * Selected user gets added to the list of users From cd6adb00d31e9aae34b5659ac355fc37617dc0b8 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 20:44:21 +0200 Subject: [PATCH 405/498] #415: Fixed pagination for collection --- .../collection-viewer/collection-viewer.component.html | 8 +++++--- .../collection-viewer/collection-viewer.component.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.html b/frontend/src/app/collection/collection-viewer/collection-viewer.component.html index 7592c3485..dc5a5e92d 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.html +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.html @@ -24,7 +24,9 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect Base Path: {{ (collection | async)?.collection?.basePath }}

-
+
+ + Filter @@ -32,7 +34,7 @@

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect - +

Media collection: "{{ (collection | async)?.collection.name }}" ({{ (collect
+
- diff --git a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts index 760f44078..d519582de 100644 --- a/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts +++ b/frontend/src/app/collection/collection-viewer/collection-viewer.component.ts @@ -29,7 +29,7 @@ export class CollectionViewerComponent implements AfterViewInit, OnDestroy { @ViewChild(MatSort) sort: MatSort; /** Material Table UI element for pagination. */ - @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild('paginator') paginator: MatPaginator; /** Data source for Material tabl.e */ dataSource = new MatTableDataSource(); From 62c9507bebde45f63d408e3c8aa0929f64dc62f6 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 27 Jul 2023 21:31:36 +0200 Subject: [PATCH 406/498] Added error handling to ListExternalItemHandler --- .../collection/ListExternalItemHandler.kt | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt index f66922724..16a3455b9 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListExternalItemHandler.kt @@ -3,8 +3,10 @@ package dev.dres.api.rest.handler.collection import dev.dres.DRES import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException import io.javalin.http.Context import io.javalin.openapi.* +import java.io.IOException import java.nio.file.FileVisitOption import java.nio.file.Files import java.util.stream.Collectors @@ -42,20 +44,24 @@ class ListExternalItemHandler : GetRestHandler> { override fun doGet(ctx: Context): List { // TODO https://github.com/javalin/javalin-openapi/issues/178 Apparently, we cannot use the slash-included notation here (https://javalin.io/documentation#endpoint-handlers) val startsWith = ctx.pathParamMap()["startsWith"] ?: "" - val files = Files.walk(DRES.EXTERNAL_ROOT, 1, FileVisitOption.FOLLOW_LINKS) - val list = files - .filter { - Files.isRegularFile(it) && - it.name.startsWith(startsWith) && - ( - it.name.endsWith(".jpg", ignoreCase = true) || - it.name.endsWith(".png", ignoreCase = true) || - it.name.endsWith(".mp4", ignoreCase = true) - ) - }.sorted { o1, o2 -> o1.name.length - o2.name.length } - .limit(50) + try { + val files = Files.walk(DRES.EXTERNAL_ROOT, 1, FileVisitOption.FOLLOW_LINKS) + val list = files + .filter { + Files.isRegularFile(it) && + it.name.startsWith(startsWith) && + ( + it.name.endsWith(".jpg", ignoreCase = true) || + it.name.endsWith(".png", ignoreCase = true) || + it.name.endsWith(".mp4", ignoreCase = true) + ) + }.sorted { o1, o2 -> o1.name.length - o2.name.length } + .limit(50) - return list.map { it.toFile().name }.collect(Collectors.toList()) + return list.map { it.toFile().name }.collect(Collectors.toList()) + } catch (ioe: IOException) { + throw ErrorStatusException(404, "Cannot access external files on '${DRES.EXTERNAL_ROOT}'", ctx) + } } From 62c9c1f638734a1ea33b8de478799fb946cfa267 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 21:45:54 +0200 Subject: [PATCH 407/498] #415: Cosmetics in task types table --- .../pipes/underscore-wordbreak.pipe.ts | 23 +++++++++++++++++++ frontend/src/app/services/services.module.ts | 5 +++- .../actionable-dynamic-table.component.html | 2 +- .../task-types-list.component.html | 17 ++++++++++---- .../task-types-list.component.scss | 7 ++++++ 5 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 frontend/src/app/services/pipes/underscore-wordbreak.pipe.ts diff --git a/frontend/src/app/services/pipes/underscore-wordbreak.pipe.ts b/frontend/src/app/services/pipes/underscore-wordbreak.pipe.ts new file mode 100644 index 000000000..73ded5b4f --- /dev/null +++ b/frontend/src/app/services/pipes/underscore-wordbreak.pipe.ts @@ -0,0 +1,23 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'underscoreWordBreak' +}) +export class UnderscoreWordBreakPipe implements PipeTransform { + + /** + * Simple pipe that adds the opportunity for the browser to break long words with underscores + */ + transform(value: any): any{ + if(value){ + if(typeof value === 'string'){ + const doc = new DOMParser().parseFromString(value.replace(/_/g, "_​"), 'text/html'); + return doc.documentElement.textContent + }else{ + return value + } + } + return ''; + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 349b13233..68508f7d1 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -21,6 +21,7 @@ import { ResolveMediaItemPreviewPipe } from './pipes/resolve-media-item-preview. import { FormatMediaItemPipe } from './pipes/format-media-item.pipe'; import { OrderByPipe } from './pipes/order-by.pipe'; import { FilterNotInPipe } from './pipes/filter-not-in.pipe'; +import { UnderscoreWordBreakPipe } from './pipes/underscore-wordbreak.pipe'; /** * Provides the {@link AppConfig} reference. @@ -55,7 +56,8 @@ export function initializeApiConfig(appConfig: AppConfig) { ResolveMediaItemPreviewPipe, FormatMediaItemPipe, OrderByPipe, - FilterNotInPipe + FilterNotInPipe, + UnderscoreWordBreakPipe ], declarations: [ RoundPipePipe, @@ -75,6 +77,7 @@ export function initializeApiConfig(appConfig: AppConfig) { FormatMediaItemPipe, OrderByPipe, FilterNotInPipe, + UnderscoreWordBreakPipe, ], providers: [ AuthenticationService, diff --git a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html index ddb38fccd..5998b3be0 100644 --- a/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html +++ b/frontend/src/app/shared/actionable-dynamic-table/actionable-dynamic-table.component.html @@ -24,7 +24,7 @@

{{tableTitle}}

- {{ element[column.property] }} + {{ element[column.property] | underscoreWordBreak }}
diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html index a256f8a8e..33ef7d79d 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.html @@ -28,10 +28,12 @@
    -
  • {{hint}}
  • +
  • {{hint | underscoreWordBreak}}
- {{element.hintOptions[0]}} + + {{element.hintOptions[0] | underscoreWordBreak}} + @@ -39,10 +41,13 @@
    -
  • {{elem}}
  • +
  • {{elem | underscoreWordBreak}}
- {{element.submissionOptions[0]}} + + {{element.submissionOptions[0] | underscoreWordBreak}} + +
@@ -53,7 +58,9 @@
  • {{hint}}
  • - {{element.taskOptions[0]}} + + {{element.taskOptions[0] | underscoreWordBreak}} + diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss index e69de29bb..16161d623 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.scss @@ -0,0 +1,7 @@ +ul { + padding-inline-start: 16px; /* 16px seems to line up bullet point with cell begin */ +} + +.mat-column-submissions { + width: 17%; /* those are the longest cells, hence manually setting the width */ +} From c00d929009111667ec5caaef2cbdb516de520f7c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 22:02:28 +0200 Subject: [PATCH 408/498] #415: Replaceing competition with evaluation related terminology, new defaults for external files (/external) and frontend code cleanup --- .../dev/dres/api/cli/EvaluationCommand.kt | 6 +- .../dres/api/cli/EvaluationTemplateCommand.kt | 6 +- .../kotlin/dev/dres/api/cli/UserCommand.kt | 4 +- .../dev/dres/api/rest/ClientOpenApiPlugin.kt | 3 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 2 +- .../EvaluationTemplateDownloadHandler.kt | 6 +- .../admin/CreateEvaluationHandler.kt | 2 +- .../submission/LegacySubmissionHandler.kt | 4 +- .../AbstractEvaluationTemplateHandler.kt | 4 +- .../CreateEvaluationTemplateHandler.kt | 2 +- .../ListEvaluationTemplatesHandler.kt | 2 +- .../UpdateEvaluationTemplateHandler.kt | 2 +- .../dev/dres/data/model/config/Config.kt | 9 +- doc/oas.json | 2 +- .../competition-create-dialog.component.html | 21 --- .../competition-create-dialog.component.ts | 35 ---- .../competition-list.component.html | 89 ---------- .../competition-list.component.scss | 3 - .../competition-list.component.ts | 153 ------------------ .../competition-start-dialog.component.html | 52 ------ .../competition-start-dialog.component.ts | 53 ------ 21 files changed, 27 insertions(+), 433 deletions(-) delete mode 100644 frontend/src/app/competition/competition-list/competition-create-dialog.component.html delete mode 100644 frontend/src/app/competition/competition-list/competition-create-dialog.component.ts delete mode 100644 frontend/src/app/competition/competition-list/competition-list.component.html delete mode 100644 frontend/src/app/competition/competition-list/competition-list.component.scss delete mode 100644 frontend/src/app/competition/competition-list/competition-list.component.ts delete mode 100644 frontend/src/app/competition/competition-list/competition-start-dialog.component.html delete mode 100644 frontend/src/app/competition/competition-list/competition-start-dialog.component.ts diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt index 7668b5bff..9774e5358 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationCommand.kt @@ -61,7 +61,7 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom data class RunSummary(val id: String, val name: String, val description: String?, val task: String?) /** - * Lists all ongoing competitions runs for the current DRES instance. + * Lists all ongoing evaluations runs for the current DRES instance. */ inner class Ongoing : CliktCommand(name = "ongoing", help = "Lists all ongoing evaluations.") { @@ -234,7 +234,7 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom } /** - * [CliktCommand] to export a specific competition run as JSON. + * [CliktCommand] to export a specific evaluation template run as JSON. */ inner class Export : CliktCommand(name = "export", help = "Exports the selected evaluation to a JSON file.", printHelpOnEmptyArgs = true) { @@ -410,4 +410,4 @@ class EvaluationCommand(internal val store: TransientEntityStore) : NoOpCliktCom } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt index 6e9e347d0..f6a94e8ac 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt @@ -67,7 +67,7 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private */ inner class Create : CliktCommand(name = "create", help = "Creates a new Template") { - private val name: String by option("-c", "--competition", help = "Name of the new Template") + private val name: String by option("-t", "--template", help = "Name of the new Template") .required() .validate { require(it.isNotEmpty()) { "Template description must be non empty." } } @@ -261,7 +261,7 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private /** - * Exports a specific competition to a JSON file. + * Exports a specific evaluation to a JSON file. */ inner class Export : AbstractEvaluationCommand(name = "export", help = "Exports a template as JSON.") { @@ -346,4 +346,4 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private } } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index b81a077ff..60c299591 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -156,7 +156,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") } /** - * Imports a specific competition from JSON. + * Imports a specific user(s) from JSON. */ inner class Import(private val store: TransientEntityStore): CliktCommand(name = "import", help = "Imports a user description from JSON. Either a single user or an array of users", printHelpOnEmptyArgs = true) { @@ -232,4 +232,4 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") println("Available roles: ${DbRole.values().joinToString(", ")}") } } -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt index 9ce1fdab7..ff34a5133 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt @@ -25,7 +25,6 @@ class ClientOpenApiPlugin : OpenApiPlugin(OpenApiPluginConfiguration() val blacklist = setOf( "/external/", "/collection", - "/competition", "/run", "/audit", "/mediaItem", @@ -50,4 +49,4 @@ class ClientOpenApiPlugin : OpenApiPlugin(OpenApiPluginConfiguration() doc.toPrettyString() } - }) \ No newline at end of file + }) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 7802e2614..c639bec21 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -153,7 +153,7 @@ object RestApi { GetSubmissionAfterInfoHandler(store), GetSubmissionHistoryInfoHandler(store), - // Competition run scores + // Evaluation run scores ListEvaluationScoreHandler(store), CurrentTaskScoreHandler(store), HistoryTaskScoreHandler(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index 8ef0660e1..7c52b3f6e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -41,14 +41,14 @@ class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractD ) override fun doGet(ctx: Context): String { /* Obtain run id and run. */ - val templateId = ctx.pathParamMap()["competitionId"] ?: throw ErrorStatusException(400, "Parameter 'templateId' is missing!'", ctx) + val templateId = ctx.pathParamMap()["templateId"] ?: throw ErrorStatusException(400, "Parameter 'templateId' is missing!'", ctx) val template = this.store.transactional(true) { DbEvaluationTemplate.query(DbEvaluationTemplate::id eq templateId).firstOrNull()?.toApi() - ?: throw ErrorStatusException(404, "Competition $templateId not found", ctx) + ?: throw ErrorStatusException(404, "Evaluation template $templateId not found", ctx) } /* Set header for download. */ - ctx.header("Content-Disposition", "attachment; filename=\"competition-${templateId}.json") + ctx.header("Content-Disposition", "attachment; filename=\"evaluation-template-${templateId}.json") /* Return value. */ val mapper = jacksonObjectMapper() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index b5cac3bc0..785569282 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -65,7 +65,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca val template = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq message.templateId) and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Evaluation template with ID ${message.templateId} could not be found.'", ctx) - /* ensure that only one synchronous run of a competition is happening at any given time */ + /* ensure that only one synchronous run of an evaluation is happening at any given time */ if (message.type == ApiEvaluationType.SYNCHRONOUS && RunExecutor.managers().any { it is InteractiveSynchronousRunManager && it.template == template && it.status != RunManagerStatus.TERMINATED } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index a56e74bd5..35bdaf44d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -155,8 +155,8 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v val managers = AccessManager.getRunManagerForUser(rac.userId).filterIsInstance(InteractiveRunManager::class.java).filter { it.currentTask(rac)?.isRunning == true } - if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible competition with an active task.", ctx) - if (managers.size > 1) throw ErrorStatusException(409, "More than one possible competition found: ${managers.joinToString { it.template.name }}", ctx) + if (managers.isEmpty()) throw ErrorStatusException(404, "There is currently no eligible evaluation with an active task.", ctx) + if (managers.size > 1) throw ErrorStatusException(409, "More than one possible evaluation found: ${managers.joinToString { it.template.name }}", ctx) return managers.first() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt index 9c7f860c9..e5451fafb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt @@ -43,5 +43,5 @@ abstract class AbstractEvaluationTemplateHandler(protected val store: TransientE /** Convenience method to extract [DbEvaluationTemplate] by ID. */ protected fun evaluationTemplateById(id: TemplateId, ctx: Context): DbEvaluationTemplate - = DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq id and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Competition with ID $id not found.'", ctx) -} \ No newline at end of file + = DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq id and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Evaluation template with ID $id not found.'", ctx) +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index 7be0620c3..3485cea38 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -58,6 +58,6 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva this.modified = DateTime.now() } } - return SuccessStatus("Competition description with ID $newId was created successfully.") + return SuccessStatus("Evaluation template with ID $newId was created successfully.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index 65ead6477..7887a38d2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -24,7 +24,7 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEval override val route: String = "template/list" @OpenApi( - summary = "Lists an overview of all available competitions with basic information about their content.", + summary = "Lists an overview of all available evaluation templates with basic information about their content.", path = "/api/v2/template/list", operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Template"], diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 2240ae93a..72033b2bc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -82,7 +82,7 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C } } - return SuccessStatus("Competition with ID ${apiValue.id} was updated successfully.") + return SuccessStatus("Evaluation template with ID ${apiValue.id} was updated successfully.") } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index db4fd3910..b3f616436 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import dev.dres.DRES import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths import java.nio.file.StandardOpenOption @JsonIgnoreProperties(ignoreUnknown = true) @@ -23,10 +24,10 @@ data class Config( val cache: CacheConfig = CacheConfig(), /** Folder taht contains the database. Defaults to `$APPLICATION_ROOT/data`*/ val dataPath: Path = DRES.APPLICATION_ROOT.resolve("data"), - /** Folder that contains external media. Defaults to `$APPLICATION_ROOT/external` */ - val externalMediaLocation : Path = DRES.APPLICATION_ROOT.resolve("external"), - /** Folder that contains task type rpesets. Defaults to `$APPLICATION_ROOT/type-presets` */ - val presetsLocation: Path = DRES.APPLICATION_ROOT.resolve("type-presets"), + /** Folder that contains external media. Defaults to `$cwd/external` */ + val externalMediaLocation : Path = Paths.get("./external"), + /** Folder that contains task type rpesets. Defaults to `$cwd/type-presets` */ + val presetsLocation: Path = Paths.get("./type-presets"), /** Folder for event data. Defaults to `$APPLICATION_ROOT/external` */ val eventsLocation: Path = DRES.APPLICATION_ROOT.resolve("events"), /** Location for Audit event log. */ diff --git a/doc/oas.json b/doc/oas.json index 879be6437..1a7525c63 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -4060,7 +4060,7 @@ "/api/v2/template/list" : { "get" : { "tags" : [ "Template" ], - "summary" : "Lists an overview of all available competitions with basic information about their content.", + "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", "operationId" : "getApiV2TemplateList", "parameters" : [ ], "responses" : { diff --git a/frontend/src/app/competition/competition-list/competition-create-dialog.component.html b/frontend/src/app/competition/competition-list/competition-create-dialog.component.html deleted file mode 100644 index e04d7b93f..000000000 --- a/frontend/src/app/competition/competition-list/competition-create-dialog.component.html +++ /dev/null @@ -1,21 +0,0 @@ -

    New competition

    -
    -
    -

    - - Name - - -

    -

    - - Description - - -

    -
    -
    -
    - - -
    diff --git a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts deleted file mode 100644 index 3cdc2db5c..000000000 --- a/frontend/src/app/competition/competition-list/competition-create-dialog.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Component } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; -import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import {ApiCreateEvaluation} from '../../../../openapi'; - -/** - * @deprecated Replaced by TemplateCreateDialog - */ -@Component({ - selector: 'app-competition-create-dialog', - templateUrl: 'competition-create-dialog.component.html', -}) -export class CompetitionCreateDialogComponent { - form: UntypedFormGroup = new UntypedFormGroup({ - name: new UntypedFormControl(''), - description: new UntypedFormControl(''), - }); - - participantsCanView = true; - - constructor(public dialogRef: MatDialogRef) {} - - public create(): void { - if (this.form.valid) { - this.dialogRef.close({ - name: this.form.get('name').value, - description: this.form.get('description').value, - } as ApiCreateEvaluation); - } - } - - public close(): void { - this.dialogRef.close(null); - } -} diff --git a/frontend/src/app/competition/competition-list/competition-list.component.html b/frontend/src/app/competition/competition-list/competition-list.component.html deleted file mode 100644 index de33eeee9..000000000 --- a/frontend/src/app/competition/competition-list/competition-list.component.html +++ /dev/null @@ -1,89 +0,0 @@ -
    - -

    Evaluation Templates

    - -
    - -
    - -
    -
    - -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Actions - - - - - - - - ID{{ row.id.substring(0, 8) }}Name{{ row.name }}Description{{ row.description }}# Tasks{{ row.taskCount }}# Teams{{ row.teamCount }}
    -
    diff --git a/frontend/src/app/competition/competition-list/competition-list.component.scss b/frontend/src/app/competition/competition-list/competition-list.component.scss deleted file mode 100644 index bed0b61ed..000000000 --- a/frontend/src/app/competition/competition-list/competition-list.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -mat-spinner { - display: inline-block !important; -} diff --git a/frontend/src/app/competition/competition-list/competition-list.component.ts b/frontend/src/app/competition/competition-list/competition-list.component.ts deleted file mode 100644 index b58889d71..000000000 --- a/frontend/src/app/competition/competition-list/competition-list.component.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { AfterViewInit, Component } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; -import { CompetitionCreateDialogComponent } from './competition-create-dialog.component'; -import { filter, take, tap } from 'rxjs/operators'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { Router } from '@angular/router'; -import { CompetitionStartDialogComponent, CompetitionStartDialogResult } from './competition-start-dialog.component'; -import { - ConfirmationDialogComponent, - ConfirmationDialogComponentData, -} from '../../shared/confirmation-dialog/confirmation-dialog.component'; -import { - ApiCreateEvaluation, - ApiEvaluationOverview, ApiEvaluationStartMessage, ApiEvaluationTemplateOverview, - DownloadService, - EvaluationAdministratorService, RunProperties, SuccessStatus, - TemplateService -} from "../../../../openapi"; -import {mergeMap} from 'rxjs'; - -/** - * @deprecated Replaced by TemplateList - */ -@Component({ - selector: 'app-competition-list', - templateUrl: './competition-list.component.html', - styleUrls: ['./competition-list.component.scss'], -}) -export class CompetitionListComponent implements AfterViewInit { - /** */ - displayedColumns = ['actions', 'id', 'name', 'description', 'taskCount', 'teamCount']; - competitions: ApiEvaluationTemplateOverview[] = []; - waitingForRun = new Map() - - constructor( - private evaluationService: TemplateService, - private runAdminService: EvaluationAdministratorService, - private downloadService: DownloadService, - private routerService: Router, - private dialog: MatDialog, - private snackBar: MatSnackBar - ) {} - - public create() { - const dialogRef = this.dialog.open(CompetitionCreateDialogComponent, { width: '500px' }); - dialogRef - .afterClosed() - .pipe( - filter((r) => r != null), - mergeMap((r: ApiCreateEvaluation) => { - return this.evaluationService.postApiV2Template(r); - }) - ) - .subscribe( - (r: SuccessStatus) => { - this.refresh(); - this.snackBar.open(`Success: ${r?.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r?.error?.description}`, null, { duration: 5000 }); - } - ); - } - - public createRun(id: string) { - const dialogRef = this.dialog.open(CompetitionStartDialogComponent, { width: '500px' }); - dialogRef - .afterClosed() - .pipe( - filter((r) => r != null), - tap((r) => (this.waitingForRun[id] = true)), - mergeMap((r: CompetitionStartDialogResult) => { - const properties = { - participantCanView: r.participantCanView, - shuffleTasks: r.shuffleTasks, - allowRepeatedTasks: r.allowRepeatedTasks, - limitSubmissionPreviews: r.limit - } as RunProperties; // ApiEvaluationProperties - return this.runAdminService.postApiV2EvaluationAdminCreate({ - templateId: id, - name: r.name, - type: r.type, - properties: properties, - } as ApiEvaluationStartMessage); - }) - ) - .subscribe( - (r: SuccessStatus) => { - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - this.waitingForRun[id] = false; - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - this.waitingForRun[id] = false; - } - ); - } - - public edit(competitionId: string) { - this.routerService.navigate(['/template/builder', competitionId]); - } - - public delete(competitionId: string) { - const dialogRef = this.dialog.open(ConfirmationDialogComponent, { - data: { - text: `Do you really want to delete competition with ID ${competitionId}?`, - color: 'warn', - } as ConfirmationDialogComponentData, - }); - dialogRef.afterClosed().subscribe((result) => { - if (result) { - this.evaluationService.deleteApiV2TemplateByTemplateId(competitionId).subscribe( - (r) => { - this.refresh(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); - } - }); - } - - public refresh() { - this.evaluationService.getApiV2TemplateList().subscribe( - (results: ApiEvaluationTemplateOverview[]) => { - this.competitions = results; - }, - (r) => { - this.competitions = []; - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); - } - - ngAfterViewInit(): void { - this.refresh(); - } - - downloadProvider = (competitionId) => { - return this.downloadService.getApiV2DownloadTemplateByTemplateId(competitionId).pipe(take(1)); - // .toPromise(); - }; - - fileProvider = (name: string) => { - return () => name; - }; - - resolveCompetitionOverviewById(_: number, item: ApiEvaluationOverview) { - return `${item}`; // FIXME re-add ID? or remove trackedBy - } -} diff --git a/frontend/src/app/competition/competition-list/competition-start-dialog.component.html b/frontend/src/app/competition/competition-list/competition-start-dialog.component.html deleted file mode 100644 index 56945c561..000000000 --- a/frontend/src/app/competition/competition-list/competition-start-dialog.component.html +++ /dev/null @@ -1,52 +0,0 @@ -

    Start competition run

    -
    -
    -

    - - Name - - -

    - -

    - Participants Can View -

    - -

    - Allow repeated tasks -

    - -

    - - Shuffle Tasks (Async only) - -

    - -

    - - Submission Display Limit - - -

    - -

    - - Type - - - {{ type }} - - - -

    -
    -
    -
    - - -
    diff --git a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts b/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts deleted file mode 100644 index 15c01d6d1..000000000 --- a/frontend/src/app/competition/competition-list/competition-start-dialog.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Component } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { MatDialogRef } from '@angular/material/dialog'; -import {ApiEvaluationType} from '../../../../openapi'; - -export interface CompetitionStartDialogResult { - name: string; - type: ApiEvaluationType; - participantCanView: boolean; - allowRepeatedTasks: boolean; - shuffleTasks: boolean; - limit: Number; -} - -/** - * @deprecated Replaced by TemplateStart - */ -@Component({ - selector: 'app-competition-start-dialog', - templateUrl: 'competition-start-dialog.component.html', -}) -export class CompetitionStartDialogComponent { - form: UntypedFormGroup = new UntypedFormGroup({ - name: new UntypedFormControl(''), - type: new UntypedFormControl(''), - participantsCanView: new UntypedFormControl(true), - shuffleTasks: new UntypedFormControl(false), - allowRepeatedTasks: new UntypedFormControl(false), - limit: new UntypedFormControl(0) - }); - runTypes: ApiEvaluationType[] = ['SYNCHRONOUS', 'ASYNCHRONOUS']; - - typeObservable = this.form.get('type').valueChanges; - - constructor(public dialogRef: MatDialogRef) {} - - public create(): void { - if (this.form.valid) { - this.dialogRef.close({ - name: this.form.get('name').value, - type: this.form.get('type').value, - participantCanView: this.form.get('participantsCanView').value, - allowRepeatedTasks: this.form.get('allowRepeatedTasks').value, - shuffleTasks: this.form.get('shuffleTasks').value, - limit: this.form.get('limit').value - } as CompetitionStartDialogResult); - } - } - - public close(): void { - this.dialogRef.close(null); - } -} From 0a34aaa9d2165d3790ba75bd6fbc7cffa20b828a Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 22:35:52 +0200 Subject: [PATCH 409/498] Code cleanup --- .../collection-list/collection-list.component.html | 2 +- .../collection/collection-list/collection-list.component.ts | 3 +++ frontend/src/app/competition/competition.module.ts | 6 +----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/collection/collection-list/collection-list.component.html b/frontend/src/app/collection/collection-list/collection-list.component.html index 9407aa721..99693b70f 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.html +++ b/frontend/src/app/collection/collection-list/collection-list.component.html @@ -34,7 +34,7 @@

    Media Collections

    - diff --git a/frontend/src/app/collection/collection-list/collection-list.component.ts b/frontend/src/app/collection/collection-list/collection-list.component.ts index d72653c79..3122b39fe 100644 --- a/frontend/src/app/collection/collection-list/collection-list.component.ts +++ b/frontend/src/app/collection/collection-list/collection-list.component.ts @@ -6,6 +6,9 @@ import { CollectionBuilderDialogComponent } from '../collection-builder/collecti import { filter, mergeMap } from 'rxjs/operators'; import {ApiMediaCollection, CollectionService} from '../../../../openapi'; +/** + * @deprecated + */ @Component({ selector: 'app-collection-list', templateUrl: './collection-list.component.html', diff --git a/frontend/src/app/competition/competition.module.ts b/frontend/src/app/competition/competition.module.ts index abf21bf40..6a0c92aeb 100644 --- a/frontend/src/app/competition/competition.module.ts +++ b/frontend/src/app/competition/competition.module.ts @@ -1,11 +1,9 @@ import { NgModule } from '@angular/core'; -import { CompetitionListComponent } from './competition-list/competition-list.component'; import { MatTableModule } from '@angular/material/table'; import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { CompetitionCreateDialogComponent } from './competition-list/competition-create-dialog.component'; import { MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; @@ -15,7 +13,6 @@ import { MatListModule } from '@angular/material/list'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatMenuModule } from '@angular/material/menu'; import { CompetitionBuilderModule } from './competition-builder/competition-builder.module'; -import { CompetitionStartDialogComponent } from './competition-list/competition-start-dialog.component'; import { MatSelectModule } from '@angular/material/select'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { SharedModule } from '../shared/shared.module'; @@ -42,8 +39,7 @@ import {MatChipsModule} from "@angular/material/chips"; SharedModule, MatChipsModule, ], - exports: [CompetitionListComponent], - declarations: [CompetitionListComponent, CompetitionCreateDialogComponent, CompetitionStartDialogComponent], + exports: [], providers: [], }) export class CompetitionModule {} From 803ca38a9ad8124ba390719dbbca552b21d042f1 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 23:16:18 +0200 Subject: [PATCH 410/498] ImportSegmentsCommand: getCollection transaction-ised --- .../src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 192bf18ea..82a9c95c3 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -723,7 +723,7 @@ class MediaCollectionCommand(private val store: TransientEntityStore, private va /* Find media collection. */ - val collection = this.getCollection() + val collection = this@MediaCollectionCommand.store.transactional { this.getCollection() } if (collection == null) { println("Collection not found.") return From a89142184ae9f68dea0fd6fb496883f71d372fb0 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 23:21:55 +0200 Subject: [PATCH 411/498] #421: Cleanup lifecycle hooks --- .../src/app/judgement/judgement-media-viewer.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.ts b/frontend/src/app/judgement/judgement-media-viewer.component.ts index ce766652a..55078ad52 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-media-viewer.component.ts @@ -1,4 +1,4 @@ -import { AfterViewChecked, AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewChecked, AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { AppConfig } from '../app.config'; import { ApiAnswerType, ApiJudgementRequest } from "../../../openapi"; @@ -8,7 +8,7 @@ import { ApiAnswerType, ApiJudgementRequest } from "../../../openapi"; templateUrl: './judgement-media-viewer.component.html', styleUrls: ['./judgement-media-viewer.component.scss'], }) -export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, AfterViewChecked { +export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterViewChecked { /** * The zero-based index in the answerset to which this viewer is for @@ -63,7 +63,7 @@ export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, return req?.answerSet?.answers[index]?.type } - ngAfterViewInit(): void { + ngOnInit(): void { /* Handling request */ this.requestSub = this.req.subscribe((req) => { if (req != null) { @@ -245,7 +245,7 @@ export class JudgementMediaViewerComponent implements AfterViewInit, OnDestroy, onTemporalContextToggle(event){ /* Reload everything to correctly recalculate the temporal context (either if its enabled or disabled) */ this.stop(); - this.ngAfterViewInit(); + this.ngOnInit(); } private resolvePath(req: ApiJudgementRequest,index: number, time = true): string { From c09839838dd297c15f4ab2b0cd945859e6cf706d Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 27 Jul 2023 23:49:24 +0200 Subject: [PATCH 412/498] Various documentation and cleanups --- .../dev/dres/data/model/submissions/DbAnswerSet.kt | 3 ++- .../dev/dres/data/model/submissions/DbSubmission.kt | 5 +++-- .../dres/data/model/submissions/DbVerdictStatus.kt | 13 +++++++++---- .../dev/dres/data/model/submissions/Submission.kt | 4 +++- .../judgement-voting-viewer.component.html | 2 +- .../judgement/judgement-voting-viewer.component.ts | 6 +++++- .../team-builder-dialog.component.html | 2 +- 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt index 6e40c7e91..cd1f26327 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbAnswerSet.kt @@ -9,10 +9,11 @@ import kotlinx.dnq.* import kotlinx.dnq.query.asSequence /** - * A [DbVerdictStatus] as submitted by a competition participant. Makes a statement about a [DbTask]. + * A [DbAnswerSet] as submitted by a evaluation participant. This is a portion of a proposed 'solution' for a [DbTask]. * * @author Ralph Gasser * @author Luca Rossetto + * @author Loris Sauter * @version 2.0.0 */ class DbAnswerSet(entity: Entity) : PersistentEntity(entity), AnswerSet { diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt index 5253d3751..12bae742d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbSubmission.kt @@ -4,7 +4,6 @@ import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.UserId -import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import jetbrains.exodus.entitystore.Entity @@ -13,12 +12,14 @@ import kotlinx.dnq.query.asSequence import kotlinx.dnq.simple.min /** - * A [DbSubmission] as submitted by a competition participant and received by DRES. + * A [DbSubmission] as submitted by an evaluation participant and received by DRES. + * Essentially, this is a proposed _solution_ for the referenced [Task]s (via [DbAnswerSet]s) * * Contains one to N [DbAnswerSet]s regarding a [Task]. * * @author Ralph Gasser * @author Luca Rossetto + * @author Loris Sauter * @version 2.0.0 */ class DbSubmission(entity: Entity) : PersistentEntity(entity), Submission { diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt index bbd1f9f72..984a86dc6 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/DbVerdictStatus.kt @@ -11,14 +11,19 @@ import kotlinx.dnq.xdRequiredStringProp * Status of a [DbSubmission] with respect to its validation. * * @author Luca Rossetto + * @author Loris Sauter * @version 2.0.0 */ class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType() { - val CORRECT by enumField { description = VerdictStatus.CORRECT.name } /** Submission has been deemed as correct. */ + /** Submission has been deemed as correct. */ + val CORRECT by enumField { description = VerdictStatus.CORRECT.name } + /** Submission has been deemed as wrong. */ val WRONG by enumField { description = VerdictStatus.WRONG.name } - val INDETERMINATE by enumField { description = VerdictStatus.INDETERMINATE.name } /** Submission has been deemed as wrong. */ - val UNDECIDABLE by enumField { description = VerdictStatus.UNDECIDABLE.name } /** Submission has not been validated yet. */ + /** Submission has not been validated yet. */ + val INDETERMINATE by enumField { description = VerdictStatus.INDETERMINATE.name } + /** Submission has been deemed as undecidable. The semantic of this depends on the consumer of this information */ + val UNDECIDABLE by enumField { description = VerdictStatus.UNDECIDABLE.name } /** * Returns a list of all [DbRole] values. @@ -53,4 +58,4 @@ class DbVerdictStatus(entity: Entity) : XdEnumEntity(entity) { override fun toString(): String = this.description -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt index 325046551..8c376d476 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/submissions/Submission.kt @@ -7,11 +7,13 @@ typealias SubmissionId = String /** * A [Submission] as issued by a DRES user. + * Essentially a proposed solution to a certain task. The task's reference is linked via the answer sets. * * This abstraction is mainly required to enable testability of implementations. * * @author Luca Rossetto * @author Ralph Gasser + * @author Loris Sauter * @version 2.0.0 */ interface Submission { @@ -31,4 +33,4 @@ interface Submission { * Returns a [Sequence] of [AnswerSet]s. */ fun answerSets(): Sequence -} \ No newline at end of file +} diff --git a/frontend/src/app/judgement/judgement-voting-viewer.component.html b/frontend/src/app/judgement/judgement-voting-viewer.component.html index 702cbfbe3..aad9c818a 100644 --- a/frontend/src/app/judgement/judgement-voting-viewer.component.html +++ b/frontend/src/app/judgement/judgement-voting-viewer.component.html @@ -10,7 +10,7 @@

    Vote for Submission

    - +
    diff --git a/frontend/src/app/judgement/judgement-voting-viewer.component.ts b/frontend/src/app/judgement/judgement-voting-viewer.component.ts index ca8e7e191..791886960 100644 --- a/frontend/src/app/judgement/judgement-voting-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-voting-viewer.component.ts @@ -50,7 +50,7 @@ export class JudgementVotingViewerComponent implements OnInit, OnDestroy { if (req.status === 202) { this.isJudgmentAvailable = false; this.judgementRequest = null; - this.judgePlayer.stop(); + this.judgePlayer?.stop(); console.log('currently nothing for audience to vote on'); return null; } else { @@ -91,6 +91,10 @@ export class JudgementVotingViewerComponent implements OnInit, OnDestroy { }); } + allAnswers(){ + return this.observableJudgementRequest?.value?.answerSet?.answers || [] + } + ngOnDestroy(): void { this.requestSub.unsubscribe(); this.requestSub = null; diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html index c211a0d20..0d4f52cc8 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html @@ -6,7 +6,7 @@

    Add team

    The logo of the team - +
    From 763fcfa551448e40843ab9471c216b80f58f07de Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 28 Jul 2023 00:18:14 +0200 Subject: [PATCH 413/498] #415: Minor beautification --- frontend/src/app/run/run-admin-view.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index ceeb16606..52f83b73e 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -36,8 +36,8 @@ > skip_next - Time left: {{ toFormattedTime((run | async)?.state.timeLeft) }}Time left: {{ toFormattedTime((run | async)?.state.timeLeft) }}
    From 35db6f34c210c5e2617fd28a07ebcca395d47e3b Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 28 Jul 2023 09:23:05 +0200 Subject: [PATCH 416/498] Codecleanup: removed deprecated admin submission view --- .../run-admin-submissions-list.component.html | 143 ---------------- .../run-admin-submissions-list.component.scss | 0 .../run-admin-submissions-list.component.ts | 156 ------------------ frontend/src/app/run/run.module.ts | 2 - 4 files changed, 301 deletions(-) delete mode 100644 frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html delete mode 100644 frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.scss delete mode 100644 frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html deleted file mode 100644 index b47ce2612..000000000 --- a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.html +++ /dev/null @@ -1,143 +0,0 @@ -
    - -
    -

    Submission overview for task {{ (taskId | async).substr(0, 8) }}

    -
    -
    - -
    - Poll for updates (every {{ pollingFrequencyFactor }}ms) - -
    - - Anonymize - -
    -
    - - - - Submissions of task run {{ id.substring(0, 8) }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ID - {{ submission.id.substring(0, 8) }} - Timestamp - {{ submission.timestamp | epoch2date | date: 'dd.MM.yyyy HH:mm:ss' }} - Submitted By - {{ submission.memberId.substring(0, 8) }} - {{ submission.memberName }}, {{ submission.teamName }} ({{ submission.memberId.substring(0, 8) }}) - Status{{ submission.status }}Text{{ submission.text != null ? submission?.text : 'N/A' }}Media Item - {{ submission.item != null ? submission?.item?.name + ' (' + submission?.item?.type + ')' : 'N/A' }} - Start - {{ submission.start != null ? (submission.start | formatTime) : 'N/A' }}ms - End - {{ submission.end != null ? (submission.end | formatTime) : 'N/A' }}ms - Preview - preview - Actions - - - check - - - close - - - help_outline - - - - -
    -
    -
    -
    diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.scss b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts b/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts deleted file mode 100644 index cb56d24f6..000000000 --- a/frontend/src/app/run/run-admin-submissions-list/run-admin-submissions-list.component.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { MatDialog } from '@angular/material/dialog'; -import { MatTable, MatTableDataSource } from '@angular/material/table'; -import { merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; -import { ActivatedRoute } from '@angular/router'; -import { MatButtonToggleGroup } from '@angular/material/button-toggle'; -import { catchError, filter, map, switchMap, withLatestFrom } from 'rxjs/operators'; -import { MatPaginator } from '@angular/material/paginator'; -import { AppConfig } from '../../app.config'; -import { ApiSubmission, ApiSubmissionInfo, ApiVerdictStatus, EvaluationAdministratorService} from 'openapi'; - -/** - * @deprecated Replaced by evaluation/submission/submission-list-component - */ -@Component({ - selector: 'app-run-admin-submissions-list', - templateUrl: './run-admin-submissions-list.component.html', - styleUrls: ['./run-admin-submissions-list.component.scss'], -}) -export class RunAdminSubmissionsListComponent implements AfterViewInit, OnDestroy { - // FIXME heavily changed data model, rewrite! - - @ViewChild('group', { static: true }) group: MatButtonToggleGroup; - - @ViewChild('table', { static: true }) table: MatTable; - - @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; - - /** ID of the evaluation run shown by this {@link RunAdminSubmissionsListComponent}. */ - public runId: Observable; - - /** ID of the task shown by this {@link RunAdminSubmissionsListComponent}. */ - public taskId: Observable; - - /** The columns displayed by the table. */ - public displayColumns = [ - 'id', - /*'taskRunId',*/ 'timestamp', - 'submitted', - 'text', - 'item', - 'start', - 'end', - 'status', - 'preview', - 'actions', - ]; - - /** Number of milliseconds to wait in between polls. */ - public pollingFrequencyFactor = 30000; // every 30 seconds - - /** Flag indicating whether list of submissions should be polled. */ - public polling = true; - - /** Flag indicating whether information about the submitter should be anonymized. */ - public anonymize = true; - - /** Subject used to manually trigger a refresh. */ - public refreshSubject: Subject = new Subject(); - - /** The data source for the table. */ - public dataSource: MatTableDataSource = new MatTableDataSource(); - - /** The data sources mapped by the taskRunId */ - public dataSources: Map> = new Map(); - - /** The list of taskRunIds there are submissions for, for convenience in the template */ - public taskRunIds: string[] = []; - - /** Subscription held by this component to load submissions. */ - private subscription: Subscription; - - constructor( - private snackBar: MatSnackBar, - private dialog: MatDialog, - private activeRoute: ActivatedRoute, - private runService: EvaluationAdministratorService, - public config: AppConfig - ) { - this.runId = this.activeRoute.paramMap.pipe(map((params) => params.get('runId'))); - this.taskId = this.activeRoute.paramMap.pipe(map((params) => params.get('taskId'))); - } - - /** - * Register subscription for submission data; - * - * TODO: In this implementation, pagination is done on the client side! - */ - ngAfterViewInit() { - this.dataSource.paginator = this.paginator; - this.subscription = merge(timer(0, this.pollingFrequencyFactor).pipe(filter((i) => this.polling)), this.refreshSubject) - .pipe( - withLatestFrom(this.runId, this.taskId), - switchMap(([i, r, t]) => this.runService.getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId(r, t)), - catchError((err, o) => { - console.log(`[RunAdminSubmissionListComponent] Error occurred while loading submissions: ${err?.message}`); - this.snackBar.open(`Error: ${err?.message}`, null, { duration: 5000 }); - return of([]); - }) - ) - .subscribe((s: ApiSubmissionInfo[]) => { - // this.dataSource.data = s; - /* Clear lists first */ - this.dataSources.clear(); - this.taskRunIds = []; - /* Repopulate */ - s.forEach((trsi) => { - const ds = new MatTableDataSource(); - ds.data = trsi.submissions; - this.dataSources.set(trsi.taskId, ds); // FIXME - this.taskRunIds.push(trsi.taskId); - }); - }); - } - - /** - * House keeping; clean up subscriptions. - */ - ngOnDestroy(): void { - this.subscription.unsubscribe(); - this.subscription = null; - } - - /** - * Updates the status of a Submission - * - * @param submission The {@link SubmissionInfo} to update. - * @param newStatus The new status. - */ - public update(submission: ApiSubmission, newStatus:ApiVerdictStatus) { - // FIXME heavily changed model. rewrite - /*submission.status = newStatus; - console.log(submission); - this.runId - .pipe(switchMap((runId) => this.runService.patchApiV1RunAdminWithRunidSubmissionOverride(runId, submission))) - .subscribe((res) => { - this.snackBar.open(`Submission ${res.id} successfully updated to ${res.status}.`, null, { duration: 5000 }); - });*/ - } - - /** - * Generates a URL for the preview image of a submission. - */ - public previewForSubmission(submission: ApiSubmission): Observable { - return this.runId.pipe(map((runId) => this.config.resolveApiUrl(`/preview/submission/${runId}/${submission.submissionId}`))); - } - - resolveIdBySelf(_: number, item: string) { - return item; - } - - resolveSubmissionById(_: number, item: ApiSubmission) { - return item.submissionId; - } -} diff --git a/frontend/src/app/run/run.module.ts b/frontend/src/app/run/run.module.ts index 284a35830..07ea1d593 100644 --- a/frontend/src/app/run/run.module.ts +++ b/frontend/src/app/run/run.module.ts @@ -19,7 +19,6 @@ import { ViewerRunListComponent } from './viewer-run-list.component'; import { RunAdminViewComponent } from './run-admin-view.component'; import { MatCardModule } from '@angular/material/card'; import { SharedModule } from '../shared/shared.module'; -import { RunAdminSubmissionsListComponent } from './run-admin-submissions-list/run-admin-submissions-list.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; @@ -65,7 +64,6 @@ import { RunAdminToolbarComponent } from './run-admin-toolbar/run-admin-toolbar. RunScoreHistoryComponent, AdminRunListComponent, ViewerRunListComponent, - RunAdminSubmissionsListComponent, RunAsyncAdminViewComponent, RunAdminToolbarComponent, ], From 42a1db2952677011cec35087ba0c39a31ade5f31 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 28 Jul 2023 11:14:52 +0200 Subject: [PATCH 417/498] TemporalOverlapAnswerSetValidator now supports multiple targets --- .../data/model/run/AbstractInteractiveTask.kt | 4 ++-- .../model/run/AbstractNonInteractiveTask.kt | 4 ++-- .../TemporalContainmentAnswerSetValidator.kt | 19 ++++++++++-------- .../TemporalOverlapAnswerSetValidator.kt | 20 +++++++++++-------- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index c0e0ffc76..ce9a41e73 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -35,8 +35,8 @@ abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { this.validator = when (val targetOption = this.template.taskGroup.type.target) { DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(this.template.targets.filter { it.item ne null }.mapDistinct { it.item }.toSet()) DbTargetOption.MEDIA_SEGMENT -> { - val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() - TemporalOverlapAnswerSetValidator(TransientMediaSegment(target.item!!, target.range!!)) + val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() + TemporalOverlapAnswerSetValidator(target) } DbTargetOption.TEXT -> TextAnswerSetValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) DbTargetOption.JUDGEMENT -> { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index b278c8227..acb284460 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -25,8 +25,8 @@ abstract class AbstractNonInteractiveTask(task: DbTask): AbstractTask(task) { this.validator = when (val targetOption = this.template.taskGroup.type.target) { DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) DbTargetOption.MEDIA_SEGMENT -> { - val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.take(1) .first() - TemporalOverlapAnswerSetValidator(TransientMediaSegment(target.item!!, target.range!!)) + val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() + TemporalOverlapAnswerSetValidator(target) } DbTargetOption.TEXT -> TextAnswerSetValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) DbTargetOption.JUDGEMENT -> { diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt index 9ca058e1e..31225671b 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt @@ -12,7 +12,8 @@ import kotlinx.dnq.query.iterator * @author Ralph Gasser * @version 2.0.0 */ -class TemporalContainmentAnswerSetValidator(private val targetSegment: TransientMediaSegment) : AnswerSetValidator { +class TemporalContainmentAnswerSetValidator(private val targetSegments: Collection) : + AnswerSetValidator { override val deferring: Boolean get() = false @@ -39,14 +40,16 @@ class TemporalContainmentAnswerSetValidator(private val targetSegment: Transient return } - /* Perform item validation. */ - if (item.mediaItemId != this.targetSegment.first.mediaItemId) { - return - } + if (targetSegments.any { targetSegment -> + /* Perform item validation. */ + if (item.mediaItemId != targetSegment.first.mediaItemId) { + return@any false + } - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if (outer.first > start || outer.second < end) { + /* Perform temporal validation. */ + val outer = targetSegment.second.toMilliseconds() + !(outer.first > start || outer.second < end) + }) { return } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt index 08d7fec3a..1a18ff517 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt @@ -17,7 +17,7 @@ typealias TransientMediaSegment = Pair * @author Ralph Gasser * @version 2.0.0 */ -class TemporalOverlapAnswerSetValidator(private val targetSegment: TransientMediaSegment) : AnswerSetValidator { +class TemporalOverlapAnswerSetValidator(private val targetSegments: Collection) : AnswerSetValidator { override val deferring: Boolean = false @@ -42,16 +42,20 @@ class TemporalOverlapAnswerSetValidator(private val targetSegment: TransientMedi return } - /* Perform item validation. */ - if (item.id != this.targetSegment.first.mediaItemId) { - return - } + if (targetSegments.any { targetSegment -> + /* Perform item validation. */ + if (item.id != targetSegment.first.mediaItemId) { + return@any false + } - /* Perform temporal validation. */ - val outer = this.targetSegment.second.toMilliseconds() - if (!((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end))) { + /* Perform temporal validation. */ + val outer = targetSegment.second.toMilliseconds() + + return@any (outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end) + }) { return } + } /* If code reaches this point, the [DbAnswerSet] is correct. */ From 869656713cb0b53591737cc01a7521768fa237df Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 28 Jul 2023 12:02:47 +0200 Subject: [PATCH 418/498] #415: Properly using the T image for textual submissions. When results arent hidden, a tooltip is displayed --- .../src/app/viewer/teams-viewer.component.html | 2 +- frontend/src/immutable/assets/images/missing.png | Bin 0 -> 1405 bytes frontend/src/immutable/assets/images/text.png | Bin 0 -> 2354 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 frontend/src/immutable/assets/images/missing.png create mode 100644 frontend/src/immutable/assets/images/text.png diff --git a/frontend/src/app/viewer/teams-viewer.component.html b/frontend/src/app/viewer/teams-viewer.component.html index 1ed5f35b1..81c9d1545 100644 --- a/frontend/src/app/viewer/teams-viewer.component.html +++ b/frontend/src/app/viewer/teams-viewer.component.html @@ -37,7 +37,7 @@

    {{ team.name }}

    ?

    - A texticon, the actual submission text is displayed via tooltip + A texticon, the actual submission text is displayed via tooltip
    diff --git a/frontend/src/immutable/assets/images/missing.png b/frontend/src/immutable/assets/images/missing.png new file mode 100644 index 0000000000000000000000000000000000000000..3cda7111d3b5f9b00beba0c40386be55756b295c GIT binary patch literal 1405 zcmeAS@N?(olHy`uVBq!ia0vp^0YIF`!3HEZBu*&+QY^(zo*^7SP{WbZ0pxQQctjR6 zFz|JPFk`Zn+C!kAWQl7;NpOBzNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~)?etEh$ zhE&XXJ2yH*CROBky{CCt^+u%?3rq#C+=}X6G;x*2S4WqF-rY&j-C|0QniD?yy6oBW z`$e;UXvWliT?_bKMUDo!3ciXBmhS!@ox9}Hmr2LkKl9Fhvuv|*^|_h8b3Zq4c)Q8+ z|4IG(=GLF*&g4DdejvnmUm4S9gC+W+c5HtVA}jhf3Rvj>t(G=m@`)0i&*ZkY>ulh= zv}Yc77)^AzW{X(1T5$Z~&0*+MQ2yAmEMfmax4B+E4A~8}2htvd@_g$ooposclK=cn z_b<*lr`1_1!qF!5T1z0!t7fKNd1k2?2b<7qJ%K4g&I=4W_E`Hf>IXU{apYW@dPKlt z@+BeX1nZ?LZ3k}7)GO4s6mouWQgTtl5rG;0?wyJojCTU14ci(F(31cSrH(g-HzqOSb7j!tReZ`9ngbJ7<=F}rJ1v+b zutJ4Ji?h)SKXYZ0>#w8)$pVodTt9AFzOPAOEs*-ad#SJFXtwJwzX!olRU8LyAK2i0 zZ|ec;E#K|FF_c?8>0S0qk7>T@e#v5%8yPdo>sM7?ddB}@OQM_w*By_Ieb-nny|`%P zvv1GK`0tWWQg?9VDAZnGlzVV4+syo_CvNxO-CLx`bUk3*daF*8-MgRO30ds(%(rXL z%N-?8_4I%Ks$25%uI_2gdW~&C6R)>zJ&?DoJ# zv;TELVe=W@$MuY#d5sQ!GN}lWSGaekV7Klre-9>CJJs|Df=Jry#H@ zPv+&@cb?Ke@~i3sZ|&2bSo~V`CU{1aa{&6~#^&-P}~PQ4#&m;UG(Et2xM z{LgpR;!n1#~y)@Fm9^cVCGPpEHl6Mk4amES7nPmL0t8 zP{tg2SFm}O>x!5M%qL20d(L}AcCY#E%QRc&)%Kv32c##;ztbp~oqX=CW-`-`dxq>< zB0E~11^ssJvy|*#vZnUH%s$JhoHd52Ipt?EQr%D095h@k)VO*=+==%GBK;&c7n*%g z|Mc@ls=m3Vmnzfdi62%3|Fzln!1~i&Ry|j@1Nk21>$1)<{ByV$qF1ou{k@P|4Kj;! zk1k((qw}z4HABRE$tR{v<;!oamdO%-x@rsWd+RG@a}RM(eEEBE;oZC+oHLJcTkYMT zvM#tnV&*=%{XaLoaa#7#RPN|r7CrC&c#Et0ho`^1n7Z|t_k)Bgnc`LPZExIWEA~H% zV-jC}ym0xi)Dznh3*W8Yb;LX2+U6fuSJp{xiay;}&04XSd%fAZm5HXCe5ZZMasBn* zDdt|9BS(l3)6^CRRmA{L(wLL(zAEMUdp~-$v^W!U?TNW|a`Q8z*zNxDzetR^dToDf RC9tSu@O1TaS?83{1OQfixy>o zC?Y`-4NR#;94&H1>j4?DilP(|Q8D1)Af@7gMGf5zSIp=i?PlJ-Z@>5bzT>_3P4)`- z%k6EwYykkU=W^JQ$Sgz0j48;QOFwf8nJ|jTXCS3>82NK#4DQ3?o9F%VG-!Xm6n znkG{)R7{-7f`Q1WnvBDmOyE=|E`}$-219ZXyO=~FQE)6dnQu)Xf^GBVG`(cBMM7bm#mOwHr z>Q|HiZGf3L92#ii8O;-xBuz$=DaOh|3M8W|WGab5{uBnPB%i_1mCs_x9!-?>Q81M# zeH1cn#%PWKXaHwxl$mO$QYiuCPp(*! z!B83`k4hIMf@mNUVy3dRHpPkkY#g%{GZHyRMz9=2+b$Y{(pZ*V7(lkPuz-1xSdz4f zErLN7+OIwonh(`4dc0ppaWk7SI)IGcX0zKp+BzmJ(mO0$1RU=}mTh>$z77DeisG^Z zqg9xG%?+Ydw;i!;pG{>XY_`ZWH|4%xqXmrGaqm* zA?|ev1s1YbeF8kbG=jiQPE1S`iRMYLL1j{@w6n9*!@8jO!Gi~yf`T*np}r%Ij*g*t zUd6$-wzl|zc23vRr;VI{E(S}6sD)cE?QXi!+zh3qojQG*yl9bHK_46(WNq#q9F)WG zD?@dbo4Y%Y&o8yR_PDjRbzq17;lrbAPDhot*U-X^dG_1w92_pBo+vL*0l}Wvulh^G*nhv+I0Q;hnk1n@bK^>wiS|v_;y3ToVE6chK7&X( z#eRM;3|Ci4)$wt0ovp3jPgbm0(cIkZ@r!4zT&22K@+8ZD`m|~FHKC1uY(nvo&4wByBV*6la+_ApPw7{ z-chg7XtY{wOG~6yTN#9$4wK1rcX#)&zIrv#qc;9_0G*!mRVKk*{Jysr58_`1=5_FU zcHBdsHi*l?;nY|x520G{jG=f%w@~^`up8UyAo4ViH0&wZmvuFtl%oooX&#o zE1Z{km7JOO+QCwJ(zd43866!R1vmEmVQcSwa+tH1{g=a+1O~ssN$X1TDZu+P_|y{k zvew&G8>%OSs`=ID#3gromo{X!*yJCIP wWrT+>4BLMoDhpqfF1g13`S$vK&K6EbFs*&WHx7m+Rp?(oH-yiw2wJ!6U#VeZ5dZ)H literal 0 HcmV?d00001 From 6e9cd0f5ab53b3de9ec52b58ca553e1eae3ee314 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 28 Jul 2023 12:06:53 +0200 Subject: [PATCH 419/498] Some fixes to get async runs to start again --- .../admin/CreateEvaluationHandler.kt | 8 +- .../run/InteractiveAsynchronousRunManager.kt | 88 ++++++++++++++++--- 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index a93802a8d..5586cc936 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -83,7 +83,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca val path = item.pathToOriginal() if (!Files.exists(path)) { logger.error("Required media file $path not found for item ${item.name}.") - //throw ErrorStatusException(500, "Required media file $path not found for item ${item.name}.", ctx) + throw ErrorStatusException(500, "Required media file $path not found for item ${item.name}.", ctx) } this@CreateEvaluationHandler.cache.asyncPreviewVideo(item, source.range.start.toMilliseconds(), source.range.end.toMilliseconds()) @@ -92,21 +92,19 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca val path = DRES.EXTERNAL_ROOT.resolve(source.path) if (!Files.exists(path)) { logger.error("ERROR: Media file $path not found for external video.") - //throw ErrorStatusException(500, "Required media file $path not found.", ctx) + throw ErrorStatusException(500, "Required media file $path not found.", ctx) } this@CreateEvaluationHandler.cache.asyncPreviewVideo(path, source.range.start.toMilliseconds(), source.range.end.toMilliseconds()) } } - } await.all { try { it.get(60, TimeUnit.SECONDS) true } catch (e: Throwable) { - //throw ErrorStatusException(500, "Required media file could not be prepared.", ctx) - true + throw ErrorStatusException(500, "Required media file could not be prepared.", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index b6450d6c3..687051b6c 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -15,7 +15,6 @@ import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.data.model.template.task.options.DbSubmissionOption -import dev.dres.data.model.template.task.options.Defaults.SCOREBOARD_UPDATE_INTERVAL_DEFAULT import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT import dev.dres.run.audit.AuditLogSource @@ -26,8 +25,13 @@ import dev.dres.run.score.ScoreTimePoint import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.updatables.* import dev.dres.run.validation.interfaces.JudgementValidator +import jetbrains.exodus.database.TransientEntityChange import jetbrains.exodus.database.TransientEntityStore +import jetbrains.exodus.database.TransientStoreSession +import jetbrains.exodus.database.TransientStoreSessionListener +import jetbrains.exodus.database.exceptions.DataIntegrityViolationException import kotlinx.dnq.query.* +import kotlinx.dnq.util.findById import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.* @@ -51,7 +55,7 @@ import kotlin.math.max class InteractiveAsynchronousRunManager( override val evaluation: InteractiveAsynchronousEvaluation, override val store: TransientEntityStore -) : InteractiveRunManager { +) : InteractiveRunManager, TransientStoreSessionListener { companion object { /** The [Logger] instance used by [InteractiveAsynchronousRunManager]. */ @@ -100,7 +104,7 @@ class InteractiveAsynchronousRunManager( } else { RunManagerStatus.CREATED } - private set + private set /** Returns list [JudgementValidator]s associated with this [InteractiveAsynchronousRunManager]. May be empty! */ override val judgementValidators: List @@ -323,7 +327,10 @@ class InteractiveAsynchronousRunManager( currentTaskRun.prepare() /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(teamId, ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId)) + RunExecutor.broadcastWsMessage( + teamId, + ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId) + ) LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") } @@ -434,10 +441,11 @@ class InteractiveAsynchronousRunManager( * @param context The [RunActionContext] used for the invocation. * @return [InteractiveAsynchronousEvaluation.IATaskRun] that is currently active or null, if no such task is active. */ - override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = this.stateLock.read { - val teamId = context.teamId() - return this.evaluation.currentTaskForTeam(teamId) - } + override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = + this.stateLock.read { + val teamId = context.teamId() + return this.evaluation.currentTaskForTeam(teamId) + } /** * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. @@ -467,7 +475,8 @@ class InteractiveAsynchronousRunManager( val teamId = context.teamId() require(teamHasRunningTask(teamId)) { "No running task for Team $teamId." } - val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("No active TaskRun found. This is a serious error!") + val currentTaskRun = this.currentTask(context) + ?: throw IllegalStateException("No active TaskRun found. This is a serious error!") val newDuration = currentTaskRun.duration + s check((newDuration * 1000L - (System.currentTimeMillis() - currentTaskRun.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } currentTaskRun.duration = newDuration @@ -565,7 +574,11 @@ class InteractiveAsynchronousRunManager( * * @return Whether the update was successful or not */ - override fun updateSubmission(context: RunActionContext, submissionId: SubmissionId, submissionStatus: ApiVerdictStatus): Boolean = this.stateLock.read { + override fun updateSubmission( + context: RunActionContext, + submissionId: SubmissionId, + submissionStatus: ApiVerdictStatus + ): Boolean = this.stateLock.read { val answerSet = DbAnswerSet.filter { it.submission.submissionId eq submissionId }.singleOrNull() ?: return false /* Actual update - currently, only status update is allowed */ @@ -671,7 +684,8 @@ class InteractiveAsynchronousRunManager( */ private fun invokeUpdatables() = this.stateLock.read { for (teamId in this.teamIds) { - val runStatus = this.statusMap[teamId] ?: throw IllegalStateException("Run status for team $teamId is not set. This is a programmers error!") + val runStatus = this.statusMap[teamId] + ?: throw IllegalStateException("Run status for team $teamId is not set. This is a programmers error!") val taskStatus = this.evaluation.currentTaskForTeam(teamId)?.status?.toApi() for (updatable in this.updatables) { if (updatable.shouldBeUpdated(runStatus, taskStatus)) { @@ -726,7 +740,8 @@ class InteractiveAsynchronousRunManager( */ private fun registerOptionalUpdatables() { /* Determine if task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options }.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options } + .filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) } @@ -775,7 +790,54 @@ class InteractiveAsynchronousRunManager( */ private fun RunActionContext.teamId(): TeamId { val userId = this.userId - return this@InteractiveAsynchronousRunManager.template.teams.filter { it.users.filter { it.id eq userId }.isNotEmpty() }.firstOrNull()?.teamId + return this@InteractiveAsynchronousRunManager.template.teams.asSequence().firstOrNull { team -> + team.users.filter { it.id eq userId }.isNotEmpty + }?.teamId ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") } + + override fun afterConstraintsFail( + session: TransientStoreSession, + exceptions: Set + ) { + /* nop */ + } + + override fun beforeFlushBeforeConstraints( + session: TransientStoreSession, + changedEntities: Set + ) { + /* nop */ + } + + /** + * [TransientStoreSessionListener] implementation: Reacts to changes to the [DbAnswerSet] entity and invalidates [Scoreboard]s and [Scorer]s if necessary. + * + * @param session The [TransientStoreSession] that triggered the invocation. + * @param changedEntities The [TransientEntityChange]s for the transaction. + */ + override fun flushed(session: TransientStoreSession, changedEntities: Set) { + if (!session.isReadonly) { + for (c in changedEntities) { + if (c.transientEntity.persistentEntity.type == "DbAnswerSet") { + val xdId = c.transientEntity.persistentEntity.id.toString() + val task = DbAnswerSet.findById(xdId).task + if (this.evaluation.id == task.evaluation.id) { + val t = this.taskForId(RunActionContext.INTERNAL, task.id) + if (t != null) { + t.scorer.invalidate() + this.scoreboards.forEach { it.invalidate() } + RunExecutor.broadcastWsMessage( + ServerMessage( + this.id, + ServerMessageType.TASK_UPDATED, + task.id + ) + ) + } + } + } + } + } + } } From c254c3db3d75f23bed15746492306f8a1a347e9f Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 28 Jul 2023 12:53:50 +0200 Subject: [PATCH 420/498] Explicitly checking for terminated state in handler --- .../handler/evaluation/viewer/GetEvaluationStateHandler.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index 80e7b02c8..8991df80a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -10,6 +10,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunManagerStatus import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -37,6 +38,9 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation ) override fun doGet(ctx: Context): ApiEvaluationState = this.store.transactional (true) { val manager = ctx.eligibleManagerForId() + if (manager.status == RunManagerStatus.TERMINATED) { + throw ErrorStatusException(404, "Evaluation has ended.", ctx) + } if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } From 230d8fd5eb7fe4e0ef2af3297b7bbc4187516a18 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 28 Jul 2023 16:00:17 +0200 Subject: [PATCH 421/498] #431: Added task controls for viewers in async case --- .../src/app/evaluation/evaluation.module.ts | 9 +- .../task-controls.component.html | 61 ++++++++++ .../task-controls.component.scss | 0 .../task-controls/task-controls.component.ts | 112 ++++++++++++++++++ frontend/src/app/run/run.module.ts | 2 + .../src/app/viewer/run-viewer.component.html | 2 + frontend/src/app/viewer/viewer.module.ts | 4 +- 7 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 frontend/src/app/evaluation/task-controls/task-controls.component.html create mode 100644 frontend/src/app/evaluation/task-controls/task-controls.component.scss create mode 100644 frontend/src/app/evaluation/task-controls/task-controls.component.ts diff --git a/frontend/src/app/evaluation/evaluation.module.ts b/frontend/src/app/evaluation/evaluation.module.ts index 79ef1c990..ce076939b 100644 --- a/frontend/src/app/evaluation/evaluation.module.ts +++ b/frontend/src/app/evaluation/evaluation.module.ts @@ -13,6 +13,8 @@ import { MatExpansionModule } from "@angular/material/expansion"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { TemplateInfoComponent } from './admin/submission/template-info/template-info.component'; +import { TaskControlsComponent } from './task-controls/task-controls.component'; +import { MatMenuModule } from "@angular/material/menu"; @@ -23,6 +25,7 @@ import { TemplateInfoComponent } from './admin/submission/template-info/template SubmissionsDetailsComponent, SubmissionsListComponent, TemplateInfoComponent, + TaskControlsComponent, ], imports: [ CommonModule, @@ -33,7 +36,11 @@ import { TemplateInfoComponent } from './admin/submission/template-info/template MatSlideToggleModule, MatExpansionModule, FormsModule, - MatCardModule + MatCardModule, + MatMenuModule + ], + exports:[ + TaskControlsComponent ] }) export class EvaluationModule { } diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.html b/frontend/src/app/evaluation/task-controls/task-controls.component.html new file mode 100644 index 000000000..3ddbb474f --- /dev/null +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.html @@ -0,0 +1,61 @@ + + + + + + +
    + Time left: {{ (runState | async)?.timeLeft | formatTime:false }} +
    + + + + + + + + + +
    diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.scss b/frontend/src/app/evaluation/task-controls/task-controls.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.ts b/frontend/src/app/evaluation/task-controls/task-controls.component.ts new file mode 100644 index 000000000..65d92224e --- /dev/null +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.ts @@ -0,0 +1,112 @@ +import { Component, Input, OnInit } from "@angular/core"; +import { Observable, Subject } from "rxjs"; +import { CombinedRun } from "../../run/run-admin-view.component"; +import { map, switchMap } from "rxjs/operators"; +import { ActivatedRoute, Router } from "@angular/router"; +import { ApiEvaluationState, EvaluationAdministratorService, EvaluationService, TemplateService } from "../../../../openapi"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { MatDialog } from "@angular/material/dialog"; +import { + ConfirmationDialogComponent, + ConfirmationDialogComponentData +} from "../../shared/confirmation-dialog/confirmation-dialog.component"; +import { AuthenticationService } from "../../services/session/authentication.sevice"; + +@Component({ + selector: 'app-task-controls', + templateUrl: './task-controls.component.html', + styleUrls: ['./task-controls.component.scss'] +}) +export class TaskControlsComponent implements OnInit{ + + @Input() runState: Observable; + @Input() refreshSubject: Subject = new Subject(); + + @Input() showTime: boolean = false; + + runId: Observable; + + constructor( + private runAdminService: EvaluationAdministratorService, + private dialog: MatDialog, + private snackBar: MatSnackBar, + public authenticationService: AuthenticationService + ) { + } + + ngOnInit(): void { + this.runId = this.runState?.pipe(map((s) => s.evaluationId)) + } + public startTask() { + this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskStart(id))).subscribe( + (r) => { + this.refreshSubject.next(); + this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + }, + (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + } + ); + } + + public nextTask() { + this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskNext(id))).subscribe( + (r) => { + this.refreshSubject.next(); + this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + }, + (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + } + ); + } + + public previousTask() { + this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskPrevious(id))).subscribe( + (r) => { + this.refreshSubject.next(); + this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + }, + (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + } + ); + } + + public abortTask() { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + text: 'Really end the task?', + color: 'warn', + } as ConfirmationDialogComponentData, + }); + dialogRef.afterClosed().subscribe((result) => { + if (result) { + this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskAbort(id))).subscribe( + (r) => { + this.refreshSubject.next(); + this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + }, + (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + } + ); + } + }); + } + + public adjustDuration(duration: number) { + this.runId + .pipe(switchMap((id) => this.runAdminService.patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration(id, duration))) + .subscribe( + (r) => { + this.refreshSubject.next(); + this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); + }, + (r) => { + this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); + } + ); + } + +} diff --git a/frontend/src/app/run/run.module.ts b/frontend/src/app/run/run.module.ts index 07ea1d593..e332a4cd4 100644 --- a/frontend/src/app/run/run.module.ts +++ b/frontend/src/app/run/run.module.ts @@ -29,6 +29,7 @@ import { MatExpansionModule } from '@angular/material/expansion'; import { RunAsyncAdminViewComponent } from './run-async-admin-view/run-async-admin-view.component'; import { MatToolbarModule } from '@angular/material/toolbar'; import { RunAdminToolbarComponent } from './run-admin-toolbar/run-admin-toolbar.component'; +import { EvaluationModule } from "../evaluation/evaluation.module"; @NgModule({ imports: [ @@ -56,6 +57,7 @@ import { RunAdminToolbarComponent } from './run-admin-toolbar/run-admin-toolbar. NgApexchartsModule, MatExpansionModule, MatToolbarModule, + EvaluationModule ], exports: [RunListComponent, RunAdminViewComponent, RunScoreHistoryComponent], declarations: [ diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index 4df16dac8..c31df56a8 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -7,6 +7,8 @@ Description: {{ (runInfo | async)?.templateDescription }} +
    +
    +
    - - - - -
    + play_circle_outline + + + +
    + skip_previous + - - - - - - - - -
    + +
    + Time left: {{ (runState | async)?.timeLeft | formatTime:false }} +
    + + + + + + + + + +
    +
    diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.ts b/frontend/src/app/evaluation/task-controls/task-controls.component.ts index 65d92224e..f5206fb88 100644 --- a/frontend/src/app/evaluation/task-controls/task-controls.component.ts +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.ts @@ -1,9 +1,9 @@ import { Component, Input, OnInit } from "@angular/core"; import { Observable, Subject } from "rxjs"; import { CombinedRun } from "../../run/run-admin-view.component"; -import { map, switchMap } from "rxjs/operators"; +import { map, switchMap, tap } from "rxjs/operators"; import { ActivatedRoute, Router } from "@angular/router"; -import { ApiEvaluationState, EvaluationAdministratorService, EvaluationService, TemplateService } from "../../../../openapi"; +import { ApiEvaluationState, ApiRole, EvaluationAdministratorService, EvaluationService, TemplateService } from "../../../../openapi"; import { MatSnackBar } from "@angular/material/snack-bar"; import { MatDialog } from "@angular/material/dialog"; import { @@ -24,19 +24,34 @@ export class TaskControlsComponent implements OnInit{ @Input() showTime: boolean = false; - runId: Observable; + @Input() runId?: Observable; + isAdmin: Observable; constructor( + private activatedRoute: ActivatedRoute, private runAdminService: EvaluationAdministratorService, private dialog: MatDialog, private snackBar: MatSnackBar, public authenticationService: AuthenticationService ) { + this.isAdmin = this.authenticationService.user.pipe(map((u) => u?.role === ApiRole.ADMIN)); } ngOnInit(): void { - this.runId = this.runState?.pipe(map((s) => s.evaluationId)) + if(!this.runId){ + this.runId = this.activatedRoute.params.pipe( + tap((a) => console.log("PARAMS:", a)), + map((a) => { + if(a.runId.includes(';')){ + return a.runId.substring(0, a.runId.indexOf(';')) + }else{ + return a.runId + } + }), + tap((runId) => console.log("RUNID: ",runId))); + } } + public startTask() { this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskStart(id))).subscribe( (r) => { diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index e9d25a9af..325192663 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -105,6 +105,7 @@ export class AbstractRunListComponent { }); } + /* public nextTask(runId: string) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskNext(runId).subscribe({ next: (r) => { @@ -128,6 +129,7 @@ export class AbstractRunListComponent { } }); } + */ scoreDownloadProvider = (runId: string) => { return this.downloadService diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index 864f1c6a9..d56be46a0 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { AfterViewInit, Component } from "@angular/core"; import { AbstractRunListComponent, RunInfoWithState } from './abstract-run-list.component'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; @@ -26,7 +26,7 @@ export interface RunInfoOverviewTuple { selector: 'app-admin-run-list', templateUrl: './admin-run-list.component.html', }) -export class AdminRunListComponent extends AbstractRunListComponent { +export class AdminRunListComponent extends AbstractRunListComponent implements AfterViewInit{ constructor( runService: EvaluationService, runAdminService: EvaluationAdministratorService, @@ -62,7 +62,8 @@ export class AdminRunListComponent extends AbstractRunListComponent { if (result) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTerminate(runId).subscribe( (r) => { - this.refreshSubject.next(); + this.refreshSubject.complete(); + this.refreshSubject.unsubscribe(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { @@ -134,4 +135,8 @@ export class AdminRunListComponent extends AbstractRunListComponent { // https://betterprogramming.pub/how-to-turn-an-array-of-observable-into-an-observable-of-array-in-angular-d6cfe42a72d4 ); } + + ngAfterViewInit(): void { + this.initStateUpdates(); + } } diff --git a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts index e6627a22a..ff0f95308 100644 --- a/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts +++ b/frontend/src/app/run/run-admin-toolbar/run-admin-toolbar.component.ts @@ -70,9 +70,10 @@ export class RunAdminToolbarComponent implements OnInit { const runId = this.runId; this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTerminate(runId).subscribe( (r) => { - this.refreshSubject.next(); + this.refreshSubject.complete() + this.refreshSubject.unsubscribe(); this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - this.navigation.back(); + this.navigation.back(true) }, (r) => { this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index 52f83b73e..dec0cfceb 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -1,61 +1,5 @@ - - - - - -
    Time left: {{ toFormattedTime((run | async)?.state.timeLeft) }}
    - - - - - - - - - +
    diff --git a/frontend/src/app/run/run-admin-view.component.ts b/frontend/src/app/run/run-admin-view.component.ts index 2af0dc120..d537289ee 100644 --- a/frontend/src/app/run/run-admin-view.component.ts +++ b/frontend/src/app/run/run-admin-view.component.ts @@ -32,7 +32,7 @@ export interface CombinedRun { export class RunAdminViewComponent { private static VIEWER_POLLING_FREQUENCY = 3 * 1000; //ms - private static STATE_POLLING_FREQUENCY = 5 * 1000; //ms + private static STATE_POLLING_FREQUENCY = 1 * 1000; //ms private static OVERVIEW_POLLING_FREQUENCY = 5 * 1000; //ms runId: Observable; @@ -165,64 +165,11 @@ export class RunAdminViewComponent { shareReplay({ bufferSize: 1, refCount: true }) ); } - - public nextTask() { - this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskNext(id))).subscribe( - (r) => { - this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); + stateFromCombined(combined: Observable): Observable{ + return combined.pipe(map((c) => c.state)) } - public previousTask() { - this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskPrevious(id))).subscribe( - (r) => { - this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); - } - - public startTask() { - this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskStart(id))).subscribe( - (r) => { - this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); - } - public abortTask() { - const dialogRef = this.dialog.open(ConfirmationDialogComponent, { - data: { - text: 'Really end the task?', - color: 'warn', - } as ConfirmationDialogComponentData, - }); - dialogRef.afterClosed().subscribe((result) => { - if (result) { - this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskAbort(id))).subscribe( - (r) => { - this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); - } - }); - } public switchTask(idx: number) { this.runId.pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx(id, idx))).subscribe( @@ -243,20 +190,6 @@ export class RunAdminViewComponent { }); } - public adjustDuration(duration: number) { - this.runId - .pipe(switchMap((id) => this.runAdminService.patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration(id, duration))) - .subscribe( - (r) => { - this.refreshSubject.next(); - this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); - }, - (r) => { - this.snackBar.open(`Error: ${r.error.description}`, null, { duration: 5000 }); - } - ); - } - public forceViewer(viewerId: string) { this.runId .pipe(switchMap((id) => this.runAdminService.postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce(id, viewerId))) @@ -271,16 +204,6 @@ export class RunAdminViewComponent { ); } - public toFormattedTime(sec: number): string { - const hours = Math.floor(sec / 3600); - const minutes = Math.floor(sec / 60) % 60; - const seconds = sec % 60; - - return [hours, minutes, seconds] - .map((v) => (v < 10 ? '0' + v : v)) - .filter((v, i) => v !== '00' || i > 0) - .join(':'); - } /** * Generates a URL for the logo of the team. diff --git a/frontend/src/app/run/viewer-run-list.component.html b/frontend/src/app/run/viewer-run-list.component.html index b54c9dcb6..1ac85c69b 100644 --- a/frontend/src/app/run/viewer-run-list.component.html +++ b/frontend/src/app/run/viewer-run-list.component.html @@ -45,7 +45,7 @@ > flaky - + From d272c1fb42917f0c924ca057c0b31597ffb16ef8 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 28 Jul 2023 19:17:39 +0200 Subject: [PATCH 426/498] Asynchronous interactive tasks can be started again --- .../handler/evaluation/admin/StartTaskHandler.kt | 4 ++-- .../run/InteractiveAsynchronousEvaluation.kt | 15 +++++++++------ .../data/model/template/task/DbTaskTemplate.kt | 6 ++++-- .../dres/run/InteractiveAsynchronousRunManager.kt | 2 ++ .../kotlin/dev/dres/run/InteractiveRunManager.kt | 2 +- .../dres/run/InteractiveSynchronousRunManager.kt | 2 ++ 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index cdcf2ac2e..82be7bf9f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -54,8 +54,8 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand return this.store.transactional { val rac = ctx.runActionContext() try { - evaluationManager.startTask(rac) - AuditLogger.taskStart(evaluationManager.id, evaluationManager.currentTask(rac)!!.taskId, evaluationManager.currentTaskTemplate(rac).toApi(), AuditLogSource.REST, ctx.sessionToken()) + val taskId = evaluationManager.startTask(rac) + AuditLogger.taskStart(evaluationManager.id, taskId, evaluationManager.currentTaskTemplate(rac).toApi(), AuditLogSource.REST, ctx.sessionToken()) SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, e.message ?: "", ctx) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 2f82dbf05..f24944d0b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption import dev.dres.data.model.template.task.options.DbTaskOption @@ -47,8 +48,8 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval /** A [ConcurrentHashMap] that maps a list of [IATaskRun]s to the [TeamId]s they belong to.*/ private val tasksMap = ConcurrentHashMap>() - /** Tracks the current [DbTaskTemplate] per [TeamId]. */ - private val navigationMap: MutableMap = HashMap() + /** Tracks the current [TaskTemplateId] per [TeamId]. */ + private val navigationMap: MutableMap = HashMap() /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ override val scoreboards: List @@ -65,11 +66,13 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval } fun goTo(teamId: TeamId, index: Int) { - this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).first() + this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).first().templateId } - fun currentTaskDescription(teamId: TeamId): DbTaskTemplate = - navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) + fun currentTaskDescription(teamId: TeamId): DbTaskTemplate { + val templateId = navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) + return DbTaskTemplate.filter { it.id eq templateId }.firstOrNull() ?: throw IllegalTeamIdException(teamId) + } init { val numberOfTasks = this.description.tasks.size() @@ -86,7 +89,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval * @param teamId The [TeamId] to lookup. */ fun currentTaskForTeam(teamId: TeamId): IATaskRun? { - val currentTaskTemplateId = this.navigationMap[teamId]!!.id + val currentTaskTemplateId = this.navigationMap[teamId]!! return this.tasksForTeam(teamId).findLast { it.template.id == currentTaskTemplateId } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index e343af698..8742296b8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -16,6 +16,8 @@ import java.lang.IllegalStateException import java.lang.Long.max import java.util.* +typealias TaskTemplateId = String + /** * Basic description of a [TaskRun] as executed in DRES. Defines basic attributes such as its name, its duration, * the [DbTaskTemplateTarget] and the [DbHint]s, that should be presented to the user. @@ -27,8 +29,8 @@ import java.util.* class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { companion object: XdNaturalEntityType() - /** The [TemplateId] of this [DbTaskTemplate]. */ - override var templateId: TemplateId + /** The [TaskTemplateId] of this [DbTaskTemplate]. */ + override var templateId: TaskTemplateId get() = this.id set(value) { this.id = value } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 19068786f..f4c31ed9e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -329,6 +329,8 @@ class InteractiveAsynchronousRunManager( ) LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") + + currentTaskRun.taskId } /** diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 70946618d..35a19b84c 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -74,7 +74,7 @@ interface InteractiveRunManager : RunManager { * @param context The [RunActionContext] used for the invocation. * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE] or [currentTask] is not set. */ - fun startTask(context: RunActionContext) + fun startTask(context: RunActionContext): TaskId /** * Force-abort the [currentTask] and thus moves the [RunManager.status] from diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 095b13cac..3b4d031ad 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -239,6 +239,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") + + this.evaluation.currentTask!!.taskId } override fun abortTask(context: RunActionContext) = this.stateLock.write { From a26f93aa117267a8de523b33941e85c16e3742a2 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 28 Jul 2023 19:53:24 +0200 Subject: [PATCH 427/498] Progress towards async runs --- .../run/InteractiveAsynchronousRunManager.kt | 92 +++++++++---------- .../TemporalOverlapAnswerSetValidator.kt | 2 +- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index f4c31ed9e..88d37009c 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -507,50 +507,48 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [InteractiveRunManager] was not in status [RunManagerStatus.ACTIVE]. */ override fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) = this.stateLock.read { - TODO("Not yet implemented") - - /* require(context.teamId != null) { "TeamId is missing from action context, which is required for interaction with run manager." } - require(teamHasRunningTask(context.teamId)) { "No running task for Team ${context.teamId}" } - require( - submission.answerSets().count() == 1 - ) { "Only single verdict per submission is allowed for InteractiveAsynchronousRunManager." } /* TODO: Do we want this restriction? */ - /* Register submission. */ - val task = this.currentTask(context) - ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[context.teamId]}. This is a programmer's error!") + /* Phase 1: Basic lookups required for validation (read-only). */ + val task = this.store.transactional(true) { + val teamId = context.teamId() - /* Sanity check. */ - check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } - check(task.teamId == submission.teamId) { "Team ${submission.teamId} is not eligible to submit to this task. This is a programmer's error!" } + require(teamHasRunningTask(teamId)) { "No running task for Team ${teamId}" } + val task = this.currentTask(context) + ?: throw IllegalStateException("Could not find ongoing task in run manager, despite being in status ${this.statusMap[teamId]}. This is a programmer's error!") - /* Check if ApiSubmission meets formal requirements. */ - task.filter.acceptOrThrow(submission) + /* Sanity check. */ + check(task.isRunning) { "Task run '${this.name}.${task.position}' is currently not running. This is a programmer's error!" } - /* Apply transformations to submissions */ - val transformedSubmission = task.transformer.transform(submission) + /* Update submission with contextual information. */ + submission.userId = context.userId + submission.teamId = teamId + submission.answerSets.forEach { + it.taskId = task.taskId /* All answers are explicitly associated with the running task. */ + } - /* Check if there are answers left after transformation */ - if (transformedSubmission.answers.isEmpty()) { - throw IllegalStateException("Submission contains no valid answer sets") + /* Check if ApiSubmission meets formal requirements. */ + task.filter.acceptOrThrow(submission) + task } - /* At this point, the submission is considered valid and is persisted */ - /* Validator is applied to each answer set */ - transformedSubmission.answerSets().forEach { - task.validator.validate(it) - } + /* Phase 2: Create DbSubmission, apply transformers and validate it. */ + this.store.transactional { + /* Convert submission to database representation. */ + val db = submission.toNewDb() - /* Persist the submission. */ - transformedSubmission.toNewDb() + /* Apply transformer(s) to submission. */ + task.transformer.transform(db) - /* Enqueue submission for post-processing. */ - this.scoresUpdatable.enqueue(task) + /* Check if there are answers left after transformation */ + if (db.answerSets.isEmpty) { + throw IllegalStateException("Submission contains no valid answer sets.") + } - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - context.teamId, - ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.taskId) - ) */ + /* Apply validators to each answer set. */ + db.answerSets.asSequence().forEach { + task.validator.validate(it) + } + } } override fun reScore(taskId: TaskId) { @@ -626,6 +624,9 @@ class InteractiveAsynchronousRunManager( } } + /** Add this InteractiveAsynchronousRunManager as a listener for changes to the data store. */ + this.store.addListener(this) + /* Initialize error counter. */ var errorCounter = 0 @@ -643,29 +644,28 @@ class InteractiveAsynchronousRunManager( } /* 3) Yield to other threads. */ - Thread.sleep(250) + Thread.sleep(500) /* 4) Reset error counter and yield to other threads. */ errorCounter = 0 } catch (ie: InterruptedException) { - LOGGER.info("Interrupted run manager thread; exiting...") + LOGGER.info("Interrupted SynchronousRunManager, exiting") return } catch (e: Throwable) { - LOGGER.error( - "Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", - e - ) + LOGGER.error("Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) + LOGGER.error("This is the ${++errorCounter}. in a row, will terminate loop after $MAXIMUM_RUN_LOOP_ERROR_COUNT errors") // oh shit, something went horribly, horribly wrong if (errorCounter >= MAXIMUM_RUN_LOOP_ERROR_COUNT) { - LOGGER.error("Reached maximum consecutive error count of $MAXIMUM_RUN_LOOP_ERROR_COUNT; terminating loop...") - break - } else { - LOGGER.error("This is the ${++errorCounter}-th in a row. Run manager will terminate loop after $MAXIMUM_RUN_LOOP_ERROR_COUNT errors") + LOGGER.error("Reached maximum consecutive error count, terminating loop") + break //terminate loop } } } + /* Remove this InteractiveAsynchronousRunManager as a listener for changes to the data store. */ + this.store.removeListener(this) + /* Finalization. */ this.stateLock.read { this.store.transactional { @@ -674,7 +674,7 @@ class InteractiveAsynchronousRunManager( } } - LOGGER.info("Run manager ${this.id} has reached end of run logic.") + LOGGER.info("InteractiveAsynchronousRunManager ${this.id} has reached end of run logic.") } /** @@ -699,7 +699,7 @@ class InteractiveAsynchronousRunManager( */ private fun internalStateUpdate() = this.stateLock.read { for (teamId in teamIds) { - if (teamHasRunningTask(teamId)) { //FIXME DbTaskTemplate[...] was removed. + if (teamHasRunningTask(teamId)) { this.stateLock.write { val task = this.evaluation.currentTaskForTeam(teamId) ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt index 1a18ff517..5115059cc 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt @@ -51,7 +51,7 @@ class TemporalOverlapAnswerSetValidator(private val targetSegments: Collection= start) || (outer.first <= end && outer.second >= end) + !((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) }) { return } From e389618a0abab875207811acdc572394be560c51 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 5 Aug 2023 15:32:49 +0200 Subject: [PATCH 428/498] Fixed an issue related to task navigation --- .../dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt | 2 +- .../api/rest/handler/evaluation/admin/PreviousTaskHandler.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index c48aa5675..525e9a360 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -52,7 +52,7 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl /* Important: Check that user can actually change this manager. */ synchronousAdminCheck(evaluationManager, ctx) - return this.store.transactional(true) { + return this.store.transactional(false) { val rac = ctx.runActionContext() if (evaluationManager is InteractiveAsynchronousRunManager && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index 43e54d66c..3c999d4a0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -39,7 +39,7 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional(true) { + return this.store.transactional(false) { val rac = ctx.runActionContext() try { if (evaluationManager.previous(rac)) { From 825d723f401c999adfec4c93ee4ce6652fb7b53c Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 5 Aug 2023 16:19:59 +0200 Subject: [PATCH 429/498] Fixed updatable initialization for async runs, now run crashes again due to invalid reference to db task --- .../data/model/run/InteractiveAsynchronousEvaluation.kt | 7 +++++-- .../dev/dres/run/InteractiveAsynchronousRunManager.kt | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index f24944d0b..97b84a2ae 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -24,6 +24,7 @@ import dev.dres.run.transformer.SubmissionTaskMatchTransformer import dev.dres.run.transformer.basics.SubmissionTransformer import dev.dres.run.transformer.basics.CombiningSubmissionTransformer import kotlinx.dnq.query.* +import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -35,6 +36,8 @@ import java.util.concurrent.ConcurrentHashMap */ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { + private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousEvaluation::class.java) + private val permutation: Map> init { @@ -88,7 +91,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval * * @param teamId The [TeamId] to lookup. */ - fun currentTaskForTeam(teamId: TeamId): IATaskRun? { + fun currentTaskForTeam(teamId: TeamId): IATaskRun? { //FIXME val currentTaskTemplateId = this.navigationMap[teamId]!! return this.tasksForTeam(teamId).findLast { it.template.id == currentTaskTemplateId @@ -162,7 +165,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval init { this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.task.team!!.id) { _, v -> val list = v ?: LinkedList() - check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } + check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } //FIXME crashes on restart list.add(this) list } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 88d37009c..49cd3898e 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -738,7 +738,7 @@ class InteractiveAsynchronousRunManager( */ private fun registerOptionalUpdatables() { /* Determine if task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options } + val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission } .filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) From e3578e81bbd4c3b3a85d1c25f683bfb949ea61d9 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 5 Aug 2023 17:45:43 +0200 Subject: [PATCH 430/498] Enhanced robustness in admin run list upon termination of a run --- frontend/src/app/run/admin-run-list.component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/run/admin-run-list.component.ts b/frontend/src/app/run/admin-run-list.component.ts index d56be46a0..571114f37 100644 --- a/frontend/src/app/run/admin-run-list.component.ts +++ b/frontend/src/app/run/admin-run-list.component.ts @@ -62,8 +62,11 @@ export class AdminRunListComponent extends AbstractRunListComponent implements A if (result) { this.runAdminService.postApiV2EvaluationAdminByEvaluationIdTerminate(runId).subscribe( (r) => { - this.refreshSubject.complete(); - this.refreshSubject.unsubscribe(); + /* Attempt to prevent senting requests twice to the backend */ + if(this.refreshSubject && this.refreshSubject?.closed){ + this.refreshSubject.complete(); + this.refreshSubject.unsubscribe(); + } this.snackBar.open(`Success: ${r.description}`, null, { duration: 5000 }); }, (r) => { From 52d1190b008086226fc29b0fe6ba2d1ead293523 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 5 Aug 2023 18:00:04 +0200 Subject: [PATCH 431/498] Re-added time controls for admins --- .../app/evaluation/task-controls/task-controls.component.html | 2 +- .../src/app/evaluation/task-controls/task-controls.component.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.html b/frontend/src/app/evaluation/task-controls/task-controls.component.html index d1544e874..38a8fd8dd 100644 --- a/frontend/src/app/evaluation/task-controls/task-controls.component.html +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.html @@ -38,7 +38,7 @@ skip_next -
    Time left: {{ (runState | async)?.timeLeft | formatTime:false }} diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.ts b/frontend/src/app/evaluation/task-controls/task-controls.component.ts index f5206fb88..efb5c9633 100644 --- a/frontend/src/app/evaluation/task-controls/task-controls.component.ts +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.ts @@ -38,6 +38,7 @@ export class TaskControlsComponent implements OnInit{ } ngOnInit(): void { + console.log("Show Time: ", this.showTime) if(!this.runId){ this.runId = this.activatedRoute.params.pipe( tap((a) => console.log("PARAMS:", a)), From 3cd719b5bbf1d2e4f2255b41e6d775a3462f0ffc Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 5 Aug 2023 18:03:08 +0200 Subject: [PATCH 432/498] Enhanced robustness in judgement media viewer --- .../judgement/judgement-media-viewer.component.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.ts b/frontend/src/app/judgement/judgement-media-viewer.component.ts index 55078ad52..bebdae909 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-media-viewer.component.ts @@ -189,12 +189,16 @@ export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterVi if(this.startInSeconds === undefined){ this.req.subscribe((req) => { this.calculateTime(req); - this.video.nativeElement.currentTime = this.startInSeconds; - this.video.nativeElement.play().then((r) => JudgementMediaViewerComponent.log('Playing video after event fired, recalc done')) + if(this?.video?.nativeElement){ + this.video.nativeElement.currentTime = this.startInSeconds; + this.video.nativeElement.play().then((r) => JudgementMediaViewerComponent.log('Playing video after event fired, recalc done')) + } }) }else{ - this.video.nativeElement.currentTime = this.startInSeconds; - this.video.nativeElement.play().then((r) => JudgementMediaViewerComponent.log('Playing video after event fired')); + if(this?.video?.nativeElement){ + this.video.nativeElement.currentTime = this.startInSeconds; + this.video.nativeElement.play().then((r) => JudgementMediaViewerComponent.log('Playing video after event fired')); + } } }); } @@ -255,7 +259,6 @@ export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterVi ngAfterViewChecked(): void { if (!this.videoPlayerInitialized) { - console.log('Initializing video'); this.initVideoPlayerHandling(); } } From 4d48965341921a2ef7903b919cf9cf024dd2be99 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sat, 5 Aug 2023 18:06:09 +0200 Subject: [PATCH 433/498] #431 Hopefully fixed uncaught in promise --- .../judgement/judgement-media-viewer.component.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.ts b/frontend/src/app/judgement/judgement-media-viewer.component.ts index bebdae909..0a58489cb 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-media-viewer.component.ts @@ -1,4 +1,4 @@ -import { AfterViewChecked, AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { AfterViewChecked, AfterViewInit, Component, ElementRef, ErrorHandler, Input, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { AppConfig } from '../app.config'; import { ApiAnswerType, ApiJudgementRequest } from "../../../openapi"; @@ -7,8 +7,11 @@ import { ApiAnswerType, ApiJudgementRequest } from "../../../openapi"; selector: 'app-judgement-media-viewer', templateUrl: './judgement-media-viewer.component.html', styleUrls: ['./judgement-media-viewer.component.scss'], + providers: [ + {provide: ErrorHandler, useClass: JudgementMediaViewerComponent} + ] }) -export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterViewChecked { +export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterViewChecked, ErrorHandler { /** * The zero-based index in the answerset to which this viewer is for @@ -54,6 +57,14 @@ export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterVi constructor(public config: AppConfig) {} + handleError(error: Error): void { + if(error?.message?.includes("uncaught in Promise")){ + // silently ignore + }else{ + throw error; + } + } + private static log(msg: string) { console.log(`[JudgeMedia] ${msg}`); } From cff7e905288c103b6b9a445b2f7616febf590e35 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 5 Aug 2023 18:45:57 +0200 Subject: [PATCH 434/498] Attaching websocket warning overlay at most once --- frontend/src/app/viewer/run-viewer.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index 23df0b35c..1bee293a6 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -299,7 +299,9 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /* Create subscription for WebSocket connection status and show overlay. */ this.overlaySubscription = this.webSocketConnectionSubject.subscribe((status) => { if (status == false) { - this.overlayRef.attach(new TemplatePortal(this.overlayTemplateRef, this._viewContainerRef)) + if(!this.overlayRef.hasAttached()) { + this.overlayRef.attach(new TemplatePortal(this.overlayTemplateRef, this._viewContainerRef)); + } } else { this.overlayRef.detach() } From ea39c0072ded5c2416d00b6fb642b42f86a72729 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 5 Aug 2023 19:29:03 +0200 Subject: [PATCH 435/498] Viewer now uses polled state rather than websocket updates --- .../src/app/viewer/run-viewer.component.ts | 41 ++++++++++--------- .../src/app/viewer/task-viewer.component.ts | 31 ++------------ .../src/app/viewer/teams-viewer.component.ts | 17 +++++--- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index 1bee293a6..d9654b016 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -220,25 +220,28 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { filter((m) => m.type !== 'PING') /* Filter out ping messages. */, map((b) => b.evaluationId) ); - this.runState = merge(this.evaluationId, wsMessages).pipe( - sampleTime(500) /* State updates are triggered only once every 500ms. */, - switchMap((evaluationId) => - this.runService.getApiV2EvaluationByEvaluationIdState(evaluationId).pipe( - catchError((err, o) => { - console.log( - `[RunViewerComponent] There was an error while loading information in the current run state: ${err?.message}` - ); - this.snackBar.open(`There was an error while loading information in the current run: ${err?.message}`, null, { - duration: 5000, - }); - if (err.status === 404) { - this.router.navigate(['/template/list']); - } - return of(null); - }), - filter((q) => q != null) - ) - ), + + wsMessages.subscribe({next: + (message) => { + //console.log("Ws Message: ", message); + } + }); + + this.runState = interval(1000).pipe(mergeMap(() => this.evaluationId)).pipe( + switchMap((id) => this.runService.getApiV2EvaluationByEvaluationIdState(id)), + catchError((err, o) => { + console.log( + `[RunViewerComponent] There was an error while loading information in the current run state: ${err?.message}` + ); + this.snackBar.open(`There was an error while loading information in the current run: ${err?.message}`, null, { + duration: 5000, + }); + if (err.status === 404) { + this.router.navigate(['/template/list']); + } + return of(null); + }), + filter((q) => q != null), shareReplay({ bufferSize: 1, refCount: true }) ); diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index f8f119171..505b8c4f1 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -123,37 +123,14 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { shareReplay({ bufferSize: 1, refCount: true }) ); - /* - * This Observable is used to poll the RunState; it merges the normal state observable with a timer and makes sure that: - * - If normal state changes (e.g. due to an external event), that state object is re-used - * - If timer fire, the state is queried. - * - Both timer + normal state only trigger an update every 500ms. - * - * Implicitly, this Observable is only used when a task is running due to how it is used in the template! - */ - const polledState = merge(interval(1000).pipe(mergeMap(() => this.evaluationId)), this.state).pipe( - sampleTime(1000) /* This is again sampled to only ever emit once every second. */, - switchMap((s) => { - if (typeof s === 'string') { - return this.runService.getApiV2EvaluationByEvaluationIdState(s); /* Timer! Load run state! */ - } else { - return of(s as ApiEvaluationState); /* This is a freshly loaded run state. */ - } - }), - catchError((err, o) => { - console.log(`[TaskViewerComponent] Error occurred while polling state: ${err?.message}`); - return of(null); - }), - filter((p) => p != null), - share() - ); + /* * This is the main switch that updates the viewer's state and the only actual subscription. * * IMPORTANT: Unsubscribe onDestroy. */ - this.viewerStateSubscription = combineLatest([currentTaskHint, polledState]).subscribe(([h, s]) => { + this.viewerStateSubscription = combineLatest([currentTaskHint, this.state]).subscribe(([h, s]) => { switch (s.taskStatus) { case 'NO_TASK': case 'CREATED': @@ -237,7 +214,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { ); /* Observable for the time that is still left. */ - this.timeLeft = polledState.pipe( + this.timeLeft = this.state.pipe( map((s) => s.timeLeft) /* Compensating for added countdown. */, tap((t) => { if (t === 30 || t === 60) { @@ -247,7 +224,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { ); /* Observable for the time that has elapsed. */ - this.timeElapsed = polledState.pipe(map((s) => s.timeElapsed)); + this.timeElapsed = this.state.pipe(map((s) => s.timeElapsed)); } /** diff --git a/frontend/src/app/viewer/teams-viewer.component.ts b/frontend/src/app/viewer/teams-viewer.component.ts index d9a97cc90..7cd98bf71 100644 --- a/frontend/src/app/viewer/teams-viewer.component.ts +++ b/frontend/src/app/viewer/teams-viewer.component.ts @@ -9,7 +9,7 @@ import { ViewChild, } from '@angular/core'; import {BehaviorSubject, combineLatest, merge, mergeMap, Observable, of, Subscription} from 'rxjs'; -import {catchError, filter, map, pairwise, retry, shareReplay, switchMap, withLatestFrom,} from 'rxjs/operators'; +import {catchError, filter, map, pairwise, retry, sampleTime, shareReplay, switchMap, withLatestFrom,} from 'rxjs/operators'; import {AppConfig} from '../app.config'; import {animate, keyframes, style, transition, trigger} from '@angular/animations'; import { @@ -20,6 +20,7 @@ import { EvaluationScoresService, EvaluationService } from 'openapi'; +import { HttpErrorResponse } from '@angular/common/http'; /** * Internal helper interface. @@ -135,11 +136,13 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { ngAfterViewInit(): void { /* Create source observable; list of all submissions. */ this.submissions = this.state.pipe( + sampleTime(2000), //only check once every two seconds switchMap((st) => this.evaluationService.getApiV2EvaluationByEvaluationIdSubmissionList(st.evaluationId).pipe( - retry(3), - catchError((err, o) => { - console.log(`[TeamsViewerComponent] Error while loading submissions: ${err?.message}.`); + catchError((err: HttpErrorResponse) => { + if (err.status != 404) { //log anything but 404 + console.log(`[TeamsViewerComponent] Error while loading submissions: ${err?.message}.`); + } return of(null); }), filter((sb) => sb != null) /* Filter null responses. */ @@ -165,8 +168,10 @@ export class TeamsViewerComponent implements AfterViewInit, OnDestroy { switchMap((st) => this.scoresService.getApiV2ScoreEvaluationByEvaluationIdCurrent(st.evaluationId).pipe( retry(3), - catchError((err, o) => { - console.log(`[TeamsViewerComponent] Error while loading scores: ${err?.message}.`); + catchError((err: HttpErrorResponse) => { + if (err.status != 404) { //log anything but 404 + console.log(`[TeamsViewerComponent] Error while loading scores: ${err?.message}.`); + } return of(null); }), filter((sc) => sc != null) /* Filter null responses. */ From 9beb81851e94a40f176d9d73b14c62fd6fb4bb2f Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 5 Aug 2023 19:29:31 +0200 Subject: [PATCH 436/498] Renamed competition-scoreboard-viewer to scoreboard-viewer --- .../src/app/viewer/run-viewer.component.html | 24 +++++++++---------- .../scoreboard-viewer.component.html} | 0 .../scoreboard-viewer.component.scss} | 0 .../scoreboard-viewer.component.ts} | 15 +++++++----- frontend/src/app/viewer/viewer.module.ts | 4 ++-- 5 files changed, 23 insertions(+), 20 deletions(-) rename frontend/src/app/viewer/{competition-scoreboard-viewer/competition-scoreboard-viewer.component.html => scoreboard-viewer/scoreboard-viewer.component.html} (100%) rename frontend/src/app/viewer/{competition-scoreboard-viewer/competition-scoreboard-viewer.component.scss => scoreboard-viewer/scoreboard-viewer.component.scss} (100%) rename frontend/src/app/viewer/{competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts => scoreboard-viewer/scoreboard-viewer.component.ts} (94%) diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index c31df56a8..84f2b9097 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -93,18 +93,18 @@ [taskEnded]="taskEnded" [webSocketSubject]="webSocketSubject" > - - + + >
    - - + + >
    - - + + >
    diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html b/frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.html similarity index 100% rename from frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.html rename to frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.html diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.scss b/frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.scss similarity index 100% rename from frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.scss rename to frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.scss diff --git a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts b/frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.ts similarity index 94% rename from frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts rename to frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.ts index d2b22e367..ae81b5345 100644 --- a/frontend/src/app/viewer/competition-scoreboard-viewer/competition-scoreboard-viewer.component.ts +++ b/frontend/src/app/viewer/scoreboard-viewer/scoreboard-viewer.component.ts @@ -13,6 +13,7 @@ import { } from 'ng-apexcharts'; import { catchError, map, switchMap } from 'rxjs/operators'; import {ApiEvaluationInfo, ApiEvaluationState, ApiScore, ApiScoreOverview, ApiTeamInfo, EvaluationScoresService} from '../../../../openapi'; +import { HttpErrorResponse } from '@angular/common/http'; /** * Component displaying a lovely scoreboard. @@ -21,11 +22,11 @@ import {ApiEvaluationInfo, ApiEvaluationState, ApiScore, ApiScoreOverview, ApiTe * competitionOverview = false -- In this mode, a bar chart of the current task group is shown */ @Component({ - selector: 'app-competition-scoreboard-viewer', - templateUrl: './competition-scoreboard-viewer.component.html', - styleUrls: ['./competition-scoreboard-viewer.component.scss'], + selector: 'app-scoreboard-viewer', + templateUrl: './scoreboard-viewer.component.html', + styleUrls: ['./scoreboard-viewer.component.scss'], }) -export class CompetitionScoreboardViewerComponent implements OnInit { +export class ScoreboardViewerComponent implements OnInit { /** * The run info of the current run */ @@ -131,8 +132,10 @@ export class CompetitionScoreboardViewerComponent implements OnInit { const score = this.state.pipe( switchMap((s) => { return this.scoreService.getApiV2ScoreEvaluationByEvaluationIdCurrent(s.evaluationId).pipe( - catchError((err) => { - console.log('Error when retrieving scores.', err); + catchError((err: HttpErrorResponse) => { + if (err.status != 404) { //log anything but 404 + console.log('Error when retrieving scores.', err); + } return of(null); }) ); diff --git a/frontend/src/app/viewer/viewer.module.ts b/frontend/src/app/viewer/viewer.module.ts index e9db95d30..a4f898700 100644 --- a/frontend/src/app/viewer/viewer.module.ts +++ b/frontend/src/app/viewer/viewer.module.ts @@ -19,7 +19,7 @@ import { TaskViewerComponent } from './task-viewer.component'; import { TeamsViewerComponent } from './teams-viewer.component'; import { QueryObjectPreviewModule } from './query-object-preview/query-object-preview.module'; import { NgApexchartsModule } from 'ng-apexcharts'; -import { CompetitionScoreboardViewerComponent } from './competition-scoreboard-viewer/competition-scoreboard-viewer.component'; +import { ScoreboardViewerComponent } from './scoreboard-viewer/scoreboard-viewer.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { SharedModule } from '../shared/shared.module'; import {FullscreenOverlayContainer, OverlayContainer, OverlayModule} from "@angular/cdk/overlay"; @@ -51,7 +51,7 @@ import { EvaluationModule } from "../evaluation/evaluation.module"; EvaluationModule ], exports: [RunViewerComponent], - declarations: [RunViewerComponent, TaskViewerComponent, TeamsViewerComponent, CompetitionScoreboardViewerComponent], + declarations: [RunViewerComponent, TaskViewerComponent, TeamsViewerComponent, ScoreboardViewerComponent], providers: [{provide: OverlayContainer, useClass: FullscreenOverlayContainer}], }) export class ViewerModule {} From 6ab45aa06b011ac4541386d77a5f1d6a7fe03847 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 6 Aug 2023 15:24:32 +0200 Subject: [PATCH 437/498] Removed WebSocket and introduced simplified ready mechanism --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 3 +- .../evaluation/admin/ListViewersHandler.kt | 4 +- .../evaluation/viewer/GetTaskHintHandler.kt | 14 ++ .../evaluation/viewer/ViewerReadyHandler.kt | 66 +++++++ .../dev/dres/api/rest/types/ViewerInfo.kt | 5 + .../api/rest/types/WebSocketConnection.kt | 63 ------ .../run/InteractiveAsynchronousRunManager.kt | 118 ++++++----- .../run/InteractiveSynchronousRunManager.kt | 103 ++++++---- .../dev/dres/run/NonInteractiveRunManager.kt | 40 ++-- .../main/kotlin/dev/dres/run/RunExecutor.kt | 186 +++++++++--------- .../main/kotlin/dev/dres/run/RunManager.kt | 25 +-- .../utilities/extensions/ContextExtensions.kt | 8 +- doc/oas-client.json | 72 +++++++ doc/oas.json | 72 +++++++ .../src/app/viewer/run-viewer.component.html | 9 +- .../src/app/viewer/run-viewer.component.ts | 158 +++++++-------- .../src/app/viewer/task-viewer.component.ts | 7 +- 17 files changed, 572 insertions(+), 381 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/ViewerInfo.kt delete mode 100644 backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index c639bec21..6df573227 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -152,6 +152,7 @@ object RestApi { GetSubmissionInfoHandler(store), GetSubmissionAfterInfoHandler(store), GetSubmissionHistoryInfoHandler(store), + ViewerReadyHandler(store), // Evaluation run scores ListEvaluationScoreHandler(store), @@ -333,7 +334,7 @@ object RestApi { } } } - ws("ws/run", runExecutor) + //ws("ws/run", runExecutor) } }.error(401) { it.json(ErrorStatus("Unauthorized request!")) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index fbfa0b29e..7f8a149cb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -40,8 +40,8 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa return this.store.transactional(true) { evaluation.viewers().map { ApiViewerInfo( - it.key.sessionId, - DbUserManager.get(AccessManager.userIdForSession(it.key.httpSessionId))?.username ?: "UNKNOWN", + it.key.sessionToken, + DbUserManager.get(AccessManager.userIdForSession(it.key.sessionToken))?.username ?: "UNKNOWN", it.key.host, it.value) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 9e97e7e7f..231755326 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -1,7 +1,9 @@ import dev.dres.DRES +import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.handler.evaluation.viewer.AbstractEvaluationViewerHandler +import dev.dres.api.rest.types.ViewerInfo import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.template.tasks.ApiHintContent import dev.dres.api.rest.types.status.ErrorStatus @@ -13,8 +15,11 @@ import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.DbHint import dev.dres.data.model.template.task.DbHintType import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.mgmt.admin.DbUserManager import dev.dres.mgmt.cache.CacheManager import dev.dres.run.InteractiveRunManager +import dev.dres.utilities.extensions.isAdmin +import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -80,6 +85,15 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa ) } + if(ctx.isParticipant() || ctx.isAdmin()) { + manager.viewerPreparing( + taskId, rac, ViewerInfo( + ctx.sessionToken()!!, + ctx.ip() + ) + ) + } + try { ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes template.toTaskHint() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt new file mode 100644 index 000000000..de4c75375 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt @@ -0,0 +1,66 @@ +package dev.dres.api.rest.handler.evaluation.viewer + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.ViewerInfo +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.data.model.run.RunActionContext.Companion.runActionContext +import dev.dres.run.InteractiveRunManager +import dev.dres.utilities.extensions.eligibleManagerForId +import dev.dres.utilities.extensions.isAdmin +import dev.dres.utilities.extensions.isParticipant +import dev.dres.utilities.extensions.sessionToken +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore + +class ViewerReadyHandler(store: TransientEntityStore) : AbstractEvaluationViewerHandler(store), GetRestHandler { + + override val route = "evaluation/{evaluationId}/hint/{taskId}/ready" + + @OpenApi( + summary = "Signals that a viewer is ready to show the hints for a particular task.", + path = "/api/v2/evaluation/{evaluationId}/hint/{taskId}/ready", + tags = ["Evaluation"], + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam("taskId", String::class, "The task ID.", required = true) + ], + responses = [ + OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("403", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ], + methods = [HttpMethod.GET] + ) + override fun doGet(ctx: Context): SuccessStatus { + + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val rac = ctx.runActionContext() + + return this.store.transactional(true) { + val manager = ctx.eligibleManagerForId() + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } + + if(ctx.isParticipant() || ctx.isAdmin()) { + manager.viewerReady( + taskId, rac, ViewerInfo( + ctx.sessionToken()!!, + ctx.ip() + ) + ) + } + + + SuccessStatus("ready received") + } + + + } + +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/ViewerInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/ViewerInfo.kt new file mode 100644 index 000000000..b63931180 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/ViewerInfo.kt @@ -0,0 +1,5 @@ +package dev.dres.api.rest.types + +import dev.dres.api.rest.handler.users.SessionToken + +data class ViewerInfo(val sessionToken: SessionToken, val host: String) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt deleted file mode 100644 index ac57ad769..000000000 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.dres.api.rest.types - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.api.rest.AccessManager -import dev.dres.mgmt.admin.DbUserManager -import io.javalin.websocket.WsContext -import org.eclipse.jetty.server.session.Session -import java.nio.ByteBuffer - -/** - * Wraps a [WsContext] and gives access to specific information regarding the user owning that [WsContext] - * - * @author Ralph Gasser - * @version 1.0.1 - */ -@JvmInline -value class WebSocketConnection(val context: WsContext) { - - companion object { - val jsonMapper = jacksonObjectMapper() - const val UNKNOWN_USER = "UNKNOWN" - } - - /** ID of the WebSocket session. */ - val sessionId - get() = this.context.sessionId - - /** ID of the HTTP session that generated this [WebSocketConnection]. */ - val httpSessionId: String - get() = this.context.cookie("SESSIONID") ?: throw IllegalStateException("Unable to obtain HTTP Session ID associated with WebSocket connection request.") - - /** IP address of the client. */ - val host: String - get() { - val xff = this.context.header("X-Forwarded-For") - return if (xff != null) { - "$xff via ${this.context.host()}" - } else { - this.context.host() - } - } - - /** - * Sends an object through this [WebSocketConnection]. The object is serialized before sending. - * - * @param message The message to send. - */ - fun send(message: Any) = this.context.send(jsonMapper.writeValueAsString(message)) - - /** - * Sends a plain [String] message through this [WebSocketConnection]. - * - * @param message The [String] to send. - */ - fun send(message: String) = this.context.send(message) - - /** - * Sends a plain [ByteBuffer] message through this [WebSocketConnection]. - * - * @param message The [ByteBuffer] to send. - */ - fun send(message: ByteBuffer) = this.context.send(message) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 49cd3898e..c079f209a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -1,7 +1,7 @@ package dev.dres.run import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.evaluation.websocket.ClientMessage @@ -76,7 +76,7 @@ class InteractiveAsynchronousRunManager( private val statusMap: MutableMap = HashMap() /** A [Map] of all viewers, i.e., DRES clients currently registered with this [InteractiveAsynchronousRunManager]. */ - private val viewers = ConcurrentHashMap() + private val viewers = ConcurrentHashMap() /** A lock for state changes to this [InteractiveAsynchronousRunManager]. */ private val stateLock = ReentrantReadWriteLock() @@ -161,8 +161,8 @@ class InteractiveAsynchronousRunManager( this.statusMap.forEach { (t, _) -> this.statusMap[t] = RunManagerStatus.ACTIVE } this.status = RunManagerStatus.ACTIVE - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) LOGGER.info("Run manager ${this.id} started") } @@ -188,8 +188,8 @@ class InteractiveAsynchronousRunManager( this.statusMap.forEach { (t, _) -> this.statusMap[t] = RunManagerStatus.TERMINATED } this.status = RunManagerStatus.TERMINATED - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } @@ -281,15 +281,15 @@ class InteractiveAsynchronousRunManager( //FIXME since task run and competition run states are separated, this is not actually a state change this.statusMap[teamId] = RunManagerStatus.ACTIVE - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - teamId, - ServerMessage( - this.id, - ServerMessageType.COMPETITION_UPDATE, - this.evaluation.currentTaskForTeam(teamId)?.taskId - ) - ) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage( +// teamId, +// ServerMessage( +// this.id, +// ServerMessageType.COMPETITION_UPDATE, +// this.evaluation.currentTaskForTeam(teamId)?.taskId +// ) +// ) LOGGER.info("SynchronousRunManager ${this.id} set to task $idx") } @@ -322,11 +322,11 @@ class InteractiveAsynchronousRunManager( val currentTaskRun = this.evaluation.IATaskRun(currentTaskTemplate, teamId) currentTaskRun.prepare() - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - teamId, - ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId) - ) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage( +// teamId, +// ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId) +// ) LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") @@ -353,8 +353,8 @@ class InteractiveAsynchronousRunManager( ?: throw IllegalStateException("Could not find active task for team ${teamId} despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") currentTask.end() - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(teamId, ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(teamId, ServerMessage(this.id, ServerMessageType.TASK_END, currentTask.taskId)) LOGGER.info("Run manager ${this.id} aborted task $currentTask.") } @@ -592,22 +592,36 @@ class InteractiveAsynchronousRunManager( * * @return List of viewer [WebSocketConnection]s for this [RunManager]. */ - override fun viewers(): Map = Collections.unmodifiableMap(this.viewers) + override fun viewers(): Map = Collections.unmodifiableMap(this.viewers) + +// /** +// * Processes WebSocket [ClientMessage] received by the [InteractiveAsynchronousRunManager]. +// * +// * @param connection The [WebSocketConnection] through which the message was received. +// * @param message The [ClientMessage] received. +// */ +// override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { +// when (message.type) { +// ClientMessageType.REGISTER -> this.viewers[connection] = true +// ClientMessageType.UNREGISTER -> this.viewers.remove(connection) +// else -> { /* No op. */ +// } +// } +// return true +// } + + override fun viewerPreparing(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + val currentTaskId = this.currentTask(rac)?.taskId + if (taskId == currentTaskId) { + this.viewers[viewerInfo] = false + } + } - /** - * Processes WebSocket [ClientMessage] received by the [InteractiveAsynchronousRunManager]. - * - * @param connection The [WebSocketConnection] through which the message was received. - * @param message The [ClientMessage] received. - */ - override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { - when (message.type) { - ClientMessageType.REGISTER -> this.viewers[connection] = true - ClientMessageType.UNREGISTER -> this.viewers.remove(connection) - else -> { /* No op. */ - } + override fun viewerReady(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + val currentTaskId = this.currentTask(rac)?.taskId + if (taskId == currentTaskId) { + this.viewers[viewerInfo] = true } - return true } /** @@ -711,11 +725,11 @@ class InteractiveAsynchronousRunManager( task.end() AuditLogger.taskEnd(this.id, task.taskId, AuditLogSource.INTERNAL, null) - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage( - teamId, - ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId) - ) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage( +// teamId, +// ServerMessage(this.id, ServerMessageType.TASK_END, task.taskId) +// ) } } } else if (teamHasPreparingTask(teamId)) { @@ -724,10 +738,10 @@ class InteractiveAsynchronousRunManager( ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") task.start() AuditLogger.taskStart(this.id, task.teamId, task.template.toApi(), AuditLogSource.REST, null) - RunExecutor.broadcastWsMessage( - teamId, - ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) - ) +// RunExecutor.broadcastWsMessage( +// teamId, +// ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) +// ) } } } @@ -825,13 +839,13 @@ class InteractiveAsynchronousRunManager( if (t != null) { t.scorer.invalidate() this.scoreboards.forEach { it.invalidate() } - RunExecutor.broadcastWsMessage( - ServerMessage( - this.id, - ServerMessageType.TASK_UPDATED, - task.id - ) - ) +// RunExecutor.broadcastWsMessage( +// ServerMessage( +// this.id, +// ServerMessageType.TASK_UPDATED, +// task.id +// ) +// ) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 3b4d031ad..0a140b1b8 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -1,7 +1,8 @@ package dev.dres.run import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.handler.users.SessionToken +import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus @@ -90,7 +91,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch get() = this.evaluation.scoreboards /** Internal data structure that tracks all [WebSocketConnection]s and their ready state. */ - private val readyLatch = ReadyLatch() + private val readyLatch = ReadyLatch() /** List of [Updatable] held by this [InteractiveSynchronousRunManager]. */ private val updatables = mutableListOf() @@ -131,8 +132,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Start the run. */ this.evaluation.start() - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_START)) /* Log and update status. */ LOGGER.info("SynchronousRunManager ${this.id} started") @@ -149,8 +150,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Update status. */ this.status = RunManagerStatus.TERMINATED - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_END)) LOGGER.info("SynchronousRunManager ${this.id} terminated") } @@ -208,8 +209,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Update active task. */ this.evaluation.goTo(index) - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE, this.evaluation.currentTask?.taskId)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.COMPETITION_UPDATE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} set to task $index") } else { throw IndexOutOfBoundsException("Index $index is out of bounds for the number of available tasks.") @@ -235,8 +236,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Reset the ReadyLatch. */ this.readyLatch.reset(VIEWER_TIMEOUT_DEFAULT) - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") @@ -251,8 +252,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* End TaskRun and persist. */ this.currentTask(context)?.end() - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.currentTask(context)?.taskId)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.currentTask(context)?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") } @@ -374,7 +375,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * * @return Map of session ID to ready state. */ - override fun viewers(): HashMap = this.readyLatch.state() + override fun viewers(): HashMap = this.readyLatch.state() /** * Can be used to manually override the READY state of a viewer. Can be used in case a viewer hangs in the PREPARING_TASK phase. @@ -387,7 +388,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch checkContext(context) return try { - val viewer = this.readyLatch.state().keys.find { it.sessionId == viewerId } + val viewer = this.readyLatch.state().keys.find { it.sessionToken == viewerId } if (viewer != null) { this.readyLatch.setReady(viewer) true @@ -399,30 +400,54 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } } - /** - * Processes WebSocket [ClientMessage] received by the [RunExecutor]. - * - * @param connection The [WebSocketConnection] through which the message was received. - * @param message The [ClientMessage] received. - */ - override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean = - this.stateLock.read { - when (message.type) { - ClientMessageType.ACK -> { - this.store.transactional(true) { - if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { - this.readyLatch.setReady(connection) - } - } +// /** +// * Processes WebSocket [ClientMessage] received by the [RunExecutor]. +// * +// * @param connection The [WebSocketConnection] through which the message was received. +// * @param message The [ClientMessage] received. +// */ +// override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean = +// this.stateLock.read { +// when (message.type) { +// ClientMessageType.ACK -> { +// this.store.transactional(true) { +// if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { +// this.readyLatch.setReady(connection) +// } +// } +// } +// ClientMessageType.REGISTER -> this.readyLatch.register(connection) +// ClientMessageType.UNREGISTER -> this.readyLatch.unregister(connection) +// ClientMessageType.PING -> { +// } //handled in [RunExecutor] +// } +// return true +// } + + override fun viewerPreparing(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + + val currentTaskId = this.currentTask(rac)?.taskId + + if (taskId == currentTaskId) { + this.readyLatch.register(viewerInfo) + } + + } + + override fun viewerReady(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + + val currentTaskId = this.currentTask(rac)?.taskId + + if (taskId == currentTaskId) { + this.store.transactional(true) { + if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { + this.readyLatch.setReady(viewerInfo) } - ClientMessageType.REGISTER -> this.readyLatch.register(connection) - ClientMessageType.UNREGISTER -> this.readyLatch.unregister(connection) - ClientMessageType.PING -> { - } //handled in [RunExecutor] } - return true } + } + /** * Processes incoming [ApiSubmission]s. If a [DbTask] is running then that [ApiSubmission] will usually * be associated with that [DbTask]. @@ -599,8 +624,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate().toApi(), AuditLogSource.INTERNAL, null) } - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_START, this.evaluation.currentTask?.taskId)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_START, this.evaluation.currentTask?.taskId)) } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ @@ -613,8 +638,8 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.taskId, AuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.taskId)) - /* Enqueue WS message for sending */ - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.evaluation.currentTask?.taskId)) +// /* Enqueue WS message for sending */ +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.evaluation.currentTask?.taskId)) } } } @@ -713,7 +738,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (t != null) { t.scorer.invalidate() this.scoreboards.forEach { it.invalidate() } - RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.id)) +// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_UPDATED, task.id)) } } } diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 14b127607..6a787f34c 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -1,13 +1,10 @@ package dev.dres.run -import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission -import dev.dres.api.rest.types.evaluation.websocket.ClientMessage -import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.data.model.run.* import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId -import dev.dres.data.model.submissions.* import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore @@ -20,8 +17,6 @@ class NonInteractiveRunManager( override val store: TransientEntityStore ) : RunManager { - private val SCOREBOARD_UPDATE_INTERVAL_MS = 10_000L // TODO make configurable - private val LOGGER = LoggerFactory.getLogger(this.javaClass) /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ @@ -98,19 +93,30 @@ class NonInteractiveRunManager( override fun taskCount(context: RunActionContext): Int = this.evaluation.tasks.size - private val viewerMap: MutableMap = mutableMapOf() - - override fun viewers(): Map = viewerMap - - override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { - when (message.type) { - ClientMessageType.REGISTER -> this.viewerMap[connection] = true - ClientMessageType.UNREGISTER -> this.viewerMap.remove(connection) - ClientMessageType.ACK, ClientMessageType.PING -> {} //nop - } - return true + private val viewerMap: MutableMap = mutableMapOf() + + override fun viewers(): Map = viewerMap + +// override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { +// when (message.type) { +// ClientMessageType.REGISTER -> this.viewerMap[connection] = true +// ClientMessageType.UNREGISTER -> this.viewerMap.remove(connection) +// ClientMessageType.ACK, ClientMessageType.PING -> {} //nop +// } +// return true +// } + + override fun viewerPreparing( + taskId: dev.dres.data.model.run.TaskId, + rac: RunActionContext, + viewerInfo: ViewerInfo + ) { + /* nop */ } + override fun viewerReady(taskId: dev.dres.data.model.run.TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + /* nop */ + } override fun run() { diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 62a24950a..6c1853d62 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -1,32 +1,22 @@ package dev.dres.run -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.types.WebSocketConnection -import dev.dres.api.rest.types.evaluation.websocket.ClientMessage -import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType -import dev.dres.api.rest.types.evaluation.websocket.ServerMessage -import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType + +import dev.dres.api.rest.types.ViewerInfo import dev.dres.data.model.config.Config import dev.dres.data.model.run.* -import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.mgmt.cache.CacheManager import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write -import io.javalin.websocket.WsConfig import io.javalin.websocket.WsContext import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.slf4j.LoggerFactory -import java.io.File import java.util.* import java.util.concurrent.Executors import java.util.concurrent.Future import java.util.concurrent.locks.StampedLock -import java.util.function.Consumer /** * The execution environment for [RunManager]s @@ -34,7 +24,7 @@ import java.util.function.Consumer * @author Ralph Gasser * @version 1.3.0 */ -object RunExecutor : Consumer { +object RunExecutor { private val logger = LoggerFactory.getLogger(this.javaClass) @@ -48,10 +38,10 @@ object RunExecutor : Consumer { private val judgementValidators = LinkedList() /** List of [WsContext] that are currently connected. */ - private val connectedClients = HashMap() + private val connectedClients = HashMap() /** List of session IDs that are currently observing an evaluation. */ - private val observingClients = HashMap>() + private val observingClients = HashMap>() /** Lock for accessing and changing all data structures related to WebSocket clients. */ private val clientLock = StampedLock() @@ -127,55 +117,55 @@ object RunExecutor : Consumer { this.cleanerThread.start() } - /** - * Callback for when registering this [RunExecutor] as handler for Javalin's WebSocket. - * - * @param t The [WsConfig] of the WebSocket endpoint. - */ - override fun accept(t: WsConfig) { - t.onConnect { - /* Add WSContext to set of connected clients. */ - this@RunExecutor.clientLock.write { - val connection = WebSocketConnection(it) - this.connectedClients[connection.httpSessionId] = connection - } - } - t.onClose { - val session = WebSocketConnection(it) - this@RunExecutor.clientLock.write { - val connection = WebSocketConnection(it) - this.connectedClients.remove(connection.httpSessionId) - this.runManagerLock.read { - for (m in this.runManagers) { - if (this.observingClients[m.key]?.remove(connection) == true) { - m.value.wsMessageReceived(session, ClientMessage(m.key, ClientMessageType.UNREGISTER)) /* Send implicit unregister message associated with a disconnect. */ - } - } - } - } - } - t.onMessage { - val message = try { - it.messageAsClass() - } catch (e: Exception) { - logger.warn("Cannot parse WebSocket message: ${e.localizedMessage}") - return@onMessage - } - val session = WebSocketConnection(it) - logger.debug("Received WebSocket message: $message from ${it.session.policy}") - this.runManagerLock.read { - if (this.runManagers.containsKey(message.evaluationId)) { - when (message.type) { - ClientMessageType.ACK -> {} - ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.add(WebSocketConnection(it)) } - ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.remove(WebSocketConnection(it)) } - ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, ServerMessageType.PING)) - } - this.runManagers[message.evaluationId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ - } - } - } - } +// /** +// * Callback for when registering this [RunExecutor] as handler for Javalin's WebSocket. +// * +// * @param t The [WsConfig] of the WebSocket endpoint. +// */ +// fun accept(t: WsConfig) { //TODO remove +// t.onConnect { +// /* Add WSContext to set of connected clients. */ +// this@RunExecutor.clientLock.write { +// val connection = WebSocketConnection(it) +// this.connectedClients[connection.httpSessionId] = connection +// } +// } +// t.onClose { +// val session = WebSocketConnection(it) +// this@RunExecutor.clientLock.write { +// val connection = WebSocketConnection(it) +// this.connectedClients.remove(connection.httpSessionId) +// this.runManagerLock.read { +// for (m in this.runManagers) { +// if (this.observingClients[m.key]?.remove(connection) == true) { +// m.value.wsMessageReceived(session, ClientMessage(m.key, ClientMessageType.UNREGISTER)) /* Send implicit unregister message associated with a disconnect. */ +// } +// } +// } +// } +// } +// t.onMessage { +// val message = try { +// it.messageAsClass() +// } catch (e: Exception) { +// logger.warn("Cannot parse WebSocket message: ${e.localizedMessage}") +// return@onMessage +// } +// val session = WebSocketConnection(it) +// logger.debug("Received WebSocket message: $message from ${it.session.policy}") +// this.runManagerLock.read { +// if (this.runManagers.containsKey(message.evaluationId)) { +// when (message.type) { +// ClientMessageType.ACK -> {} +// ClientMessageType.REGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.add(WebSocketConnection(it)) } +// ClientMessageType.UNREGISTER -> this@RunExecutor.clientLock.write { this.observingClients[message.evaluationId]?.remove(WebSocketConnection(it)) } +// ClientMessageType.PING -> it.send(ServerMessage(message.evaluationId, ServerMessageType.PING)) +// } +// this.runManagers[message.evaluationId]!!.wsMessageReceived(session, message) /* Forward message to RunManager. */ +// } +// } +// } +// } /** * Lists all [RunManager]s currently executed by this [RunExecutor] @@ -196,40 +186,40 @@ object RunExecutor : Consumer { return this.runManagers[evaluationId] } - /** - * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager]. - * - * @param message The [ServerMessage] that should be broadcast. - */ - fun broadcastWsMessage(message: ServerMessage) = this.clientLock.read { - this.runManagerLock.read { - this.connectedClients.values.filter { - this.observingClients[message.evaluationId]?.contains(it) ?: false - }.forEach { - it.send(message) - } - } - } - - /** - * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager] and are member of the specified team. - * - * @param teamId The [TeamId] of the relevant team - * @param message The [ServerMessage] that should be broadcast. - */ - fun broadcastWsMessage(teamId: TeamId, message: ServerMessage) = this.clientLock.read { - val manager = managerForId(message.evaluationId) - if (manager != null) { - val teamMembers = manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() - this.runManagerLock.read { - this.connectedClients.values.filter { - this.observingClients[message.evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.sessionId) in teamMembers - }.forEach { - it.send(message) - } - } - } - } +// /** +// * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager]. +// * +// * @param message The [ServerMessage] that should be broadcast. +// */ +// fun broadcastWsMessage(message: ServerMessage) = this.clientLock.read { +// this.runManagerLock.read { +// this.connectedClients.values.filter { +// this.observingClients[message.evaluationId]?.contains(it) ?: false +// }.forEach { +// it.send(message) +// } +// } +// } + +// /** +// * Broadcasts a [ServerMessage] to all clients currently connected and observing a specific [RunManager] and are member of the specified team. +// * +// * @param teamId The [TeamId] of the relevant team +// * @param message The [ServerMessage] that should be broadcast. +// */ +// fun broadcastWsMessage(teamId: TeamId, message: ServerMessage) = this.clientLock.read { +// val manager = managerForId(message.evaluationId) +// if (manager != null) { +// val teamMembers = manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() +// this.runManagerLock.read { +// this.connectedClients.values.filter { +// this.observingClients[message.evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.sessionId) in teamMembers +// }.forEach { +// it.send(message) +// } +// } +// } +// } /** * Stops all runs diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 9db25a8df..509d8eede 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -1,9 +1,8 @@ package dev.dres.run -import dev.dres.api.rest.types.WebSocketConnection +import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission -import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun @@ -121,24 +120,16 @@ interface RunManager : Runnable { fun postSubmission(context: RunActionContext, submission: ApiClientSubmission) /** - * Returns a list of viewer [WebSocketConnection]s for this [RunManager] alongside with their respective state. + * Returns a list of viewer [ViewerInfo]s for this [RunManager] alongside with their respective state. * - * @return List of viewer [WebSocketConnection]s for this [RunManager]. + * @return List of viewer [ViewerInfo]s for this [RunManager]. */ - fun viewers(): Map + fun viewers(): Map + + fun viewerPreparing(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) + + fun viewerReady(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) - /** - * Invoked by an external caller such in order to inform the [RunManager] that it has received a [ClientMessage]. - * - * This method does not throw an exception and instead returns false if a [DbSubmission] was - * ignored for whatever reason (usually a state mismatch). It is up to the caller to re-invoke - * this method again. - * - * @param connection The [WebSocketConnection] through which the message was received. - * @param message The [ClientMessage] that was received. - * @return True if [ClientMessage] was processed, false otherwise - */ - fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean /** * Triggers a re-scoring of submissions. Used by Judgement validation mechanism. diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index ad3b60f53..a9c927401 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -127,12 +127,12 @@ inline fun Context.eligibleManagerForId(): T { val userId = this.userId() val evaluationId = this.evaluationId() val manager = RunExecutor.managerForId(evaluationId) as? T ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) - if (this.isJudge() && manager.template.judges.filter { it.userId eq userId }.isNotEmpty) { - return manager + return if (this.isJudge() && manager.template.judges.filter { it.userId eq userId }.isNotEmpty) { + manager } else if (this.isParticipant() && manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isNotEmpty) { - return manager + manager } else if (this.isAdmin()) { - return manager + manager } else { throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) } diff --git a/doc/oas-client.json b/doc/oas-client.json index 33756d0c2..0a4a93ed0 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -379,6 +379,78 @@ "security" : [ ] } }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}/ready" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Signals that a viewer is ready to show the hints for a particular task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskIdReady", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/evaluation/{evaluationId}/info" : { "get" : { "tags" : [ "Evaluation" ], diff --git a/doc/oas.json b/doc/oas.json index 1a7525c63..0f13307dd 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -2020,6 +2020,78 @@ "security" : [ ] } }, + "/api/v2/evaluation/{evaluationId}/hint/{taskId}/ready" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Signals that a viewer is ready to show the hints for a particular task.", + "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskIdReady", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/evaluation/{evaluationId}/info" : { "get" : { "tags" : [ "Evaluation" ], diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index 84f2b9097..ebfbf589c 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -90,8 +90,7 @@ [state]="runState" [taskChanged]="taskChanged" [taskStarted]="taskStarted" - [taskEnded]="taskEnded" - [webSocketSubject]="webSocketSubject" + [taskEnded]="taskEnded" >
    - + diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index d9654b016..6d4c7d28d 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -38,11 +38,11 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /** The WebSocketSubject that represent the WebSocket connection to the DRES endpoint. */ webSocketSubject: WebSocketSubject; - /** A {@link BehaviorSubject} that reflect the WebSocket connection status. */ - webSocketConnectionSubject: BehaviorSubject = new BehaviorSubject(false); + // /** A {@link BehaviorSubject} that reflect the WebSocket connection status. */ + // webSocketConnectionSubject: BehaviorSubject = new BehaviorSubject(false); - /** Observable for incoming WebSocket messages. */ - webSocket: Observable; + // /** Observable for incoming WebSocket messages. */ + // webSocket: Observable; /** Observable for current run ID. */ evaluationId: Observable; @@ -105,23 +105,23 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { private _viewContainerRef: ViewContainerRef ) { /** Initialize basic WebSocketSubject. */ - const wsurl = this.config.webSocketUrl; - const connectionSubject = this.webSocketConnectionSubject - this.webSocketSubject = webSocket({ - url: wsurl, - openObserver: { - next(openEvent) { - console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} established!`); - connectionSubject.next(true) - }, - }, - closeObserver: { - next(closeEvent: CloseEvent) { - console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} closed: ${closeEvent.reason}.`); - connectionSubject.next(false) - }, - }, - } as WebSocketSubjectConfig); + // const wsurl = this.config.webSocketUrl; + // const connectionSubject = this.webSocketConnectionSubject + // this.webSocketSubject = webSocket({ + // url: wsurl, + // openObserver: { + // next(openEvent) { + // console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} established!`); + // connectionSubject.next(true) + // }, + // }, + // closeObserver: { + // next(closeEvent: CloseEvent) { + // console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} closed: ${closeEvent.reason}.`); + // connectionSubject.next(false) + // }, + // }, + // } as WebSocketSubjectConfig); /** Observable for the current run ID. */ this.evaluationId = this.activeRoute.params.pipe( @@ -178,54 +178,54 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { shareReplay({ bufferSize: 1, refCount: true }) ); - /* Basic observable for web socket messages received from the DRES server. */ - this.webSocket = this.evaluationId.pipe( - mergeMap((evaluationId) => - this.webSocketSubject.multiplex( - () => { - return { evaluationId: evaluationId, type: 'REGISTER' } as IWsClientMessage; - }, - () => { - return { evaluationId: evaluationId, type: 'UNREGISTER' } as IWsClientMessage; - }, - (message) => message.evaluationId === evaluationId || message.evaluationId === null - ) - .pipe( - retryWhen((err) => - err.pipe( - tap((e) => - console.error( - '[RunViewerComponent] An error occurred with the WebSocket communication channel. Trying to reconnect in 1 second.', - e - ) - ), - delay(5000) - ) - ), - map((m) => m as IWsServerMessage), - filter((q) => q != null), - tap((m) => console.log(`[RunViewerComponent] WebSocket message received: ${m.type}`)) - ) - ), - share() - ); - - /* - * Observable for run state info; this information is dynamic and is subject to change over the course of a run. - * - * Updates to the RunState are triggered by WebSocket messages received by the viewer. To not overwhelm the server, - * the RunState is updated every 500ms at most. - */ - const wsMessages = this.webSocket.pipe( - filter((m) => m.type !== 'PING') /* Filter out ping messages. */, - map((b) => b.evaluationId) - ); - - wsMessages.subscribe({next: - (message) => { - //console.log("Ws Message: ", message); - } - }); + // /* Basic observable for web socket messages received from the DRES server. */ + // this.webSocket = this.evaluationId.pipe( + // mergeMap((evaluationId) => + // this.webSocketSubject.multiplex( + // () => { + // return { evaluationId: evaluationId, type: 'REGISTER' } as IWsClientMessage; + // }, + // () => { + // return { evaluationId: evaluationId, type: 'UNREGISTER' } as IWsClientMessage; + // }, + // (message) => message.evaluationId === evaluationId || message.evaluationId === null + // ) + // .pipe( + // retryWhen((err) => + // err.pipe( + // tap((e) => + // console.error( + // '[RunViewerComponent] An error occurred with the WebSocket communication channel. Trying to reconnect in 1 second.', + // e + // ) + // ), + // delay(5000) + // ) + // ), + // map((m) => m as IWsServerMessage), + // filter((q) => q != null), + // tap((m) => console.log(`[RunViewerComponent] WebSocket message received: ${m.type}`)) + // ) + // ), + // share() + // ); + + // /* + // * Observable for run state info; this information is dynamic and is subject to change over the course of a run. + // * + // * Updates to the RunState are triggered by WebSocket messages received by the viewer. To not overwhelm the server, + // * the RunState is updated every 500ms at most. + // */ + // const wsMessages = this.webSocket.pipe( + // filter((m) => m.type !== 'PING') /* Filter out ping messages. */, + // map((b) => b.evaluationId) + // ); + + // wsMessages.subscribe({next: + // (message) => { + // //console.log("Ws Message: ", message); + // } + // }); this.runState = interval(1000).pipe(mergeMap(() => this.evaluationId)).pipe( switchMap((id) => this.runService.getApiV2EvaluationByEvaluationIdState(id)), @@ -299,16 +299,16 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { hasBackdrop: true }); - /* Create subscription for WebSocket connection status and show overlay. */ - this.overlaySubscription = this.webSocketConnectionSubject.subscribe((status) => { - if (status == false) { - if(!this.overlayRef.hasAttached()) { - this.overlayRef.attach(new TemplatePortal(this.overlayTemplateRef, this._viewContainerRef)); - } - } else { - this.overlayRef.detach() - } - }) + // /* Create subscription for WebSocket connection status and show overlay. */ + // this.overlaySubscription = this.webSocketConnectionSubject.subscribe((status) => { + // if (status == false) { + // if(!this.overlayRef.hasAttached()) { + // this.overlayRef.attach(new TemplatePortal(this.overlayTemplateRef, this._viewContainerRef)); + // } + // } else { + // this.overlayRef.detach() + // } + // }) } /** diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index 505b8c4f1..ed9ebf4d8 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -50,7 +50,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { @Input() taskStarted: Observable; @Input() taskChanged: Observable; @Input() taskEnded: Observable; - @Input() webSocketSubject: WebSocketSubject; + /** Time that is still left (only when a task is running). */ timeLeft: Observable; @@ -138,9 +138,10 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { break; case 'PREPARING': this.viewerState.next(ViewerState.VIEWER_SYNC); + /* Send ACK. */ if (h != null) { - this.webSocketSubject.next({ evaluationId: s.evaluationId, type: 'ACK' } as IWsClientMessage); - } /* Send ACK. */ + this.runService.getApiV2EvaluationByEvaluationIdHintByTaskIdReady(s.evaluationId, h.taskId).subscribe(); + } break; case 'RUNNING': if (s.timeElapsed < 0) { From 023484749904e5280d7da5186a94de79480fda90 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 7 Aug 2023 11:05:31 +0200 Subject: [PATCH 438/498] #431: Enhanced robustness for judgment meida viewer --- .../app/evaluation/task-controls/task-controls.component.html | 1 + frontend/src/app/judgement/judgement-media-viewer.component.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.html b/frontend/src/app/evaluation/task-controls/task-controls.component.html index 38a8fd8dd..3d1e16fe0 100644 --- a/frontend/src/app/evaluation/task-controls/task-controls.component.html +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.html @@ -39,6 +39,7 @@
    Time left: {{ (runState | async)?.timeLeft | formatTime:false }} diff --git a/frontend/src/app/judgement/judgement-media-viewer.component.ts b/frontend/src/app/judgement/judgement-media-viewer.component.ts index 0a58489cb..0ff189e71 100644 --- a/frontend/src/app/judgement/judgement-media-viewer.component.ts +++ b/frontend/src/app/judgement/judgement-media-viewer.component.ts @@ -112,7 +112,7 @@ export class JudgementMediaViewerComponent implements OnInit, OnDestroy, AfterVi ngOnDestroy(): void { this.stop(); - this.requestSub.unsubscribe(); + this.requestSub?.unsubscribe(); } togglePlaying() { From 7a1cfe5e6a6505f0edb3f3faf3ad7de558bd3c6c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 7 Aug 2023 11:45:52 +0200 Subject: [PATCH 439/498] #431: New submission handler communicates verdict if available --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 4 +- .../handler/submission/SubmissionHandler.kt | 50 ++++++++++++++++--- doc/oas-client.json | 16 ++++-- doc/oas.json | 16 ++++-- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index c639bec21..a3f8e51a6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -135,7 +135,7 @@ object RestApi { // Submission LegacySubmissionHandler(store, cache), - SubmissionHandler(), + SubmissionHandler(store), // Log QueryLogHandler(), @@ -226,7 +226,7 @@ object RestApi { t.contact = contact val license = OpenApiLicense() license.name = "MIT" - license.identifier = "MIT" + // license.identifier = "MIT" t.license = license } u.withSecurity(SecurityComponentConfiguration() diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index 61815d995..c8f8c2c8b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -4,11 +4,16 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.run.RunActionContext.Companion.runActionContext +import dev.dres.data.model.submissions.DbSubmission +import dev.dres.data.model.submissions.DbVerdictStatus +import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.audit.AuditLogSource import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.IllegalRunStateException @@ -19,9 +24,14 @@ import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.first +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.transactional import org.slf4j.LoggerFactory -class SubmissionHandler: PostRestHandler, AccessManagedRestHandler { +class SubmissionHandler(private val store: TransientEntityStore): PostRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.PARTICIPANT) @@ -41,15 +51,16 @@ class SubmissionHandler: PostRestHandler, AccessManagedRestHandle OpenApiParam("evaluationId", String::class, "The ID of the evaluation the submission belongs to.", required = true), ], responses = [ - OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), + OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)], description = "The submission was accepted by the server and there was a verdict"), + OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)],description = "The submission was accepted by the server and there has not yet been a verdict available"), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), - OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)]) + OpenApiResponse("412", [OpenApiContent(ErrorStatus::class)], description = "The submission was rejected by the server") ], tags = ["Submission"] ) - override fun doPost(ctx: Context): SuccessStatus { + override fun doPost(ctx: Context): SuccessfulSubmissionsStatus { /* Obtain run action context and parse submission. */ val rac = ctx.runActionContext() val apiClientSubmission = try { @@ -77,7 +88,34 @@ class SubmissionHandler: PostRestHandler, AccessManagedRestHandle AuditLogger.submission(apiClientSubmission, rac.evaluationId!!, AuditLogSource.REST, ctx.sessionToken(), ctx.ip()) } + /* Return status. */ - return SuccessStatus("Submission received.") + return this.store.transactional(readonly = true) { + var correct = 0 + var wrong = 0 + var undcidable = 0 + var indeterminate = 0 + DbSubmission.filter { it.id eq apiClientSubmission.submissionId }.firstOrNull()?.answerSets()?.map { it.status() }?.forEach { + when(it){ + VerdictStatus.CORRECT -> correct++ + VerdictStatus.WRONG -> wrong++ + VerdictStatus.INDETERMINATE -> indeterminate++ + VerdictStatus.UNDECIDABLE -> undcidable++ + } + } + val max = listOf(correct,wrong,undcidable,indeterminate).max() + when( max){ + 0 -> throw ErrorStatusException(500, "No verdict information available ${apiClientSubmission.submissionId}. This is a serious bug and should be reported!", ctx) + correct -> SuccessfulSubmissionsStatus(ApiVerdictStatus.CORRECT, "Submission correct, well done!") + wrong -> SuccessfulSubmissionsStatus(ApiVerdictStatus.WRONG, "Submission wrong, try again!") + undcidable -> SuccessfulSubmissionsStatus(ApiVerdictStatus.UNDECIDABLE, "Submission undecidable, try again!") + indeterminate -> { + ctx.status(202) + SuccessfulSubmissionsStatus(ApiVerdictStatus.INDETERMINATE, "Submission received, awaiting verdict!") + } + + else -> throw ErrorStatusException(500, "Error while calculating submission verdict for submission ${apiClientSubmission.submissionId}. This is a serious bug and should be reported!", ctx) + } + } } -} \ No newline at end of file +} diff --git a/doc/oas-client.json b/doc/oas-client.json index 33756d0c2..ebfa95889 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1250,11 +1250,21 @@ }, "responses" : { "200" : { - "description" : "OK", + "description" : "The submission was accepted by the server and there was a verdict", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "The submission was accepted by the server and there has not yet been a verdict available", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } @@ -1290,7 +1300,7 @@ } }, "412" : { - "description" : "Precondition Failed", + "description" : "The submission was rejected by the server", "content" : { "application/json" : { "schema" : { diff --git a/doc/oas.json b/doc/oas.json index 1a7525c63..0a2079eda 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -3941,11 +3941,21 @@ }, "responses" : { "200" : { - "description" : "OK", + "description" : "The submission was accepted by the server and there was a verdict", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "The submission was accepted by the server and there has not yet been a verdict available", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" } } } @@ -3981,7 +3991,7 @@ } }, "412" : { - "description" : "Precondition Failed", + "description" : "The submission was rejected by the server", "content" : { "application/json" : { "schema" : { From 2138e7a7638fa10455efb883ffd573ed36275de6 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 7 Aug 2023 12:00:15 +0200 Subject: [PATCH 440/498] #431: Time adjustments now return with proper message. --- .../kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 3b4d031ad..23e8ac480 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -328,7 +328,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* Adjust duration. */ val newDuration = task.duration + s - check((newDuration * 1000L - (System.currentTimeMillis() - task.started!!)) > 0) { "New duration $s can not be applied because too much time has already elapsed." } + if((newDuration * 1000L - (System.currentTimeMillis() - task.started!!)) < 0) { throw IllegalArgumentException( "New duration $s can not be applied because too much time has already elapsed.") } task.duration = newDuration return (task.duration * 1000L - (System.currentTimeMillis() - task.started!!)) } @@ -720,4 +720,4 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } } } -} \ No newline at end of file +} From 828fa9958dc964af7a9b60c2176e661d78f1e92c Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 7 Aug 2023 13:29:43 +0200 Subject: [PATCH 441/498] #431 Upon task duration adjustment below zero, task is aborted now --- .../evaluation/admin/AdjustDurationHandler.kt | 61 +++++++++++++++---- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 71cb66206..58dec4728 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -22,7 +22,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { +class AdjustDurationHandler(store: TransientEntityStore) : AbstractEvaluationAdminHandler(store), + PatchRestHandler { override val route: String = "evaluation/admin/{evaluationId}/adjust/{duration}" @OpenApi( @@ -31,8 +32,20 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.PATCH], pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), - OpenApiParam("duration", Int::class, "Duration to add. Can be negative.", required = true, allowEmptyValue = false) + OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + ), + OpenApiParam( + "duration", + Int::class, + "Duration to add. Can be negative.", + required = true, + allowEmptyValue = false + ) ], tags = ["Evaluation Administrator"], responses = [ @@ -44,22 +57,44 @@ class AdjustDurationHandler(store: TransientEntityStore): AbstractEvaluationAdmi ) override fun doPatch(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val duration = ctx.pathParamMap()["duration"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'duration' is missing!'", ctx) - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val duration = ctx.pathParamMap()["duration"]?.toIntOrNull() ?: throw ErrorStatusException( + 404, + "Parameter 'duration' is missing!'", + ctx + ) + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException( + 404, + "Evaluation $evaluationId not found", + ctx + ) val rac = ctx.runActionContext() return this.store.transactional { try { - evaluationManager.adjustDuration(rac, duration) - AuditLogger.taskModified(evaluationManager.id, evaluationManager.currentTaskTemplate(rac).id, "Task duration adjusted by ${duration}s.", AuditLogSource.REST, ctx.sessionToken()) - SuccessStatus("Duration for run $evaluationId was successfully adjusted.") + try { + evaluationManager.adjustDuration(rac, duration) + AuditLogger.taskModified( + evaluationManager.id, + evaluationManager.currentTaskTemplate(rac).id, + "Task duration adjusted by ${duration}s.", + AuditLogSource.REST, + ctx.sessionToken() + ) + SuccessStatus("Duration for run $evaluationId was successfully adjusted.") + } catch (e: IllegalArgumentException) { + evaluationManager.abortTask(rac) + SuccessStatus("Successfully stopped task since the duration was below zero after adjusting.") + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) + } } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalArgumentException) { - throw ErrorStatusException(400, "Duration for run $evaluationId could not be adjusted because new duration would drop bellow zero (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) + throw ErrorStatusException( + 400, + "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) } + } } } From 0167b60547363e3783b18ae5653556b4e3c9db41 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 7 Aug 2023 14:34:00 +0200 Subject: [PATCH 442/498] #431 Fixed target not shown when task over --- frontend/src/app/viewer/task-viewer.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index 505b8c4f1..cbdc2eb0b 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -52,6 +52,12 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { @Input() taskEnded: Observable; @Input() webSocketSubject: WebSocketSubject; + /** Seconds duration remaining for early warning. Default is 60 */ + @Input() earlyWarningThreshold: number = 60; + + /** Seconds duration remaining for late warning. Default is 30 */ + @Input() lateWarningThreshold: number = 30; + /** Time that is still left (only when a task is running). */ timeLeft: Observable; @@ -169,7 +175,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { } return from(h.sequence).pipe( delayWhen((c: ApiContentElement) => interval(1000 * c.offset)), - repeat(-1) + repeat(1) ); }) ); @@ -217,7 +223,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { this.timeLeft = this.state.pipe( map((s) => s.timeLeft) /* Compensating for added countdown. */, tap((t) => { - if (t === 30 || t === 60) { + if (t === this.lateWarningThreshold || t === this.earlyWarningThreshold) { this.playOnce(this.glass.nativeElement); /* Reminder that time is running out. */ } }) From b70abe9df8b66fe4ce590059ad967b284e32c645 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 10 Aug 2023 13:35:15 +0200 Subject: [PATCH 443/498] Removed explicit transaction contexts and use of Db classes from user-related API handlers See #432 --- backend/src/main/kotlin/dev/dres/DRES.kt | 4 + .../kotlin/dev/dres/api/cli/UserCommand.kt | 37 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 18 +- .../evaluation/admin/ListViewersHandler.kt | 5 +- .../api/rest/handler/system/LoginHandler.kt | 42 +- .../api/rest/handler/system/LogoutHandler.kt | 28 +- .../rest/handler/users/AbstractUserHandler.kt | 11 +- .../rest/handler/users/CreateUsersHandler.kt | 30 +- .../rest/handler/users/DeleteUsersHandler.kt | 11 +- .../handler/users/ListActiveUsersHandler.kt | 13 +- .../rest/handler/users/ListUsersHandler.kt | 8 +- .../handler/users/ShowCurrentUserHandler.kt | 8 +- .../rest/handler/users/UpdateUsersHandler.kt | 45 +- .../rest/handler/users/UserDetailsHandler.kt | 4 +- .../api/rest/types/WebSocketConnection.kt | 3 - .../dev/dres/api/rest/types/users/ApiRole.kt | 13 + .../dev/dres/data/model/admin/DbRole.kt | 11 +- .../{DbUserManager.kt => UserManager.kt} | 328 ++++---- .../dres/mgmt/cache/AbstractPreviewRequest.kt | 38 +- .../dev/dres/mgmt/cache/CacheManager.kt | 772 +++++++++--------- 20 files changed, 701 insertions(+), 728 deletions(-) rename backend/src/main/kotlin/dev/dres/mgmt/admin/{DbUserManager.kt => UserManager.kt} (55%) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 368cb239c..955616534 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -15,6 +15,7 @@ import dev.dres.data.model.template.team.DbTeamAggregator import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.run.* import dev.dres.data.model.submissions.* +import dev.dres.mgmt.admin.UserManager import dev.dres.mgmt.cache.CacheManager import dev.dres.run.RunExecutor import dev.dres.run.audit.AuditLogger @@ -101,6 +102,9 @@ object DRES { /* Initialize the global Cache Manager. */ val global = CacheManager(CONFIG, store) + /* Initialize data managers */ + UserManager.init(store) + /* Initialize RunExecutor. */ RunExecutor.init(CONFIG, store, global) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt index 60c299591..99c4dbdec 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/UserCommand.kt @@ -13,7 +13,7 @@ import dev.dres.data.model.admin.DbUser import dev.dres.data.model.admin.DbUser.Companion.MIN_LENGTH_PASSWORD import dev.dres.data.model.admin.DbUser.Companion.MIN_LENGTH_USERNAME import dev.dres.data.model.admin.UserId -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import jetbrains.exodus.database.TransientEntityStore import java.nio.file.Files import java.nio.file.Paths @@ -57,14 +57,16 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") /** The desired [DbRole] of the newly created user. */ private val apiRole: ApiRole by option("-r", "--role", help = "Role of the new user.").convert { ApiRole.valueOf(it) }.required() - override fun run() = this.store.transactional { - val successful = DbUserManager.create(username = this.username, password = this.password.hash(), role = apiRole) - if (successful) { - println("New user '${DbUserManager.get(username = this.username)}' created.") - } else { - println("Could not create user '${this.username}' because a user with that name already exists.") + override fun run() { + try { + val created = UserManager.create(username = this.username, password = this.password.hash(), role = apiRole) + println("New user '${UserManager.get(username = this.username)}' created.") + } catch (e: Exception) { + println("Could not create user '${this.username}': ${e.message}") } + } + } /** @@ -83,16 +85,15 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") .validate { require(it.password.length >= MIN_LENGTH_PASSWORD) { "Password for DRES password must consist of at least $MIN_LENGTH_PASSWORD characters." } } /** The new [DbRole] of the updated user. Left unchanged if null! */ - private val role: DbRole? by option("-r", "--role", help = "New user Role").convert { DbRole.parse(it) } + private val role: ApiRole? by option("-r", "--role", help = "New user Role").convert { ApiRole.parse(it) } - override fun run() = this.store.transactional { + override fun run() { if (this.id == null && this.username == null) { println("You must specify a valid username or user ID in order to update a user!") - return@transactional + return } - if (DbUserManager.update(id = this.id, username = this.username, password = this.password, role = this.role)) { + if (UserManager.update(id = this.id, username = this.username, password = this.password?.hash(), role = this.role) != null) { println("User updated successfully!") - return@transactional } else { println("User updated failed. It probably doesn't exist!") } @@ -111,7 +112,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") if (this.id == null && this.username == null) { println("You must specify a valid username or user ID in order to delete a user!") } - val success = DbUserManager.delete(id = this.id, username = this.username) + val success = UserManager.delete(id = this.id, username = this.username) if (success) { println("User deleted successfully!") return@transactional @@ -131,7 +132,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") private val path: String by option("-o", "--output").required() override fun run() = this.store.transactional(true) { if (this.id == null && this.username == null) { - val users = DbUserManager.list() + val users = UserManager.list() val path = Paths.get(this.path) val mapper = ObjectMapper() Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE).use { writer -> @@ -140,7 +141,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") println("Successfully wrote ${users.size} users to $path.") return@transactional } else { - val user = DbUserManager.get(id = this.id, username = this.username) + val user = UserManager.get(id = this.id, username = this.username) if (user != null) { val path = Paths.get(this.path) val mapper = ObjectMapper() @@ -180,9 +181,9 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") import.forEach { if (new) { - DbUserManager.create(it.username, it.hashedPassword(), it.role) + UserManager.create(it.username, it.hashedPassword(), it.role.toApi()) } else { - DbUserManager.update(it.id, it.username, it.hashedPassword(), it.role) + UserManager.update(it.id, it.username, it.hashedPassword(), it.role.toApi()) } } println("done") @@ -196,7 +197,7 @@ class UserCommand(store: TransientEntityStore) : NoOpCliktCommand(name = "user") inner class List(private val store: TransientEntityStore): CliktCommand(name = "list", help = "Lists all Users") { val plain by option("-p", "--plain", help = "Plain print: No fancy table. Might be easier if the output should be processed").flag(default = false) override fun run() = this.store.transactional(true) { - val users = DbUserManager.list() + val users = UserManager.list() println("Available users: ${users.size}") if (plain) { for (user in users) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index a3f8e51a6..7fa16947f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -90,16 +90,16 @@ object RestApi { val apiRestHandlers = listOf( // User - LoginHandler(store), - LogoutHandler(store), - ListUsersHandler(store), - ListActiveUsersHandler(store), - ShowCurrentUserHandler(store), + LoginHandler(), + LogoutHandler(), + ListUsersHandler(), + ListActiveUsersHandler(), + ShowCurrentUserHandler(), ShowCurrentSessionHandler(), - CreateUsersHandler(store), - DeleteUsersHandler(store), - UpdateUsersHandler(store), - UserDetailsHandler(store), + CreateUsersHandler(), + DeleteUsersHandler(), + UpdateUsersHandler(), + UserDetailsHandler(), // Media PreviewImageHandler(store, cache), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index fbfa0b29e..2485e27e7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -6,8 +6,7 @@ import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiViewerInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -41,7 +40,7 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa evaluation.viewers().map { ApiViewerInfo( it.key.sessionId, - DbUserManager.get(AccessManager.userIdForSession(it.key.httpSessionId))?.username ?: "UNKNOWN", + UserManager.get(AccessManager.userIdForSession(it.key.httpSessionId))?.username ?: "UNKNOWN", it.key.host, it.value) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt index cdfca3f2c..eb48282f6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LoginHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.Password -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import dev.dres.run.audit.AuditLogSource import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.getOrCreateSessionToken @@ -22,14 +22,15 @@ import jetbrains.exodus.database.TransientEntityStore * @version 1.0.0 * @author Luca Rossetto */ -class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostRestHandler { +class LoginHandler() : RestHandler, PostRestHandler { override val route = "login" override val apiVersion = "v2" data class LoginRequest(var username: String, var password: String) - @OpenApi(summary = "Sets roles for session based on user account and returns a session cookie.", + @OpenApi( + summary = "Sets roles for session based on user account and returns a session cookie.", path = "/api/v2/login", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], @@ -45,32 +46,31 @@ class LoginHandler(private val store: TransientEntityStore) : RestHandler, PostR val loginRequest = try { ctx.bodyAsClass(LoginRequest::class.java) - }catch (e: BadRequestResponse){ + } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error.", ctx) } - return this.store.transactional { - /* Validate login request. */ - val username = loginRequest.username - val password = Password.Plain(loginRequest.password) - val user = DbUserManager.getMatchingApiUser(username, password) ?: throw ErrorStatusException( - 401, - "Invalid credentials. Please try again!", - ctx - ) + /* Validate login request. */ + val username = loginRequest.username + val password = Password.Plain(loginRequest.password) + val user = UserManager.getMatchingApiUser(username, password) ?: throw ErrorStatusException( + 401, + "Invalid credentials. Please try again!", + ctx + ) - val sessionToken = ctx.getOrCreateSessionToken() + val sessionToken = ctx.getOrCreateSessionToken() - AccessManager.registerUserForSession(sessionToken, user) - AuditLogger.login(loginRequest.username, AuditLogSource.REST, sessionToken) + AccessManager.registerUserForSession(sessionToken, user) + AuditLogger.login(loginRequest.username, AuditLogSource.REST, sessionToken) - //explicitly set cookie on login - ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) + //explicitly set cookie on login + ctx.cookie(AccessManager.SESSION_COOKIE_NAME, sessionToken, AccessManager.SESSION_COOKIE_LIFETIME) + + user.sessionId = sessionToken + return user - user.sessionId = sessionToken - return@transactional user - } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt index 4175a2a50..eef877fbb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/system/LogoutHandler.kt @@ -11,7 +11,6 @@ import dev.dres.run.audit.AuditLogger import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore /** * A [GetRestHandler] that handles user-requests to logout. @@ -19,11 +18,12 @@ import jetbrains.exodus.database.TransientEntityStore * @version 2.0.0 * @author Luca Rossetto */ -class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetRestHandler { +class LogoutHandler() : RestHandler, GetRestHandler { override val route = "logout" override val apiVersion = "v2" - @OpenApi(summary = "Clears all user roles of the current session.", + @OpenApi( + summary = "Clears all user roles of the current session.", path = "/api/v2/logout", operationId = OpenApiOperation.AUTO_GENERATE, tags = ["User"], @@ -37,13 +37,21 @@ class LogoutHandler(private val store: TransientEntityStore) : RestHandler, GetR methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): SuccessStatus { - val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) - val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException(400, "You are currently not logged in.", ctx) - return store.transactional { - AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionToken()!!) - AccessManager.deregisterUserSession(ctx.sessionToken()!!) + val username = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( + 400, + "You are currently not logged in.", + ctx + ) + val userId = AccessManager.userIdForSession(ctx.sessionToken()) ?: throw ErrorStatusException( + 400, + "You are currently not logged in.", + ctx + ) + + AuditLogger.logout(userId, AuditLogSource.REST, ctx.sessionToken()!!) + AccessManager.deregisterUserSession(ctx.sessionToken()!!) + + return SuccessStatus("User '${username}' logged out successfully.") - SuccessStatus("User '${username}' logged out successfully.") - } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt index a449c0d37..89aab2d75 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/AbstractUserHandler.kt @@ -4,8 +4,9 @@ import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -22,14 +23,14 @@ abstract class AbstractUserHandler: RestHandler, AccessManagedRestHandler { override val apiVersion = "v2" /** Convenience method to extract [DbUser] from current session. */ - protected fun userFromSession(ctx: Context): DbUser { - return DbUserManager.get(id = AccessManager.userIdForSession(ctx.sessionToken())!!) + protected fun userFromSession(ctx: Context): ApiUser { + return UserManager.get(id = AccessManager.userIdForSession(ctx.sessionToken())!!) ?: throw ErrorStatusException(404, "User could not be found!", ctx) } /** Convenience method to extract [DbUser] from [Context] (userId parameter). */ - protected fun userFromContext(ctx: Context): DbUser { + protected fun userFromContext(ctx: Context): ApiUser { val id = ctx.pathParamMap()["userId"] ?: throw ErrorStatusException(404, "Parameter 'userId' is missing!'", ctx) - return DbUserManager.get(id = id) ?: throw ErrorStatusException(404, "User ($id) not found!", ctx) + return UserManager.get(id = id) ?: throw ErrorStatusException(404, "User ($id) not found!", ctx) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt index d109af390..d57e864ac 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/CreateUsersHandler.kt @@ -9,7 +9,7 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.Password import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -21,7 +21,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class CreateUsersHandler(private val store: TransientEntityStore) : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { +class CreateUsersHandler() : AbstractUserHandler(), PostRestHandler, AccessManagedRestHandler { override val route = "user" /** [CreateUsersHandler] requires [ApiRole.ADMIN]. */ @@ -46,32 +46,18 @@ class CreateUsersHandler(private val store: TransientEntityStore) : AbstractUser throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - if (req.password == null || req.password.length < DbUser.MIN_LENGTH_PASSWORD) + if (req.password == null ) throw ErrorStatusException(400, "Invalid parameters. Password must consist of at least ${DbUser.MIN_LENGTH_PASSWORD} characters.", ctx) if (req.username.length < DbUser.MIN_LENGTH_USERNAME) throw ErrorStatusException(400, "Invalid parameters. Username must consist of at least ${DbUser.MIN_LENGTH_USERNAME} characters.", ctx) if (req.role == null) throw ErrorStatusException(400, "Invalid parameters. Role must be defined.", ctx) - val success = this.store.transactional { - DbUserManager.create( - req.username, - Password.Plain(req.password), - req.role.toDb() ?: throw ErrorStatusException( - 400, - "Invalid parameters. Provided role is undefined or invalid!", - ctx - ) - ) - } - //needs to be in a new transaction - //TODO is there a nicer way of doing this? - return this.store.transactional { - if (success) { - return@transactional DbUserManager.get(username = req.username)!!.toApi() - } else { - throw ErrorStatusException(400, "The request could not be fulfilled.", ctx) - } + try { + return UserManager.create(req.username, Password.Plain(req.password).hash(), req.role) + } catch (e: Exception) { + throw ErrorStatusException(400, e.message ?: "could not create user", ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt index ebe9d2ee6..f9f8ab568 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/DeleteUsersHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -18,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class DeleteUsersHandler(private val store: TransientEntityStore) : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { +class DeleteUsersHandler() : AbstractUserHandler(), DeleteRestHandler, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.ADMIN) /** [DeleteUsersHandler] requires [ApiRole.ADMIN]. */ @@ -36,11 +36,10 @@ class DeleteUsersHandler(private val store: TransientEntityStore) : AbstractUser OpenApiResponse("500", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doDelete(ctx: Context): ApiUser = this.store.transactional { + override fun doDelete(ctx: Context): ApiUser { val user = userFromContext(ctx) - val apiUser = user.toApi() //needs to be done before, since it can no longer be transformed to ApiUser after successful delete - if (DbUserManager.delete(id = user.id)) { - return@transactional apiUser + if (UserManager.delete(id = user.id)) { + return user } else { throw ErrorStatusException(500, "Could not delete the user (${user.id})", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt index aa4f3ec6c..01ff381ee 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListActiveUsersHandler.kt @@ -7,7 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -18,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ListActiveUsersHandler(private val store: TransientEntityStore) : GetRestHandler>, AccessManagedRestHandler { +class ListActiveUsersHandler() : GetRestHandler>, AccessManagedRestHandler { override val permittedRoles = setOf(ApiRole.ADMIN) /** All [UserDetailsHandler] requires [ApiRole.ADMIN]. */ @@ -37,11 +37,12 @@ class ListActiveUsersHandler(private val store: TransientEntityStore) : GetRestH ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional { - AccessManager.currentSessions.map { session -> + override fun doGet(ctx: Context): List { + return AccessManager.currentSessions.map { session -> AccessManager.userIdForSession(session)?.let { - DbUserManager.get(id = it) - }?.toApi() ?: return@map ApiUser("??", "??", ApiRole.VIEWER, session) + UserManager.get(id = it) + } ?: return@map ApiUser("??", "??", ApiRole.VIEWER, session) } } + } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt index 886a6111c..f8499065e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ListUsersHandler.kt @@ -5,7 +5,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.types.users.ApiUser import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ListUsersHandler(private val store: TransientEntityStore): AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { +class ListUsersHandler(): AbstractUserHandler(), GetRestHandler>, AccessManagedRestHandler { override val route = "user/list" @@ -31,7 +31,5 @@ class ListUsersHandler(private val store: TransientEntityStore): AbstractUserHan responses = [OpenApiResponse("200", [OpenApiContent(Array::class)])], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = this.store.transactional { - DbUserManager.list().map { it.toApi() } - } + override fun doGet(ctx: Context) = UserManager.list() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt index f247ce707..0cea8b42b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/ShowCurrentUserHandler.kt @@ -15,7 +15,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class ShowCurrentUserHandler(private val store: TransientEntityStore) : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class ShowCurrentUserHandler() : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user" /** [ShowCurrentUserHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ @@ -32,9 +32,9 @@ class ShowCurrentUserHandler(private val store: TransientEntityStore) : Abstract ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiUser = this.store.transactional(readonly = true){ - val user = userFromSession(ctx).toApi() + override fun doGet(ctx: Context): ApiUser { + val user = userFromSession(ctx) user.sessionId = ctx.sessionToken() - user + return user } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt index 0f408469e..925259867 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UpdateUsersHandler.kt @@ -9,7 +9,7 @@ import dev.dres.api.rest.types.users.ApiUser import dev.dres.api.rest.types.users.UserRequest import dev.dres.data.model.admin.DbRole import dev.dres.data.model.admin.DbUser -import dev.dres.mgmt.admin.DbUserManager +import dev.dres.mgmt.admin.UserManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -21,7 +21,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class UpdateUsersHandler(private val store: TransientEntityStore) : AbstractUserHandler(), PatchRestHandler, AccessManagedRestHandler { +class UpdateUsersHandler() : AbstractUserHandler(), PatchRestHandler, AccessManagedRestHandler { /** [UpdateUsersHandler] can be used by [ApiRole.ADMIN], [[ApiRole.VIEWER], [ApiRole.PARTICIPANT]*/ override val permittedRoles = setOf(ApiRole.VIEWER, ApiRole.ADMIN, ApiRole.PARTICIPANT) @@ -49,33 +49,32 @@ class UpdateUsersHandler(private val store: TransientEntityStore) : AbstractUser throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - return this.store.transactional { - /* Fetch existing objects. */ - val user = userFromContext(ctx) - val caller = userFromSession(ctx) + /* Fetch existing objects. */ + val user = userFromContext(ctx) + val caller = userFromSession(ctx) - if (caller.role == DbRole.ADMIN || user.id == caller.id) { + if (caller.role == ApiRole.ADMIN || user.id == caller.id) { - val sanitized = if (caller.role == DbRole.ADMIN) { - request - } else { //non admins can only change their passwords, nothing else - request.copy(username = user.username, role = user.role.toApi()) - } + val sanitized = if (caller.role == ApiRole.ADMIN) { + request + } else { //non admins can only change their passwords, nothing else + request.copy(username = user.username!!, role = user.role) + } - val success = DbUserManager.update(id = user.id, request = sanitized) - if (success) { - return@transactional DbUserManager.get(id = user.id)!!.toApi() - } else { - throw ErrorStatusException(500, "Could not update user!", ctx) - } + val updated = UserManager.update(id = user.id, request = sanitized) + if (updated != null) { + return updated } else { - throw ErrorStatusException( - 403, - "You do not have permissions to edit user (${user.id}) as ${caller.id}!", - ctx - ) + throw ErrorStatusException(500, "Could not update user!", ctx) } + } else { + throw ErrorStatusException( + 403, + "You do not have permissions to edit user (${user.id}) as ${caller.id}!", + ctx + ) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt index 1f388dcbf..7c2a7991d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/users/UserDetailsHandler.kt @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class UserDetailsHandler(private val store: TransientEntityStore) : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { +class UserDetailsHandler() : AbstractUserHandler(), GetRestHandler, AccessManagedRestHandler { override val route = "user/{userId}" /** [UserDetailsHandler] requires [ApiRole.ADMIN]. */ @@ -37,7 +37,7 @@ class UserDetailsHandler(private val store: TransientEntityStore) : AbstractUser ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = this.store.transactional { userFromContext(ctx).toApi() } + override fun doGet(ctx: Context) = userFromContext(ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt index ac57ad769..fb885e032 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/WebSocketConnection.kt @@ -1,10 +1,7 @@ package dev.dres.api.rest.types import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.dres.api.rest.AccessManager -import dev.dres.mgmt.admin.DbUserManager import io.javalin.websocket.WsContext -import org.eclipse.jetty.server.session.Session import java.nio.ByteBuffer /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt index c8ff9df77..996d97760 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/users/ApiRole.kt @@ -14,6 +14,19 @@ import io.javalin.security.RouteRole enum class ApiRole : RouteRole { ANYONE, VIEWER, PARTICIPANT, JUDGE, ADMIN; + companion object { + /** + * Parses a [DbRole] instance from a [String]. + */ + fun parse(string: String) = when (string.uppercase()) { + "VIEWER" -> VIEWER + "PARTICIPANT" -> PARTICIPANT + "JUDGE" -> JUDGE + "ADMIN", "ADMINISTRATOR" -> ADMIN + else -> throw IllegalArgumentException("Failed to parse role '$string'.") + } + } + /** * Converts this [ApiMediaType] to a [DbMediaType] representation. Requires an ongoing transaction. * diff --git a/backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt b/backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt index 68ec152df..8d0c08304 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/admin/DbRole.kt @@ -26,16 +26,7 @@ class DbRole(entity: Entity) : XdEnumEntity(entity) { */ fun values() = listOf(VIEWER, PARTICIPANT, JUDGE, ADMIN) - /** - * Parses a [DbRole] instance from a [String]. - */ - fun parse(string: String) = when(string.uppercase()) { - "VIEWER" -> VIEWER - "PARTICIPANT" -> PARTICIPANT - "JUDGE" -> JUDGE - "ADMIN", "ADMINISTRATOR" -> ADMIN - else -> throw IllegalArgumentException("Failed to parse role '$string'.") - } + } /** Name / description of the [DbRole]. */ diff --git a/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt similarity index 55% rename from backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt rename to backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt index d4e52ccf2..9161f2bf6 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/admin/DbUserManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/admin/UserManager.kt @@ -1,176 +1,152 @@ -package dev.dres.mgmt.admin - -import dev.dres.api.rest.types.users.ApiRole -import dev.dres.api.rest.types.users.ApiUser -import dev.dres.api.rest.types.users.UserRequest -import dev.dres.data.model.admin.* -import kotlinx.dnq.query.* -import java.util.* - -/** - * User management class of DRES. Requires transaction context. - * - * @author Loris Sauter - * @version 2.0.1 - */ -object DbUserManager { - - - /** - * Creates a [DbUser] with the given [username], [password] and [role]. - * - * @param username The name of the [DbUser]. Must be unique. - * @param password The [Password.Hashed] of the user. - * @param role The [DbRole] of the new user. - */ - fun create(username: String, password: Password.Hashed, role: DbRole): Boolean { - try { - DbUser.new { - this.username = username.lowercase() - this.password = password.password - this.role = role - } - - } catch (e: Throwable) { - return false - } - return true - } - - /** - * Creates a [DbUser] with the given [username], [password] and [role]. - * - * @param username The name of the [DbUser]. Must be unique. - * @param password The [Password.Hashed] of the user. - * @param role The [ApiRole] of the new user. - */ - fun create(username: String, password: Password.Hashed, role: ApiRole): Boolean { - return create(username, password, role.toDb() ?: throw IllegalArgumentException("Invalid Role")) - } - - /** - * Creates a [DbUser] with the given [username], [password] and [role]. - * - * @param username The name of the [DbUser]. Must be unique. - * @param password The [Password.Plain] of the user. - * @param role The [DbRole] of the new user. - */ - fun create(username: String, password: Password.Plain, role: DbRole): Boolean { - return create(username, password.hash(), role) - } - - /** - * Updates a [DbUser] with the given [UserId], [username], [password] and [role]. - * - * @param id The [UserId] of the user to update. - * @param username The name of the [DbUser]. Must be unique. - * @param password The [Password.Hashed] of the user. - * @param role The [DbRole] of the new user. - */ - fun update(id: UserId?, username: String?, password: Password.Hashed?, role: DbRole?): Boolean { - val user = if (id != null) { - DbUser.query(DbUser::id eq id).firstOrNull() - } else if (username != null) { - DbUser.query(DbUser::username eq username).firstOrNull() - } else { - null - } - if (user == null) return false - if (username != null) user.username = username.lowercase() - if (password != null) user.password = password.password - if (role != null) user.role = role - return true - } - - /** - * Updates a [DbUser] with the given [UserId], [username], [password] and [role]. - * - * @param id The [UserId] of the user to update. - * @param username The name of the [DbUser]. Must be unique. - * @param password The [Password.Plain] of the user. - * @param role The [DbRole] of the new user. - */ - fun update(id: UserId?, username: String?, password: Password.Plain?, role: DbRole?): Boolean - = update(id, username, password?.hash(), role) - - /** - * Updates a [DbUser] for the given [id] based o the [request]. - * - * @param id The [UserId] of the user to update. - * @param request The [UserRequest] detailing the update - * @return True on success, false otherwise. - */ - fun update(id: UserId?, request: UserRequest): Boolean - = update(id = id, username = request.username, password = request.password?.let { if (it.isNotBlank()) Password.Plain(it) else null }, role = request.role?.toDb()) - - /** - * Deletes the [DbUser] for the given [UserId]. - * - * @param username The name of the [DbUser] to delete. - * @return True on success, false otherwise. - */ - fun delete(id: UserId? = null, username: String? = null): Boolean { - val user = if (id != null) { - DbUser.query(DbUser::id eq id).firstOrNull() - } else if (username != null) { - DbUser.query(DbUser::username eq username.lowercase()).firstOrNull() - } else { - null - } - if (user != null) { - user.delete() - return true - } else { - return false - } - } - - /** - * Lists all [DbUser] objects in DRES. - * - * @return List of all [DbUser]s. - */ - fun list(): List = DbUser.all().toList() - - - /** - * Checks for the existence of the [DbUser] with the given [EvaluationId]. - * - * @param id [EvaluationId] to check. - * @return True if [DbUser] exists, false otherwise. - */ - fun exists(id: UserId? = null, username: String? = null): Boolean { - return if (id != null) { - DbUser.query(DbUser::id eq id).isNotEmpty - } else if (username != null) { - DbUser.query(DbUser::username eq username.lowercase()).isNotEmpty - } else { - throw IllegalArgumentException("Either user ID or username must be non-null!") - } - } - - /** - * Returns the [DbUser] for the given [EvaluationId] or null if [DbUser] doesn't exist. - * - * @param id The [EvaluationId] of the [DbUser] to fetch. - * @return [DbUser] or null - */ - fun get(id: UserId? = null, username: String? = null): DbUser? { - return if (id != null) { - DbUser.query(DbUser::id eq id).firstOrNull() - } else if (username != null) { - // Note: during after create, the query below is empty within a readonly transaction (unexpected), but non-empty out of the transaction - DbUser.query(DbUser::username eq username.lowercase()).firstOrNull() - } else { - null - } - } - - /** - * Either returns a user for this username/password tuple or null - */ - fun getMatchingApiUser(username: String, password: Password.Plain) : ApiUser? { - val user = get(null, username) - return if (user?.hashedPassword()?.check(password) == true) user.toApi() else null - } -} +package dev.dres.mgmt.admin + +import dev.dres.api.rest.types.users.ApiRole +import dev.dres.api.rest.types.users.ApiUser +import dev.dres.api.rest.types.users.UserRequest +import dev.dres.data.model.admin.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import java.util.* + +/** + * User management class of DRES. Requires transaction context. + * + * @author Loris Sauter + * @version 2.0.1 + */ +object UserManager { + + private lateinit var store: TransientEntityStore + + fun init(store: TransientEntityStore) { + this.store = store + } + + /** + * Creates a [DbUser] with the given [username], [password] and [role]. + * + * @param username The name of the [DbUser]. Must be unique. + * @param password The [Password.Hashed] of the user. + * @param role The [DbRole] of the new user. + */ + fun create(username: String, password: Password.Hashed, role: ApiRole): ApiUser = + this.store.transactional { + val dbUser = + DbUser.new { + this.username = username.lowercase() + this.password = password.password + this.role = role.toDb() ?: throw IllegalArgumentException("Cannot create user with role '$role'") + } + return@transactional dbUser.toApi() + } + + /** + * Updates a [DbUser] with the given [UserId], [username], [password] and [role]. + * + * @param id The [UserId] of the user to update. + * @param username The name of the [DbUser]. Must be unique. + * @param password The [Password.Hashed] of the user. + * @param role The [DbRole] of the new user. + */ + fun update(id: UserId?, username: String?, password: Password.Hashed?, role: ApiRole?): ApiUser? = + this.store.transactional { + val user = if (id != null) { + DbUser.query(DbUser::id eq id).firstOrNull() + } else if (username != null) { + DbUser.query(DbUser::username eq username).firstOrNull() + } else { + null + } + if (user == null) return@transactional null + if (username != null) user.username = username.lowercase() + if (password != null) user.password = password.password + if (role != null) user.role = role.toDb() ?: throw IllegalArgumentException("Cannot update user to role '$role'") + return@transactional user.toApi() + } + + + /** + * Updates a [DbUser] for the given [id] based o the [request]. + * + * @param id The [UserId] of the user to update. + * @param request The [UserRequest] detailing the update + * @return True on success, false otherwise. + */ + fun update(id: UserId?, request: UserRequest): ApiUser? = update( + id = id, + username = request.username, + password = request.password?.let { if (it.isNotBlank()) Password.Plain(it).hash() else null }, + role = request.role + ) + + /** + * Deletes the [DbUser] for the given [UserId]. + * + * @param username The name of the [DbUser] to delete. + * @return True on success, false otherwise. + */ + fun delete(id: UserId? = null, username: String? = null): Boolean = this.store.transactional { + val user = if (id != null) { + DbUser.query(DbUser::id eq id).firstOrNull() + } else if (username != null) { + DbUser.query(DbUser::username eq username.lowercase()).firstOrNull() + } else { + null + } + return@transactional if (user != null) { + user.delete() + true + } else { + false + } + } + + /** + * Lists all [DbUser] objects in DRES. + * + * @return List of all [DbUser]s. + */ + fun list(): List = this.store.transactional(true) { DbUser.all().toList().map { it.toApi() } } + + + /** + * Checks for the existence of the [DbUser] with the given [EvaluationId]. + * + * @param id [EvaluationId] to check. + * @return True if [DbUser] exists, false otherwise. + */ + fun exists(id: UserId? = null, username: String? = null): Boolean = this.store.transactional(true) { + if (id != null) { + DbUser.query(DbUser::id eq id).isNotEmpty + } else if (username != null) { + DbUser.query(DbUser::username eq username.lowercase()).isNotEmpty + } else { + throw IllegalArgumentException("Either user ID or username must be non-null!") + } + } + + /** + * Returns the [DbUser] for the given [EvaluationId] or null if [DbUser] doesn't exist. + * + * @param id The [EvaluationId] of the [DbUser] to fetch. + * @return [DbUser] or null + */ + fun get(id: UserId? = null, username: String? = null): ApiUser? = this.store.transactional(true) { + if (id != null) { + DbUser.query(DbUser::id eq id).firstOrNull()?.toApi() + } else if (username != null) { + // Note: during after create, the query below is empty within a readonly transaction (unexpected), but non-empty out of the transaction + DbUser.query(DbUser::username eq username.lowercase()).firstOrNull()?.toApi() + } else { + null + } + } + + /** + * Either returns a user for this username/password tuple or null + */ + fun getMatchingApiUser(username: String, password: Password.Plain): ApiUser? = this.store.transactional(true) { + val user = DbUser.query(DbUser::username eq username.lowercase()).firstOrNull() + if (user?.hashedPassword()?.check(password) == true) user.toApi() else null + } +} diff --git a/backend/src/main/kotlin/dev/dres/mgmt/cache/AbstractPreviewRequest.kt b/backend/src/main/kotlin/dev/dres/mgmt/cache/AbstractPreviewRequest.kt index 8086be9b7..efdf47b25 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/cache/AbstractPreviewRequest.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/cache/AbstractPreviewRequest.kt @@ -1,20 +1,20 @@ -package dev.dres.mgmt.cache - -import java.nio.file.Files -import java.nio.file.Path -import java.util.concurrent.Callable - -/** - * - * @author Ralph Gasser - * @version 1.0 - */ -abstract class AbstractPreviewRequest(protected val input: Path, protected val output: Path): Callable { - - init { - require(Files.exists(this.input)) { "Could not generate preview because file $input does not exist." } - if (!Files.exists(output.parent)) { - Files.createDirectories(output.parent) - } - } +package dev.dres.mgmt.cache + +import java.nio.file.Files +import java.nio.file.Path +import java.util.concurrent.Callable + +/** + * + * @author Ralph Gasser + * @version 1.0 + */ +abstract class AbstractPreviewRequest(protected val input: Path, protected val output: Path): Callable { + + init { + require(Files.exists(this.input)) { "Could not generate preview because file $input does not exist." } + if (!Files.exists(output.parent)) { + Files.createDirectories(output.parent) + } + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt index 196836ecf..e6db3caf6 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/cache/CacheManager.kt @@ -1,386 +1,386 @@ -package dev.dres.mgmt.cache - -import com.github.kokorin.jaffree.ffmpeg.FFmpeg -import com.github.kokorin.jaffree.ffmpeg.UrlInput -import com.github.kokorin.jaffree.ffmpeg.UrlOutput -import dev.dres.DRES -import dev.dres.data.model.config.Config -import dev.dres.data.model.media.DbMediaItem -import dev.dres.data.model.media.DbMediaType -import dev.dres.data.model.media.MediaItemType -import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.DbEvaluationStatus -import dev.dres.utilities.CompletedFuture -import dev.dres.utilities.FailedFuture -import jetbrains.exodus.database.TransientEntityStore -import jetbrains.exodus.kotlin.synchronized -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.filter -import kotlinx.dnq.query.flatMapDistinct -import org.slf4j.LoggerFactory -import org.slf4j.MarkerFactory -import java.awt.Image -import java.awt.image.BufferedImage -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardOpenOption -import java.util.concurrent.* -import javax.imageio.ImageIO - -/** - * A [CacheManager] used to manager and generate, access and manage cached image and video previews. - * - * @author Ralph Gasser - * @version 1.0.0 - */ -class CacheManager(private val config: Config, private val store: TransientEntityStore) { - companion object { - private val LOGGER = LoggerFactory.getLogger(PreviewImageFromVideoRequest::class.java) - private val MARKER = MarkerFactory.getMarker("FFMPEG") - } - - /** The path to the FFmpeg binary used by this [CacheManager] instance. */ - private val ffmpegBin = this.config.cache.ffmpegPath() - - /** The [Path] to the cache location. */ - private val cacheLocation = DRES.CONFIG.cache.cachePath - - /** A [ConcurrentHashMap] of all [Path]s* that are currently being calculated. */ - private val inTransit = ConcurrentHashMap>() - - /** The [ExecutorService] used by this [CacheManager]. */ - private val executor: ExecutorService = Executors.newFixedThreadPool(this.config.cache.maxRenderingThreads) - - init { - println("Found FFmpeg at ${this.ffmpegBin}...") - if (!Files.exists(cacheLocation)) { - println("Created cache location at ${this.cacheLocation}...") - Files.createDirectories(cacheLocation) - } - } - - /** - * Generates a preview image from a Path to an external image. - * - * @param imagePath the source path. - * @return [Future] of the [Path] to the generated image. - */ - fun asyncPreviewImage(imagePath: Path): Future { - val output = this@CacheManager.cacheLocation.resolve("${imagePath.fileName}.jpg") - if (Files.exists(output)) { - this.inTransit.remove(output) - return CompletedFuture(output) - } - val inTransit = this.inTransit[output] - if (inTransit != null) { - return inTransit - } - synchronized { - return try { - val ret = this.executor.submit(PreviewImageFromImageRequest(imagePath, output)) - this@CacheManager.inTransit[output] = ret - ret - } catch (e: Throwable) { - LOGGER.error(e.message) - FailedFuture("Could generate preview image: ${e.message}") - } - } - } - - /** - * Generates a preview image from a [DbMediaItem]. - * - * @param item The [DbMediaItem] to generate the preview image from. Must be of [DbMediaType.VIDEO] or [DbMediaType.IMAGE]. - * @param timestamp The timestamp in milliseconds to generate the preview image from. Defaults to 0. - * @return [Future] of the [Path] to the generated image. - */ - fun asyncPreviewImage(item: DbMediaItem, timestamp: Long = 0): Future { - require(timestamp >= 0) { "Frame numbers cannot be negative." } - /** The output path generated by this [PreviewImageFromVideoRequest]. */ - val output = this@CacheManager.cacheLocation.resolve("${item.mediaItemId}_${timestamp}-${timestamp}.jpg") - if (Files.exists(output)) { - this.inTransit.remove(output) - return CompletedFuture(output) - } - val inTransit = this.inTransit[output] - if (inTransit != null) { - return inTransit - } - synchronized { - return try { - when(item.type()) { - MediaItemType.IMAGE -> { - val ret = this.executor.submit(PreviewImageFromImageRequest(item.pathToOriginal(), output)) - this@CacheManager.inTransit[output] = ret - ret - } - MediaItemType.VIDEO -> { - if (timestamp <= item.durationMs!!) { //bounds check - val ret = this.executor.submit( - PreviewImageFromVideoRequest( - item.pathToOriginal(), - output, - timestamp - ) - ) - this@CacheManager.inTransit[output] = ret - ret - }else { - FailedFuture("Requested time is outside of video.") - } - } - else -> FailedFuture("Cannot generate a preview image from a media item that is neither a video nor an image.") - } - } catch (e: Throwable) { - LOGGER.error(e.message) - FailedFuture("Could generate preview image: ${e.message}") - } - } - } - - /** - * Generates a preview video from a [DbMediaItem]. - * - * @param item The [DbMediaItem] to generate the preview video from. Must be of [DbMediaType.VIDEO]. - * @param start The start position in milliseconds to generate the preview video from. - * @param start The end position in milliseconds to generate the preview video from. Defaults to 0. - * @return [Future] of the [Path] to the generated video. - */ - fun asyncPreviewVideo(item: DbMediaItem, start: Long, end: Long): Future { - require(start >= 0) { "Start timestamp cannot be negative." } - require(end > start) { "End timestamp must be greater than start." } - /** The output path generated by this [PreviewImageFromVideoRequest]. */ - val output = this@CacheManager.cacheLocation.resolve("${item.mediaItemId}_${start}-${end}.mp4") - if (Files.exists(output)) { - this.inTransit.remove(output) - return CompletedFuture(output) - } - val inTransit = this.inTransit[output] - if (inTransit != null) { - return inTransit - } - synchronized { - return try { - when(item.type()) { - MediaItemType.VIDEO -> { - if (start < item.durationMs!! && end <= item.durationMs!!) { - val ret = this.executor.submit( - PreviewVideoFromVideoRequest( - item.pathToOriginal(), - output, - start, - end - ) - ) - this@CacheManager.inTransit[output] = ret - ret - } else { - FailedFuture("Requested time is outside of video.") - } - } - else -> FailedFuture("Cannot generate a video preview from a media item that is not a video.") - } - } catch (e: Throwable) { - LOGGER.error(e.message) - FailedFuture("Could generate preview video: ${e.message}") - } - } - } - /** - * Generates a preview video from a Path. - * - * @param item The [DbMediaItem] to generate the preview video from. Must be of [DbMediaType.VIDEO]. - * @param start The start position in milliseconds to generate the preview video from. - * @param start The end position in milliseconds to generate the preview video from. Defaults to 0. - * @return [Future] of the [Path] to the generated video. - */ - fun asyncPreviewVideo(input: Path, start: Long, end: Long): Future { - require(start >= 0) { "Start timestamp cannot be negative." } - require(end > start) { "End timestamp must be greater than start." } - /** The output path generated by this [PreviewImageFromVideoRequest]. */ - val output = this@CacheManager.cacheLocation.resolve("${input.fileName}_${start}-${end}.mp4") - if (Files.exists(output)) { - this.inTransit.remove(output) - return CompletedFuture(output) - } - val inTransit = this.inTransit[output] - if (inTransit != null) { - return inTransit - } - synchronized { - return try { - val ret = this.executor.submit( - PreviewVideoFromVideoRequest( - input, output, start, end - ) - ) - this@CacheManager.inTransit[output] = ret - ret - } catch (e: Throwable) { - LOGGER.error(e.message) - FailedFuture("Could generate preview video: ${e.message}") - } - } - } - - /** - * Cleans the cache directory associated with this [CacheManager]. The cleanup mechanism makes sure, that all - * as part of an ongoing evaluation are preserved and not deleted. - * - * Requires an active transaction. - */ - fun cleanup() = try { - /* Fetch all items related to a hint preview. */ - val blackList = mutableSetOf() - this.store.transactional(true) { - DbEvaluation.filter { it.status eq DbEvaluationStatus.ACTIVE } - .flatMapDistinct { it.template.tasks } - .flatMapDistinct { it.hints } - .filter { it.item ne null } - .asSequence().forEach { - blackList.add(this@CacheManager.cacheLocation.resolve("${it.item!!.mediaItemId}_${it.start ?: 0}-${it.end ?: 0}")) - } - - /* Fetch all items related to a target preview. */ - DbEvaluation.filter { it.status eq DbEvaluationStatus.ACTIVE } - .flatMapDistinct { it.template.tasks } - .flatMapDistinct { it.targets } - .filter { it.item ne null } - .asSequence().forEach { - blackList.add(this@CacheManager.cacheLocation.resolve("${it.item!!.mediaItemId}_${it.range?.start ?: 0}-${it.range?.end ?: 0}")) - } - } - - /* Delete all files that have not been blacklisted. */ - Files.walk(this.cacheLocation).use { walk -> - walk.sorted(Comparator.reverseOrder()).forEach { - val time = Files.getLastModifiedTime(it) - if ((System.currentTimeMillis() - time.toMillis()) > 300000L && !blackList.contains(it)) { - Files.delete(it) - } - } - } - } catch(e: Throwable) { - LOGGER.warn("Failed to cleanup cache directory ${this.cacheLocation}: ${e.message}") - } - - /** - * Clears the cache directory associated with this [CacheManager]. - */ - fun clear() = try { - Files.walk(this.cacheLocation).use { walk -> - walk.sorted(Comparator.reverseOrder()).forEach { - Files.delete(it) - } - } - } catch(e: Throwable) { - LOGGER.warn("Failed to clear cache directory ${this.cacheLocation}: ${e.message}") - } - - /** - * Stops this [CacheManager] and all requests that are in transit. - */ - fun stop() { - this.executor.shutdown() - this.executor.awaitTermination(5000, TimeUnit.MILLISECONDS) - } - - /** - * Utility method to convert from milliseconds to timecode representation. - * - * @param ms The timestamp in milliseconds. - * @return The timecode [String]. - */ - private fun millisecondToTimestamp(ms: Long): String { - val hours = ms / (1000 * 3600) - val minutes = (ms % (1000 * 3600)) / (60_000) - val seconds = (ms % 60_000) / 1000 - val milliseconds = ms % 1000 - return "$hours:${"%02d".format(minutes)}:${"%02d".format(seconds)}.${"%03d".format(milliseconds)}" - } - - /** - * A [Callable] that generates a preview image from an image file. - */ - inner class PreviewImageFromImageRequest constructor(input: Path, output: Path, private val size: Int = this@CacheManager.config.cache.previewImageMaxSize): AbstractPreviewRequest(input, output) { - - override fun call(): Path = try {/* Try to read image. */ - Files.newInputStream(this.input, StandardOpenOption.READ).use {i -> - val image = ImageIO.read(i) - - /* Scale image to a maximum of 500x500 pixels. */ - val scaled: Image = if (image.width > image.height) { - image.getScaledInstance(size, -1, Image.SCALE_DEFAULT) - } else { - image.getScaledInstance(-1, size, Image.SCALE_DEFAULT) - } - val outputImage = BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB) - outputImage.graphics.drawImage(scaled, 0, 0, null) - - Files.newOutputStream(this.output, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING).use { o -> - ImageIO.write(outputImage, "jpg", o) - } - } - this.output - } catch (e: Exception) { - LOGGER.error("Error in FFMpeg: ${e.message}") - throw e - } finally { - this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ - } - } - - /** - * A [Callable] that generates a preview image from a video file. - */ - inner class PreviewImageFromVideoRequest constructor(input: Path, output: Path, private val start: Long, private val size: Int = this@CacheManager.config.cache.previewImageMaxSize): AbstractPreviewRequest(input, output) { - override fun call(): Path = try { - FFmpeg.atPath(this@CacheManager.ffmpegBin). - addInput(UrlInput.fromPath(this.input)) - .addOutput(UrlOutput.toPath(this.output)) - .setOverwriteOutput(true) - .addArguments("-ss", millisecondToTimestamp(this.start)) - .addArguments("-frames:v", "1") - .addArguments("-filter:v", "scale=${this@CacheManager.config.cache.previewImageMaxSize}:-1") - .setOutputListener { l -> LOGGER.debug(MARKER, l); } - .execute() - this.output - } catch (e: Exception) { - LOGGER.error("Error in FFMpeg: ${e.message}") - throw e - } finally { - this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ - } - } - - /** - * A [Callable] that generates a preview video from a video file. - */ - inner class PreviewVideoFromVideoRequest constructor(input: Path, output: Path, private val start: Long, private val end: Long): AbstractPreviewRequest(input, output){ - override fun call(): Path = try { - val startTimecode = millisecondToTimestamp(this.start) - val endTimecode = millisecondToTimestamp(this.end) - LOGGER.info(MARKER, "Start rendering segment for video $input from $startTimecode to $endTimecode") - FFmpeg.atPath(this@CacheManager.ffmpegBin) - .addInput(UrlInput.fromPath(this.input)) - .addOutput(UrlOutput.toPath(this.output)) - .setOverwriteOutput(true) - .addArguments("-ss", startTimecode) - .addArguments("-to", endTimecode) - .addArguments("-c:v", "libx264") - .addArguments("-c:a", "aac") - .addArguments("-b:v", "2000k") - .addArguments("-filter:v", "scale=${this@CacheManager.config.cache.previewVideoMaxSize}:-1") - .addArguments("-tune", "zerolatency") - .addArguments("-preset", "slow") - .setOutputListener { l -> LOGGER.debug(MARKER, l); } - .execute() - this.output - } catch (e: Exception) { - LOGGER.error("Error in FFMpeg: ${e.message}") - throw e - } finally { - this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ - } - } -} +package dev.dres.mgmt.cache + +import com.github.kokorin.jaffree.ffmpeg.FFmpeg +import com.github.kokorin.jaffree.ffmpeg.UrlInput +import com.github.kokorin.jaffree.ffmpeg.UrlOutput +import dev.dres.DRES +import dev.dres.data.model.config.Config +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.DbMediaType +import dev.dres.data.model.media.MediaItemType +import dev.dres.data.model.run.DbEvaluation +import dev.dres.data.model.run.DbEvaluationStatus +import dev.dres.utilities.CompletedFuture +import dev.dres.utilities.FailedFuture +import jetbrains.exodus.database.TransientEntityStore +import jetbrains.exodus.kotlin.synchronized +import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.filter +import kotlinx.dnq.query.flatMapDistinct +import org.slf4j.LoggerFactory +import org.slf4j.MarkerFactory +import java.awt.Image +import java.awt.image.BufferedImage +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import java.util.concurrent.* +import javax.imageio.ImageIO + +/** + * A [CacheManager] used to manager and generate, access and manage cached image and video previews. + * + * @author Ralph Gasser + * @version 1.0.0 + */ +class CacheManager(private val config: Config, private val store: TransientEntityStore) { + companion object { + private val LOGGER = LoggerFactory.getLogger(PreviewImageFromVideoRequest::class.java) + private val MARKER = MarkerFactory.getMarker("FFMPEG") + } + + /** The path to the FFmpeg binary used by this [CacheManager] instance. */ + private val ffmpegBin = this.config.cache.ffmpegPath() + + /** The [Path] to the cache location. */ + private val cacheLocation = DRES.CONFIG.cache.cachePath + + /** A [ConcurrentHashMap] of all [Path]s* that are currently being calculated. */ + private val inTransit = ConcurrentHashMap>() + + /** The [ExecutorService] used by this [CacheManager]. */ + private val executor: ExecutorService = Executors.newFixedThreadPool(this.config.cache.maxRenderingThreads) + + init { + println("Found FFmpeg at ${this.ffmpegBin}...") + if (!Files.exists(cacheLocation)) { + println("Created cache location at ${this.cacheLocation}...") + Files.createDirectories(cacheLocation) + } + } + + /** + * Generates a preview image from a Path to an external image. + * + * @param imagePath the source path. + * @return [Future] of the [Path] to the generated image. + */ + fun asyncPreviewImage(imagePath: Path): Future { + val output = this@CacheManager.cacheLocation.resolve("${imagePath.fileName}.jpg") + if (Files.exists(output)) { + this.inTransit.remove(output) + return CompletedFuture(output) + } + val inTransit = this.inTransit[output] + if (inTransit != null) { + return inTransit + } + synchronized { + return try { + val ret = this.executor.submit(PreviewImageFromImageRequest(imagePath, output)) + this@CacheManager.inTransit[output] = ret + ret + } catch (e: Throwable) { + LOGGER.error(e.message) + FailedFuture("Could generate preview image: ${e.message}") + } + } + } + + /** + * Generates a preview image from a [DbMediaItem]. + * + * @param item The [DbMediaItem] to generate the preview image from. Must be of [DbMediaType.VIDEO] or [DbMediaType.IMAGE]. + * @param timestamp The timestamp in milliseconds to generate the preview image from. Defaults to 0. + * @return [Future] of the [Path] to the generated image. + */ + fun asyncPreviewImage(item: DbMediaItem, timestamp: Long = 0): Future { + require(timestamp >= 0) { "Frame numbers cannot be negative." } + /** The output path generated by this [PreviewImageFromVideoRequest]. */ + val output = this@CacheManager.cacheLocation.resolve("${item.mediaItemId}_${timestamp}-${timestamp}.jpg") + if (Files.exists(output)) { + this.inTransit.remove(output) + return CompletedFuture(output) + } + val inTransit = this.inTransit[output] + if (inTransit != null) { + return inTransit + } + synchronized { + return try { + when(item.type()) { + MediaItemType.IMAGE -> { + val ret = this.executor.submit(PreviewImageFromImageRequest(item.pathToOriginal(), output)) + this@CacheManager.inTransit[output] = ret + ret + } + MediaItemType.VIDEO -> { + if (timestamp <= item.durationMs!!) { //bounds check + val ret = this.executor.submit( + PreviewImageFromVideoRequest( + item.pathToOriginal(), + output, + timestamp + ) + ) + this@CacheManager.inTransit[output] = ret + ret + }else { + FailedFuture("Requested time is outside of video.") + } + } + else -> FailedFuture("Cannot generate a preview image from a media item that is neither a video nor an image.") + } + } catch (e: Throwable) { + LOGGER.error(e.message) + FailedFuture("Could generate preview image: ${e.message}") + } + } + } + + /** + * Generates a preview video from a [DbMediaItem]. + * + * @param item The [DbMediaItem] to generate the preview video from. Must be of [DbMediaType.VIDEO]. + * @param start The start position in milliseconds to generate the preview video from. + * @param start The end position in milliseconds to generate the preview video from. Defaults to 0. + * @return [Future] of the [Path] to the generated video. + */ + fun asyncPreviewVideo(item: DbMediaItem, start: Long, end: Long): Future { + require(start >= 0) { "Start timestamp cannot be negative." } + require(end > start) { "End timestamp must be greater than start." } + /** The output path generated by this [PreviewImageFromVideoRequest]. */ + val output = this@CacheManager.cacheLocation.resolve("${item.mediaItemId}_${start}-${end}.mp4") + if (Files.exists(output)) { + this.inTransit.remove(output) + return CompletedFuture(output) + } + val inTransit = this.inTransit[output] + if (inTransit != null) { + return inTransit + } + synchronized { + return try { + when(item.type()) { + MediaItemType.VIDEO -> { + if (start < item.durationMs!! && end <= item.durationMs!!) { + val ret = this.executor.submit( + PreviewVideoFromVideoRequest( + item.pathToOriginal(), + output, + start, + end + ) + ) + this@CacheManager.inTransit[output] = ret + ret + } else { + FailedFuture("Requested time is outside of video.") + } + } + else -> FailedFuture("Cannot generate a video preview from a media item that is not a video.") + } + } catch (e: Throwable) { + LOGGER.error(e.message) + FailedFuture("Could generate preview video: ${e.message}") + } + } + } + /** + * Generates a preview video from a Path. + * + * @param item The [DbMediaItem] to generate the preview video from. Must be of [DbMediaType.VIDEO]. + * @param start The start position in milliseconds to generate the preview video from. + * @param start The end position in milliseconds to generate the preview video from. Defaults to 0. + * @return [Future] of the [Path] to the generated video. + */ + fun asyncPreviewVideo(input: Path, start: Long, end: Long): Future { + require(start >= 0) { "Start timestamp cannot be negative." } + require(end > start) { "End timestamp must be greater than start." } + /** The output path generated by this [PreviewImageFromVideoRequest]. */ + val output = this@CacheManager.cacheLocation.resolve("${input.fileName}_${start}-${end}.mp4") + if (Files.exists(output)) { + this.inTransit.remove(output) + return CompletedFuture(output) + } + val inTransit = this.inTransit[output] + if (inTransit != null) { + return inTransit + } + synchronized { + return try { + val ret = this.executor.submit( + PreviewVideoFromVideoRequest( + input, output, start, end + ) + ) + this@CacheManager.inTransit[output] = ret + ret + } catch (e: Throwable) { + LOGGER.error(e.message) + FailedFuture("Could generate preview video: ${e.message}") + } + } + } + + /** + * Cleans the cache directory associated with this [CacheManager]. The cleanup mechanism makes sure, that all + * as part of an ongoing evaluation are preserved and not deleted. + * + * Requires an active transaction. + */ + fun cleanup() = try { + /* Fetch all items related to a hint preview. */ + val blackList = mutableSetOf() + this.store.transactional(true) { + DbEvaluation.filter { it.status eq DbEvaluationStatus.ACTIVE } + .flatMapDistinct { it.template.tasks } + .flatMapDistinct { it.hints } + .filter { it.item ne null } + .asSequence().forEach { + blackList.add(this@CacheManager.cacheLocation.resolve("${it.item!!.mediaItemId}_${it.start ?: 0}-${it.end ?: 0}")) + } + + /* Fetch all items related to a target preview. */ + DbEvaluation.filter { it.status eq DbEvaluationStatus.ACTIVE } + .flatMapDistinct { it.template.tasks } + .flatMapDistinct { it.targets } + .filter { it.item ne null } + .asSequence().forEach { + blackList.add(this@CacheManager.cacheLocation.resolve("${it.item!!.mediaItemId}_${it.range?.start ?: 0}-${it.range?.end ?: 0}")) + } + } + + /* Delete all files that have not been blacklisted. */ + Files.walk(this.cacheLocation).use { walk -> + walk.sorted(Comparator.reverseOrder()).forEach { + val time = Files.getLastModifiedTime(it) + if ((System.currentTimeMillis() - time.toMillis()) > 300000L && !blackList.contains(it)) { + Files.delete(it) + } + } + } + } catch(e: Throwable) { + LOGGER.warn("Failed to cleanup cache directory ${this.cacheLocation}: ${e.message}") + } + + /** + * Clears the cache directory associated with this [CacheManager]. + */ + fun clear() = try { + Files.walk(this.cacheLocation).use { walk -> + walk.sorted(Comparator.reverseOrder()).forEach { + Files.delete(it) + } + } + } catch(e: Throwable) { + LOGGER.warn("Failed to clear cache directory ${this.cacheLocation}: ${e.message}") + } + + /** + * Stops this [CacheManager] and all requests that are in transit. + */ + fun stop() { + this.executor.shutdown() + this.executor.awaitTermination(5000, TimeUnit.MILLISECONDS) + } + + /** + * Utility method to convert from milliseconds to timecode representation. + * + * @param ms The timestamp in milliseconds. + * @return The timecode [String]. + */ + private fun millisecondToTimestamp(ms: Long): String { + val hours = ms / (1000 * 3600) + val minutes = (ms % (1000 * 3600)) / (60_000) + val seconds = (ms % 60_000) / 1000 + val milliseconds = ms % 1000 + return "$hours:${"%02d".format(minutes)}:${"%02d".format(seconds)}.${"%03d".format(milliseconds)}" + } + + /** + * A [Callable] that generates a preview image from an image file. + */ + inner class PreviewImageFromImageRequest constructor(input: Path, output: Path, private val size: Int = this@CacheManager.config.cache.previewImageMaxSize): AbstractPreviewRequest(input, output) { + + override fun call(): Path = try {/* Try to read image. */ + Files.newInputStream(this.input, StandardOpenOption.READ).use {i -> + val image = ImageIO.read(i) + + /* Scale image to a maximum of 500x500 pixels. */ + val scaled: Image = if (image.width > image.height) { + image.getScaledInstance(size, -1, Image.SCALE_DEFAULT) + } else { + image.getScaledInstance(-1, size, Image.SCALE_DEFAULT) + } + val outputImage = BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB) + outputImage.graphics.drawImage(scaled, 0, 0, null) + + Files.newOutputStream(this.output, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING).use { o -> + ImageIO.write(outputImage, "jpg", o) + } + } + this.output + } catch (e: Exception) { + LOGGER.error("Error in FFMpeg: ${e.message}") + throw e + } finally { + this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ + } + } + + /** + * A [Callable] that generates a preview image from a video file. + */ + inner class PreviewImageFromVideoRequest constructor(input: Path, output: Path, private val start: Long, private val size: Int = this@CacheManager.config.cache.previewImageMaxSize): AbstractPreviewRequest(input, output) { + override fun call(): Path = try { + FFmpeg.atPath(this@CacheManager.ffmpegBin). + addInput(UrlInput.fromPath(this.input)) + .addOutput(UrlOutput.toPath(this.output)) + .setOverwriteOutput(true) + .addArguments("-ss", millisecondToTimestamp(this.start)) + .addArguments("-frames:v", "1") + .addArguments("-filter:v", "scale=${this@CacheManager.config.cache.previewImageMaxSize}:-1") + .setOutputListener { l -> LOGGER.debug(MARKER, l); } + .execute() + this.output + } catch (e: Exception) { + LOGGER.error("Error in FFMpeg: ${e.message}") + throw e + } finally { + this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ + } + } + + /** + * A [Callable] that generates a preview video from a video file. + */ + inner class PreviewVideoFromVideoRequest constructor(input: Path, output: Path, private val start: Long, private val end: Long): AbstractPreviewRequest(input, output){ + override fun call(): Path = try { + val startTimecode = millisecondToTimestamp(this.start) + val endTimecode = millisecondToTimestamp(this.end) + LOGGER.info(MARKER, "Start rendering segment for video $input from $startTimecode to $endTimecode") + FFmpeg.atPath(this@CacheManager.ffmpegBin) + .addInput(UrlInput.fromPath(this.input)) + .addOutput(UrlOutput.toPath(this.output)) + .setOverwriteOutput(true) + .addArguments("-ss", startTimecode) + .addArguments("-to", endTimecode) + .addArguments("-c:v", "libx264") + .addArguments("-c:a", "aac") + .addArguments("-b:v", "2000k") + .addArguments("-filter:v", "scale=${this@CacheManager.config.cache.previewVideoMaxSize}:-1") + .addArguments("-tune", "zerolatency") + .addArguments("-preset", "slow") + .setOutputListener { l -> LOGGER.debug(MARKER, l); } + .execute() + this.output + } catch (e: Exception) { + LOGGER.error("Error in FFMpeg: ${e.message}") + throw e + } finally { + this@CacheManager.inTransit.remove(this.output) /* Remove this PreviewImageFromVideoRequest. */ + } + } +} From a4410312077a633eaecc6d48cd895bdba4eeb5c4 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 10 Aug 2023 15:47:32 +0200 Subject: [PATCH 444/498] Removed explicit references to Db classes from collection api handlers See #432 --- backend/src/main/kotlin/dev/dres/DRES.kt | 2 + .../main/kotlin/dev/dres/api/rest/RestApi.kt | 24 +-- .../collection/AbstractCollectionHandler.kt | 10 +- .../collection/AddCollectionHandler.kt | 19 +-- .../handler/collection/AddMediaItemHandler.kt | 28 +--- .../collection/DeleteCollectionHandler.kt | 23 +-- .../collection/DeleteMediaItemHandler.kt | 20 +-- .../collection/ListCollectionHandler.kt | 9 +- .../collection/ListMediaItemHandler.kt | 21 +-- .../collection/RandomMediaItemHandler.kt | 18 +-- .../ResolveMediaItemListByNameHandler.kt | 9 +- .../collection/ShowCollectionHandler.kt | 18 +-- .../collection/ShowMediaItemHandler.kt | 19 +-- .../collection/UpdateCollectionHandler.kt | 18 +-- .../collection/UpdateMediaItemHandler.kt | 19 +-- .../rest/types/status/ErrorStatusException.kt | 3 - .../data/model/media/DbMediaCollection.kt | 3 + .../dev/dres/mgmt/MediaCollectionManager.kt | 146 ++++++++++++++++++ 18 files changed, 258 insertions(+), 151 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/mgmt/MediaCollectionManager.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 955616534..8ae051c29 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -15,6 +15,7 @@ import dev.dres.data.model.template.team.DbTeamAggregator import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.run.* import dev.dres.data.model.submissions.* +import dev.dres.mgmt.MediaCollectionManager import dev.dres.mgmt.admin.UserManager import dev.dres.mgmt.cache.CacheManager import dev.dres.run.RunExecutor @@ -104,6 +105,7 @@ object DRES { /* Initialize data managers */ UserManager.init(store) + MediaCollectionManager.init(store) /* Initialize RunExecutor. */ RunExecutor.init(CONFIG, store, global) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 7fa16947f..ea57f962b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -108,18 +108,18 @@ object RestApi { GetMediaHandler(store), // Collection - ListCollectionHandler(store), - ShowCollectionHandler(store), - AddCollectionHandler(store), - UpdateCollectionHandler(store), - DeleteCollectionHandler(store), - AddMediaItemHandler(store), - UpdateMediaItemHandler(store), - DeleteMediaItemHandler(store), - RandomMediaItemHandler(store), // Must be before ListMediaItem - ShowMediaItemHandler(store), - ResolveMediaItemListByNameHandler(store), // Must be before ListMediaItem - ListMediaItemHandler(store), + ListCollectionHandler(), + ShowCollectionHandler(), + AddCollectionHandler(), + UpdateCollectionHandler(), + DeleteCollectionHandler(), + AddMediaItemHandler(), + UpdateMediaItemHandler(), + DeleteMediaItemHandler(), + RandomMediaItemHandler(), // Must be before ListMediaItem + ShowMediaItemHandler(), + ResolveMediaItemListByNameHandler(), // Must be before ListMediaItem + ListMediaItemHandler(), ListExternalItemHandler(), // Template diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt index dcc1a01f8..aeca7c1d3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AbstractCollectionHandler.kt @@ -3,8 +3,10 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.types.users.ApiRole import dev.dres.api.rest.handler.AccessManagedRestHandler import dev.dres.api.rest.handler.RestHandler +import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.media.DbMediaCollection +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.security.RouteRole import jetbrains.exodus.database.TransientEntityStore @@ -17,7 +19,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0 */ -abstract class AbstractCollectionHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { +abstract class AbstractCollectionHandler() : RestHandler, AccessManagedRestHandler { /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN) @@ -25,9 +27,5 @@ abstract class AbstractCollectionHandler(protected val store: TransientEntitySto /** All [AbstractCollectionHandler]s are part of the v1 API. */ override val apiVersion = "v2" - /** Convenience method to extract [DbMediaCollection] from [Context]. */ - protected fun collectionFromContext(ctx: Context): DbMediaCollection { - val id = ctx.pathParamMap()["collectionId"] ?: throw ErrorStatusException(404, "Parameter 'collectionId' is missing!'", ctx) - return DbMediaCollection.query(DbMediaCollection::id eq id).firstOrNull() ?: throw ErrorStatusException(404, "Collection with ID $id not found.'", ctx) - } + protected fun collectionId(ctx: Context) = ctx.pathParamMap()["collectionId"] ?: throw ErrorStatusException(404, "Parameter 'collectionId' is missing!'", ctx) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt index 3714c41b0..a28c3c02b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddCollectionHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.media.DbMediaCollection +import dev.dres.mgmt.MediaCollectionManager import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -17,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler { +class AddCollectionHandler() : AbstractCollectionHandler(), PostRestHandler { override val route: String = "collection" @@ -45,14 +46,14 @@ class AddCollectionHandler(store: TransientEntityStore) : AbstractCollectionHand throw ErrorStatusException(400, "Invalid parameters, collection base path not set.", ctx) } - val collectionId = this.store.transactional { - DbMediaCollection.new { - this.name = restCollection.name - this.description = restCollection.description - this.path = restCollection.basePath.cleanPathString() - }.id - } + val collectionId = MediaCollectionManager.createCollection( + restCollection.name, restCollection.description, restCollection.basePath.cleanPathString() + )?.id - return SuccessStatus("Collection $collectionId added successfully.") + if (collectionId != null) { + return SuccessStatus("Collection $collectionId added successfully.") + } else { + throw ErrorStatusException(400, "Could not create collection", ctx) + } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt index 0139b3641..5280b6a0f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/AddMediaItemHandler.kt @@ -7,6 +7,7 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.media.DbMediaItem +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -18,7 +19,7 @@ import kotlinx.dnq.query.* * @author Ralph Gasser * @version 1.0 */ -class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler { +class AddMediaItemHandler : AbstractCollectionHandler(), PostRestHandler { override val route: String = "mediaItem" @@ -45,25 +46,12 @@ class AddMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandl throw ErrorStatusException(400, e.message ?: "Invalid parameters. This is a programmers error!", ctx) } - /* Try to persist media item. */ - val collectionId = mediaItem.collectionId - return this.store.transactional { - val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() - ?: throw ErrorStatusException(400, "Invalid parameters, collection with ID $collectionId does not exist.", ctx) - if (collection.items.filter { it.name eq mediaItem.name }.isNotEmpty) { - throw ErrorStatusException(400, "Media item with name '${mediaItem.name}' already exists in collection ${collection.name}.", ctx) - } - - val item = DbMediaItem.new { - this.type = mediaItem.type.toDb() - this.name = mediaItem.name - this.location = mediaItem.location - this.fps = mediaItem.fps - this.durationMs = mediaItem.durationMs - } - collection.items.add(item) - - SuccessStatus("Media item added successfully.") + try { + MediaCollectionManager.addMediaItem(mediaItem) + return SuccessStatus("Media item added successfully.") + } catch (e: Exception) { + throw ErrorStatusException(400, e.message ?: "Could not create item", ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt index 5a01f6013..f2ea7eebd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteCollectionHandler.kt @@ -2,7 +2,9 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -12,7 +14,9 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), DeleteRestHandler { +class DeleteCollectionHandler : AbstractCollectionHandler(), DeleteRestHandler { + + override val route: String = "collection/{collectionId}" @OpenApi( summary = "Deletes a media collection identified by a collection id.", @@ -28,15 +32,16 @@ class DeleteCollectionHandler(store: TransientEntityStore) : AbstractCollectionH ] ) override fun doDelete(ctx: Context): SuccessStatus { - val status = this.store.transactional { - val collection = collectionFromContext(ctx) - val collectionId = collection.id - collection.delete() - SuccessStatus("Collection $collectionId deleted successfully.") + + val collectionId = collectionId(ctx) + + val deleted = MediaCollectionManager.deleteCollection(collectionId) + + if (deleted != null) { + return SuccessStatus("Collection $collectionId deleted successfully.") + } else { + throw ErrorStatusException(404, "Collection $collectionId not found", ctx) } - return status } - override val route: String = "collection/{collectionId}" - } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt index 33bb10bd4..5392c09ca 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/DeleteMediaItemHandler.kt @@ -4,20 +4,17 @@ import dev.dres.api.rest.handler.DeleteRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus -import dev.dres.data.model.media.DbMediaItem +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.query.query +import java.lang.Exception /** * * @author Ralph Gasser * @version 1.0 */ -class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), DeleteRestHandler { +class DeleteMediaItemHandler : AbstractCollectionHandler(), DeleteRestHandler { override val route: String = "mediaItem/{mediaId}" @@ -42,12 +39,11 @@ class DeleteMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa throw ErrorStatusException(404, "Parameter 'mediaId' is missing!'", ctx) } - return this.store.transactional { - val item = DbMediaItem.query(DbMediaItem::id eq mediaId).firstOrNull() - ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) - val itemId = item.id - item.delete() - SuccessStatus("Media item $itemId deleted successfully.") + try { + MediaCollectionManager.deleteMediaItem(mediaId) + return SuccessStatus("Media item $mediaId deleted successfully.") + } catch (e: Exception) { + throw ErrorStatusException(404, e.message ?: "Could not delete item", ctx) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt index 3b91aa915..bc84c4a80 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListCollectionHandler.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.media.DbMediaCollection +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -14,7 +15,7 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 1.0.0 */ -class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { +class ListCollectionHandler : AbstractCollectionHandler(), GetRestHandler> { override val route: String = "collection/list" @@ -29,9 +30,5 @@ class ListCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): List { - return this.store.transactional(true) { - DbMediaCollection.all().asSequence().map { ApiMediaCollection.fromMediaCollection(it) }.toList() - } - } + override fun doGet(ctx: Context): List = MediaCollectionManager.getCollections() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt index 3fd176b51..9967b7558 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ListMediaItemHandler.kt @@ -3,21 +3,17 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.media.DbMediaItem +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.query -import kotlinx.dnq.query.startsWith -import kotlinx.dnq.query.take /** * * @author Ralph Gasser * @version 1.0.0 */ -class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler> { +class ListMediaItemHandler : AbstractCollectionHandler(), GetRestHandler> { override val route: String = "collection/{collectionId}/{startsWith}" @OpenApi( @@ -37,14 +33,9 @@ class ListMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { - val collection = collectionFromContext(ctx) + override fun doGet(ctx: Context): List { + val collectionId = collectionId(ctx) val start = ctx.pathParamMap()["startsWith"] - val query = if (!start.isNullOrBlank()) { - collection.items.query(DbMediaItem::name startsWith start) - } else { - collection.items - } - query.take(50).asSequence().map { it.toApi() }.toList() + return MediaCollectionManager.getMediaItemsByPartialName(collectionId, start) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt index bceb2e763..e6a59c39b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/RandomMediaItemHandler.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -18,9 +19,7 @@ import java.util.SplittableRandom * @author Ralph Gasser * @version 1.0 */ -class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { - - private val rand = SplittableRandom(System.currentTimeMillis()) +class RandomMediaItemHandler : AbstractCollectionHandler(), GetRestHandler { override val route: String = "collection/{collectionId}/random" @@ -40,10 +39,11 @@ class RandomMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doGet(ctx: Context): ApiMediaItem = this.store.transactional(true) { - val collection = collectionFromContext(ctx) - val item = collection.items.drop(this.rand.nextInt(0, collection.items.size())).take(1).firstOrNull() ?: - throw ErrorStatusException(404, "Failed to ferch media item. It seems that the given collection ${collection.id} is empty.", ctx) - item.toApi() - } + override fun doGet(ctx: Context): ApiMediaItem = + MediaCollectionManager.getRandomMediaItem(collectionId(ctx)) ?: throw ErrorStatusException( + 404, + "Failed to fetch media item. It seems that the given collection is empty.", + ctx + ) + } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt index 195cc52e2..988122af3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ResolveMediaItemListByNameHandler.kt @@ -4,10 +4,10 @@ import dev.dres.api.rest.handler.PostRestHandler import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter @@ -16,7 +16,7 @@ import kotlinx.dnq.query.filter * @author Ralph Gasser * @version 1.0 */ -class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PostRestHandler> { +class ResolveMediaItemListByNameHandler : AbstractCollectionHandler(), PostRestHandler> { override val route = "collection/{collectionId}/resolve" @OpenApi( @@ -49,10 +49,7 @@ class ResolveMediaItemListByNameHandler(store: TransientEntityStore) : AbstractC } /** Execute query. */ - return this.store.transactional(true) { - val collection = collectionFromContext(ctx) - collection.items.filter { it.name isIn(queriedNames.asIterable()) }.asSequence().map { it.toApi() }.toList() - } + return MediaCollectionManager.getMediaItemsByName(collectionId(ctx), queriedNames.toList()) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt index 72cebe9d2..2d1728021 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowCollectionHandler.kt @@ -2,22 +2,19 @@ package dev.dres.api.rest.handler.collection import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiPopulatedMediaCollection -import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.media.DbMediaItem +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.query + /** * * @author Ralph Gasser * @version 1.0.0 */ -class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { +class ShowCollectionHandler() : AbstractCollectionHandler(), GetRestHandler { override val route: String = "collection/{collectionId}" @@ -34,9 +31,8 @@ class ShowCollectionHandler(store: TransientEntityStore) : AbstractCollectionHan ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiPopulatedMediaCollection = this.store.transactional(true) { - val collection = collectionFromContext(ctx) //also checks if collection exists - val items = DbMediaItem.query(DbMediaItem::collection eq collection).asSequence().map { it.toApi() }.toList() - ApiPopulatedMediaCollection(ApiMediaCollection.fromMediaCollection(collection), items) + override fun doGet(ctx: Context): ApiPopulatedMediaCollection { + val id = collectionId(ctx) + return MediaCollectionManager.getPopulatedCollection(id) ?: throw ErrorStatusException(404, "Collection '$id' not found.'", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt index 536893e32..d7f4d7215 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/ShowMediaItemHandler.kt @@ -4,20 +4,16 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.media.DbMediaItem +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.eq -import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.query.query /** * * @author Ralph Gasser * @version 1.0.0 */ -class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), GetRestHandler { +class ShowMediaItemHandler : AbstractCollectionHandler(), GetRestHandler { override val route: String = "mediaItem/{mediaItemId}" @OpenApi( @@ -41,10 +37,11 @@ class ShowMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHand throw ErrorStatusException(404, "Parameter 'mediaItemId' is missing!'", ctx) } - return this.store.transactional(true) { - val item = DbMediaItem.query(DbMediaItem::id eq mediaId).firstOrNull() - ?: throw ErrorStatusException(404, "Media item with ID $mediaId not found.", ctx) - item.toApi() - } + return MediaCollectionManager.getMediaItem(mediaId) ?: throw ErrorStatusException( + 404, + "Media item with ID $mediaId not found.", + ctx + ) + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt index 5856b13eb..c6237fa92 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateCollectionHandler.kt @@ -6,11 +6,11 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.media.DbMediaCollection +import dev.dres.mgmt.MediaCollectionManager import dev.dres.utilities.extensions.cleanPathString import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.eq import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query @@ -20,7 +20,8 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0 */ -class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PatchRestHandler { +class UpdateCollectionHandler : AbstractCollectionHandler(), + PatchRestHandler { override val route: String = "collection" @@ -45,15 +46,12 @@ class UpdateCollectionHandler(store: TransientEntityStore) : AbstractCollectionH throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - val status = this.store.transactional { - val collection = DbMediaCollection.query(DbMediaCollection::id eq restCollection.id).firstOrNull() - ?: throw ErrorStatusException(400, "Invalid parameters, collection with ID ${restCollection.id} does not exist.", ctx) - collection.name = restCollection.name.trim() - collection.description = restCollection.description?.trim() ?: collection.description - collection.path = restCollection.basePath?.cleanPathString() ?: collection.path - SuccessStatus("Collection ${collection.id} updated successfully.") + try { + MediaCollectionManager.updateCollection(restCollection) + return SuccessStatus("Collection ${restCollection.id} updated successfully.") + } catch (e: Exception) { + throw ErrorStatusException(400, e.message ?: "Could not update collection", ctx) } - return status } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt index 85b7f893d..217dd7b41 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/collection/UpdateMediaItemHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.media.DbMediaItem +import dev.dres.mgmt.MediaCollectionManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* @@ -19,7 +20,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0.0 */ -class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHandler(store), PatchRestHandler { +class UpdateMediaItemHandler : AbstractCollectionHandler(), PatchRestHandler { override val route: String = "mediaitem" @@ -45,17 +46,11 @@ class UpdateMediaItemHandler(store: TransientEntityStore) : AbstractCollectionHa throw ErrorStatusException(400, e.message ?: "Invalid parameters. This is a programmers error!", ctx) } - return this.store.transactional { - val item = DbMediaItem.query(DbMediaItem::id eq mediaItem.mediaItemId).firstOrNull() - ?: throw ErrorStatusException(404, "Media item with ID ${mediaItem.mediaItemId} not found.", ctx) - - item.type = mediaItem.type.toDb() - item.name = mediaItem.name - item.location = mediaItem.location - item.fps = mediaItem.fps - item.durationMs = mediaItem.durationMs - - SuccessStatus("Media item ${item.id} updated successfully.") + try{ + MediaCollectionManager.updateMediaItem(mediaItem) + return SuccessStatus("Media item ${mediaItem.mediaItemId} updated successfully.") + } catch (e: Exception) { + throw ErrorStatusException(404, e.message ?: "Could not update item", ctx) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt index 569c70add..0e2a84f64 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/status/ErrorStatusException.kt @@ -4,17 +4,14 @@ import io.javalin.http.Context import org.slf4j.LoggerFactory data class ErrorStatusException(val statusCode: Int, val status: String, private val ctx: Context, val doNotLog: Boolean = false) : Exception(status) { - companion object { private val logger = LoggerFactory.getLogger(this::class.java) } - init { if(!doNotLog){ logger.info("ErrorStatusException with code $statusCode and message '$status' thrown by ${stackTrace.first()} for request from ${ctx.req().remoteAddr}") } } - val errorStatus: ErrorStatus get() = ErrorStatus(status) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt index d02ab62b0..a0653c8c1 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/media/DbMediaCollection.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.media +import dev.dres.api.rest.types.collection.ApiMediaCollection import dev.dres.data.model.PersistentEntity import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* @@ -24,6 +25,8 @@ class DbMediaCollection(entity: Entity): PersistentEntity(entity), MediaItemColl /** A list of [DbMediaItem]s in this [DbMediaCollection]. */ val items by xdChildren0_N(DbMediaItem::collection) + + fun toApi() = ApiMediaCollection(id, name, description, path) } diff --git a/backend/src/main/kotlin/dev/dres/mgmt/MediaCollectionManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/MediaCollectionManager.kt new file mode 100644 index 000000000..b1de9bf15 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/mgmt/MediaCollectionManager.kt @@ -0,0 +1,146 @@ +package dev.dres.mgmt + +import dev.dres.api.rest.types.collection.ApiMediaCollection +import dev.dres.api.rest.types.collection.ApiMediaItem +import dev.dres.api.rest.types.collection.ApiPopulatedMediaCollection +import dev.dres.data.model.media.CollectionId +import dev.dres.data.model.media.DbMediaCollection +import dev.dres.data.model.media.DbMediaItem +import dev.dres.data.model.media.MediaItemId +import dev.dres.utilities.extensions.cleanPathString +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.* +import java.lang.Exception +import kotlin.random.Random + +object MediaCollectionManager { + + private lateinit var store: TransientEntityStore + + fun init(store: TransientEntityStore) { + this.store = store + } + + fun getCollections(): List = this.store.transactional (true) { + DbMediaCollection.all().asSequence().map { ApiMediaCollection.fromMediaCollection(it) }.toList() + } + + fun getCollection(collectionId: CollectionId) : ApiMediaCollection? = this.store.transactional (true) { + DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull()?.toApi() + } + + fun getPopulatedCollection(collectionId: CollectionId) : ApiPopulatedMediaCollection? = this.store.transactional (true) { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() ?: return@transactional null + val items = DbMediaItem.query(DbMediaItem::collection eq collection).asSequence().map { it.toApi() }.toList() + ApiPopulatedMediaCollection(ApiMediaCollection.fromMediaCollection(collection), items) + } + + fun createCollection(name: String, description: String?, basePath: String): ApiMediaCollection? = this.store.transactional { + try{ + DbMediaCollection.new { + this.name = name + this.description = description + this.path = basePath.cleanPathString() + }.toApi() + } catch (e: Exception) { + null + } + } + + fun updateCollection(apiMediaCollection: ApiMediaCollection) { + this.store.transactional { + val collection = DbMediaCollection.query(DbMediaCollection::id eq apiMediaCollection.id).firstOrNull() + ?: throw IllegalArgumentException( + "Invalid parameters, collection with ID ${apiMediaCollection.id} does not exist.", + ) + collection.name = apiMediaCollection.name.trim() + collection.description = apiMediaCollection.description?.trim() ?: collection.description + collection.path = apiMediaCollection.basePath?.cleanPathString() ?: collection.path + } + } + + fun deleteCollection(collectionId: CollectionId) : ApiMediaCollection? = this.store.transactional { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() ?: return@transactional null + val api = collection.toApi() + collection.delete() + api + } + + fun addMediaItem(mediaItem: ApiMediaItem) { + + val collectionId = mediaItem.collectionId + + this.store.transactional { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() + ?: throw IllegalArgumentException("Invalid parameters, collection with ID $collectionId does not exist.") + if (collection.items.filter { it.name eq mediaItem.name }.isNotEmpty) { + throw IllegalArgumentException("Media item with name '${mediaItem.name}' already exists in collection ${collection.name}.") + } + + val item = DbMediaItem.new { + this.type = mediaItem.type.toDb() + this.name = mediaItem.name + this.location = mediaItem.location + this.fps = mediaItem.fps + this.durationMs = mediaItem.durationMs + } + collection.items.add(item) + } + + } + + fun getMediaItem(mediaItemId: MediaItemId) : ApiMediaItem? = this.store.transactional(true) { + DbMediaItem.query(DbMediaItem::id eq mediaItemId).firstOrNull()?.toApi() + } + + fun getMediaItemsByName(collectionId: CollectionId, names: List) : List = this.store.transactional(true) { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() ?: return@transactional emptyList() + collection.items.filter { it.name isIn names }.asSequence().map { it.toApi() }.toList() + } + + fun getMediaItemsByPartialName(collectionId: CollectionId, name: String?, limit: Int = 50) : List = this.store.transactional(true) { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() ?: return@transactional emptyList() + if (!name.isNullOrBlank()) { + collection.items.query(DbMediaItem::name startsWith name).sortedBy(DbMediaItem::name) + } else { + collection.items + }.take(limit).asSequence().map { it.toApi() }.toList() + } + + fun getRandomMediaItem(collectionId: CollectionId) : ApiMediaItem? = this.store.transactional(true) { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() ?: return@transactional null + collection.items.drop(Random.nextInt(0, collection.items.size())).take(1).firstOrNull()?.toApi() + } + + fun updateMediaItem(mediaItem: ApiMediaItem) { + + val collectionId = mediaItem.collectionId + + this.store.transactional { + val collection = DbMediaCollection.query(DbMediaCollection::id eq collectionId).firstOrNull() + ?: throw IllegalArgumentException("Invalid parameters, collection with ID $collectionId does not exist.") + if (collection.items.filter { it.name eq mediaItem.name }.isNotEmpty) { + throw IllegalArgumentException("Media item with name '${mediaItem.name}' already exists in collection ${collection.name}.") + } + + val item = DbMediaItem.query(DbMediaItem::id eq mediaItem.mediaItemId).firstOrNull() + ?: throw IllegalArgumentException("Media item with ID ${mediaItem.mediaItemId} not found.") + + item.type = mediaItem.type.toDb() + item.name = mediaItem.name + item.location = mediaItem.location + item.fps = mediaItem.fps + item.durationMs = mediaItem.durationMs + + } + + } + + fun deleteMediaItem(mediaItemId: MediaItemId) = this.store.transactional { + val item = DbMediaItem.query(DbMediaItem::id eq mediaItemId).firstOrNull() + ?: throw IllegalArgumentException("Media item with ID $mediaItemId not found.") + item.delete() + } + + +} \ No newline at end of file From 2b26c85f2b53aa54f11b8eb87abbc8902c87de9c Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 10 Aug 2023 18:56:13 +0200 Subject: [PATCH 445/498] Removed explicit Db class access from template api handlers See #432 --- backend/src/main/kotlin/dev/dres/DRES.kt | 2 + .../dres/api/cli/EvaluationTemplateCommand.kt | 6 +- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 16 ++--- .../AbstractEvaluationTemplateHandler.kt | 18 +++--- .../CreateEvaluationTemplateHandler.kt | 18 ++---- .../DeleteEvaluationTemplateHandler.kt | 14 +++-- .../handler/template/GetTeamLogoHandler.kt | 28 +++++---- .../ListEvaluationTemplatesHandler.kt | 9 +-- .../rest/handler/template/ListTasksHandler.kt | 8 +-- .../rest/handler/template/ListTeamHandler.kt | 8 +-- .../template/ShowEvaluationTemplateHandler.kt | 8 +-- .../UpdateEvaluationTemplateHandler.kt | 40 ++++-------- .../TemplateManager.kt} | 63 +++++++++++++++++-- 13 files changed, 136 insertions(+), 102 deletions(-) rename backend/src/main/kotlin/dev/dres/{utilities/TemplateUtil.kt => mgmt/TemplateManager.kt} (82%) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 8ae051c29..0a59bc812 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -16,6 +16,7 @@ import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.run.* import dev.dres.data.model.submissions.* import dev.dres.mgmt.MediaCollectionManager +import dev.dres.mgmt.TemplateManager import dev.dres.mgmt.admin.UserManager import dev.dres.mgmt.cache.CacheManager import dev.dres.run.RunExecutor @@ -106,6 +107,7 @@ object DRES { /* Initialize data managers */ UserManager.init(store) MediaCollectionManager.init(store) + TemplateManager.init(store) /* Initialize RunExecutor. */ RunExecutor.init(CONFIG, store, global) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt index f6a94e8ac..84c995a7d 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt @@ -15,7 +15,7 @@ import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.mgmt.cache.CacheManager -import dev.dres.utilities.TemplateUtil +import dev.dres.mgmt.TemplateManager import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.joda.time.DateTime @@ -121,7 +121,7 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private return@transactional } - TemplateUtil.copyTemplate(evaluationTemplate) + TemplateManager.copyTemplate(evaluationTemplate) println("template copied") } //println("Successfully copied template.") @@ -338,7 +338,7 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private ) - TemplateUtil.updateDbTemplate(target, import) + TemplateManager.updateDbTemplate(target, import) println("template imported") } else { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index ea57f962b..349cac554 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -123,15 +123,15 @@ object RestApi { ListExternalItemHandler(), // Template - ListEvaluationTemplatesHandler(store), - CreateEvaluationTemplateHandler(store), - UpdateEvaluationTemplateHandler(store, config), - ShowEvaluationTemplateHandler(store), - DeleteEvaluationTemplateHandler(store), - ListTeamHandler(store), - ListTasksHandler(store), + ListEvaluationTemplatesHandler(), + CreateEvaluationTemplateHandler(), + UpdateEvaluationTemplateHandler(), + ShowEvaluationTemplateHandler(), + DeleteEvaluationTemplateHandler(), + ListTeamHandler(), + ListTasksHandler(), ListTaskTypePresetsHandler(), - GetTeamLogoHandler(store), + GetTeamLogoHandler(), // Submission LegacySubmissionHandler(store, cache), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt index e5451fafb..6b85e0857 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/AbstractEvaluationTemplateHandler.kt @@ -23,7 +23,7 @@ import kotlinx.dnq.query.query * @author Loris Sauter * @version 1.0.0 */ -abstract class AbstractEvaluationTemplateHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { +abstract class AbstractEvaluationTemplateHandler() : RestHandler, AccessManagedRestHandler { /** All [AbstractCollectionHandler]s require [ApiRole.ADMIN]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN) @@ -32,16 +32,16 @@ abstract class AbstractEvaluationTemplateHandler(protected val store: TransientE override val apiVersion = "v2" /** Convenience method to extract [DbEvaluationTemplate]'s ID from [Context]. */ - private fun templateIdFromContext(ctx: Context): TemplateId = + protected fun templateIdFromContext(ctx: Context): TemplateId = ctx.pathParamMap().getOrElse("templateId") { throw ErrorStatusException(404, "Parameter 'templateId' is missing!'", ctx) } - /** Convenience method to extract [DbEvaluationTemplate] from [Context]. */ - protected fun evaluationTemplateFromContext(ctx: Context): DbEvaluationTemplate - = evaluationTemplateById(templateIdFromContext(ctx), ctx) - - /** Convenience method to extract [DbEvaluationTemplate] by ID. */ - protected fun evaluationTemplateById(id: TemplateId, ctx: Context): DbEvaluationTemplate - = DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq id and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Evaluation template with ID $id not found.'", ctx) +// /** Convenience method to extract [DbEvaluationTemplate] from [Context]. */ +// protected fun evaluationTemplateFromContext(ctx: Context): DbEvaluationTemplate +// = evaluationTemplateById(templateIdFromContext(ctx), ctx) +// +// /** Convenience method to extract [DbEvaluationTemplate] by ID. */ +// protected fun evaluationTemplateById(id: TemplateId, ctx: Context): DbEvaluationTemplate +// = DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq id and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Evaluation template with ID $id not found.'", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt index 3485cea38..d14de7e8b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CreateEvaluationTemplateHandler.kt @@ -6,6 +6,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.mgmt.TemplateManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass @@ -22,7 +23,7 @@ import java.util.* * @author Loris Sauter * @version 2.0.0 */ -class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), PostRestHandler { +class CreateEvaluationTemplateHandler : AbstractEvaluationTemplateHandler(), PostRestHandler { override val route: String = "template" @@ -47,17 +48,8 @@ class CreateEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - val newId = UUID.randomUUID().toString() - this.store.transactional { - DbEvaluationTemplate.new { - this.id = newId - this.instance = false - this.name = createRequest.name - this.description = createRequest.description - this.created = DateTime.now() - this.modified = DateTime.now() - } - } - return SuccessStatus("Evaluation template with ID $newId was created successfully.") + val template = TemplateManager.createEvaluationTemplate(createRequest.name, createRequest.description) + + return SuccessStatus("Evaluation template with ID ${template.id} was created successfully.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt index b25f7668e..24781ed88 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/DeleteEvaluationTemplateHandler.kt @@ -5,6 +5,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.mgmt.TemplateManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -17,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), DeleteRestHandler { +class DeleteEvaluationTemplateHandler : AbstractEvaluationTemplateHandler(), DeleteRestHandler { override val route: String = "template/{templateId}" @OpenApi( @@ -34,10 +35,13 @@ class DeleteEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEva OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doDelete(ctx: Context): SuccessStatus = this.store.transactional { - val template = evaluationTemplateFromContext(ctx) - template.delete() - SuccessStatus("Evaluation template with ID ${ctx.pathParam("templateId")} was deleted successfully.") + override fun doDelete(ctx: Context): SuccessStatus { + val template = TemplateManager.deleteTemplate(templateIdFromContext(ctx)) + if (template != null) { + return SuccessStatus("Evaluation template with ID ${template.id} was deleted successfully.") + } else { + throw ErrorStatusException(404, "Template not found", ctx) + } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt index e73ad7504..4b050be74 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/GetTeamLogoHandler.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.template.team.DbTeam +import dev.dres.mgmt.TemplateManager import io.javalin.http.Context import io.javalin.openapi.* import io.javalin.security.RouteRole @@ -20,7 +21,7 @@ import kotlinx.dnq.query.query * @author Loris Sauter * @version 1.0.0 */ -class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler { +class GetTeamLogoHandler : AbstractEvaluationTemplateHandler(), GetRestHandler { override val route = "template/logo/{teamId}" override val apiVersion = "v2" @@ -29,7 +30,8 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla override fun doGet(ctx: Context): Any = "" /** All authorised users can access the team logo. */ - override val permittedRoles: Set = setOf(ApiRole.PARTICIPANT, ApiRole.VIEWER, ApiRole.JUDGE, ApiRole.ADMIN) + override val permittedRoles: Set = + setOf(ApiRole.PARTICIPANT, ApiRole.VIEWER, ApiRole.JUDGE, ApiRole.ADMIN) @OpenApi( summary = "Returns the logo for the given team ID.", @@ -43,17 +45,17 @@ class GetTeamLogoHandler(store: TransientEntityStore) : AbstractEvaluationTempla ) override fun get(ctx: Context) { /* Extract logoId. */ - val teamId = ctx.pathParamMap()["teamId"] ?: throw ErrorStatusException(400, "Parameter 'teamId' is missing!'", ctx) - this.store.transactional(true) { - val logo = DbTeam.query(DbTeam::id eq teamId).firstOrNull()?.logo - if (logo != null) { - ctx.contentType("image/png") - ctx.result(logo) - } else { - ctx.status(404) - ctx.contentType("image/png") - ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) - } + val teamId = + ctx.pathParamMap()["teamId"] ?: throw ErrorStatusException(400, "Parameter 'teamId' is missing!'", ctx) + ctx.contentType("image/png") + + val logo = TemplateManager.getTeamLogo(teamId) + if (logo != null) { + ctx.result(logo) + } else { + ctx.status(404) + ctx.result(this.javaClass.getResourceAsStream("/img/missing.png")!!) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt index 7887a38d2..7b11fd664 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListEvaluationTemplatesHandler.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.template.ApiEvaluationTemplateOverview import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.mgmt.TemplateManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -20,7 +21,7 @@ import kotlinx.dnq.query.size * @author Loris Sauter * @version 2.0.0 */ -class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler> { +class ListEvaluationTemplatesHandler() : AbstractEvaluationTemplateHandler(), GetRestHandler> { override val route: String = "template/list" @OpenApi( @@ -34,9 +35,5 @@ class ListEvaluationTemplatesHandler(store: TransientEntityStore) : AbstractEval ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context) = this.store.transactional(true) { - DbEvaluationTemplate.query(DbEvaluationTemplate::instance eq false).asSequence().map { - ApiEvaluationTemplateOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) - }.toList() - } + override fun doGet(ctx: Context) = TemplateManager.getTemplateOverview() } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt index 4732774d5..066ea539b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTasksHandler.kt @@ -3,7 +3,9 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.mgmt.TemplateManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -18,7 +20,7 @@ import kotlinx.dnq.query.filter * @author Loris Sauter * @version 2.0.0 */ -class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler> { +class ListTasksHandler() : AbstractEvaluationTemplateHandler(), GetRestHandler> { override val route: String = "template/{templateId}/task/list" @@ -39,8 +41,6 @@ class ListTasksHandler(store: TransientEntityStore) : AbstractEvaluationTemplate override fun doGet(ctx: Context): List { val templateId = ctx.pathParam("templateId") - return this.store.transactional(true) { - DbTaskTemplate.filter { it.evaluation.id eq templateId }.asSequence().map { it.toApi() }.toList() - } + return TemplateManager.getTemplate(templateId)?.tasks ?: throw ErrorStatusException(404, "Template with id '$templateId' not found.", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt index ca1f3750a..1cac72e49 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTeamHandler.kt @@ -3,7 +3,9 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.template.team.ApiTeam import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.template.team.DbTeam +import dev.dres.mgmt.TemplateManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -18,7 +20,7 @@ import kotlinx.dnq.query.filter * @author Loris Sauter * @version 2.0.0 */ -class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler> { +class ListTeamHandler : AbstractEvaluationTemplateHandler(), GetRestHandler> { override val route: String = "template/{templateId}/team/list" @@ -39,8 +41,6 @@ class ListTeamHandler(store: TransientEntityStore) : AbstractEvaluationTemplateH override fun doGet(ctx: Context): List { val templateId = ctx.pathParam("templateId") - return this.store.transactional(true) { - DbTeam.filter { it.evaluation.id eq templateId }.asSequence().map { it.toApi() }.toList() - } + return TemplateManager.getTemplate(templateId)?.teams ?: throw ErrorStatusException(404, "Template with id '$templateId' not found.", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt index 968dd75f5..1649a31ad 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ShowEvaluationTemplateHandler.kt @@ -3,7 +3,9 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.mgmt.TemplateManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -16,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvaluationTemplateHandler(store), GetRestHandler { +class ShowEvaluationTemplateHandler : AbstractEvaluationTemplateHandler(), GetRestHandler { override val route: String = "template/{templateId}" @OpenApi( @@ -33,7 +35,5 @@ class ShowEvaluationTemplateHandler(store: TransientEntityStore) : AbstractEvalu ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context)= this.store.transactional(true) { - evaluationTemplateFromContext(ctx).toApi() - } + override fun doGet(ctx: Context) = TemplateManager.getTemplate(templateIdFromContext(ctx)) ?: throw ErrorStatusException(404, "Evaluation template not found.'", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt index 72033b2bc..fe5abd75d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/UpdateEvaluationTemplateHandler.kt @@ -2,29 +2,16 @@ package dev.dres.api.rest.handler.template import dev.dres.api.rest.handler.PatchRestHandler import dev.dres.api.rest.types.template.ApiEvaluationTemplate -import dev.dres.api.rest.types.template.tasks.ApiTargetType import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.config.Config -import dev.dres.data.model.admin.DbUser -import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.DbEvaluationTemplate -import dev.dres.data.model.template.task.* -import dev.dres.data.model.template.task.options.DbConfiguredOption -import dev.dres.data.model.template.team.DbTeam -import dev.dres.data.model.template.team.DbTeamGroup -import dev.dres.data.model.media.DbMediaItem -import dev.dres.utilities.TemplateUtil +import dev.dres.mgmt.TemplateManager import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.creator.findOrNew -import kotlinx.dnq.query.* -import kotlinx.dnq.util.getSafe -import org.joda.time.DateTime -import kotlin.time.ExperimentalTime /** * A [AbstractEvaluationTemplateHandler] that can be used to create a new [DbEvaluationTemplate]. @@ -34,8 +21,8 @@ import kotlin.time.ExperimentalTime * @author Loris Sauter * @version 2.1.0 */ -class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: Config) : - AbstractEvaluationTemplateHandler(store), PatchRestHandler { +class UpdateEvaluationTemplateHandler : + AbstractEvaluationTemplateHandler(), PatchRestHandler { override val route: String = "template/{templateId}" @@ -68,20 +55,17 @@ class UpdateEvaluationTemplateHandler(store: TransientEntityStore, val config: C throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - /* Store change. */ - this.store.transactional { - val existing = this.evaluationTemplateById(apiValue.id, ctx) - if (existing.modified?.millis != apiValue.modified) { - throw ErrorStatusException(409, "Evaluation template ${apiValue.id} has been modified in the meantime. Reload and try again!", ctx) - } - - try { - TemplateUtil.updateDbTemplate(existing, apiValue) - } catch (e: IllegalArgumentException) { - throw ErrorStatusException(404, e.message ?: "", ctx) - } + val existing = TemplateManager.getTemplate(apiValue.id) ?: throw ErrorStatusException(404, "Template with id '' not found", ctx) + if (existing.modified != apiValue.modified) { + throw ErrorStatusException(409, "Evaluation template ${apiValue.id} has been modified in the meantime. Reload and try again!", ctx) + } + try { + TemplateManager.updateDbTemplate(apiValue) + } catch (e: IllegalArgumentException) { + throw ErrorStatusException(404, e.message ?: "", ctx) } + return SuccessStatus("Evaluation template with ID ${apiValue.id} was updated successfully.") } } diff --git a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt similarity index 82% rename from backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt rename to backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index c7aa23df9..e66ea1fb8 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/TemplateUtil.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -1,6 +1,7 @@ -package dev.dres.utilities +package dev.dres.mgmt import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.api.rest.types.template.ApiEvaluationTemplateOverview import dev.dres.api.rest.types.template.tasks.ApiTargetType import dev.dres.data.model.admin.DbUser import dev.dres.data.model.media.DbMediaCollection @@ -11,19 +12,72 @@ import dev.dres.data.model.template.task.* import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamGroup +import dev.dres.data.model.template.team.TeamId +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.query.* import kotlinx.dnq.util.getSafe import org.joda.time.DateTime +import java.io.InputStream -object TemplateUtil { +object TemplateManager { + + private lateinit var store: TransientEntityStore + + fun init(store: TransientEntityStore) { + this.store = store + } + + fun createEvaluationTemplate(name: String, description: String): ApiEvaluationTemplate = this.store.transactional { + val template = DbEvaluationTemplate.new { + this.instance = false + this.name = name + this.description = description + this.created = DateTime.now() + this.modified = DateTime.now() + } + template.toApi() + } + + fun deleteTemplate(templateId: TemplateId): ApiEvaluationTemplate? = this.store.transactional { + val template = + DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq templateId and (DbEvaluationTemplate::instance eq false)) + .firstOrNull() ?: return@transactional null + val api = template.toApi() + template.delete() + api + } + + fun getTemplateOverview(): List = this.store.transactional(true) { + DbEvaluationTemplate.query(DbEvaluationTemplate::instance eq false).asSequence().map { + ApiEvaluationTemplateOverview(it.id, it.name, it.description, it.tasks.size(), it.teams.size()) + }.toList() + } + + fun getTemplate(templateId: TemplateId): ApiEvaluationTemplate? = this.store.transactional(true) { + DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq templateId and (DbEvaluationTemplate::instance eq false)) + .firstOrNull()?.toApi() + } + + fun getTeamLogo(teamId: TeamId) : InputStream? = this.store.transactional(true) { + DbTeam.query(DbTeam::id eq teamId).firstOrNull()?.logo + } /** - * Writes the state of an [ApiEvaluationTemplate] into an existing [DbEvaluationTemplate]. + * Writes the state of an [ApiEvaluationTemplate]. * Requires a transaction context. */ + fun updateDbTemplate(apiEvaluationTemplate: ApiEvaluationTemplate) { + + val dbEvaluationTemplate = + DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq apiEvaluationTemplate.id and (DbEvaluationTemplate::instance eq false)) + .firstOrNull() ?: throw IllegalArgumentException("No template with id '${apiEvaluationTemplate.id}'") + + updateDbTemplate(dbEvaluationTemplate, apiEvaluationTemplate) + } + @Throws(IllegalArgumentException::class) - fun updateDbTemplate(dbEvaluationTemplate: DbEvaluationTemplate, apiEvaluationTemplate: ApiEvaluationTemplate) { + fun updateDbTemplate(dbEvaluationTemplate: DbEvaluationTemplate, apiEvaluationTemplate: ApiEvaluationTemplate) = this.store.transactional { /* Update core information. */ @@ -158,7 +212,6 @@ object TemplateUtil { } - /* Update team information. */ val teamIds = apiEvaluationTemplate.teams.map { it.id }.toTypedArray() dbEvaluationTemplate.teams.removeAll( From 316ea06040cd5916879019875cfa166ac989855b Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Thu, 10 Aug 2023 21:27:43 +0200 Subject: [PATCH 446/498] RunManager no longer exposes DbEvaluationTemplate --- .../kotlin/dev/dres/api/rest/AccessManager.kt | 4 ++-- .../handler/download/ScoreDownloadHandler.kt | 6 ++--- .../admin/CreateEvaluationHandler.kt | 2 +- .../evaluation/admin/ListPastTaskHandler.kt | 2 +- .../admin/StartEvaluationHandler.kt | 2 +- .../client/AbstractEvaluationClientHandler.kt | 2 +- .../client/ClientTaskInfoHandler.kt | 2 +- .../scores/CurrentTaskScoreHandler.kt | 2 +- .../viewer/AbstractEvaluationViewerHandler.kt | 8 ++++--- .../viewer/GetCurrentTaskHandler.kt | 2 +- .../judgement/AbstractJudgementHandler.kt | 2 +- .../types/evaluation/ApiEvaluationInfo.kt | 2 +- .../types/evaluation/ApiEvaluationState.kt | 2 +- .../types/evaluation/ApiTaskTemplateInfo.kt | 3 ++- .../api/rest/types/evaluation/ApiTeamInfo.kt | 3 ++- .../dres/data/model/run/AbstractEvaluation.kt | 2 +- .../dev/dres/data/model/run/AbstractTask.kt | 2 +- .../run/InteractiveAsynchronousEvaluation.kt | 10 ++++----- .../run/InteractiveSynchronousEvaluation.kt | 12 +++++----- .../model/run/NonInteractiveEvaluation.kt | 10 ++++----- .../model/run/interfaces/EvaluationRun.kt | 2 +- .../dres/data/model/run/interfaces/TaskRun.kt | 5 +++-- .../run/InteractiveAsynchronousRunManager.kt | 21 ++++++++++-------- .../dev/dres/run/InteractiveRunManager.kt | 3 +-- .../run/InteractiveSynchronousRunManager.kt | 22 +++++++++++-------- .../dev/dres/run/NonInteractiveRunManager.kt | 5 ++--- .../main/kotlin/dev/dres/run/RunExecutor.kt | 5 +++-- .../main/kotlin/dev/dres/run/RunManager.kt | 5 +++-- .../utilities/extensions/ContextExtensions.kt | 10 ++++----- 29 files changed, 85 insertions(+), 73 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt index 7f368b7ca..7e926924c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/AccessManager.kt @@ -117,11 +117,11 @@ object AccessManager { * @param runManager The [RunManager] to register. */ fun registerRunManager(runManager: RunManager) = this.locks.write { - runManager.template.teams.flatMapDistinct { it.users }.asSequence().forEach { + runManager.template.teams.flatMap { it.users }.forEach { if (this.usersToRunMap.containsKey(it.id)) { this.usersToRunMap[it.id]?.add(runManager) } else { - this.usersToRunMap[it.id] = mutableSetOf(runManager) + this.usersToRunMap[it.id!!] = mutableSetOf(runManager) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index dcc0c3d04..cc1e8a1c8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -38,7 +38,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): String = this.store.transactional(true) { + override fun doGet(ctx: Context): String { val manager = ctx.eligibleManagerForId() val rac = ctx.runActionContext() @@ -47,12 +47,12 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle ctx.header("Content-Disposition", "attachment; filename=\"scores-${manager.id}.csv\"") /* Prepare and return response. */ - "startTime,task,group,team,score\n" + manager.tasks(rac).filter { + return "startTime,task,group,team,score\n" + manager.tasks(rac).filter { it.started != null }.sortedBy { it.started }.flatMap { task -> - task.scorer.scores().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.filter { t -> t.id eq it.first }.firstOrNull()?.name ?: "???"}\",${it.third}" } + task.scorer.scores().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.firstOrNull { t -> t.id == it.first }?.name ?: "???"}\",${it.third}" } }.joinToString(separator = "\n") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 5586cc936..374c9f941 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -67,7 +67,7 @@ class CreateEvaluationHandler(store: TransientEntityStore, private val cache: Ca /* ensure that only one synchronous run of an evaluation is happening at any given time */ if (message.type == ApiEvaluationType.SYNCHRONOUS && RunExecutor.managers().any { - it is InteractiveSynchronousRunManager && it.template == template && it.status != RunManagerStatus.TERMINATED + it is InteractiveSynchronousRunManager && it.template.id == template.id && it.status != RunManagerStatus.TERMINATED } ) { throw ErrorStatusException(400, "Synchronous run of evaluation template ${template.name} already exists.", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index fb3be8f23..607218cbd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -42,7 +42,7 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH val rac = ctx.runActionContext() return this.store.transactional (true) { runManager.tasks(rac).filter { it.hasEnded }.map { - ApiTaskTemplateInfo(it.template) + ApiTaskTemplateInfo(it.template.toApi()) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index 45e785eb0..43e1992e3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -46,7 +46,7 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd val rac = ctx.runActionContext() try { evaluationManager.start(rac) - AuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template.toApi(), AuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) + AuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template, AuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) SuccessStatus("Evaluation $evaluationId was successfully started.") } catch (e: IllegalStateException) { throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt index a2c5b57c1..14c2cc54b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt @@ -30,5 +30,5 @@ abstract class AbstractEvaluationClientHandler(protected val store: TransientEnt * @return [RunManager] or null */ fun getRelevantManagers(ctx: Context): List = - RunExecutor.managers().filter { m -> m.template.teams.filter { t -> t.users.filter { u -> u.id eq ctx.userId() }.isNotEmpty() }.isNotEmpty } + RunExecutor.managers().filter { m -> m.template.teams.any { t -> t.users.any { u -> u.id == ctx.userId() } } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index e7a234e39..af14fb20b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -43,6 +43,6 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie val rac = ctx.runActionContext() if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) - ApiTaskTemplateInfo(task.template) + ApiTaskTemplateInfo(task.template.toApi()) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 4da07dc75..1d97eaad8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -62,7 +62,7 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle return@transactional ApiScoreOverview( "task", manager.currentTaskTemplate(rac).taskGroup.name, - manager.template.teams.asSequence().map { team -> ApiScore(team.id, scores[team.id] ?: 0.0) }.toList() + manager.template.teams.asSequence().map { team -> ApiScore(team.id!!, scores[team.id] ?: 0.0) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt index 681ce8ff4..d87b10353 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt @@ -40,7 +40,7 @@ abstract class AbstractEvaluationViewerHandler(protected val store: TransientEnt fun getEvaluationManager(ctx: Context, evaluationId: EvaluationId): InteractiveRunManager? { val run = RunExecutor.managerForId(evaluationId) ?: return null if (run !is InteractiveRunManager) return null - if (ctx.isParticipant() && run.template.teams.flatMapDistinct { it.users }.filter { it.id eq ctx.userId() }.isEmpty) return null + if (ctx.isParticipant() && run.template.teams.flatMap { it.users }.none { it.id == ctx.userId() }) return null return run } @@ -53,8 +53,10 @@ abstract class AbstractEvaluationViewerHandler(protected val store: TransientEnt fun getRelevantManagers(ctx: Context): List { val managers = RunExecutor.managers().filterIsInstance(InteractiveRunManager::class.java) return when { - ctx.isParticipant() -> managers.filter { m -> m.template.teams.flatMapDistinct { it.users }.filter { it.id eq ctx.userId() }.isNotEmpty } - ctx.isJudge() -> managers.filter { m -> m.template.judges.filter { u -> u.id eq ctx.userId() }.isNotEmpty } + ctx.isParticipant() -> managers.filter { m -> + m.template.teams.flatMap { it.users }.any { it.id == ctx.userId() } + } + ctx.isJudge() -> managers.filter { m -> m.template.judges.any { u -> u == ctx.userId() } } else -> managers } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index bb97ae3f9..84845a97c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -53,6 +53,6 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView throw ErrorStatusException(403, "Access denied.", ctx) } val rac = ctx.runActionContext() - ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) + ApiTaskTemplateInfo(manager.currentTaskTemplate(rac).toApi()) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt index bccb59edd..d6565d143 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt @@ -38,7 +38,7 @@ abstract class AbstractJudgementHandler(protected val store: TransientEntityStor if (AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN)) { return //Admins require no further check } - if (runManager.template.judges.filter { it.userId eq userId }.isEmpty) { + if (runManager.template.judges.none { it == userId }) { throw ErrorStatusException(403, "Access to specified run is denied.", ctx) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt index ec8a27d2b..816151678 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -41,6 +41,6 @@ data class ApiEvaluationInfo( }, manager.runProperties, manager.template.teams.asSequence().map { team -> ApiTeamInfo(team) }.toList(), - manager.template.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { task -> ApiTaskTemplateInfo(task) }.toList() + manager.template.tasks.map { task -> ApiTaskTemplateInfo(task) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt index e4892afbe..5c221eaad 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt @@ -24,7 +24,7 @@ data class ApiEvaluationState( run.status.toApi(), run.currentTask(context)?.status?.toApi() ?: ApiTaskStatus.NO_TASK, try { - ApiTaskTemplateInfo(run.currentTaskTemplate(context)) + ApiTaskTemplateInfo(run.currentTaskTemplate(context).toApi()) } catch (e: IllegalArgumentException) { ApiTaskTemplateInfo.EMPTY_INFO }, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt index 76637349d..cf2221c49 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.template.task.DbTaskTemplate /** @@ -13,7 +14,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate */ data class ApiTaskTemplateInfo(val templateId: String, val name: String, val taskGroup: String, val taskType: String, val duration: Long) { - constructor(task: DbTaskTemplate) : this(task.id, task.name, task.taskGroup.name, task.taskGroup.type.name, task.duration) + constructor(task: ApiTaskTemplate) : this(task.id!!, task.name, task.taskGroup, task.taskType, task.duration) companion object { val EMPTY_INFO = ApiTaskTemplateInfo("", "N/A", "N/A", "N/A", 0) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt index 6c391e1b6..f5205c964 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTeamInfo.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.evaluation +import dev.dres.api.rest.types.template.team.ApiTeam import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.template.team.TeamId @@ -13,5 +14,5 @@ import dev.dres.data.model.template.team.TeamId * @version 1.1.0 */ data class ApiTeamInfo(val id: TeamId, val name: String, val color: String) { - constructor(team: DbTeam) : this(team.id, team.name, team.color) + constructor(team: ApiTeam) : this(team.id!!, team.name!!, team.color!!) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index 095c2c7a3..cbf90348f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -36,7 +36,7 @@ abstract class AbstractEvaluation(evaluation: DbEvaluation): EvaluationRun { * * Since this cannot change during the lifetime of an evaluation, it is stored in memory. */ - override val description: DbEvaluationTemplate = evaluation.template + override val template: DbEvaluationTemplate = evaluation.template /** * Accessor for the [DbEvaluation] underpinning this [AbstractEvaluation] diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index fd5d7135b..3dd76c141 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -152,7 +152,7 @@ abstract class AbstractTask(task: DbTask): TaskRun { /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ val teamGroupAggregators: Map by lazy { - this.competition.description.teamGroups.asSequence().associate { it.id to it.newAggregator() } + this.competition.template.teamGroups.asSequence().associate { it.id to it.newAggregator() } } /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 97b84a2ae..93e685114 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -62,14 +62,14 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } /* Prepare the evaluation scoreboards. */ - val teams = this.description.teams.asSequence().map { it.teamId }.toList() - this.scoreboards = this.description.taskGroups.asSequence().map { group -> + val teams = this.template.teams.asSequence().map { it.teamId }.toList() + this.scoreboards = this.template.taskGroups.asSequence().map { group -> MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) }.toList() } fun goTo(teamId: TeamId, index: Int) { - this.navigationMap[teamId] = this.description.tasks.drop(this.permutation[teamId]!![index]).first().templateId + this.navigationMap[teamId] = this.template.tasks.drop(this.permutation[teamId]!![index]).first().templateId } fun currentTaskDescription(teamId: TeamId): DbTaskTemplate { @@ -78,9 +78,9 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval } init { - val numberOfTasks = this.description.tasks.size() + val numberOfTasks = this.template.tasks.size() require(numberOfTasks > 0) { "Cannot create a run from a competition that doesn't have any tasks. " } - this.description.teams.asSequence().forEach { + this.template.teams.asSequence().forEach { this.tasksMap[it.id] = ArrayList(numberOfTasks) goTo(it.id, 0) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 0ac151e9d..5c4d7ac23 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -39,15 +39,15 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu init { require(this.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } - require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } - require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } + require(this.template.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } + require(this.template.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ override val tasks = LinkedList() /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ - private val templates = this.description.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { it.templateId }.toList() + private val templates = this.template.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { it.templateId }.toList() /** Returns the last [TaskRun]. */ val currentTask: AbstractInteractiveTask? @@ -65,8 +65,8 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu this.evaluation.tasks.asSequence().forEach { ISTaskRun(it) } /* Prepare the evaluation scoreboards. */ - val teams = this.description.teams.asSequence().map { it.teamId }.toList() - this.scoreboards = this.description.taskGroups.asSequence().map { group -> + val teams = this.template.teams.asSequence().map { it.teamId }.toList() + this.scoreboards = this.template.taskGroups.asSequence().map { group -> MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) }.toList() } @@ -139,7 +139,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu override var duration: Long = this.template.duration /** */ - override val teams: List = this@InteractiveSynchronousEvaluation.description.teams.asSequence().map { it.teamId }.toList() + override val teams: List = this@InteractiveSynchronousEvaluation.template.teams.asSequence().map { it.teamId }.toList() init { check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index b739b084b..f42bd3a16 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -35,8 +35,8 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev init { require(this.evaluation.type == DbEvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } - require(this.description.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } - require(this.description.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } + require(this.template.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } + require(this.template.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [TaskRun]s registered for this [NonInteractiveEvaluation]. */ @@ -48,8 +48,8 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev override val scoreboards: List init { - val teams = this.description.teams.asSequence().map { it.teamId }.toList() - this.scoreboards = this.description.taskGroups.asSequence().map { group -> + val teams = this.template.teams.asSequence().map { it.teamId }.toList() + this.scoreboards = this.template.taskGroups.asSequence().map { group -> MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) }.toList() } @@ -105,7 +105,7 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev } /** List of [TeamId]s that work on this [NITaskRun]. */ - override val teams: List = this@NonInteractiveEvaluation.description.teams.asSequence().map { it.teamId }.toList() + override val teams: List = this@NonInteractiveEvaluation.template.teams.asSequence().map { it.teamId }.toList() /** */ override val duration: Long = 0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index 74b4f8e98..10217d9ef 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -18,7 +18,7 @@ interface EvaluationRun: Run { val name: String /** Reference to the [DbEvaluationTemplate] that describes the content of this [EvaluationRun]. */ - val description: DbEvaluationTemplate + val template: DbEvaluationTemplate /** Collection of [TaskRun]s that make up this [EvaluationRun]. */ val tasks: List diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 08ba863d9..ed2942fad 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable import dev.dres.run.score.scorer.CachingTaskScorer @@ -24,8 +25,8 @@ interface TaskRun: Run, Scoreable { /** List of [TeamId]s that worked on this [TaskRun] */ override val teams: List - /** The unique [TemplateId] that identifies the task template underpinning [TaskRun]. */ - val templateId: TemplateId + /** The unique [TaskTemplateId] that identifies the task template underpinning [TaskRun]. */ + val templateId: TaskTemplateId /** The current [DbTaskStatus] of this [TaskRun]. This is typically a transient property. */ val status: DbTaskStatus diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 49cd3898e..f62a243db 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -8,6 +8,7 @@ import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType import dev.dres.api.rest.types.evaluation.websocket.ServerMessage import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType +import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate @@ -70,7 +71,7 @@ class InteractiveAsynchronousRunManager( this.evaluation.limitSubmissionPreviews ) - private val teamIds = this.evaluation.description.teams.asSequence().map { it.teamId }.toList() + private val teamIds = this.evaluation.template.teams.asSequence().map { it.teamId }.toList() /** Tracks the current [DbTaskTemplate] per [TeamId]. */ private val statusMap: MutableMap = HashMap() @@ -92,9 +93,11 @@ class InteractiveAsynchronousRunManager( override val name: String get() = this.evaluation.name - /** The [DbEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template: DbEvaluationTemplate - get() = this.evaluation.description + /** The [ApiEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template = this.evaluation.template.toApi() + + private val dbTemplate + get() = this.evaluation.template //TODO is there a nicer way to handle this? /** The global [RunManagerStatus] of this [InteractiveAsynchronousRunManager]. */ @Volatile @@ -226,7 +229,7 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.template.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) - 1 + val newIndex = this.dbTemplate.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) - 1 return try { this.goTo(context, newIndex) true @@ -247,7 +250,7 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.template.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) + 1 + val newIndex = this.dbTemplate.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) + 1 return try { this.goTo(context, newIndex) true @@ -272,7 +275,7 @@ class InteractiveAsynchronousRunManager( checkTeamStatus(teamId, RunManagerStatus.ACTIVE)//, RunManagerStatus.TASK_ENDED) require(!teamHasRunningTask(teamId)) { "Cannot change task while task is active" } - val idx = (index + this.template.tasks.size()) % this.template.tasks.size() + val idx = (index + this.template.tasks.size) % this.template.tasks.size /* Update active task. */ @@ -738,7 +741,7 @@ class InteractiveAsynchronousRunManager( */ private fun registerOptionalUpdatables() { /* Determine if task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission } + val endOnSubmit = this.dbTemplate.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission } .filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) @@ -788,7 +791,7 @@ class InteractiveAsynchronousRunManager( */ private fun RunActionContext.teamId(): TeamId { val userId = this.userId - return this@InteractiveAsynchronousRunManager.template.teams.asSequence().firstOrNull { team -> + return this@InteractiveAsynchronousRunManager.dbTemplate.teams.asSequence().firstOrNull { team -> team.users.filter { it.id eq userId }.isNotEmpty }?.teamId ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 35a19b84c..2662f7172 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -8,8 +8,7 @@ import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.SubmissionId -import dev.dres.run.score.ScoreTimePoint -import dev.dres.run.score.scoreboard.Scoreboard + interface InteractiveRunManager : RunManager { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 23e8ac480..5fa8882ab 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -14,7 +14,6 @@ import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* -import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.task.options.DbTaskOption @@ -68,9 +67,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override val name: String get() = this.evaluation.name - /** The [DbEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template: DbEvaluationTemplate - get() = this.evaluation.description + /** The [ApiEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template = this.evaluation.template.toApi() + + private val dbTemplate + get() = this.evaluation.template //TODO is there a nicer way to handle this? /** The status of this [RunManager]. */ @Volatile @@ -204,7 +205,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() this.evaluation.tasks.any { it.status == DbTaskStatus.RUNNING } - if (index >= 0 && index < this.template.tasks.size()) { + if (index >= 0 && index < this.template.tasks.size) { /* Update active task. */ this.evaluation.goTo(index) @@ -405,7 +406,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param connection The [WebSocketConnection] through which the message was received. * @param message The [ClientMessage] received. */ - override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean = + override fun wsMessageReceived( + connection: WebSocketConnection, + message: ClientMessage + ): Boolean = this.stateLock.read { when (message.type) { ClientMessageType.ACK -> { @@ -625,13 +629,13 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun registerOptionalUpdatables() { /* Determine if any task should be prolonged upon submission. */ - val prolongOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options }.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }.any() + val prolongOnSubmit = this.dbTemplate.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options }.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }.any() if (prolongOnSubmit) { this.updatables.add(ProlongOnSubmitUpdatable(this)) } /* Determine if any task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.template.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission }.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + val endOnSubmit = this.dbTemplate.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission }.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) } @@ -675,7 +679,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch val userId = this.userId val user = DbUser.filter { u -> u.id eq userId }.singleOrNull() ?: throw IllegalArgumentException("Could not find user with ID ${userId}.") - return this@InteractiveSynchronousRunManager.template.teams.filter { t -> t.users.contains(user) }.singleOrNull()?.teamId + return this@InteractiveSynchronousRunManager.dbTemplate.teams.filter { t -> t.users.contains(user) }.singleOrNull()?.teamId ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") } diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 14b127607..655c99ed8 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -44,9 +44,8 @@ class NonInteractiveRunManager( override val name: String get() = this.evaluation.name - /** The [DbEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template: DbEvaluationTemplate - get() = this.evaluation.description + /** The [ApiEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ + override val template = this.evaluation.template.toApi() /** The [List] of [Scoreboard]s maintained by this [NonInteractiveRunManager]. */ override val scoreboards: List diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 62a24950a..4aee48b3d 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -220,10 +220,11 @@ object RunExecutor : Consumer { fun broadcastWsMessage(teamId: TeamId, message: ServerMessage) = this.clientLock.read { val manager = managerForId(message.evaluationId) if (manager != null) { - val teamMembers = manager.template.teams.filter { it.id eq teamId }.flatMapDistinct { it.users }.asSequence().map { it.userId }.toList() + val teamMembers = manager.template.teams.filter { it.id == teamId }.flatMap { it.users } this.runManagerLock.read { this.connectedClients.values.filter { - this.observingClients[message.evaluationId]?.contains(it) ?: false && AccessManager.userIdForSession(it.sessionId) in teamMembers + val userId = AccessManager.userIdForSession(it.sessionId) + this.observingClients[message.evaluationId]?.contains(it) ?: false && teamMembers.any {u -> u.id == userId} }.forEach { it.send(message) } diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 9db25a8df..b4cac92f0 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -4,6 +4,7 @@ import dev.dres.api.rest.types.WebSocketConnection import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.evaluation.websocket.ClientMessage +import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun @@ -39,8 +40,8 @@ interface RunManager : Runnable { /** The [DbEvaluation] instance that backs this [RunManager]. */ val evaluation: EvaluationRun - /** The [DbEvaluationTemplate] that is executed / run by this [RunManager]. */ - val template: DbEvaluationTemplate + /** The [EvaluationTemplate] that is executed / run by this [RunManager]. */ + val template: ApiEvaluationTemplate /** List of [Scoreboard]s for this [RunManager]. */ val scoreboards: List diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index ad3b60f53..0cf85ab16 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -127,12 +127,12 @@ inline fun Context.eligibleManagerForId(): T { val userId = this.userId() val evaluationId = this.evaluationId() val manager = RunExecutor.managerForId(evaluationId) as? T ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) - if (this.isJudge() && manager.template.judges.filter { it.userId eq userId }.isNotEmpty) { - return manager - } else if (this.isParticipant() && manager.template.teams.flatMapDistinct { it.users }.filter { it.userId eq userId }.isNotEmpty) { - return manager + return if (this.isJudge() && manager.template.judges.any { it == userId }) { + manager + } else if (this.isParticipant() && manager.template.teams.flatMap { it.users }.any { it.id == userId }) { + manager } else if (this.isAdmin()) { - return manager + manager } else { throw ErrorStatusException(401, "Current user is not allowed to access evaluation $evaluationId as participant.", this) } From afebdf231145df9d1790be93bf0d06c720d45812 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 11 Aug 2023 07:55:12 +0200 Subject: [PATCH 447/498] Added convenience function to check if a user is participant of an evaluation --- .../rest/types/template/ApiEvaluationTemplate.kt | 16 +++++++++++++++- .../utilities/extensions/ContextExtensions.kt | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationTemplate.kt index c335a94c4..a9dd482e8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationTemplate.kt @@ -1,12 +1,15 @@ package dev.dres.api.rest.types.template +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.api.rest.types.template.tasks.ApiTaskGroup import dev.dres.api.rest.types.template.tasks.ApiTaskType import dev.dres.api.rest.types.template.team.ApiTeam import dev.dres.api.rest.types.template.team.ApiTeamGroup +import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.TemplateId +import io.javalin.openapi.OpenApiIgnore /** * The RESTful API equivalent for [DbEvaluationTemplate]. @@ -27,5 +30,16 @@ data class ApiEvaluationTemplate( val teams: List, val teamGroups: List, val judges: List, -) +) { + + @get:JsonIgnore + @delegate:JsonIgnore + @get:OpenApiIgnore + private val allParticipantIds by lazy { teams.flatMap { team -> team.users.mapNotNull { it.id } }.toSet() } + + @JsonIgnore + @OpenApiIgnore + fun hasParticipant(userId: UserId): Boolean = allParticipantIds.contains(userId) + +} diff --git a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt index 0cf85ab16..8062db6ef 100644 --- a/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt +++ b/backend/src/main/kotlin/dev/dres/utilities/extensions/ContextExtensions.kt @@ -127,9 +127,9 @@ inline fun Context.eligibleManagerForId(): T { val userId = this.userId() val evaluationId = this.evaluationId() val manager = RunExecutor.managerForId(evaluationId) as? T ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", this) - return if (this.isJudge() && manager.template.judges.any { it == userId }) { + return if (this.isJudge() && manager.template.judges.contains(userId)) { manager - } else if (this.isParticipant() && manager.template.teams.flatMap { it.users }.any { it.id == userId }) { + } else if (this.isParticipant() && manager.template.hasParticipant(userId)) { manager } else if (this.isAdmin()) { manager From 46fd939a1328aa5dccdf2668cae3d75b30c08953 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 11 Aug 2023 16:54:41 +0200 Subject: [PATCH 448/498] Removed references to Db templates from relevant interfaces --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 62 +++--- .../download/AbstractDownloadHandler.kt | 2 +- .../download/EvaluationDownloadHandler.kt | 2 +- .../EvaluationTemplateDownloadHandler.kt | 2 +- .../handler/download/ScoreDownloadHandler.kt | 4 +- .../admin/AbstractEvaluationAdminHandler.kt | 2 +- .../evaluation/admin/AdjustDurationHandler.kt | 47 ++--- .../admin/AdjustPropertiesHandler.kt | 13 +- .../admin/CreateEvaluationHandler.kt | 2 +- .../admin/EvaluationOverviewHandler.kt | 11 +- .../evaluation/admin/ForceViewerHandler.kt | 29 +-- .../evaluation/admin/ListPastTaskHandler.kt | 18 +- .../admin/ListSubmissionsHandler.kt | 20 +- .../evaluation/admin/ListViewersHandler.kt | 27 ++- .../evaluation/admin/NextTaskHandler.kt | 66 ++++-- .../admin/OverrideAnswerSetVerdictHandler.kt | 2 +- .../evaluation/admin/PreviousTaskHandler.kt | 51 +++-- .../admin/StartEvaluationHandler.kt | 45 ++-- .../evaluation/admin/StartTaskHandler.kt | 41 ++-- .../evaluation/admin/StopEvaluationHandler.kt | 46 ++-- .../evaluation/admin/StopTaskHandler.kt | 42 ++-- .../evaluation/admin/SwitchTaskHandler.kt | 48 +++-- .../client/AbstractEvaluationClientHandler.kt | 2 +- .../client/ClientListEvaluationsHandler.kt | 34 +-- .../client/ClientTaskInfoHandler.kt | 16 +- .../evaluation/scores/AbstractScoreHandler.kt | 2 +- .../scores/CurrentTaskScoreHandler.kt | 9 +- .../scores/HistoryTaskScoreHandler.kt | 30 +-- .../scores/ListEvaluationScoreHandler.kt | 6 +- .../scores/ListScoreSeriesHandler.kt | 3 +- .../scores/ListScoreboardsHandler.kt | 6 +- .../scores/TeamGroupScoreHandler.kt | 8 +- .../viewer/AbstractEvaluationViewerHandler.kt | 2 +- .../viewer/GetCurrentTaskHandler.kt | 6 +- .../viewer/GetEvaluationInfoHandler.kt | 6 +- .../viewer/GetEvaluationStateHandler.kt | 6 +- .../viewer/GetSubmissionAfterInfoHandler.kt | 40 ++-- .../viewer/GetSubmissionHistoryInfoHandler.kt | 21 +- .../viewer/GetSubmissionInfoHandler.kt | 19 +- .../evaluation/viewer/GetTaskHintHandler.kt | 196 +++++++++++++----- .../evaluation/viewer/GetTaskTargetHandler.kt | 28 ++- .../viewer/ListEvaluationInfoHandler.kt | 6 +- .../viewer/ListEvaluationStatesHandler.kt | 8 +- .../evaluation/viewer/ViewerReadyHandler.kt | 33 ++- .../submission/LegacySubmissionHandler.kt | 9 +- .../types/evaluation/ApiEvaluationState.kt | 4 +- .../rest/types/evaluation/ApiTaskOverview.kt | 10 +- .../template/team/ApiTeamAggregatorType.kt | 15 ++ .../rest/types/template/team/ApiTeamGroup.kt | 12 +- .../dres/data/model/run/AbstractEvaluation.kt | 57 ++--- .../data/model/run/AbstractInteractiveTask.kt | 64 ++++-- .../model/run/AbstractNonInteractiveTask.kt | 43 ++-- .../dev/dres/data/model/run/AbstractTask.kt | 80 +++---- .../dev/dres/data/model/run/DbEvaluation.kt | 6 +- .../run/InteractiveAsynchronousEvaluation.kt | 110 +++++----- .../run/InteractiveSynchronousEvaluation.kt | 105 ++++++---- .../model/run/NonInteractiveEvaluation.kt | 62 +++--- .../model/run/interfaces/EvaluationRun.kt | 16 +- .../dres/data/model/run/interfaces/TaskRun.kt | 17 +- .../model/template/team/DbTeamAggregator.kt | 14 -- .../data/model/template/team/DbTeamGroup.kt | 9 +- .../model/template/team/TeamAggregatorImpl.kt | 13 +- .../run/InteractiveAsynchronousRunManager.kt | 54 ++--- .../dev/dres/run/InteractiveRunManager.kt | 3 +- .../run/InteractiveSynchronousRunManager.kt | 89 ++++---- .../dev/dres/run/NonInteractiveRunManager.kt | 2 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 4 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 3 +- .../run/updatables/EndOnSubmitUpdatable.kt | 10 +- .../updatables/ProlongOnSubmitUpdatable.kt | 17 +- 70 files changed, 1131 insertions(+), 766 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index dfb7f9d55..9282fbff0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -142,43 +142,43 @@ object RestApi { ResultLogHandler(), // Evaluation - ListEvaluationInfoHandler(store), - ListEvaluationStatesHandler(store), - GetEvaluationInfoHandler(store), - GetEvaluationStateHandler(store), + ListEvaluationInfoHandler(), + ListEvaluationStatesHandler(), + GetEvaluationInfoHandler(), + GetEvaluationStateHandler(), GetTaskHintHandler(store, cache), GetTaskTargetHandler(store, cache), - GetCurrentTaskHandler(store), + GetCurrentTaskHandler(), GetSubmissionInfoHandler(store), - GetSubmissionAfterInfoHandler(store), - GetSubmissionHistoryInfoHandler(store), - ViewerReadyHandler(store), + GetSubmissionAfterInfoHandler(), + GetSubmissionHistoryInfoHandler(), + ViewerReadyHandler(), // Evaluation run scores - ListEvaluationScoreHandler(store), - CurrentTaskScoreHandler(store), - HistoryTaskScoreHandler(store), - ListScoreSeriesHandler(store), - ListScoreboardsHandler(store), - TeamGroupScoreHandler(store), + ListEvaluationScoreHandler(), + CurrentTaskScoreHandler(), + HistoryTaskScoreHandler(), + ListScoreSeriesHandler(), + ListScoreboardsHandler(), + TeamGroupScoreHandler(), // Evaluation administration CreateEvaluationHandler(store, cache), - StartEvaluationHandler(store), - StopEvaluationHandler(store), - NextTaskHandler(store), - PreviousTaskHandler(store), - SwitchTaskHandler(store), - StartTaskHandler(store), - StopTaskHandler(store), - AdjustDurationHandler(store), - AdjustPropertiesHandler(store), + StartEvaluationHandler(), + StopEvaluationHandler(), + NextTaskHandler(), + PreviousTaskHandler(), + SwitchTaskHandler(), + StartTaskHandler(), + StopTaskHandler(), + AdjustDurationHandler(), + AdjustPropertiesHandler(), OverrideAnswerSetVerdictHandler(store), - ForceViewerHandler(store), - ListViewersHandler(store), - ListSubmissionsHandler(store), - ListPastTaskHandler(store), - EvaluationOverviewHandler(store), + ForceViewerHandler(), + ListViewersHandler(), + ListSubmissionsHandler(), + ListPastTaskHandler(), + EvaluationOverviewHandler(), ListAllTeamsHandler(store), CreateTeamHandler(store), UpdateTeamHandler(store), @@ -195,13 +195,13 @@ object RestApi { InfoHandler(), //API Client - ClientListEvaluationsHandler(store), - ClientTaskInfoHandler(store), + ClientListEvaluationsHandler(), + ClientTaskInfoHandler(), // Downloads EvaluationDownloadHandler(store), EvaluationTemplateDownloadHandler(store), - ScoreDownloadHandler(store) + ScoreDownloadHandler() ) javalin = Javalin.create { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt index 3f419573c..8cb20fb8b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/AbstractDownloadHandler.kt @@ -11,7 +11,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractDownloadHandler(protected val store: TransientEntityStore) : AccessManagedRestHandler { +abstract class AbstractDownloadHandler : AccessManagedRestHandler { /** The version of the API this [AbstractDownloadHandler] belongs to. */ override val apiVersion = "v2" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index cd1a83e6b..d881f49c7 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -18,7 +18,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0.0 */ -class EvaluationDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandler(store), GetRestHandler { +class EvaluationDownloadHandler(private val store: TransientEntityStore) : AbstractDownloadHandler(), GetRestHandler { /** The route of this [EvaluationDownloadHandler]. */ override val route = "download/evaluation/{evaluationId}" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index 7c52b3f6e..c222f7d0f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -18,7 +18,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0.0 */ -class EvaluationTemplateDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandler(store), GetRestHandler { +class EvaluationTemplateDownloadHandler(private val store: TransientEntityStore) : AbstractDownloadHandler(), GetRestHandler { /** The route of this [EvaluationTemplateDownloadHandler]. */ override val route = "download/template/{templateId}" diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt index cc1e8a1c8..07505ff34 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/ScoreDownloadHandler.kt @@ -18,7 +18,7 @@ import kotlinx.dnq.query.firstOrNull * @author Ralph Gasser * @version 1.0.0 */ -class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandler(store), GetRestHandler { +class ScoreDownloadHandler : AbstractDownloadHandler(), GetRestHandler { override val route = "download/evaluation/{evaluationId}/scores" @@ -52,7 +52,7 @@ class ScoreDownloadHandler(store: TransientEntityStore) : AbstractDownloadHandle }.sortedBy { it.started }.flatMap { task -> - task.scorer.scores().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup.name}\",\"${manager.template.teams.firstOrNull { t -> t.id == it.first }?.name ?: "???"}\",${it.third}" } + task.scorer.scores().map { "${task.started},\"${task.template.name}\",\"${task.template.taskGroup}\",\"${manager.template.teams.firstOrNull { t -> t.id == it.first }?.name ?: "???"}\",${it.third}" } }.joinToString(separator = "\n") } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt index 1ea465266..2c743247e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AbstractEvaluationAdminHandler.kt @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -abstract class AbstractEvaluationAdminHandler(protected val store: TransientEntityStore) : AccessManagedRestHandler { +abstract class AbstractEvaluationAdminHandler : AccessManagedRestHandler { /** By default [AbstractEvaluationAdminHandler] can only be used by [ApiRole.ADMIN]. */ override val permittedRoles: Set = setOf(ApiRole.ADMIN) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt index 58dec4728..5f78cb7c5 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustDurationHandler.kt @@ -22,7 +22,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class AdjustDurationHandler(store: TransientEntityStore) : AbstractEvaluationAdminHandler(store), +class AdjustDurationHandler : AbstractEvaluationAdminHandler(), PatchRestHandler { override val route: String = "evaluation/admin/{evaluationId}/adjust/{duration}" @@ -69,32 +69,31 @@ class AdjustDurationHandler(store: TransientEntityStore) : AbstractEvaluationAdm ) val rac = ctx.runActionContext() - return this.store.transactional { + return try { try { - try { - evaluationManager.adjustDuration(rac, duration) - AuditLogger.taskModified( - evaluationManager.id, - evaluationManager.currentTaskTemplate(rac).id, - "Task duration adjusted by ${duration}s.", - AuditLogSource.REST, - ctx.sessionToken() - ) - SuccessStatus("Duration for run $evaluationId was successfully adjusted.") - } catch (e: IllegalArgumentException) { - evaluationManager.abortTask(rac) - SuccessStatus("Successfully stopped task since the duration was below zero after adjusting.") - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException( - 400, - "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", - ctx + evaluationManager.adjustDuration(rac, duration) + AuditLogger.taskModified( + evaluationManager.id, + evaluationManager.currentTaskTemplate(rac).id!!, + "Task duration adjusted by ${duration}s.", + AuditLogSource.REST, + ctx.sessionToken() ) + SuccessStatus("Duration for run $evaluationId was successfully adjusted.") + } catch (e: IllegalArgumentException) { + evaluationManager.abortTask(rac) + SuccessStatus("Successfully stopped task since the duration was below zero after adjusting.") + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } - + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Duration for run $evaluationId could not be adjusted because it is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) } + + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index c6fddb64e..79626897f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -21,7 +21,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class AdjustPropertiesHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { +class AdjustPropertiesHandler : AbstractEvaluationAdminHandler(), PatchRestHandler { override val route = "evaluation/admin/{evaluationId}/properties" @OpenApi( @@ -48,11 +48,12 @@ class AdjustPropertiesHandler(store: TransientEntityStore): AbstractEvaluationAd throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional { - evaluationManager.updateProperties(properties) - SuccessStatus("Properties updated successfully!") - } + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + evaluationManager.updateProperties(properties) + return SuccessStatus("Properties updated successfully!") + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 374c9f941..8d144d70b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -34,7 +34,7 @@ import java.util.concurrent.TimeUnit * @author Loris Sauter * @version 2.0.0 */ -class CreateEvaluationHandler(store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationAdminHandler(store), PostRestHandler { +class CreateEvaluationHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationAdminHandler(), PostRestHandler { private val logger = LoggerFactory.getLogger(this.javaClass) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt index 5c1f3bab7..ac650c821 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -14,8 +14,9 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler { +class EvaluationOverviewHandler : AbstractEvaluationAdminHandler(), GetRestHandler { override val route = "evaluation/admin/{evaluationId}/overview" + @OpenApi( summary = "Provides a complete overview of a run.", path = "/api/v2/evaluation/admin/{evaluationId}/overview", @@ -34,9 +35,9 @@ class EvaluationOverviewHandler(store: TransientEntityStore): AbstractEvaluation ) override fun doGet(ctx: Context): ApiEvaluationOverview { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional(true) { - ApiEvaluationOverview.of(evaluationManager) - } + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + return ApiEvaluationOverview.of(evaluationManager) + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index 9d8920950..e60880ebf 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class ForceViewerHandler : AbstractEvaluationAdminHandler(), PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" @OpenApi( @@ -41,20 +41,25 @@ class ForceViewerHandler(store: TransientEntityStore): AbstractEvaluationAdminHa ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val viewerId = ctx.pathParamMap()["viewerId"] ?: throw ErrorStatusException(404, "Parameter 'viewerId' is missing!'", ctx) - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + val viewerId = + ctx.pathParamMap()["viewerId"] ?: throw ErrorStatusException(404, "Parameter 'viewerId' is missing!'", ctx) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) val rac = ctx.runActionContext() - return this.store.transactional(true) { - try { - if (evaluationManager.overrideReadyState(rac, viewerId)) { - SuccessStatus("State for viewer $viewerId (evaluation '$evaluationId') forced successfully.") - } else { - throw ErrorStatusException(404, "Viewer $viewerId does not exist!'", ctx) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "State for viewer $viewerId (evaluation '$evaluationId') could not be enforced because evaluation is in the wrong state (state = ${evaluationManager.status}).", ctx) + try { + if (evaluationManager.overrideReadyState(rac, viewerId)) { + return SuccessStatus("State for viewer $viewerId (evaluation '$evaluationId') forced successfully.") + } else { + throw ErrorStatusException(404, "Viewer $viewerId does not exist!'", ctx) } + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "State for viewer $viewerId (evaluation '$evaluationId') could not be enforced because evaluation is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt index 607218cbd..b5df43d65 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListPastTaskHandler.kt @@ -17,7 +17,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { +class ListPastTaskHandler : AbstractEvaluationAdminHandler(), + GetRestHandler> { override val route: String = "evaluation/admin/{evaluationId}/task/past/list" @OpenApi( @@ -30,7 +31,10 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH ], tags = ["Evaluation Administrator"], responses = [ - OpenApiResponse("200", [OpenApiContent(Array::class)]), // FIXME this handler provided information about submissions in past tasks + OpenApiResponse( + "200", + [OpenApiContent(Array::class)] + ), // FIXME this handler provided information about submissions in past tasks OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) @@ -38,12 +42,12 @@ class ListPastTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH ) override fun doGet(ctx: Context): List { val evaluationId = ctx.evaluationId() - val runManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val runManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) val rac = ctx.runActionContext() - return this.store.transactional (true) { - runManager.tasks(rac).filter { it.hasEnded }.map { - ApiTaskTemplateInfo(it.template.toApi()) - } + return runManager.tasks(rac).filter { it.hasEnded }.map { + ApiTaskTemplateInfo(it.template) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index 847eb3187..3630b35fb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -5,7 +5,6 @@ import dev.dres.utilities.extensions.evaluationId import dev.dres.api.rest.types.evaluation.ApiSubmissionInfo import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* @@ -16,7 +15,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { +class ListSubmissionsHandler : AbstractEvaluationAdminHandler(), + GetRestHandler> { override val route: String = "evaluation/admin/{evaluationId}/submission/list/{templateId}" @OpenApi( @@ -38,13 +38,17 @@ class ListSubmissionsHandler(store: TransientEntityStore): AbstractEvaluationAdm ) override fun doGet(ctx: Context): List { val evaluationId = ctx.evaluationId() - val templateId = ctx.pathParamMap()["templateId"] ?: throw ErrorStatusException(404, "Parameter 'templateId' is missing!'", ctx) - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val templateId = ctx.pathParamMap()["templateId"] ?: throw ErrorStatusException( + 404, + "Parameter 'templateId' is missing!'", + ctx + ) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) val rac = ctx.runActionContext() - return this.store.transactional(true) { - evaluationManager.tasks(rac).filter { it.template.templateId == templateId }.map { - ApiSubmissionInfo(evaluationId, it.taskId, it.getSubmissions().map { sub -> sub.toApi() }.toList()) - } + return evaluationManager.tasks(rac).filter { it.templateId == templateId }.map { + ApiSubmissionInfo(evaluationId, it.taskId, it.getDbSubmissions().map { sub -> sub.toApi() }.toList()) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt index b9564a7f3..d997e8007 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListViewersHandler.kt @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0 */ -class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), GetRestHandler> { +class ListViewersHandler : AbstractEvaluationAdminHandler(), GetRestHandler> { override val route: String = "evaluation/admin/{evaluationId}/viewer/list" @OpenApi( @@ -24,7 +24,13 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa path = "/api/v2/evaluation/admin/{evaluationId}/viewer/list", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), @@ -36,14 +42,15 @@ class ListViewersHandler(store: TransientEntityStore): AbstractEvaluationAdminHa override fun doGet(ctx: Context): List { val evaluationId = ctx.evaluationId() val evaluation = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) - return this.store.transactional(true) { - evaluation.viewers().map { - ApiViewerInfo( - it.key.sessionToken, - UserManager.get(AccessManager.userIdForSession(it.key.sessionToken))?.username ?: "UNKNOWN", - it.key.host, - it.value) - } + + return evaluation.viewers().map { + ApiViewerInfo( + it.key.sessionToken, + UserManager.get(AccessManager.userIdForSession(it.key.sessionToken))?.username ?: "UNKNOWN", + it.key.host, + it.value + ) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt index 525e9a360..af78b68fd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/NextTaskHandler.kt @@ -2,6 +2,7 @@ package dev.dres.api.rest.handler.evaluation.admin import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.evaluation.ApiTaskStatus import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus @@ -26,7 +27,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class NextTaskHandler : AbstractEvaluationAdminHandler(), + PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/task/next" /** The [NextTaskHandler] can be used by [ApiRole.ADMIN] and [ApiRole.PARTICIPANT] (in case of asynchronous evaluations). */ @@ -37,7 +39,13 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl path = "/api/v2/evaluation/admin/{evaluationId}/task/next", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -45,32 +53,48 @@ class NextTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ] ) - override fun doPost(ctx: Context): SuccessStatus{ + override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) /* Important: Check that user can actually change this manager. */ synchronousAdminCheck(evaluationManager, ctx) - return this.store.transactional(false) { - val rac = ctx.runActionContext() - if (evaluationManager is InteractiveAsynchronousRunManager - && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) - && evaluationManager.currentTask(rac)?.status !in setOf(DbTaskStatus.ENDED, DbTaskStatus.IGNORED)) { - throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) - } - try { - if (evaluationManager.next(rac)) { - SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") - } else { - throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because there are no tasks left.", ctx) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because evaluation is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) + val rac = ctx.runActionContext() + if (evaluationManager is InteractiveAsynchronousRunManager + && !AccessManager.rolesOfSession(ctx.sessionToken()).contains(ApiRole.ADMIN) + && evaluationManager.currentTask(rac)?.status !in setOf(ApiTaskStatus.ENDED, ApiTaskStatus.IGNORED) + ) { + throw ErrorStatusException(400, "Cannot advance to next task before current task is completed.", ctx) + } + + return try { + if (evaluationManager.next(rac)) { + SuccessStatus( + "Task for evaluation $evaluationId was successfully moved to '${ + evaluationManager.currentTaskTemplate( + rac + ).name + }'." + ) + } else { + throw ErrorStatusException( + 400, + "Task for evaluation $evaluationId could not be changed because there are no tasks left.", + ctx + ) } + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Task for evaluation $evaluationId could not be changed because evaluation is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt index e5f4b6044..a38ef9321 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/OverrideAnswerSetVerdictHandler.kt @@ -29,7 +29,7 @@ import kotlinx.dnq.query.first * @author Loris Sauter * @version 2.0.0 */ -class OverrideAnswerSetVerdictHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PatchRestHandler { +class OverrideAnswerSetVerdictHandler(private val store: TransientEntityStore): AbstractEvaluationAdminHandler(), PatchRestHandler { override val route: String = "evaluation/admin/{evaluationId}/override/{answerSetId}" @OpenApi( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt index 3c999d4a0..a91c1f43b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/PreviousTaskHandler.kt @@ -20,7 +20,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class PreviousTaskHandler : AbstractEvaluationAdminHandler(), PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/task/previous" @OpenApi( @@ -28,7 +28,13 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH path = "/api/v2/evaluation/admin/{evaluationId}/task/previous", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -38,20 +44,35 @@ class PreviousTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminH ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional(false) { - val rac = ctx.runActionContext() - try { - if (evaluationManager.previous(rac)) { - SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") - } else { - throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because there are no tasks left.", ctx) - } - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because evaluation is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + val rac = ctx.runActionContext() + try { + if (evaluationManager.previous(rac)) { + return SuccessStatus( + "Task for evaluation $evaluationId was successfully moved to '${ + evaluationManager.currentTaskTemplate( + rac + ).name + }'." + ) + } else { + throw ErrorStatusException( + 400, + "Task for evaluation $evaluationId could not be changed because there are no tasks left.", + ctx + ) } + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Task for evaluation $evaluationId could not be changed because evaluation is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt index 43e1992e3..22c654f0c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartEvaluationHandler.kt @@ -22,7 +22,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAdminHandler(store), PostRestHandler { +class StartEvaluationHandler : AbstractEvaluationAdminHandler(), PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/start" @OpenApi( @@ -30,7 +30,13 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd path = "/api/v2/evaluation/admin/{evaluationId}/start", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -40,19 +46,30 @@ class StartEvaluationHandler(store: TransientEntityStore) : AbstractEvaluationAd ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional { - val rac = ctx.runActionContext() - try { - evaluationManager.start(rac) - AuditLogger.evaluationStart(evaluationManager.id, evaluationManager.template, AuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) - SuccessStatus("Evaluation $evaluationId was successfully started.") - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } + + val rac = ctx.runActionContext() + try { + evaluationManager.start(rac) + AuditLogger.evaluationStart( + evaluationManager.id, + evaluationManager.template, + AuditLogSource.REST, + AccessManager.userIdForSession(ctx.sessionToken())!!, + ctx.sessionToken() + ) + return SuccessStatus("Evaluation $evaluationId was successfully started.") + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Evaluation $evaluationId could not be started because it is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt index 82be7bf9f..5cbd46ead 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StartTaskHandler.kt @@ -24,7 +24,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class StartTaskHandler : AbstractEvaluationAdminHandler(), + PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/task/start" @@ -36,7 +37,13 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand path = "/api/v2/evaluation/admin/{evaluationId}/task/start", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], - pathParams = [OpenApiParam("evaluationId", String::class, "The evalation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evalation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -46,22 +53,28 @@ class StartTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHand ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) /* Important: Check that user can actually change this manager. */ synchronousAdminCheck(evaluationManager, ctx) - return this.store.transactional { - val rac = ctx.runActionContext() - try { - val taskId = evaluationManager.startTask(rac) - AuditLogger.taskStart(evaluationManager.id, taskId, evaluationManager.currentTaskTemplate(rac).toApi(), AuditLogSource.REST, ctx.sessionToken()) - SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, e.message ?: "", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } + val rac = ctx.runActionContext() + return try { + val taskId = evaluationManager.startTask(rac) + AuditLogger.taskStart( + evaluationManager.id, + taskId, + evaluationManager.currentTaskTemplate(rac), + AuditLogSource.REST, + ctx.sessionToken() + ) + SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully started.") + } catch (e: IllegalStateException) { + throw ErrorStatusException(400, e.message ?: "", ctx) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt index c896f4c21..e45e87ac0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopEvaluationHandler.kt @@ -22,7 +22,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class StopEvaluationHandler : AbstractEvaluationAdminHandler(), PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/terminate" @@ -31,7 +31,13 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi path = "/api/v2/evaluation/admin/{evaluationId}/terminate", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.POST], - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -41,19 +47,29 @@ class StopEvaluationHandler(store: TransientEntityStore): AbstractEvaluationAdmi ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) - - return this.store.transactional { - val rac = ctx.runActionContext() - try { - evaluationManager.end(rac) - AuditLogger.evaluationEnd(evaluationManager.id, AuditLogSource.REST, AccessManager.userIdForSession(ctx.sessionToken())!!, ctx.sessionToken()) - SuccessStatus("Evaluation $evaluationId was successfully stopped.") - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + + + val rac = ctx.runActionContext() + try { + evaluationManager.end(rac) + AuditLogger.evaluationEnd( + evaluationManager.id, + AuditLogSource.REST, + AccessManager.userIdForSession(ctx.sessionToken())!!, + ctx.sessionToken() + ) + return SuccessStatus("Evaluation $evaluationId was successfully stopped.") + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Evaluation $evaluationId could not be stopped because it is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt index 93be1bb2a..e20028014 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/StopTaskHandler.kt @@ -22,7 +22,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class StopTaskHandler : AbstractEvaluationAdminHandler(), + PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/task/abort" @OpenApi( @@ -30,7 +31,13 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl path = "/api/v2/evaluation/admin/{evaluationId}/task/abort", methods = [HttpMethod.POST], operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false)], + pathParams = [OpenApiParam( + "evaluationId", + String::class, + "The evaluation ID.", + required = true, + allowEmptyValue = false + )], tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -40,19 +47,24 @@ class StopTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandl ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional { - val rac = ctx.runActionContext() - try { - val task = evaluationManager.currentTaskTemplate(rac) - evaluationManager.abortTask(rac) - AuditLogger.taskEnd(evaluationManager.id, task.id, AuditLogSource.REST, ctx.sessionToken()) - SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + + val rac = ctx.runActionContext() + return try { + val task = evaluationManager.currentTaskTemplate(rac) + evaluationManager.abortTask(rac) + AuditLogger.taskEnd(evaluationManager.id, task.id!!, AuditLogSource.REST, ctx.sessionToken()) + SuccessStatus("Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId was successfully aborted.") + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Task '${evaluationManager.currentTaskTemplate(rac).name}' for evaluation $evaluationId could not be aborted because run is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt index f3bd02a3e..a3caf20ac 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/SwitchTaskHandler.kt @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHandler(store), PostRestHandler { +class SwitchTaskHandler : AbstractEvaluationAdminHandler(), PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/task/switch/{idx}" @OpenApi( @@ -40,20 +40,38 @@ class SwitchTaskHandler(store: TransientEntityStore): AbstractEvaluationAdminHan ) override fun doPost(ctx: Context): SuccessStatus { val evaluationId = ctx.evaluationId() - val idx = ctx.pathParamMap()["idx"]?.toIntOrNull() ?: throw ErrorStatusException(404, "Parameter 'idx' is missing!'", ctx) - val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) - return this.store.transactional(true) { - val rac = ctx.runActionContext() - try { - evaluationManager.goTo(rac, idx) - SuccessStatus("Task for evaluation $evaluationId was successfully moved to '${evaluationManager.currentTaskTemplate(rac).name}'.") - } catch (e: IllegalStateException) { - throw ErrorStatusException(400, "Task for evaluation $evaluationId could not be changed because run is in the wrong state (state = ${evaluationManager.status}).", ctx) - } catch (e: IndexOutOfBoundsException) { - throw ErrorStatusException(404, "Task for evaluation $evaluationId could not be changed because index $idx is out of bounds for number of available tasks.", ctx) - } catch (e: IllegalAccessError) { - throw ErrorStatusException(403, e.message!!, ctx) - } + val idx = ctx.pathParamMap()["idx"]?.toIntOrNull() ?: throw ErrorStatusException( + 404, + "Parameter 'idx' is missing!'", + ctx + ) + val evaluationManager = + getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val rac = ctx.runActionContext() + try { + evaluationManager.goTo(rac, idx) + return SuccessStatus( + "Task for evaluation $evaluationId was successfully moved to '${ + evaluationManager.currentTaskTemplate( + rac + ).name + }'." + ) + } catch (e: IllegalStateException) { + throw ErrorStatusException( + 400, + "Task for evaluation $evaluationId could not be changed because run is in the wrong state (state = ${evaluationManager.status}).", + ctx + ) + } catch (e: IndexOutOfBoundsException) { + throw ErrorStatusException( + 404, + "Task for evaluation $evaluationId could not be changed because index $idx is out of bounds for number of available tasks.", + ctx + ) + } catch (e: IllegalAccessError) { + throw ErrorStatusException(403, e.message!!, ctx) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt index 14c2cc54b..7d8717a22 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/AbstractEvaluationClientHandler.kt @@ -16,7 +16,7 @@ import kotlinx.dnq.query.* * @author Ralph Gasser * @version 1.0 */ -abstract class AbstractEvaluationClientHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { +abstract class AbstractEvaluationClientHandler : RestHandler, AccessManagedRestHandler { /** By default [AbstractEvaluationClientHandler] can only be used by [ApiRole.VIEWER]. */ override val permittedRoles: Set = setOf(ApiRole.VIEWER) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt index 45653d58d..4db399114 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ClientListEvaluationsHandler(store: TransientEntityStore): AbstractEvaluationClientHandler(store), GetRestHandler> { +class ClientListEvaluationsHandler : AbstractEvaluationClientHandler(), GetRestHandler> { override val route = "client/evaluation/list" @@ -34,20 +34,22 @@ class ClientListEvaluationsHandler(store: TransientEntityStore): AbstractEvaluat ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { tx -> - getRelevantManagers(ctx).map { ApiEvaluationInfo( - id = it.id, - name = it.name, - templateId = it.template.id, - templateDescription = it.template.description, - when (it) { - is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS - is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS - else -> TODO() - }, - properties = it.runProperties, - teams = emptyList(), - tasks = emptyList() - ) } + override fun doGet(ctx: Context): List { + return getRelevantManagers(ctx).map { + ApiEvaluationInfo( + id = it.id, + name = it.name, + templateId = it.template.id, + templateDescription = it.template.description, + when (it) { + is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS + is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS + else -> TODO() + }, + properties = it.runProperties, + teams = emptyList(), + tasks = emptyList() + ) + } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index af14fb20b..8af9f1707 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -20,7 +20,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClientHandler(store), GetRestHandler { +class ClientTaskInfoHandler : AbstractEvaluationClientHandler(), + GetRestHandler { override val route = "client/evaluation/currentTask/{runId}" @OpenApi( @@ -38,11 +39,16 @@ class ClientTaskInfoHandler(store: TransientEntityStore): AbstractEvaluationClie ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional(true) { + override fun doGet(ctx: Context): ApiTaskTemplateInfo { val run = ctx.eligibleManagerForId() val rac = ctx.runActionContext() - if (run !is InteractiveRunManager) throw ErrorStatusException(404, "Specified evaluation is not interactive.", ctx) - val task = run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) - ApiTaskTemplateInfo(task.template.toApi()) + if (run !is InteractiveRunManager) throw ErrorStatusException( + 404, + "Specified evaluation is not interactive.", + ctx + ) + val task = + run.currentTask(rac) ?: throw ErrorStatusException(404, "Specified evaluation has no active task.", ctx) + return ApiTaskTemplateInfo(task.template) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt index d9200470a..b16a87ee2 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/AbstractScoreHandler.kt @@ -14,7 +14,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 2.0.0 */ -abstract class AbstractScoreHandler(protected val store: TransientEntityStore) : RestHandler, AccessManagedRestHandler { +abstract class AbstractScoreHandler : RestHandler, AccessManagedRestHandler { /** By default [AbstractScoreHandler] can only be used by [ApiRole.VIEWER]. */ override val permittedRoles: Set = setOf(ApiRole.VIEWER) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index 1d97eaad8..bdba17a67 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -25,7 +25,7 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 2.0.0 */ -class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), +class CurrentTaskScoreHandler : AbstractScoreHandler(), GetRestHandler { override val route = "score/evaluation/{evaluationId}/current" @@ -44,14 +44,13 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiScoreOverview = this.store.transactional(true) { + override fun doGet(ctx: Context): ApiScoreOverview { val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } - val rac = ctx.runActionContext() val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException( 404, @@ -59,9 +58,9 @@ class CurrentTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle ctx ) val scores = scorer.scoreMap() - return@transactional ApiScoreOverview( + return ApiScoreOverview( "task", - manager.currentTaskTemplate(rac).taskGroup.name, + manager.currentTaskTemplate(rac).taskGroup, manager.template.teams.asSequence().map { team -> ApiScore(team.id!!, scores[team.id] ?: 0.0) }.toList() ) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt index 52535d179..b6f231292 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/HistoryTaskScoreHandler.kt @@ -27,7 +27,7 @@ import kotlinx.dnq.query.asSequence * @author Ralph Gasser * @version 2.0.0 */ -class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler { +class HistoryTaskScoreHandler : AbstractScoreHandler(), GetRestHandler { override val route = "score/evaluation/{evaluationId}/history/{taskId}" @@ -49,20 +49,26 @@ class HistoryTaskScoreHandler(store: TransientEntityStore) : AbstractScoreHandle methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiScoreOverview { - val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' is missing!'", ctx) + val taskId = + ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' is missing!'", ctx) if (!ctx.isAdmin()) throw ErrorStatusException(403, "Access denied.", ctx) - return this.store.transactional(true) { - val manager = ctx.eligibleManagerForId() - val rac = ctx.runActionContext() - val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException(404, "No task run with ID $taskId in run ${manager.id}.", ctx) - val scores = scorer.scoreMap() - ApiScoreOverview("task", - manager.currentTaskTemplate(rac).taskGroup.name, - manager.template.teams.asSequence().map { ApiScore(it.teamId, scores[it.teamId] ?: 0.0) }.toList() - ) - } + val manager = ctx.eligibleManagerForId() + + val rac = ctx.runActionContext() + val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException( + 404, + "No task run with ID $taskId in run ${manager.id}.", + ctx + ) + val scores = scorer.scoreMap() + return ApiScoreOverview( + "task", + manager.currentTaskTemplate(rac).taskGroup, + manager.template.teams.asSequence().map { ApiScore(it.teamId, scores[it.teamId] ?: 0.0) }.toList() + ) + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt index 51b261983..41c602b8a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListEvaluationScoreHandler.kt @@ -18,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class ListEvaluationScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { +class ListEvaluationScoreHandler : AbstractScoreHandler(), GetRestHandler> { override val route = "score/evaluation/{evaluationId}" @@ -35,8 +35,8 @@ class ListEvaluationScoreHandler(store: TransientEntityStore) : AbstractScoreHan ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional (true) { + override fun doGet(ctx: Context): List { val manager = ctx.eligibleManagerForId() - manager.scoreboards.map { it.overview().toApi() } + return manager.scoreboards.map { it.overview().toApi() } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt index 6aeba7432..61d8bd001 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreSeriesHandler.kt @@ -10,13 +10,12 @@ import dev.dres.run.eventstream.* import dev.dres.run.score.ScoreTimePoint import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore import java.util.concurrent.ConcurrentHashMap /** * A [GetRestHandler] that returns a time series of all data points for a given run and scoreboard. */ -class ListScoreSeriesHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler>, StreamEventHandler { +class ListScoreSeriesHandler : AbstractScoreHandler(), GetRestHandler>, StreamEventHandler { override val route = "score/evaluation/{evaluationId}/series/{scoreboard}" init { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt index 042898974..89df1c66d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/ListScoreboardsHandler.kt @@ -11,7 +11,7 @@ import jetbrains.exodus.database.TransientEntityStore /** * A [GetRestHandler] that returns the names of all available scoreboards for a given run. */ -class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { +class ListScoreboardsHandler : AbstractScoreHandler(), GetRestHandler> { override val route = "score/evaluation/{evaluationId}/scoreboard/list" @OpenApi( @@ -30,8 +30,8 @@ class ListScoreboardsHandler(store: TransientEntityStore) : AbstractScoreHandler ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional (true) { + override fun doGet(ctx: Context): List { val manager = ctx.eligibleManagerForId() - manager.scoreboards.map { it.name } + return manager.scoreboards.map { it.name } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt index 7e300e1de..b6cf26846 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/TeamGroupScoreHandler.kt @@ -18,7 +18,7 @@ import kotlinx.dnq.query.toList /** * */ -class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler(store), GetRestHandler> { +class TeamGroupScoreHandler : AbstractScoreHandler(), GetRestHandler> { override val route = "score/evaluation/{evaluationId}/teamGroup/list" @OpenApi( @@ -43,13 +43,13 @@ class TeamGroupScoreHandler(store: TransientEntityStore) : AbstractScoreHandler( throw ErrorStatusException(403, "Access denied.", ctx) } - return this.store.transactional(true) { + val rac = ctx.runActionContext() /* TODO: Not suite sure where the teamGroupAggregator got lost.*/ //val aggregators = manager.currentTask(rac)?.teamGroupAggregators ?: throw ErrorStatusException(404, "No active task in evaluation ${ctx.evaluationId()}.", ctx) //val teamGroups = manager.template.teamsGroups.toList() //teamGroups.map { ApiTeamGroupValue(it.name, aggregators[it.teamGroupId]?.lastValue ?: 0.0) } - emptyList() - } + return emptyList() + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt index d87b10353..616e7322a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/AbstractEvaluationViewerHandler.kt @@ -24,7 +24,7 @@ import kotlinx.dnq.query.isNotEmpty * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractEvaluationViewerHandler(protected val store: TransientEntityStore): RestHandler, AccessManagedRestHandler { +abstract class AbstractEvaluationViewerHandler: RestHandler, AccessManagedRestHandler { /** By default [AbstractEvaluationViewerHandler] can only be used by [ApiRole.VIEWER]. */ override val permittedRoles: Set = setOf(ApiRole.VIEWER) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt index 84845a97c..892527634 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetCurrentTaskHandler.kt @@ -29,7 +29,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler { +class GetCurrentTaskHandler : AbstractEvaluationViewerHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/task" @@ -47,12 +47,12 @@ class GetCurrentTaskHandler(store: TransientEntityStore): AbstractEvaluationView ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiTaskTemplateInfo = this.store.transactional (true) { + override fun doGet(ctx: Context): ApiTaskTemplateInfo { val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access denied.", ctx) } val rac = ctx.runActionContext() - ApiTaskTemplateInfo(manager.currentTaskTemplate(rac).toApi()) + return ApiTaskTemplateInfo(manager.currentTaskTemplate(rac)) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt index 27a8be29d..ddcb13b0e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationInfoHandler.kt @@ -16,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore /** * */ -class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluationViewerHandler(store), GetRestHandler { +class GetEvaluationInfoHandler : AbstractEvaluationViewerHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/info" @@ -34,11 +34,11 @@ class GetEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluation ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiEvaluationInfo = this.store.transactional (true) { + override fun doGet(ctx: Context): ApiEvaluationInfo { val manager = ctx.eligibleManagerForId() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { throw ErrorStatusException(403, "Access Denied", ctx) } - ApiEvaluationInfo(manager) + return ApiEvaluationInfo(manager) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt index 8991df80a..24a48c00f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetEvaluationStateHandler.kt @@ -18,7 +18,7 @@ import jetbrains.exodus.database.TransientEntityStore /** * */ -class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler { +class GetEvaluationStateHandler : AbstractEvaluationViewerHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/state" @@ -36,7 +36,7 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): ApiEvaluationState = this.store.transactional (true) { + override fun doGet(ctx: Context): ApiEvaluationState { val manager = ctx.eligibleManagerForId() if (manager.status == RunManagerStatus.TERMINATED) { throw ErrorStatusException(404, "Evaluation has ended.", ctx) @@ -45,6 +45,6 @@ class GetEvaluationStateHandler(store: TransientEntityStore): AbstractEvaluation throw ErrorStatusException(403, "Access Denied", ctx) } val rac = ctx.runActionContext() - ApiEvaluationState(manager, rac) + return ApiEvaluationState(manager, rac) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt index 823667494..656dec762 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionAfterInfoHandler.kt @@ -7,6 +7,7 @@ import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager @@ -18,7 +19,7 @@ import kotlinx.dnq.query.contains /** * */ -class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { +class GetSubmissionAfterInfoHandler : AbstractEvaluationViewerHandler(), GetRestHandler> { override val route = "evaluation/{evaluationId}/submission/list/after/{timestamp}" @OpenApi( @@ -27,8 +28,13 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ - OpenApiParam("evaluationId", String::class,"The evaluation ID.", required = true), - OpenApiParam("timestamp", Long::class, "Timestamp that marks the lower bound for returned submissions.", required = true) + OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), + OpenApiParam( + "timestamp", + Long::class, + "Timestamp that marks the lower bound for returned submissions.", + required = true + ) ], responses = [ OpenApiResponse("200", [OpenApiContent(Array::class)]), @@ -39,17 +45,25 @@ class GetSubmissionAfterInfoHandler(store: TransientEntityStore): AbstractEvalua methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { - val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException(400, "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", ctx) - return this.store.transactional (true) { - val rac = ctx.runActionContext() - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access denied.", ctx) - } + val manager = ctx.eligibleManagerForId() as? InteractiveRunManager ?: throw ErrorStatusException( + 400, + "Specified evaluation ${ctx.evaluationId()} does not have an evaluation state.'", + ctx + ) - val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() - val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) - val blind = currentTask.template.taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS) && currentTask.isRunning - manager.currentSubmissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } + val rac = ctx.runActionContext() + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied.", ctx) } + + val timestamp = ctx.pathParamMap().getOrDefault("timestamp", "0").toLong() + val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) + + val blind = + currentTask.isRunning && manager.template.taskTypes.find { it.name == currentTask.template.taskType }?.taskOptions?.contains( + ApiTaskOption.HIDDEN_RESULTS + ) == true + return manager.currentSubmissions(rac).filter { it.timestamp >= timestamp }.map { it.toApi(blind) } + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index a9b902b2f..a7bd6e6b3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -6,16 +6,14 @@ import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.any -import kotlinx.dnq.query.filter -class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { + +class GetSubmissionHistoryInfoHandler: AbstractEvaluationViewerHandler(), GetRestHandler> { override val route = "evaluation/{evaluationId}/task/{taskId}/submission/list" @@ -36,7 +34,7 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional (true) { + override fun doGet(ctx: Context): List { val manager = ctx.eligibleManagerForId() val rac = ctx.runActionContext() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { @@ -44,15 +42,18 @@ class GetSubmissionHistoryInfoHandler(store: TransientEntityStore): AbstractEval } val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(404, "Missing task id", ctx) val task = manager.currentTask(rac) - if (task?.template?.id == taskId && task.isRunning) { + return if (task?.template?.id == taskId && task.isRunning) { if (task.isRunning) { - val hidden = manager.currentTaskTemplate(rac).taskGroup.type.options.filter { it eq DbTaskOption.HIDDEN_RESULTS }.any() + val hidden = manager.template.taskTypes.find { it.name == task.template.taskType }?.taskOptions?.contains( + ApiTaskOption.HIDDEN_RESULTS + ) == true manager.currentSubmissions(rac).map { it.toApi(hidden) } } else { - manager.taskForId(rac, taskId)?.getSubmissions()?.map { it.toApi() }?.toList() ?: emptyList() + manager.taskForId(rac, taskId)?.getDbSubmissions()?.map { it.toApi() }?.toList() ?: emptyList() } + } else { + emptyList() } - emptyList() } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index 8ed9dcdc3..4e9223d72 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -6,6 +6,7 @@ import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.task.options.DbTaskOption @@ -18,7 +19,7 @@ import kotlinx.dnq.query.contains /** * */ -class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { +class GetSubmissionInfoHandler(private val store: TransientEntityStore): AbstractEvaluationViewerHandler(), GetRestHandler> { override val route = "evaluation/{evaluationId}/submission/list" @OpenApi( @@ -37,7 +38,7 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional (true) { + override fun doGet(ctx: Context): List { val manager = ctx.eligibleManagerForId() val rac = ctx.runActionContext() @@ -47,13 +48,17 @@ class GetSubmissionInfoHandler(store: TransientEntityStore): AbstractEvaluationV val limit = manager.runProperties.limitSubmissionPreviews val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) - val blind = currentTask.template.taskGroup.type.options.contains(DbTaskOption.HIDDEN_RESULTS) && currentTask.isRunning + val blind = currentTask.isRunning && manager.template.taskTypes.find { it.name == currentTask.template.taskType }?.taskOptions?.contains( + ApiTaskOption.HIDDEN_RESULTS + ) == true /* Obtain current task run and check status. */ - if (limit > 0) { - limitSubmissions(manager.currentSubmissions(rac), limit, blind) - } else { - manager.currentSubmissions(rac).map { it.toApi(blind) } + return this.store.transactional (true) { + if (limit > 0) { + limitSubmissions(manager.currentSubmissions(rac), limit, blind) + } else { + manager.currentSubmissions(rac).map { it.toApi(blind) } + } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index fc2495bcc..7b9469b67 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -1,5 +1,4 @@ import dev.dres.DRES -import dev.dres.api.rest.AccessManager import dev.dres.api.rest.handler.GetRestHandler import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.api.rest.handler.evaluation.viewer.AbstractEvaluationViewerHandler @@ -10,10 +9,13 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType +import dev.dres.api.rest.types.template.tasks.ApiHint +import dev.dres.api.rest.types.template.tasks.ApiHintType +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate +import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.DbHint -import dev.dres.data.model.template.task.DbHintType import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.mgmt.cache.CacheManager import dev.dres.run.InteractiveRunManager @@ -22,7 +24,9 @@ import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files @@ -41,8 +45,8 @@ import java.util.* * @author Loris Sauter * @version 2.0.0 */ -class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheManager) : - AbstractEvaluationViewerHandler(store), GetRestHandler { +class GetTaskHintHandler(private val store: TransientEntityStore, private val cache: CacheManager) : + AbstractEvaluationViewerHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/hint/{taskId}" @@ -112,23 +116,42 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa * @throws FileNotFoundException * @throws IOException */ - private fun DbTaskTemplate.toTaskHint(): ApiHintContent { - val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> +// private fun DbTaskTemplate.toTaskHint(): ApiHintContent { +// val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> +// var index = 0 +// group.value.sortedBy { it.start ?: 0 }.flatMap { +// val ret = mutableListOf(it.toContentElement()) +// if (it.end != null) { +// if (index == (group.value.size - 1)) { +// ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) +// } else if ((group.value[index + 1].start ?: 0) > it.end!!) { +// ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) +// } +// } +// index += 1 +// ret +// } +// } +// return ApiHintContent(this.id, sequence, false) +// } + + private fun ApiTaskTemplate.toTaskHint(): ApiHintContent = store.transactional(true){ + val sequence = this.hints.groupBy { it.type }.flatMap { (type, hints) -> var index = 0 - group.value.sortedBy { it.start ?: 0 }.flatMap { + hints.sortedBy { it.start ?: 0 }.flatMap { val ret = mutableListOf(it.toContentElement()) if (it.end != null) { - if (index == (group.value.size - 1)) { - ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) - } else if ((group.value[index + 1].start ?: 0) > it.end!!) { - ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) + if (index == (hints.size - 1)) { + ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end)) + } else if ((hints[index + 1].start ?: 0) > it.end) { + ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end)) } } - index += 1 + ++index ret } } - return ApiHintContent(this.id, sequence, false) + ApiHintContent(this.id!!, sequence, false) } /** @@ -139,17 +162,115 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa * @throws FileNotFoundException * @throws IOException */ - private fun DbHint.toContentElement(): ApiContentElement { +// private fun DbHint.toContentElement(): ApiContentElement { +// val content = when (this.type) { +// DbHintType.IMAGE -> { +// val path = if (this.item != null) { +// this@GetTaskHintHandler.cache.asyncPreviewImage(this.item!!) +// .get() /* This should return immediately, since the previews have been prepared. */ +// } else { +// this@GetTaskHintHandler.cache.asyncPreviewImage( +// DRES.EXTERNAL_ROOT.resolve( +// this.path +// ?: throw IllegalStateException("DbHint of type IMAGE is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") +// ) +// ).get() +// } +// if (Files.exists(path)) { +// if (path.toString().endsWith(".jpg", ignoreCase = true)) { +// Base64.getEncoder().encodeToString(Files.readAllBytes(path)) +// } else { //should never happen +// null +// } +// } else { +// null +// } +// } +// +// DbHintType.VIDEO -> { +// val start = this.temporalRangeStart +// ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid start timestamp but doesn't! This is a programmer's error!") +// val end = this.temporalRangeEnd +// ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid end timestamp but doesn't!! This is a programmer's error!") +// val path = if (this.item != null) { +// this@GetTaskHintHandler.cache.asyncPreviewVideo(this.item!!, start, end) +// .get() /* This should return immediately, since the previews have been prepared. */ +// } else { +// val source = DRES.EXTERNAL_ROOT.resolve( +// this.path +// ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") +// ) +// this@GetTaskHintHandler.cache.asyncPreviewVideo(source, start, end).get() +// } +// if (Files.exists(path)) { +// Base64.getEncoder().encodeToString(Files.readAllBytes(path)) +// } else { +// null +// } +// } +// +// DbHintType.TEXT -> this.text +// ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") +// +// DbHintType.EMPTY -> "" +// else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") +// } +// +// val contentType = when (this.type) { +// DbHintType.IMAGE -> ApiContentType.IMAGE +// DbHintType.VIDEO -> ApiContentType.VIDEO +// DbHintType.TEXT -> ApiContentType.TEXT +// DbHintType.EMPTY -> ApiContentType.EMPTY +// else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") +// } +// +// return ApiContentElement(contentType = contentType, content = content, offset = this.start ?: 0L) +// } + + private fun ApiHint.toContentElement(): ApiContentElement { + + //TODO find a better place for this lookup + val item = this.mediaItem?.let { + DbMediaItem.query(DbMediaItem::mediaItemId eq it).firstOrNull() + } + val range = if (item?.fps != null) { + this.range?.toTemporalRange(item.fps!!) + } else { + null + } + val content = when (this.type) { - DbHintType.IMAGE -> { - val path = if (this.item != null) { - this@GetTaskHintHandler.cache.asyncPreviewImage(this.item!!) + ApiHintType.EMPTY -> "" + ApiHintType.TEXT -> this.description + ApiHintType.VIDEO -> { + val start = range?.start?.toMilliseconds() + ?: throw IllegalStateException("ApiHint of type VIDEO is expected to hold a valid start timestamp but doesn't! This is a programmer's error!") + val end = range.end.toMilliseconds() + val path = if (item != null) { + this@GetTaskHintHandler.cache.asyncPreviewVideo(item, start, end) + .get() /* This should return immediately, since the previews have been prepared. */ + } else { + val source = DRES.EXTERNAL_ROOT.resolve( + this.path + ?: throw IllegalStateException("ApiHint of type VIDEO is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") + ) + this@GetTaskHintHandler.cache.asyncPreviewVideo(source, start, end).get() + } + if (Files.exists(path)) { + Base64.getEncoder().encodeToString(Files.readAllBytes(path)) + } else { + null + } + } + ApiHintType.IMAGE -> { + val path = if (item != null) { + this@GetTaskHintHandler.cache.asyncPreviewImage(item) .get() /* This should return immediately, since the previews have been prepared. */ } else { this@GetTaskHintHandler.cache.asyncPreviewImage( DRES.EXTERNAL_ROOT.resolve( this.path - ?: throw IllegalStateException("DbHint of type IMAGE is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") + ?: throw IllegalStateException("ApiHint of type IMAGE is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") ) ).get() } @@ -163,42 +284,13 @@ class GetTaskHintHandler(store: TransientEntityStore, private val cache: CacheMa null } } - - DbHintType.VIDEO -> { - val start = this.temporalRangeStart - ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid start timestamp but doesn't! This is a programmer's error!") - val end = this.temporalRangeEnd - ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid end timestamp but doesn't!! This is a programmer's error!") - val path = if (this.item != null) { - this@GetTaskHintHandler.cache.asyncPreviewVideo(this.item!!, start, end) - .get() /* This should return immediately, since the previews have been prepared. */ - } else { - val source = DRES.EXTERNAL_ROOT.resolve( - this.path - ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") - ) - this@GetTaskHintHandler.cache.asyncPreviewVideo(source, start, end).get() - } - if (Files.exists(path)) { - Base64.getEncoder().encodeToString(Files.readAllBytes(path)) - } else { - null - } - } - - DbHintType.TEXT -> this.text - ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") - - DbHintType.EMPTY -> "" - else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") } val contentType = when (this.type) { - DbHintType.IMAGE -> ApiContentType.IMAGE - DbHintType.VIDEO -> ApiContentType.VIDEO - DbHintType.TEXT -> ApiContentType.TEXT - DbHintType.EMPTY -> ApiContentType.EMPTY - else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") + ApiHintType.EMPTY -> ApiContentType.EMPTY + ApiHintType.TEXT -> ApiContentType.TEXT + ApiHintType.VIDEO -> ApiContentType.VIDEO + ApiHintType.IMAGE -> ApiContentType.IMAGE } return ApiContentElement(contentType = contentType, content = content, offset = this.start ?: 0L) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index b5b0ea570..12f593f8c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiTaskStatus import dev.dres.utilities.extensions.eligibleManagerForId import dev.dres.utilities.extensions.isParticipant import dev.dres.api.rest.types.template.tasks.ApiTargetContent @@ -8,6 +9,7 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.media.DbMediaType import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.RunActionContext @@ -21,7 +23,7 @@ import dev.dres.run.InteractiveRunManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.* import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files @@ -35,7 +37,7 @@ import java.util.* * @author Loris Sauter * @version 2.0.0 */ -class GetTaskTargetHandler(store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(store), GetRestHandler { +class GetTaskTargetHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/target/{taskId}" @@ -71,7 +73,7 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val cache: Cache if (task == null) { task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) } - if (task.status != DbTaskStatus.ENDED) { + if (task.status != ApiTaskStatus.ENDED) { throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) } @@ -96,16 +98,28 @@ class GetTaskTargetHandler(store: TransientEntityStore, private val cache: Cache * @throws FileNotFoundException * @throws IOException */ - private fun DbTaskTemplate.toTaskTarget(): ApiTargetContent { +// private fun DbTaskTemplate.toTaskTarget(): ApiTargetContent { +// var cummulativeOffset = 0L +// val sequence = this.targets.asSequence().flatMap { +// cummulativeOffset += Math.floorDiv(it.item?.durationMs ?: 10000L, 1000L) + 1L +// listOf( +// it.toQueryContentElement(), +// ApiContentElement(ApiContentType.EMPTY, null, cummulativeOffset) +// ) +// }.toList() +// return ApiTargetContent(this.id, sequence) +// } + + private fun ApiTaskTemplate.toTaskTarget(): ApiTargetContent { //TODO there must be a better way to do this var cummulativeOffset = 0L - val sequence = this.targets.asSequence().flatMap { + val sequence = DbTaskTemplate.query(DbTaskTemplate::templateId eq this.id).firstOrNull()?.targets?.asSequence()?.flatMap { cummulativeOffset += Math.floorDiv(it.item?.durationMs ?: 10000L, 1000L) + 1L listOf( it.toQueryContentElement(), ApiContentElement(ApiContentType.EMPTY, null, cummulativeOffset) ) - }.toList() - return ApiTargetContent(this.id, sequence) + }?.toList() ?: emptyList() + return ApiTargetContent(this.id!!, sequence) } /** diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt index 0cf2f526a..c84ce3dd4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationInfoHandler.kt @@ -10,7 +10,7 @@ import jetbrains.exodus.database.TransientEntityStore /** * */ -class ListEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluationViewerHandler(store), GetRestHandler> { +class ListEvaluationInfoHandler : AbstractEvaluationViewerHandler(), GetRestHandler> { override val route = "evaluation/info/list" @@ -25,8 +25,8 @@ class ListEvaluationInfoHandler(store: TransientEntityStore) : AbstractEvaluatio ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { - this.getRelevantManagers(ctx).map { manager -> + override fun doGet(ctx: Context): List { + return this.getRelevantManagers(ctx).map { manager -> ApiEvaluationInfo(manager) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt index 2edcf3a3e..7f4c53147 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ListEvaluationStatesHandler.kt @@ -3,16 +3,14 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.status.ErrorStatus -import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore /** * */ -class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluationViewerHandler(store), GetRestHandler> { +class ListEvaluationStatesHandler: AbstractEvaluationViewerHandler(), GetRestHandler> { override val route = "evaluation/state/list" @@ -27,8 +25,8 @@ class ListEvaluationStatesHandler(store: TransientEntityStore): AbstractEvaluati ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { - this.getRelevantManagers(ctx).map { + override fun doGet(ctx: Context): List { + return this.getRelevantManagers(ctx).map { val rac = ctx.runActionContext() ApiEvaluationState(it, rac) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt index de4c75375..c2dc62d5b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt @@ -15,7 +15,7 @@ import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -class ViewerReadyHandler(store: TransientEntityStore) : AbstractEvaluationViewerHandler(store), GetRestHandler { +class ViewerReadyHandler : AbstractEvaluationViewerHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/hint/{taskId}/ready" @@ -38,28 +38,27 @@ class ViewerReadyHandler(store: TransientEntityStore) : AbstractEvaluationViewer ) override fun doGet(ctx: Context): SuccessStatus { - val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val taskId = + ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) val rac = ctx.runActionContext() - return this.store.transactional(true) { - val manager = ctx.eligibleManagerForId() - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access Denied", ctx) - } - - if(ctx.isParticipant() || ctx.isAdmin()) { - manager.viewerReady( - taskId, rac, ViewerInfo( - ctx.sessionToken()!!, - ctx.ip() - ) - ) - } + val manager = ctx.eligibleManagerForId() + if (!manager.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } - SuccessStatus("ready received") + if (ctx.isParticipant() || ctx.isAdmin()) { + manager.viewerReady( + taskId, rac, ViewerInfo( + ctx.sessionToken()!!, + ctx.ip() + ) + ) } + return SuccessStatus("ready received") + } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 35bdaf44d..f9f09ee07 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -8,6 +8,8 @@ import dev.dres.api.rest.types.evaluation.submission.* import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessfulSubmissionsStatus +import dev.dres.api.rest.types.template.tasks.options.ApiSubmissionOption +import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption import dev.dres.data.model.admin.UserId import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.media.* @@ -177,9 +179,10 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v val answer = if (textParam != null) { ApiClientAnswer(text = textParam) } else if (itemParam != null) { - val collection = runManager.currentTaskTemplate(rac).collection /* TODO: Do we need the option to explicitly set the collection name? */ - val mapToSegment = runManager.currentTaskTemplate(rac).taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) - val item = DbMediaItem.query((DbMediaItem::name eq itemParam) and (DbMediaItem::collection eq collection)).firstOrNull() + val collection = runManager.currentTaskTemplate(rac).collectionId /* TODO: Do we need the option to explicitly set the collection name? */ + val taskType = runManager.currentTaskTemplate(rac).taskType + val mapToSegment = runManager.template.taskTypes.find { it.name == taskType }?.taskOptions?.contains(ApiTaskOption.MAP_TO_SEGMENT) == true + val item = DbMediaCollection.query(DbMediaCollection::id eq collection).firstOrNull()?.items?.filter { it.name eq itemParam }?.firstOrNull() ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) val range: Pair? = when { map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt index 5c221eaad..0da4d3215 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt @@ -22,9 +22,9 @@ data class ApiEvaluationState( constructor(run: InteractiveRunManager, context: RunActionContext) : this( run.id, run.status.toApi(), - run.currentTask(context)?.status?.toApi() ?: ApiTaskStatus.NO_TASK, + run.currentTask(context)?.status ?: ApiTaskStatus.NO_TASK, try { - ApiTaskTemplateInfo(run.currentTaskTemplate(context).toApi()) + ApiTaskTemplateInfo(run.currentTaskTemplate(context)) } catch (e: IllegalArgumentException) { ApiTaskTemplateInfo.EMPTY_INFO }, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt index de07c16ff..d5a7db493 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskOverview.kt @@ -3,7 +3,7 @@ package dev.dres.api.rest.types.evaluation import dev.dres.data.model.run.interfaces.TaskRun data class ApiTaskOverview( - val id:String, + val id: String, val name: String, val type: String, val group: String, @@ -13,13 +13,13 @@ data class ApiTaskOverview( val started: Long?, val ended: Long?) { constructor(task: TaskRun) : this( - task.template.id, + task.template.id!!, task.template.name, - task.template.taskGroup.name, - task.template.taskGroup.type.name, + task.template.taskGroup, + task.template.taskType, task.template.duration, task.taskId, - task.status.toApi(), + task.status, task.started, task.ended) } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamAggregatorType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamAggregatorType.kt index f6842a7d1..731e87849 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamAggregatorType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamAggregatorType.kt @@ -1,6 +1,8 @@ package dev.dres.api.rest.types.template.team import dev.dres.data.model.template.team.DbTeamAggregator +import dev.dres.data.model.template.team.TeamAggregatorImpl +import dev.dres.data.model.template.team.TeamId /** * The RESTful API equivalent ofr [DbTeamAggregator]. @@ -25,4 +27,17 @@ enum class ApiTeamAggregatorType { LAST -> DbTeamAggregator.LAST } + /** + * Creates and returns a new [TeamAggregatorImpl] for this [DbTeamAggregator]. + * + * @param teamIds The list of [TeamId]s to create the [TeamAggregatorImpl] for. + * @return [TeamAggregatorImpl] + */ + fun newInstance(teamIds: Set) = when(this) { + MAX -> TeamAggregatorImpl.Max(teamIds) + MIN -> TeamAggregatorImpl.Min(teamIds) + MEAN -> TeamAggregatorImpl.Mean(teamIds) + LAST -> TeamAggregatorImpl.Last(teamIds) + } + } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamGroup.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamGroup.kt index 8b230c9ac..b538d4e6d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/team/ApiTeamGroup.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.types.template.team import dev.dres.data.model.template.team.DbTeamGroup +import dev.dres.data.model.template.team.TeamAggregatorImpl import dev.dres.data.model.template.team.TeamGroupId /** @@ -14,4 +15,13 @@ data class ApiTeamGroup( val name: String? = null, val teams: List = emptyList(), val aggregation: ApiTeamAggregatorType -) +) { + /** + * Returns a new [TeamAggregatorImpl] for this [DbTeamGroup]. + * + * This is a convenience method and requires an active transaction context. + * + * @return [TeamAggregatorImpl] + */ + fun newAggregator() : TeamAggregatorImpl = this.aggregation.newInstance(this.teams.map { it.teamId }.toSet()) +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index cbf90348f..bb0daf6c8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -1,9 +1,11 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.Run +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.util.findById /** @@ -12,31 +14,31 @@ import kotlinx.dnq.util.findById * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractEvaluation(evaluation: DbEvaluation): EvaluationRun { +abstract class AbstractEvaluation(protected val store: TransientEntityStore, evaluation: DbEvaluation): EvaluationRun { /** The internal [xdId] of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - private val xdId = evaluation.xdId + private val xdId = this.store.transactional (true) { evaluation.xdId } /** The [EvaluationId] of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - override val id: EvaluationId = evaluation.id + override val id: EvaluationId = this.store.transactional (true) { evaluation.id } /** The name of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. * */ - override val name: String = evaluation.name + override val name: String = this.store.transactional (true) { evaluation.name } /** The [DbEvaluationTemplate] used by this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is stored in memory. */ - override val template: DbEvaluationTemplate = evaluation.template + override val template: ApiEvaluationTemplate = this.store.transactional (true) { evaluation.template.toApi() } /** * Accessor for the [DbEvaluation] underpinning this [AbstractEvaluation] @@ -46,38 +48,35 @@ abstract class AbstractEvaluation(evaluation: DbEvaluation): EvaluationRun { /** Timestamp of when this [AbstractEvaluation] was started. */ override var started: Long? - get() = this.evaluation.started + get() = this.store.transactional (true) { this.evaluation.started } protected set(value) { this.evaluation.started = value } /** Timestamp of when this [AbstractEvaluation] was ended. */ override var ended: Long? - get() = this.evaluation.ended + get() = this.store.transactional (true) { this.evaluation.ended } protected set(value) { this.evaluation.ended = value } /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ override var participantCanView: Boolean - get() = this.evaluation.participantCanView - set(value) { - this.evaluation.participantCanView = value - } + get() = this.store.transactional (true) { this.evaluation.participantCanView } + internal set(value) { this.evaluation.participantCanView = value} + /** Flag indicating that tasks can be repeated.*/ override var allowRepeatedTasks: Boolean - get() = this.evaluation.allowRepeatedTasks - set(value) { - this.evaluation.allowRepeatedTasks = value - } + get() = this.store.transactional (true) { this.evaluation.allowRepeatedTasks } + internal set(value) { this.evaluation.allowRepeatedTasks = value} + /** A fixed limit on submission previews. */ override var limitSubmissionPreviews: Int - get() = this.evaluation.limitSubmissionPreviews - set(value) { - this.evaluation.limitSubmissionPreviews = value - } + get() = this.store.transactional (true) { this.evaluation.limitSubmissionPreviews } + internal set(value) {this.evaluation.limitSubmissionPreviews = value} + /** * Starts this [AbstractEvaluation]. @@ -86,26 +85,32 @@ abstract class AbstractEvaluation(evaluation: DbEvaluation): EvaluationRun { if (this.hasStarted) { throw IllegalStateException("Run has already been started.") } - this.started = System.currentTimeMillis() + this.store.transactional { + this.started = System.currentTimeMillis() + } } /** * Ends this [AbstractEvaluation]. */ override fun end() { - if (!this.isRunning) { - this.started = System.currentTimeMillis() + this.store.transactional { + if (!this.isRunning) { + this.started = System.currentTimeMillis() + } + this.ended = System.currentTimeMillis() } - this.ended = System.currentTimeMillis() } /** * Re-activates this [AbstractEvaluation] */ override fun reactivate() { - if (this.ended == null){ - throw IllegalStateException("Run has not yet ended.") + this.store.transactional { + if (this.ended == null) { + throw IllegalStateException("Run has not yet ended.") + } + this.ended = null } - this.ended = null } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index ce9a41e73..0f988833c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -14,6 +14,7 @@ import dev.dres.run.validation.interfaces.AnswerSetValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.BasicVoteValidator import dev.dres.run.validation.judged.ItemRange +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* /** @@ -22,37 +23,56 @@ import kotlinx.dnq.query.* * @author Luca Rossetto & Ralph Gasser * @version 1.0.0 */ -abstract class AbstractInteractiveTask(task: DbTask): AbstractTask(task) { +abstract class AbstractInteractiveTask(store: TransientEntityStore, task: DbTask) : AbstractTask(store, task) { /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ - override abstract var duration: Long + abstract override var duration: Long /** The [AnswerSetValidator] used to validate [DbSubmission]s. */ final override val validator: AnswerSetValidator init { - this.validator = when (val targetOption = this.template.taskGroup.type.target) { - DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(this.template.targets.filter { it.item ne null }.mapDistinct { it.item }.toSet()) - DbTargetOption.MEDIA_SEGMENT -> { - val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() - TemporalOverlapAnswerSetValidator(target) - } - DbTargetOption.TEXT -> TextAnswerSetValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) - DbTargetOption.JUDGEMENT -> { - val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { - ItemRange(it.item?.name!!, it.start!!, it.end!!) - }.toSet() - BasicJudgementValidator(knownCorrectRanges = knownRanges) - } - DbTargetOption.VOTE -> { - val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { - ItemRange(it.item?.name!!, it.start!!, it.end!!) - }.toSet() - val parameters = this.template.taskGroup.type.configurations.filter { it.key eq targetOption.description }.asSequence().associate { it.key to it.value } - BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) + this.validator = this.store.transactional(true) { + val template = task.template + when (val targetOption = template.taskGroup.type.target) { + DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(template.targets.filter { it.item ne null } + .mapDistinct { it.item }.toSet()) + + DbTargetOption.MEDIA_SEGMENT -> { + val target = + template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } + .asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() + TemporalOverlapAnswerSetValidator(target) + } + + DbTargetOption.TEXT -> TextAnswerSetValidator( + template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList() + ) + + DbTargetOption.JUDGEMENT -> { + val knownRanges = + template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } + .asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + BasicJudgementValidator(knownCorrectRanges = knownRanges) + } + + DbTargetOption.VOTE -> { + val knownRanges = + template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } + .asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + val parameters = + template.taskGroup.type.configurations.filter { it.key eq targetOption.description } + .asSequence().associate { it.key to it.value } + BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) + } + + else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } - else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index acb284460..cb8f0bd68 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -8,6 +8,7 @@ import dev.dres.run.validation.TransientMediaSegment import dev.dres.run.validation.interfaces.AnswerSetValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.ItemRange +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* /** @@ -17,25 +18,39 @@ import kotlinx.dnq.query.* * @author Ralph Gasser * @version 2.0.0 */ -abstract class AbstractNonInteractiveTask(task: DbTask): AbstractTask(task) { +abstract class AbstractNonInteractiveTask(store: TransientEntityStore, task: DbTask) : AbstractTask(store, task) { /** The [AnswerSetValidator] used by this [AbstractNonInteractiveTask]. */ final override val validator: AnswerSetValidator init { - this.validator = when (val targetOption = this.template.taskGroup.type.target) { - DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(this.template.targets.mapDistinct { it.item }.filter { it ne null }.toSet()) - DbTargetOption.MEDIA_SEGMENT -> { - val target = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null)}.asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() - TemporalOverlapAnswerSetValidator(target) - } - DbTargetOption.TEXT -> TextAnswerSetValidator(this.template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList()) - DbTargetOption.JUDGEMENT -> { - val knownRanges = this.template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) }.asSequence().map { - ItemRange(it.item?.name!!, it.start!!, it.end!!) - }.toSet() - BasicJudgementValidator(knownCorrectRanges = knownRanges) + this.validator = store.transactional(true) { + val template = task.template + when (val targetOption = template.taskGroup.type.target) { + DbTargetOption.MEDIA_ITEM -> MediaItemsAnswerSetValidator(template.targets.mapDistinct { it.item } + .filter { it ne null }.toSet()) + + DbTargetOption.MEDIA_SEGMENT -> { + val target = + template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } + .asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() + TemporalOverlapAnswerSetValidator(target) + } + + DbTargetOption.TEXT -> TextAnswerSetValidator( + template.targets.filter { it.text ne null }.asSequence().map { it.text!! }.toList() + ) + + DbTargetOption.JUDGEMENT -> { + val knownRanges = + template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } + .asSequence().map { + ItemRange(it.item?.name!!, it.start!!, it.end!!) + }.toSet() + BasicJudgementValidator(knownCorrectRanges = knownRanges) + } + + else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } - else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 3dd76c141..6beb3c340 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.run -import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbAnswerSet @@ -12,6 +13,7 @@ import dev.dres.data.model.template.team.TeamId import dev.dres.run.filter.basics.SubmissionFilter import dev.dres.run.transformer.basics.SubmissionTransformer import dev.dres.run.validation.interfaces.AnswerSetValidator +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.mapDistinct @@ -23,18 +25,18 @@ import kotlinx.dnq.util.findById * @author Ralph Gasser * @version 1.1.0 */ -abstract class AbstractTask(task: DbTask): TaskRun { +abstract class AbstractTask(protected val store: TransientEntityStore, task: DbTask) : TaskRun { /** The internal [xdId] of this [AbstractEvaluation]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - private val xdId = task.xdId + private val xdId: String = this.store.transactional(true) { task.xdId } /** * Accessor for the [DbTask] underpinning this [AbstractTask] */ - protected val task: DbTask + protected val dbTask: DbTask get() = DbTask.findById(this.xdId) /** @@ -42,20 +44,27 @@ abstract class AbstractTask(task: DbTask): TaskRun { * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - final override val taskId: TaskId = this.task.id + final override val taskId: TaskId = this.store.transactional(true) { task.taskId } /** * The [TemplateId] of this [AbstractTask]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - final override val templateId: TemplateId = this.task.template.templateId + final override val templateId: TemplateId = this.store.transactional(true) { task.template.templateId } + + /** + * Reference to the [ApiTaskTemplate] describing this [AbstractTask]. + * + * Requires active database transaction! + */ + final override val template: ApiTaskTemplate = this.store.transactional(true) { this.dbTask.template.toApi() } /** The current [DbTaskStatus] of this [AbstractTask]. This is a transient property. */ - final override var status: DbTaskStatus - get() = this.task.status + final override var status: ApiTaskStatus + get() = this.store.transactional(true) { this.dbTask.status.toApi() } protected set(value) { - this.task.status = value /* Update backing database field. */ + this.dbTask.status = value.toDb() /* Update backing database field. */ } /** @@ -64,9 +73,9 @@ abstract class AbstractTask(task: DbTask): TaskRun { * Setter requires active database transaction! */ final override var started: Long? - get() = this.task.started + get() = this.store.transactional(true) { this.dbTask.started } protected set(value) { - this.task.started = value /* Update backing database field. */ + this.dbTask.started = value /* Update backing database field. */ } /** @@ -75,18 +84,11 @@ abstract class AbstractTask(task: DbTask): TaskRun { * Setter requires active database transaction! */ final override var ended: Long? - get() = this.task.ended + get() = this.store.transactional(true) { this.dbTask.ended } protected set(value) { - this.task.ended = value /* Update backing database field. */ + this.dbTask.ended = value /* Update backing database field. */ } - /** - * Reference to the [DbTaskTemplate] describing this [AbstractTask]. - * - * Requires active database transaction! - */ - final override val template: DbTaskTemplate - get() = this.task.template /** The [SubmissionFilter] used to filter [DbSubmission]s. */ abstract val filter: SubmissionFilter @@ -107,52 +109,60 @@ abstract class AbstractTask(task: DbTask): TaskRun { if (this.hasStarted) { throw IllegalStateException("Run has already been started.") } - this.status = DbTaskStatus.PREPARING + this.store.transactional { + this.status = ApiTaskStatus.PREPARING + } } /** * Starts this [AbstractTask]. */ override fun start() { - if (this.started != null || this.status == DbTaskStatus.RUNNING) { + if (this.started != null || this.status == ApiTaskStatus.RUNNING) { throw IllegalStateException("Run has already been started.") } - this.started = System.currentTimeMillis() - this.status = DbTaskStatus.RUNNING + this.store.transactional { + this.started = System.currentTimeMillis() + this.status = ApiTaskStatus.RUNNING + } } /** * Ends this [AbstractTask]. */ override fun end() { - if (this.started == null) { - this.started = System.currentTimeMillis() + this.store.transactional { + if (this.started == null) { + this.started = System.currentTimeMillis() + } + this.ended = System.currentTimeMillis() + this.status = ApiTaskStatus.ENDED } - this.ended = System.currentTimeMillis() - this.status = DbTaskStatus.ENDED } /** * Reactivates this [AbstractTask]. */ override fun reactivate() { - if (this.ended == null){ - throw IllegalStateException("Run has not yet ended.") + this.store.transactional { + if (this.ended == null) { + throw IllegalStateException("Run has not yet ended.") + } + this.ended = null + this.status = ApiTaskStatus.RUNNING } - this.ended = null - this.status = DbTaskStatus.RUNNING } /** Returns a [Sequence] of all [DbSubmission]s connected to this [AbstractTask]. */ - override fun getSubmissions() = DbAnswerSet.filter { - a -> a.task.id eq this@AbstractTask.taskId + override fun getDbSubmissions() = DbAnswerSet.filter { a -> + a.task.id eq this@AbstractTask.taskId }.mapDistinct { it.submission }.asSequence() /** Map of [TeamGroupId] to [TeamAggregatorImpl]. */ val teamGroupAggregators: Map by lazy { - this.competition.template.teamGroups.asSequence().associate { it.id to it.newAggregator() } + this.evaluationRun.template.teamGroups.associate { it.id!! to it.newAggregator() } } /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt index 0188602d5..ea1456c1e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbEvaluation.kt @@ -180,9 +180,9 @@ class DbEvaluation(entity: Entity) : PersistentEntity(entity), Evaluation { * @return [EvaluationRun] */ fun toRunManager(store: TransientEntityStore): RunManager = when (this.type) { - DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousRunManager(InteractiveSynchronousEvaluation(this), store) - DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousRunManager(InteractiveAsynchronousEvaluation(this), store) - DbEvaluationType.NON_INTERACTIVE -> NonInteractiveRunManager(NonInteractiveEvaluation(this), store) + DbEvaluationType.INTERACTIVE_SYNCHRONOUS -> InteractiveSynchronousRunManager(InteractiveSynchronousEvaluation(store, this), store) + DbEvaluationType.INTERACTIVE_ASYNCHRONOUS -> InteractiveAsynchronousRunManager(InteractiveAsynchronousEvaluation(store, this), store) + DbEvaluationType.NON_INTERACTIVE -> NonInteractiveRunManager(NonInteractiveEvaluation(store, this), store) else -> throw IllegalArgumentException("Unsupported run type ${this.type.description}. This is a programmer's error!") } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 93e685114..58ef733a0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -1,10 +1,12 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption @@ -23,6 +25,7 @@ import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchTransformer import dev.dres.run.transformer.basics.SubmissionTransformer import dev.dres.run.transformer.basics.CombiningSubmissionTransformer +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import org.slf4j.LoggerFactory import java.util.* @@ -34,7 +37,8 @@ import java.util.concurrent.ConcurrentHashMap * [InteractiveAsynchronousEvaluation]s can be started and ended, and they can be used to create new [IATaskRun]s and access the current [IATaskRun]. * */ -class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { +class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: DbEvaluation) : + AbstractEvaluation(store, evaluation) { private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousEvaluation::class.java) @@ -52,7 +56,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval private val tasksMap = ConcurrentHashMap>() /** Tracks the current [TaskTemplateId] per [TeamId]. */ - private val navigationMap: MutableMap = HashMap() + private val navigationMap: MutableMap = HashMap() /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ override val scoreboards: List @@ -64,24 +68,23 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval /* Prepare the evaluation scoreboards. */ val teams = this.template.teams.asSequence().map { it.teamId }.toList() this.scoreboards = this.template.taskGroups.asSequence().map { group -> - MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) + MaxNormalizingScoreBoard(group.name, this, teams, { task -> task.taskGroup == group.name }, group.name) }.toList() } fun goTo(teamId: TeamId, index: Int) { - this.navigationMap[teamId] = this.template.tasks.drop(this.permutation[teamId]!![index]).first().templateId + this.navigationMap[teamId] = this.template.tasks[this.permutation[teamId]!![index]] } - fun currentTaskDescription(teamId: TeamId): DbTaskTemplate { - val templateId = navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) - return DbTaskTemplate.filter { it.id eq templateId }.firstOrNull() ?: throw IllegalTeamIdException(teamId) + fun currentTaskTemplate(teamId: TeamId): ApiTaskTemplate { + return navigationMap[teamId] ?: throw IllegalTeamIdException(teamId) } init { - val numberOfTasks = this.template.tasks.size() + val numberOfTasks = this.template.tasks.size require(numberOfTasks > 0) { "Cannot create a run from a competition that doesn't have any tasks. " } - this.template.teams.asSequence().forEach { - this.tasksMap[it.id] = ArrayList(numberOfTasks) + this.template.teams.forEach { + this.tasksMap[it.id!!] = ArrayList(numberOfTasks) goTo(it.id, 0) } } @@ -92,7 +95,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval * @param teamId The [TeamId] to lookup. */ fun currentTaskForTeam(teamId: TeamId): IATaskRun? { //FIXME - val currentTaskTemplateId = this.navigationMap[teamId]!! + val currentTaskTemplateId = this.navigationMap[teamId]!!.id return this.tasksForTeam(teamId).findLast { it.template.id == currentTaskTemplateId } @@ -104,8 +107,8 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval * @param teamId The [TeamId] to lookup. * @return List [] */ - fun tasksForTeam(teamId: TeamId) - = this.tasksMap[teamId] ?: throw IllegalArgumentException("Given $teamId is unknown to this competition $id.") + fun tasksForTeam(teamId: TeamId) = + this.tasksMap[teamId] ?: throw IllegalArgumentException("Given $teamId is unknown to this competition $id.") /** * Generates and returns a [String] representation for this [InteractiveAsynchronousEvaluation]. @@ -115,7 +118,7 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval /** * A [AbstractInteractiveTask] that takes place as part of the [InteractiveAsynchronousEvaluation]. */ - inner class IATaskRun internal constructor(task: DbTask) : AbstractInteractiveTask(task) { + inner class IATaskRun internal constructor(task: DbTask) : AbstractInteractiveTask(store, task) { init { /* Sanity check. */ @@ -132,17 +135,18 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval status = DbTaskStatus.CREATED evaluation = this@InteractiveAsynchronousEvaluation.evaluation template = t - team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId }.singleOrNull() + team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId } + .singleOrNull() ?: throw IllegalArgumentException("Cannot start a new task run for team with ID ${teamId}. Team is not registered for competition.") }) /** The [InteractiveAsynchronousEvaluation] this [IATaskRun] belongs to.*/ - override val competition: InteractiveAsynchronousEvaluation + override val evaluationRun: InteractiveAsynchronousEvaluation get() = this@InteractiveAsynchronousEvaluation /** The position of this [IATaskRun] within the [InteractiveAsynchronousEvaluation]. */ override val position: Int - get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.task.team!!.id]?.indexOf(this) ?: -1 + get() = this@InteractiveAsynchronousEvaluation.tasksMap[this.dbTask.team!!.id]?.indexOf(this) ?: -1 /** The [SubmissionFilter] instance used by this [IATaskRun]. */ override val filter: SubmissionFilter @@ -155,15 +159,14 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval /** The total duration in milliseconds of this task. Usually determined by the [DbTaskTemplate] but can be adjusted! */ override var duration: Long = this.template.duration - val teamId = this.task.team!!.id + val teamId = this.dbTask.team!!.id /** The [List] of [TeamId]s working on this [IATaskRun]. */ override val teams: List = listOf(teamId) - init { - this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.task.team!!.id) { _, v -> + this@InteractiveAsynchronousEvaluation.tasksMap.compute(this.dbTask.team!!.id) { _, v -> val list = v ?: LinkedList() check(list.isEmpty() || list.last().hasEnded) { "Cannot create a new task. Another task is currently running." } //FIXME crashes on restart list.add(this) @@ -171,41 +174,50 @@ class InteractiveAsynchronousEvaluation(evaluation: DbEvaluation) : AbstractEval } /* Initialize submission filter. */ - if (this.template.taskGroup.type.submission.isEmpty) { - this.filter = AcceptAllSubmissionFilter - } else { - this.filter = CombiningSubmissionFilter( - this.template.taskGroup.type.submission.asSequence().map { option -> - val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) - .asSequence().map { it.key to it.value }.toMap() - option.newFilter(parameters) - }.toList() - ) + this.filter = store.transactional { + if (task.template.taskGroup.type.submission.isEmpty) { + AcceptAllSubmissionFilter + } else { + CombiningSubmissionFilter( + task.template.taskGroup.type.submission.asSequence().map { option -> + val parameters = + task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() + ) + } } - this.transformer = if (this.template.taskGroup.type.options.asSequence().any { it == DbTaskOption.MAP_TO_SEGMENT }) { - CombiningSubmissionTransformer( - listOf( - SubmissionTaskMatchTransformer(this.taskId), - MapToSegmentTransformer() + this.transformer = store.transactional { + if (task.template.taskGroup.type.options.asSequence().any { it == DbTaskOption.MAP_TO_SEGMENT }) { + CombiningSubmissionTransformer( + listOf( + SubmissionTaskMatchTransformer(this.taskId), + MapToSegmentTransformer() + ) ) - ) - } else { - SubmissionTaskMatchTransformer(this.taskId) + } else { + SubmissionTaskMatchTransformer(this.taskId) + } } /* Initialize task scorer. */ - this.scorer = CachingTaskScorer( - when(val scoreOption = this.template.taskGroup.type.score) { - DbScoreOption.KIS -> KisTaskScorer( - this, - this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description).asSequence().map { it.key to it.value }.toMap() - ) - DbScoreOption.AVS -> AvsTaskScorer(this) - DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) - else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") - } - ) + this.scorer = store.transactional { + CachingTaskScorer( + when (val scoreOption = task.template.taskGroup.type.score) { + DbScoreOption.KIS -> KisTaskScorer( + this, + task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description) + .asSequence().map { it.key to it.value }.toMap() + ) + + DbScoreOption.AVS -> AvsTaskScorer(this) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) + else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") + } + ) + } } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 5c4d7ac23..c09c0de01 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -1,5 +1,6 @@ package dev.dres.data.model.run +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.interfaces.Run @@ -23,6 +24,7 @@ import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchTransformer import dev.dres.run.transformer.basics.SubmissionTransformer import dev.dres.run.transformer.basics.CombiningSubmissionTransformer +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import java.lang.IndexOutOfBoundsException import java.util.LinkedList @@ -35,26 +37,27 @@ import java.util.LinkedList * @author Ralph Gasser * @param 2.0.0 */ -class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { +class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: DbEvaluation) : + AbstractEvaluation(store, evaluation) { init { - require(this.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } - require(this.template.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } - require(this.template.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } + store.transactional(true) { require(this.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } } + require(this.template.tasks.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any tasks." } + require(this.template.teams.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ override val tasks = LinkedList() - /** Reference to the currently active [DbTaskTemplate]. This is part of the task navigation. */ - private val templates = this.template.tasks.sortedBy(DbTaskTemplate::idx).asSequence().map { it.templateId }.toList() + + private val templates = this.template.tasks /** Returns the last [TaskRun]. */ val currentTask: AbstractInteractiveTask? - get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex] } + get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex].id } /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ - var templateIndex: Int = this.currentTask?.templateId?.let { this.templates.indexOf(it) } ?: 0 + var templateIndex: Int = this.currentTask?.templateId?.let { id -> this.templates.indexOfFirst { template -> template.id == id } } ?: 0 private set /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ @@ -67,7 +70,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu /* Prepare the evaluation scoreboards. */ val teams = this.template.teams.asSequence().map { it.teamId }.toList() this.scoreboards = this.template.taskGroups.asSequence().map { group -> - MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) + MaxNormalizingScoreBoard(group.name, this, teams, { task -> task.taskGroup == group.name }, group.name) }.toList() } @@ -76,7 +79,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu * * @return [TemplateId] */ - fun getCurrentTemplateId(): TemplateId = this.templates[this.templateIndex] + fun getCurrentTemplateId(): TemplateId = this.currentTask?.templateId!! /** * Returns the [DbTaskTemplate] this [InteractiveSynchronousEvaluation] is currently pointing to. @@ -85,9 +88,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu * * @return [DbTaskTemplate] */ - fun getCurrentTemplate(): DbTaskTemplate = this.evaluation.template.tasks.filter { - it.id eq this@InteractiveSynchronousEvaluation.getCurrentTemplateId() - }.first() + fun getCurrentTemplate(): ApiTaskTemplate = this.currentTask?.template!! /** * Moves this [InteractiveSynchronousEvaluation] to the given task index. @@ -106,7 +107,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu * Represents a concrete [Run] of a [DbTaskTemplate]. [DbTask]s always exist within a [InteractiveSynchronousEvaluation]. * As a [InteractiveSynchronousEvaluation], [DbTask]s can be started and ended, and they can be used to register [DbSubmission]s. */ - inner class ISTaskRun(task: DbTask): AbstractInteractiveTask(task) { + inner class ISTaskRun(task: DbTask) : AbstractInteractiveTask(store, task) { /** * Constructor used to generate an [ISTaskRun] from a [DbTaskTemplate]. @@ -120,7 +121,7 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu }) /** The [InteractiveSynchronousEvaluation] this [DbTask] belongs to.*/ - override val competition: InteractiveSynchronousEvaluation + override val evaluationRun: InteractiveSynchronousEvaluation get() = this@InteractiveSynchronousEvaluation /** The position of this [DbTask] within the [InteractiveSynchronousEvaluation]. */ @@ -139,50 +140,62 @@ class InteractiveSynchronousEvaluation(evaluation: DbEvaluation) : AbstractEvalu override var duration: Long = this.template.duration /** */ - override val teams: List = this@InteractiveSynchronousEvaluation.template.teams.asSequence().map { it.teamId }.toList() + override val teams: List = + this@InteractiveSynchronousEvaluation.template.teams.asSequence().map { it.teamId }.toList() init { + check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { "Cannot create a new task. Another task is currently running." } (this@InteractiveSynchronousEvaluation.tasks).add(this) /* Initialize submission filter. */ - if (this.template.taskGroup.type.submission.isEmpty) { - this.filter = AcceptAllSubmissionFilter - } else { - this.filter = CombiningSubmissionFilter( - this.template.taskGroup.type.submission.asSequence().map { option -> - val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) - .asSequence().map { it.key to it.value }.toMap() - option.newFilter(parameters) - }.toList() - ) - } - val mapToSegment = this.template.taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) - this.transformer = if (mapToSegment) { - CombiningSubmissionTransformer( - listOf( - SubmissionTaskMatchTransformer(this.taskId), - MapToSegmentTransformer() + this.filter = store.transactional { + if (task.template.taskGroup.type.submission.isEmpty) { + AcceptAllSubmissionFilter + } else { + CombiningSubmissionFilter( + task.template.taskGroup.type.submission.asSequence().map { option -> + val parameters = + task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() ) - ) - } else { - SubmissionTaskMatchTransformer(this.taskId) + } } - /* Initialize task scorer. */ - this.scorer = CachingTaskScorer( - when(val scoreOption = this.template.taskGroup.type.score) { - DbScoreOption.KIS -> KisTaskScorer( - this, - this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description).asSequence().map { it.key to it.value }.toMap() + this.transformer = store.transactional { + val mapToSegment = task.template.taskGroup.type.options.contains(DbTaskOption.MAP_TO_SEGMENT) + if (mapToSegment) { + CombiningSubmissionTransformer( + listOf( + SubmissionTaskMatchTransformer(this.taskId), + MapToSegmentTransformer() + ) ) - DbScoreOption.AVS -> AvsTaskScorer(this) - DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) - else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") + } else { + SubmissionTaskMatchTransformer(this.taskId) } - ) + } + + /* Initialize task scorer. */ + this.scorer = store.transactional { + CachingTaskScorer( + when (val scoreOption = task.template.taskGroup.type.score) { + DbScoreOption.KIS -> KisTaskScorer( + this, + task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description) + .asSequence().map { it.key to it.value }.toMap() + ) + + DbScoreOption.AVS -> AvsTaskScorer(this) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) + else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") + } + ) + } } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index f42bd3a16..1cc1f420b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -20,6 +20,7 @@ import dev.dres.run.transformer.MapToSegmentTransformer import dev.dres.run.transformer.SubmissionTaskMatchTransformer import dev.dres.run.transformer.basics.SubmissionTransformer import dev.dres.run.transformer.basics.CombiningSubmissionTransformer +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* @@ -31,12 +32,12 @@ import kotlinx.dnq.query.* * @author Luca Rossetto * @param 1.0.0 */ -class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(evaluation) { +class NonInteractiveEvaluation(store: TransientEntityStore, evaluation: DbEvaluation) : AbstractEvaluation(store, evaluation) { init { require(this.evaluation.type == DbEvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } - require(this.template.tasks.size() > 0) { "Cannot create a run from a competition that doesn't have any tasks." } - require(this.template.teams.size() > 0) { "Cannot create a run from a competition that doesn't have any teams." } + require(this.template.tasks.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any tasks." } + require(this.template.teams.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [TaskRun]s registered for this [NonInteractiveEvaluation]. */ @@ -50,16 +51,16 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev init { val teams = this.template.teams.asSequence().map { it.teamId }.toList() this.scoreboards = this.template.taskGroups.asSequence().map { group -> - MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup.name == group.name}, group.name) + MaxNormalizingScoreBoard(group.name, this, teams, {task -> task.taskGroup == group.name}, group.name) }.toList() } /** * The [TaskRun] used by a [NonInteractiveEvaluation]. */ - inner class NITaskRun(task: DbTask): AbstractNonInteractiveTask(task) { + inner class NITaskRun(task: DbTask): AbstractNonInteractiveTask(store, task) { /** Reference to the [EvaluationRun] hosting this [NITaskRun]. */ - override val competition: EvaluationRun + override val evaluationRun: EvaluationRun get() = this@NonInteractiveEvaluation /** The position of this [NITaskRun] within the [NonInteractiveEvaluation]. */ @@ -67,45 +68,50 @@ class NonInteractiveEvaluation(evaluation: DbEvaluation) : AbstractEvaluation(ev get() = this@NonInteractiveEvaluation.tasks.indexOf(this) /** The [CachingTaskScorer] instance used by this [NITaskRun]. */ - override val scorer: CachingTaskScorer = CachingTaskScorer( - when(val scoreOption = this.template.taskGroup.type.score) { + override val scorer: CachingTaskScorer = store.transactional { CachingTaskScorer( + when(val scoreOption = task.template.taskGroup.type.score) { DbScoreOption.KIS -> throw IllegalStateException("KIS task scorer is not applicable to non-interactive evaluations") DbScoreOption.AVS -> AvsTaskScorer(this) DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } - ) + ) } - override val transformer: SubmissionTransformer = if (this.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { - CombiningSubmissionTransformer( - listOf( - SubmissionTaskMatchTransformer(this.taskId), - MapToSegmentTransformer() + override val transformer: SubmissionTransformer = store.transactional { + if (task.template.taskGroup.type.options.filter { it eq DbTaskOption.MAP_TO_SEGMENT }.any()) { + CombiningSubmissionTransformer( + listOf( + SubmissionTaskMatchTransformer(this.taskId), + MapToSegmentTransformer() + ) ) - ) - } else { - SubmissionTaskMatchTransformer(this.taskId) + } else { + SubmissionTaskMatchTransformer(this.taskId) + } } /** The [SubmissionFilter] instance used by this [NITaskRun]. */ override val filter: SubmissionFilter init{ - if (this.template.taskGroup.type.submission.isEmpty) { - this.filter = AcceptAllSubmissionFilter - } else { - this.filter = CombiningSubmissionFilter( - this.template.taskGroup.type.submission.asSequence().map { option -> - val parameters = this.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) - .asSequence().map { it.key to it.value }.toMap() - option.newFilter(parameters) - }.toList() - ) + this.filter = store.transactional { + if (task.template.taskGroup.type.submission.isEmpty) { + AcceptAllSubmissionFilter + } else { + CombiningSubmissionFilter( + task.template.taskGroup.type.submission.asSequence().map { option -> + val parameters = + task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq option.description) + .asSequence().map { it.key to it.value }.toMap() + option.newFilter(parameters) + }.toList() + ) + } } } /** List of [TeamId]s that work on this [NITaskRun]. */ - override val teams: List = this@NonInteractiveEvaluation.template.teams.asSequence().map { it.teamId }.toList() + override val teams: List = this@NonInteractiveEvaluation.template.teams.map { it.teamId } /** */ override val duration: Long = 0 diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index 10217d9ef..3ac117728 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -1,6 +1,6 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.template.DbEvaluationTemplate +import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.run.DbEvaluation import dev.dres.run.score.scoreboard.Scoreboard @@ -14,23 +14,23 @@ interface EvaluationRun: Run { /** The unique [EvaluationId] that identifies this [EvaluationRun]. */ val id: EvaluationId - /** The name human readable of this [EvaluationRun]. */ + /** The name human-readable of this [EvaluationRun]. */ val name: String - /** Reference to the [DbEvaluationTemplate] that describes the content of this [EvaluationRun]. */ - val template: DbEvaluationTemplate + /** Reference to the [ApiEvaluationTemplate] that describes the content of this [EvaluationRun]. */ + val template: ApiEvaluationTemplate /** Collection of [TaskRun]s that make up this [EvaluationRun]. */ val tasks: List - /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ - var participantCanView: Boolean + /** Flag indicating that participants can also use the viewer for this [EvaluationRun]. */ + val participantCanView: Boolean /** Flag indicating that tasks can be repeated.*/ - var allowRepeatedTasks: Boolean + val allowRepeatedTasks: Boolean /** A fixed limit on submission previews. */ - var limitSubmissionPreviews: Int + val limitSubmissionPreviews: Int /** List of [Scoreboard] implementations for this [EvaluationRun] */ val scoreboards: List diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index ed2942fad..a5abb6231 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,10 +1,9 @@ package dev.dres.data.model.run.interfaces -import dev.dres.data.model.run.DbTaskStatus -import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission -import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable @@ -28,17 +27,17 @@ interface TaskRun: Run, Scoreable { /** The unique [TaskTemplateId] that identifies the task template underpinning [TaskRun]. */ val templateId: TaskTemplateId - /** The current [DbTaskStatus] of this [TaskRun]. This is typically a transient property. */ - val status: DbTaskStatus + /** The current [ApiTaskStatus] of this [TaskRun]. This is typically a transient property. */ + val status: ApiTaskStatus /** Reference to the [EvaluationRun] this [IATaskRun] belongs to. */ - val competition: EvaluationRun + val evaluationRun: EvaluationRun /** The position of this [IATaskRun] within the enclosing [EvaluationRun]. */ val position: Int - /** Reference to the [DbTaskTemplate] describing this [IATaskRun]. */ - val template: DbTaskTemplate + /** Reference to the [ApiTaskTemplate] describing this [IATaskRun]. */ + val template: ApiTaskTemplate /** The [CachingTaskScorer] used to update score for this [IATaskRun]. */ val scorer: CachingTaskScorer @@ -53,5 +52,5 @@ interface TaskRun: Run, Scoreable { * * @return [List] of [DbSubmission]s */ - fun getSubmissions(): Sequence + fun getDbSubmissions(): Sequence } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt index 29a8ae222..35f75f84e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamAggregator.kt @@ -24,20 +24,6 @@ class DbTeamAggregator(entity: Entity) : XdEnumEntity(entity) { var description by xdRequiredStringProp(unique = true) private set - /** - * Creates and returns a new [TeamAggregatorImpl] for this [DbTeamAggregator]. - * - * @param teams The list of [DbTeam]s to create the [TeamAggregatorImpl] for. - * @return [TeamAggregatorImpl] - */ - fun newInstance(teams: List) = when(this) { - MAX -> TeamAggregatorImpl.Max(teams) - MIN -> TeamAggregatorImpl.Min(teams) - MEAN -> TeamAggregatorImpl.Mean(teams) - LAST -> TeamAggregatorImpl.Last(teams) - else -> throw IllegalStateException("Failed to generated aggregator for unknown team group ${this.description}.") - } - override fun toString() = this.description fun toApi(): ApiTeamAggregatorType = ApiTeamAggregatorType.values().find { it.toDb() == this } ?: throw IllegalStateException("TeamAggregator ${this.description} is not supported.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt index 574696e47..fa4121dad 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/DbTeamGroup.kt @@ -49,12 +49,5 @@ class DbTeamGroup(entity: Entity) : PersistentEntity(entity) { */ fun toApi() = ApiTeamGroup(this.teamGroupId, this.name, this.teams.asSequence().map { it.toApi() }.toList(), this.defaultAggregator.toApi()) - /** - * Returns a new [TeamAggregatorImpl] for this [DbTeamGroup]. - * - * This is a convenience method and requires an active transaction context. - * - * @return [TeamAggregatorImpl] - */ - fun newAggregator() : TeamAggregatorImpl = this.defaultAggregator.newInstance(this.teams.toList()) + } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt index cbca9467d..d456eeb2f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/team/TeamAggregatorImpl.kt @@ -6,10 +6,7 @@ package dev.dres.data.model.template.team * @author Luca Rossetto * @version 1.0.0 */ -sealed class TeamAggregatorImpl constructor(teams: List) { - - /** Internal set of [TeamId]s. */ - private val teamIds = teams.map { it.teamId }.toSet() +sealed class TeamAggregatorImpl constructor(private val teamIds: Set) { var lastValue: Double = 0.0 private set @@ -22,22 +19,22 @@ sealed class TeamAggregatorImpl constructor(teams: List) { return lastValue } - class Max(teams: List) : TeamAggregatorImpl(teams) { + class Max(teamIds: Set) : TeamAggregatorImpl(teamIds) { override fun computeAggregation(teamScores: Map): Double = teamScores.map { it.value }.maxOrNull() ?: 0.0 } - class Min(teams: List) : TeamAggregatorImpl(teams) { + class Min(teamIds: Set) : TeamAggregatorImpl(teamIds) { override fun computeAggregation(teamScores: Map): Double = teamScores.map { it.value }.minOrNull() ?: 0.0 } - class Mean(teams: List) : TeamAggregatorImpl(teams) { + class Mean(teamIds: Set) : TeamAggregatorImpl(teamIds) { override fun computeAggregation(teamScores: Map): Double = if (teamScores.isEmpty()) 0.0 else teamScores.map { it.value }.sum() / teamScores.size } - class Last(teams: List) : TeamAggregatorImpl(teams) { + class Last(teamIds: Set) : TeamAggregatorImpl(teamIds) { private val lastScores = mutableListOf>() override fun computeAggregation(teamScores: Map): Double { teamScores.forEach{ diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index aedd72056..51d7602af 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -2,13 +2,12 @@ package dev.dres.run import dev.dres.api.rest.AccessManager import dev.dres.api.rest.types.ViewerInfo +import dev.dres.api.rest.types.evaluation.ApiTaskStatus import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus -import dev.dres.api.rest.types.evaluation.websocket.ClientMessage -import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType -import dev.dres.api.rest.types.evaluation.websocket.ServerMessage -import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate +import dev.dres.api.rest.types.template.tasks.options.ApiSubmissionOption import dev.dres.api.rest.types.users.ApiRole import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate @@ -94,10 +93,8 @@ class InteractiveAsynchronousRunManager( get() = this.evaluation.name /** The [ApiEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template = this.evaluation.template.toApi() + override val template = this.evaluation.template - private val dbTemplate - get() = this.evaluation.template //TODO is there a nicer way to handle this? /** The global [RunManagerStatus] of this [InteractiveAsynchronousRunManager]. */ @Volatile @@ -136,7 +133,7 @@ class InteractiveAsynchronousRunManager( /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ this.evaluation.tasks.forEach { task -> - task.getSubmissions().forEach { sub -> + task.getDbSubmissions().forEach { sub -> for (answerSet in sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }) { task.validator.validate(answerSet) } @@ -212,8 +209,8 @@ class InteractiveAsynchronousRunManager( * @param context The [RunActionContext] used for the invocation. * @return The [DbTaskTemplate] for the given team. */ - override fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate { - return this.evaluation.currentTaskDescription(context.teamId()) + override fun currentTaskTemplate(context: RunActionContext): ApiTaskTemplate { + return this.evaluation.currentTaskTemplate(context.teamId()) } /** @@ -229,7 +226,7 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun previous(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.dbTemplate.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) - 1 + val newIndex = this.template.tasks.indexOf(this.currentTaskTemplate(context)) - 1 return try { this.goTo(context, newIndex) true @@ -250,7 +247,7 @@ class InteractiveAsynchronousRunManager( * @throws IllegalStateException If [RunManager] was not in status [RunManagerStatus.ACTIVE] */ override fun next(context: RunActionContext): Boolean = this.stateLock.write { - val newIndex = this.dbTemplate.tasks.sortedBy(DbTaskTemplate::idx).indexOf(this.currentTaskTemplate(context)) + 1 + val newIndex = this.template.tasks.indexOf(this.currentTaskTemplate(context)) + 1 return try { this.goTo(context, newIndex) true @@ -313,7 +310,7 @@ class InteractiveAsynchronousRunManager( checkTeamStatus(teamId, RunManagerStatus.ACTIVE) /* Create task and update status. */ - val currentTaskTemplate = this.evaluation.currentTaskDescription(teamId) + val currentTaskTemplate = this.evaluation.currentTaskTemplate(teamId) /* Check for duplicate task runs */ if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasksForTeam(teamId) @@ -322,8 +319,13 @@ class InteractiveAsynchronousRunManager( throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used") } - val currentTaskRun = this.evaluation.IATaskRun(currentTaskTemplate, teamId) - currentTaskRun.prepare() + val currentTaskRun = store.transactional { + val dbTaskTemplate = DbTaskTemplate.query(DbTaskTemplate::templateId eq currentTaskTemplate.id!!).first() + val currentTaskRun = this.evaluation.IATaskRun(dbTaskTemplate, teamId) + currentTaskRun.prepare() + currentTaskRun + } + // /* Enqueue WS message for sending */ // RunExecutor.broadcastWsMessage( @@ -455,7 +457,7 @@ class InteractiveAsynchronousRunManager( * @return List of [DbSubmission]s. */ override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { - this.evaluation.tasks.flatMap { it.getSubmissions() } + this.evaluation.tasks.flatMap { it.getDbSubmissions() } } /** @@ -465,7 +467,7 @@ class InteractiveAsynchronousRunManager( * @return List of [DbSubmission]s. */ override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { - this.currentTask(context)?.getSubmissions()?.toList() ?: emptyList() + this.currentTask(context)?.getDbSubmissions()?.toList() ?: emptyList() } /** @@ -669,7 +671,10 @@ class InteractiveAsynchronousRunManager( LOGGER.info("Interrupted SynchronousRunManager, exiting") return } catch (e: Throwable) { - LOGGER.error("Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", e) + LOGGER.error( + "Uncaught exception in run loop for competition run ${this.id}. Loop will continue to work but this error should be handled!", + e + ) LOGGER.error("This is the ${++errorCounter}. in a row, will terminate loop after $MAXIMUM_RUN_LOOP_ERROR_COUNT errors") // oh shit, something went horribly, horribly wrong @@ -701,7 +706,7 @@ class InteractiveAsynchronousRunManager( for (teamId in this.teamIds) { val runStatus = this.statusMap[teamId] ?: throw IllegalStateException("Run status for team $teamId is not set. This is a programmers error!") - val taskStatus = this.evaluation.currentTaskForTeam(teamId)?.status?.toApi() + val taskStatus = this.evaluation.currentTaskForTeam(teamId)?.status for (updatable in this.updatables) { if (updatable.shouldBeUpdated(runStatus, taskStatus)) { updatable.update(runStatus, taskStatus, RunActionContext("SYSTEM", teamId, setOf(ApiRole.ADMIN))) @@ -740,7 +745,7 @@ class InteractiveAsynchronousRunManager( val task = this.evaluation.currentTaskForTeam(teamId) ?: throw IllegalStateException("Could not find active task for team $teamId despite status of the team being ${this.statusMap[teamId]}. This is a programmer's error!") task.start() - AuditLogger.taskStart(this.id, task.teamId, task.template.toApi(), AuditLogSource.REST, null) + AuditLogger.taskStart(this.id, task.teamId, task.template, AuditLogSource.REST, null) // RunExecutor.broadcastWsMessage( // teamId, // ServerMessage(this.id, ServerMessageType.TASK_START, task.taskId) @@ -755,8 +760,7 @@ class InteractiveAsynchronousRunManager( */ private fun registerOptionalUpdatables() { /* Determine if task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.dbTemplate.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission } - .filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + val endOnSubmit = this.template.taskTypes.flatMap { it.submissionOptions }.contains(ApiSubmissionOption.LIMIT_CORRECT_PER_TEAM) //FIXME should take into account all limits if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) } @@ -795,7 +799,7 @@ class InteractiveAsynchronousRunManager( * */ private fun teamHasPreparingTask(teamId: TeamId) = - this.evaluation.currentTaskForTeam(teamId)?.status == DbTaskStatus.PREPARING + this.evaluation.currentTaskForTeam(teamId)?.status == ApiTaskStatus.PREPARING /** * Convenience method: Tries to find a matching [TeamId] in the context of this [InteractiveAsynchronousEvaluation] @@ -805,9 +809,7 @@ class InteractiveAsynchronousRunManager( */ private fun RunActionContext.teamId(): TeamId { val userId = this.userId - return this@InteractiveAsynchronousRunManager.dbTemplate.teams.asSequence().firstOrNull { team -> - team.users.filter { it.id eq userId }.isNotEmpty - }?.teamId + return this@InteractiveAsynchronousRunManager.template.teams.firstOrNull { team -> team.users.any { it.id == userId } }?.teamId ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 2662f7172..8d62ed09f 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -1,6 +1,7 @@ package dev.dres.run import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* @@ -62,7 +63,7 @@ interface InteractiveRunManager : RunManager { * @param context The [RunActionContext] used for the invocation. * @return [DbTaskTemplate] */ - fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate + fun currentTaskTemplate(context: RunActionContext): ApiTaskTemplate /** * Starts the [currentTask] and thus moves the [RunManager.status] from diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 718eed003..e6e4f2156 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -1,15 +1,14 @@ package dev.dres.run import dev.dres.api.rest.AccessManager -import dev.dres.api.rest.handler.users.SessionToken import dev.dres.api.rest.types.ViewerInfo +import dev.dres.api.rest.types.evaluation.ApiTaskStatus import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus -import dev.dres.api.rest.types.evaluation.websocket.ClientMessage -import dev.dres.api.rest.types.evaluation.websocket.ClientMessageType -import dev.dres.api.rest.types.evaluation.websocket.ServerMessage -import dev.dres.api.rest.types.evaluation.websocket.ServerMessageType +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate +import dev.dres.api.rest.types.template.tasks.options.ApiSubmissionOption +import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption import dev.dres.data.model.admin.DbUser import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId @@ -69,10 +68,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch get() = this.evaluation.name /** The [ApiEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template = this.evaluation.template.toApi() - - private val dbTemplate - get() = this.evaluation.template //TODO is there a nicer way to handle this? + override val template = this.evaluation.template /** The status of this [RunManager]. */ @Volatile @@ -111,14 +107,14 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /* End ongoing tasks upon initialization (in case server crashed during task execution). */ for (task in this.evaluation.tasks) { - if (task.isRunning || task.status == DbTaskStatus.RUNNING) { + if (task.isRunning || task.status == ApiTaskStatus.RUNNING) { task.end() } } /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ this.evaluation.tasks.forEach { task -> - task.getSubmissions().forEach { sub -> + task.getDbSubmissions().forEach { sub -> sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.asSequence().forEach { task.validator.validate(it) } @@ -163,9 +159,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param properties The set of new [RunProperties] */ override fun updateProperties(properties: RunProperties) { - this.evaluation.allowRepeatedTasks = properties.allowRepeatedTasks - this.evaluation.limitSubmissionPreviews = properties.limitSubmissionPreviews - this.evaluation.participantCanView = properties.participantCanView + store.transactional { + this.evaluation.allowRepeatedTasks = properties.allowRepeatedTasks + this.evaluation.limitSubmissionPreviews = properties.limitSubmissionPreviews + this.evaluation.participantCanView = properties.participantCanView + } } /** @@ -175,7 +173,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * * @return [DbTaskTemplate] */ - override fun currentTaskTemplate(context: RunActionContext): DbTaskTemplate = this.stateLock.write { + override fun currentTaskTemplate(context: RunActionContext): ApiTaskTemplate = this.stateLock.write { checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) this.evaluation.getCurrentTemplate() } @@ -205,7 +203,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override fun goTo(context: RunActionContext, index: Int) { checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() - this.evaluation.tasks.any { it.status == DbTaskStatus.RUNNING } + this.evaluation.tasks.any { it.status == ApiTaskStatus.RUNNING } if (index >= 0 && index < this.template.tasks.size) { /* Update active task. */ this.evaluation.goTo(index) @@ -228,14 +226,19 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used.") } - /* Create and prepare pipeline for submission. */ - this.evaluation.ISTaskRun(currentTaskTemplate) + this.store.transactional { - /* Update status. */ - this.evaluation.currentTask!!.prepare() + val dbTaskTemplate = DbTaskTemplate.query(DbTaskTemplate::templateId eq currentTaskTemplate.id!!).first() + + /* Create and prepare pipeline for submission. */ + this.evaluation.ISTaskRun(dbTaskTemplate) - /* Reset the ReadyLatch. */ - this.readyLatch.reset(VIEWER_TIMEOUT_DEFAULT) + /* Update status. */ + this.evaluation.currentTask!!.prepare() + + /* Reset the ReadyLatch. */ + this.readyLatch.reset(VIEWER_TIMEOUT_DEFAULT) + } // /* Enqueue WS message for sending */ // RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) @@ -250,8 +253,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch assertTaskPreparingOrRunning() checkContext(context) - /* End TaskRun and persist. */ - this.currentTask(context)?.end() + this.store.transactional { + /* End TaskRun and persist. */ + this.currentTask(context)?.end() + } // /* Enqueue WS message for sending */ // RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.currentTask(context)?.taskId)) @@ -270,9 +275,9 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override fun currentTask(context: RunActionContext) = this.stateLock.read { this.evaluation.currentTask when (this.evaluation.currentTask?.status) { - DbTaskStatus.PREPARING, - DbTaskStatus.RUNNING, - DbTaskStatus.ENDED -> this.evaluation.currentTask + ApiTaskStatus.PREPARING, + ApiTaskStatus.RUNNING, + ApiTaskStatus.ENDED -> this.evaluation.currentTask else -> null } } @@ -292,7 +297,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return List of [DbSubmission]s. */ override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { - this.evaluation.tasks.flatMap { it.getSubmissions() } + this.evaluation.tasks.flatMap { it.getDbSubmissions() } } /** @@ -302,7 +307,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return List of [DbSubmission]s for the currently active [DbTask] */ override fun currentSubmissions(context: RunActionContext): List = this.stateLock.read { - this.currentTask(context)?.getSubmissions()?.toList() ?: emptyList() + this.currentTask(context)?.getDbSubmissions()?.toList() ?: emptyList() } /** @@ -343,7 +348,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - return if (this.evaluation.currentTask?.status == DbTaskStatus.RUNNING) { + return if (this.evaluation.currentTask?.status == ApiTaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") max( @@ -362,7 +367,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { - return if (this.evaluation.currentTask?.status == DbTaskStatus.RUNNING) { + return if (this.evaluation.currentTask?.status == ApiTaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) @@ -444,7 +449,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch if (taskId == currentTaskId) { this.store.transactional(true) { - if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING) { + if (this.evaluation.currentTask?.status == ApiTaskStatus.PREPARING) { this.readyLatch.setReady(viewerInfo) } } @@ -608,7 +613,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun invokeUpdatables() { val runStatus = this.status - val taskStatus = this.evaluation.currentTask?.status?.toApi() + val taskStatus = this.evaluation.currentTask?.status this.updatables.forEach { if (it.shouldBeUpdated(runStatus, taskStatus)) { it.update(runStatus, taskStatus) @@ -622,10 +627,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ - if (this.evaluation.currentTask?.status == DbTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { + if (this.evaluation.currentTask?.status == ApiTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { this.evaluation.currentTask!!.start() - AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate().toApi(), AuditLogSource.INTERNAL, null) + AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate(), AuditLogSource.INTERNAL, null) } // /* Enqueue WS message for sending */ @@ -633,7 +638,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ - if (this.evaluation.currentTask?.status == DbTaskStatus.RUNNING) { + if (this.evaluation.currentTask?.status == ApiTaskStatus.RUNNING) { this.stateLock.write { val task = this.evaluation.currentTask!! val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) @@ -654,13 +659,13 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun registerOptionalUpdatables() { /* Determine if any task should be prolonged upon submission. */ - val prolongOnSubmit = this.dbTemplate.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.options }.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }.any() + val prolongOnSubmit = this.template.taskTypes.flatMap { it.taskOptions }.contains(ApiTaskOption.PROLONG_ON_SUBMISSION) if (prolongOnSubmit) { this.updatables.add(ProlongOnSubmitUpdatable(this)) } /* Determine if any task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.dbTemplate.taskGroups.mapDistinct { it.type }.flatMapDistinct { it.submission }.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + val endOnSubmit = this.template.taskTypes.flatMap { it.submissionOptions }.contains(ApiSubmissionOption.LIMIT_CORRECT_PER_TEAM) //FIXME should take into account all limits if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) } @@ -682,7 +687,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun assertTaskPreparingOrRunning() { val status = this.evaluation.currentTask?.status - if (status != DbTaskStatus.RUNNING && status != DbTaskStatus.PREPARING) throw IllegalStateException("Task is neither preparing nor running.") + if (status != ApiTaskStatus.RUNNING && status != ApiTaskStatus.PREPARING) throw IllegalStateException("Task is neither preparing nor running.") } /** @@ -691,7 +696,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @throws IllegalArgumentException If task is neither preparing nor running. */ private fun assureNoRunningTask() { - if (this.evaluation.tasks.any { it.status == DbTaskStatus.RUNNING }) throw IllegalStateException("Task is already running!") + if (this.evaluation.tasks.any { it.status == ApiTaskStatus.RUNNING }) throw IllegalStateException("Task is already running!") } /** @@ -702,9 +707,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun RunActionContext.teamId(): TeamId { val userId = this.userId - val user = DbUser.filter { u -> u.id eq userId }.singleOrNull() - ?: throw IllegalArgumentException("Could not find user with ID ${userId}.") - return this@InteractiveSynchronousRunManager.dbTemplate.teams.filter { t -> t.users.contains(user) }.singleOrNull()?.teamId + return this@InteractiveSynchronousRunManager.template.teams.singleOrNull { t -> t.users.any { it.id == userId } }?.teamId ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") } diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 115fdcfa4..70e5fca44 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -40,7 +40,7 @@ class NonInteractiveRunManager( get() = this.evaluation.name /** The [ApiEvaluationTemplate] executed by this [InteractiveSynchronousRunManager]. */ - override val template = this.evaluation.template.toApi() + override val template = this.evaluation.template /** The [List] of [Scoreboard]s maintained by this [NonInteractiveRunManager]. */ override val scoreboards: List diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 4ae16aadd..52ef79deb 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -10,6 +10,8 @@ import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.interfaces.EvaluationId +import dev.dres.data.model.run.interfaces.TaskId +import dev.dres.data.model.template.TemplateId import dev.dres.run.eventstream.* import dev.dres.run.validation.interfaces.JudgementValidator @@ -164,7 +166,7 @@ object AuditLogger { * @param api The [AuditLogSource] * @param session The identifier of the user session. */ - fun taskEnd(evaluationId: EvaluationId, taskId: EvaluationId, api: AuditLogSource, session: SessionToken?) { + fun taskEnd(evaluationId: EvaluationId, taskId: TemplateId, api: AuditLogSource, session: SessionToken?) { log(TaskEndAuditLogEntry(evaluationId, taskId, api, session)) EventStreamProcessor.event(TaskEndEvent(evaluationId, taskId)) } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 2fe366cba..6bf56fd58 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -1,5 +1,6 @@ package dev.dres.run.score.scoreboard +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.interfaces.EvaluationRun @@ -18,7 +19,7 @@ import kotlin.math.max * @author Ralph Gasser * @version 2.0.0 */ -class MaxNormalizingScoreBoard(override val name: String, override val run: EvaluationRun, private val teamIds: List, private val taskFilter: (DbTaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 1000.0) : Scoreboard { +class MaxNormalizingScoreBoard(override val name: String, override val run: EvaluationRun, private val teamIds: List, private val taskFilter: (ApiTaskTemplate) -> Boolean, private val taskGroupName: String? = null, private val maxScoreNormalized: Double = 1000.0) : Scoreboard { /** A [StampedLock] to synchronise access to this [MaxNormalizingScoreBoard]. */ private val lock = StampedLock() diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt index aecaf7b5e..7b30b1c34 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt @@ -2,6 +2,7 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.api.rest.types.template.tasks.options.ApiSubmissionOption import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.DbVerdictStatus import dev.dres.data.model.template.task.options.DbSubmissionOption @@ -34,16 +35,15 @@ class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private v val currentTask = this.manager.currentTask(this.context) ?: return /* Determine of endOnSubmit is true for given task. */ - val endOnSubmit = currentTask.template.taskGroup.type.submission.filter { it.description eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description }.any() + val taskType = this.manager.template.taskTypes.firstOrNull { it.name == currentTask.template.taskType }!! + val endOnSubmit = taskType.submissionOptions.contains(ApiSubmissionOption.LIMIT_CORRECT_PER_TEAM) if (endOnSubmit) { val teams = currentTask.teams.associateWith { 0 }.toMutableMap() - val submissions = currentTask.getSubmissions().toList() + val submissions = currentTask.getDbSubmissions().toList() /* If there is no submission, we can abort here. */ if (submissions.isNotEmpty()) { - val limit = currentTask.template.taskGroup.type.configurations.filter { - it.key eq DbSubmissionOption.LIMIT_CORRECT_PER_TEAM.description - }.firstOrNull()?.value?.toIntOrNull() ?: 1 + val limit = taskType.configuration["LIMIT_CORRECT_PER_TEAM"]?.toIntOrNull() ?: 1 /* Count number of correct submissions per team. */ if (this.manager.timeLeft(this.context) > 0) { diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt index 342e31026..8ce41edcb 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/ProlongOnSubmitUpdatable.kt @@ -2,11 +2,11 @@ package dev.dres.run.updatables import dev.dres.api.rest.types.evaluation.ApiEvaluationState import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.DbVerdictStatus -import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.task.options.Defaults import dev.dres.data.model.template.task.options.Parameters import dev.dres.run.InteractiveRunManager @@ -34,18 +34,13 @@ class ProlongOnSubmitUpdatable(private val manager: InteractiveRunManager): Upda override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { if (runStatus == RunManagerStatus.ACTIVE && taskStatus == ApiTaskStatus.RUNNING) { val currentTask = this.manager.currentTask(context) ?: return - val prolongOnSubmit = currentTask.template.taskGroup.type.options.filter { it.description eq DbTaskOption.PROLONG_ON_SUBMISSION.description }.any() + val taskType = this.manager.template.taskTypes.firstOrNull { it.name == currentTask.template.taskType }!! + val prolongOnSubmit = taskType.taskOptions.contains(ApiTaskOption.PROLONG_ON_SUBMISSION) if (prolongOnSubmit) { /* Retrieve relevant parameters. */ - val limit = currentTask.template.taskGroup.type.configurations.filter { - it.key eq Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM - }.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT - val prolongBy = currentTask.template.taskGroup.type.configurations.filter { - it.key eq Parameters.PROLONG_ON_SUBMISSION_BY_PARAM - }.firstOrNull()?.value?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_BY_DEFAULT - val correctOnly = currentTask.template.taskGroup.type.configurations.filter { - it.key eq Parameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM - }.firstOrNull()?.value?.toBooleanStrictOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT + val limit = taskType.configuration[Parameters.PROLONG_ON_SUBMISSION_LIMIT_PARAM]?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_LIMIT_DEFAULT + val prolongBy = taskType.configuration[Parameters.PROLONG_ON_SUBMISSION_BY_PARAM]?.toIntOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_BY_DEFAULT + val correctOnly = taskType.configuration[Parameters.PROLONG_ON_SUBMISSION_CORRECT_PARAM]?.toBooleanStrictOrNull() ?: Defaults.PROLONG_ON_SUBMISSION_CORRECT_DEFAULT /* Apply prolongation if necessary. */ val lastSubmission: DbSubmission? = DbAnswerSet.filter { it.task.id eq currentTask.taskId }.mapDistinct { it.submission }.lastOrNull() From 6fb9d7190fcc9fc945b7441c4e9d23de03bccaa7 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 11 Aug 2023 19:54:53 +0200 Subject: [PATCH 449/498] Various fixes, synchronous tasks can be started again --- .../admin/CreateEvaluationHandler.kt | 7 +++- .../dres/data/model/run/AbstractEvaluation.kt | 22 ++++++------- .../run/InteractiveAsynchronousEvaluation.kt | 14 ++++---- .../run/InteractiveSynchronousEvaluation.kt | 32 +++++++------------ .../model/run/NonInteractiveEvaluation.kt | 8 ++--- .../model/template/task/DbTaskTemplate.kt | 2 +- .../kotlin/dev/dres/mgmt/TemplateManager.kt | 3 +- .../run/InteractiveSynchronousRunManager.kt | 18 +++++++++-- .../run/score/scorer/AbstractTaskScorer.kt | 10 ++++-- .../dres/run/score/scorer/AvsTaskScorer.kt | 10 +++--- .../run/score/scorer/CachingTaskScorer.kt | 2 +- .../scorer/InferredAveragePrecisionScorer.kt | 3 +- .../dres/run/score/scorer/KisTaskScorer.kt | 11 ++++--- .../run/score/scorer/LegacyAvsTaskScorer.kt | 3 +- .../run/score/scorer/AvsTaskScorerTest.kt | 2 +- .../run/score/scorer/KisTaskScorerTest.kt | 2 +- .../score/scorer/LegacyAvsTaskScorerTest.kt | 2 +- 17 files changed, 84 insertions(+), 67 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt index 8d144d70b..4f75e41f3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/CreateEvaluationHandler.kt @@ -61,7 +61,7 @@ class CreateEvaluationHandler(private val store: TransientEntityStore, private v } /* Prepare run manager. */ - val manager = this.store.transactional { + val evaluation = this.store.transactional { val template = DbEvaluationTemplate.query((DbEvaluationTemplate::id eq message.templateId) and (DbEvaluationTemplate::instance eq false)).firstOrNull() ?: throw ErrorStatusException(404, "Evaluation template with ID ${message.templateId} could not be found.'", ctx) @@ -120,6 +120,11 @@ class CreateEvaluationHandler(private val store: TransientEntityStore, private v initPermutation() } + evaluation + + } + + val manager = this.store.transactional { /* Create evaluation + run manager and end transaction. */ evaluation.toRunManager(this.store) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt index bb0daf6c8..b664bf401 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractEvaluation.kt @@ -43,39 +43,39 @@ abstract class AbstractEvaluation(protected val store: TransientEntityStore, eva /** * Accessor for the [DbEvaluation] underpinning this [AbstractEvaluation] */ - protected val evaluation: DbEvaluation + internal val dbEvaluation: DbEvaluation get() = DbEvaluation.findById(this.xdId) /** Timestamp of when this [AbstractEvaluation] was started. */ override var started: Long? - get() = this.store.transactional (true) { this.evaluation.started } + get() = this.store.transactional (true) { this.dbEvaluation.started } protected set(value) { - this.evaluation.started = value + this.dbEvaluation.started = value } /** Timestamp of when this [AbstractEvaluation] was ended. */ override var ended: Long? - get() = this.store.transactional (true) { this.evaluation.ended } + get() = this.store.transactional (true) { this.dbEvaluation.ended } protected set(value) { - this.evaluation.ended = value + this.dbEvaluation.ended = value } /** Flag indicating that participants can also use the viewer for this [DbEvaluation]. */ override var participantCanView: Boolean - get() = this.store.transactional (true) { this.evaluation.participantCanView } - internal set(value) { this.evaluation.participantCanView = value} + get() = this.store.transactional (true) { this.dbEvaluation.participantCanView } + internal set(value) { this.dbEvaluation.participantCanView = value} /** Flag indicating that tasks can be repeated.*/ override var allowRepeatedTasks: Boolean - get() = this.store.transactional (true) { this.evaluation.allowRepeatedTasks } - internal set(value) { this.evaluation.allowRepeatedTasks = value} + get() = this.store.transactional (true) { this.dbEvaluation.allowRepeatedTasks } + internal set(value) { this.dbEvaluation.allowRepeatedTasks = value} /** A fixed limit on submission previews. */ override var limitSubmissionPreviews: Int - get() = this.store.transactional (true) { this.evaluation.limitSubmissionPreviews } - internal set(value) {this.evaluation.limitSubmissionPreviews = value} + get() = this.store.transactional (true) { this.dbEvaluation.limitSubmissionPreviews } + internal set(value) {this.dbEvaluation.limitSubmissionPreviews = value} /** diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index 58ef733a0..d03fdcd0d 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -6,7 +6,6 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.run.interfaces.Run -import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.task.options.DbConfiguredOption import dev.dres.data.model.template.task.options.DbScoreOption @@ -63,7 +62,7 @@ class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: init { /* Load all ongoing tasks. */ - this.evaluation.tasks.asSequence().forEach { IATaskRun(it) } + this.dbEvaluation.tasks.asSequence().forEach { IATaskRun(it) } /* Prepare the evaluation scoreboards. */ val teams = this.template.teams.asSequence().map { it.teamId }.toList() @@ -133,9 +132,9 @@ class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: */ constructor(t: DbTaskTemplate, teamId: TeamId) : this(DbTask.new { status = DbTaskStatus.CREATED - evaluation = this@InteractiveAsynchronousEvaluation.evaluation + evaluation = this@InteractiveAsynchronousEvaluation.dbEvaluation template = t - team = this@InteractiveAsynchronousEvaluation.evaluation.template.teams.filter { it.teamId eq teamId } + team = this@InteractiveAsynchronousEvaluation.dbEvaluation.template.teams.filter { it.teamId eq teamId } .singleOrNull() ?: throw IllegalArgumentException("Cannot start a new task run for team with ID ${teamId}. Team is not registered for competition.") }) @@ -209,11 +208,12 @@ class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: DbScoreOption.KIS -> KisTaskScorer( this, task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description) - .asSequence().map { it.key to it.value }.toMap() + .asSequence().map { it.key to it.value }.toMap(), + store ) - DbScoreOption.AVS -> AvsTaskScorer(this) - DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) + DbScoreOption.AVS -> AvsTaskScorer(this, store) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this, store) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index c09c0de01..6f863e7cb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -41,7 +41,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: AbstractEvaluation(store, evaluation) { init { - store.transactional(true) { require(this.evaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } } + store.transactional(true) { require(this.dbEvaluation.type == DbEvaluationType.INTERACTIVE_SYNCHRONOUS) { "Incompatible competition type ${this.dbEvaluation.type}. This is a programmer's error!" } } require(this.template.tasks.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.template.teams.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any teams." } } @@ -57,7 +57,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex].id } /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ - var templateIndex: Int = this.currentTask?.templateId?.let { id -> this.templates.indexOfFirst { template -> template.id == id } } ?: 0 + var templateIndex: Int = 0 private set /** List of [Scoreboard]s maintained by this [NonInteractiveEvaluation]. */ @@ -65,7 +65,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: init { /* Load all ongoing tasks. */ - this.evaluation.tasks.asSequence().forEach { ISTaskRun(it) } + this.dbEvaluation.tasks.asSequence().forEach { ISTaskRun(it) } /* Prepare the evaluation scoreboards. */ val teams = this.template.teams.asSequence().map { it.teamId }.toList() @@ -82,13 +82,13 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: fun getCurrentTemplateId(): TemplateId = this.currentTask?.templateId!! /** - * Returns the [DbTaskTemplate] this [InteractiveSynchronousEvaluation] is currently pointing to. + * Returns the [ApiTaskTemplate] this [InteractiveSynchronousEvaluation] is currently pointing to. * * Requires an active database transaction. * - * @return [DbTaskTemplate] + * @return [ApiTaskTemplate] */ - fun getCurrentTemplate(): ApiTaskTemplate = this.currentTask?.template!! + fun getCurrentTemplate(): ApiTaskTemplate = this.templates[this.templateIndex] /** * Moves this [InteractiveSynchronousEvaluation] to the given task index. @@ -109,17 +109,6 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: */ inner class ISTaskRun(task: DbTask) : AbstractInteractiveTask(store, task) { - /** - * Constructor used to generate an [ISTaskRun] from a [DbTaskTemplate]. - * - * @param t [DbTaskTemplate] to generate [ISTaskRun] from. - */ - constructor(t: DbTaskTemplate) : this(DbTask.new { - status = DbTaskStatus.CREATED - evaluation = this@InteractiveSynchronousEvaluation.evaluation - template = t - }) - /** The [InteractiveSynchronousEvaluation] this [DbTask] belongs to.*/ override val evaluationRun: InteractiveSynchronousEvaluation get() = this@InteractiveSynchronousEvaluation @@ -141,7 +130,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: /** */ override val teams: List = - this@InteractiveSynchronousEvaluation.template.teams.asSequence().map { it.teamId }.toList() + this@InteractiveSynchronousEvaluation.template.teams.map { it.teamId }.toList() init { @@ -187,11 +176,12 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: DbScoreOption.KIS -> KisTaskScorer( this, task.template.taskGroup.type.configurations.query(DbConfiguredOption::key eq scoreOption.description) - .asSequence().map { it.key to it.value }.toMap() + .asSequence().map { it.key to it.value }.toMap(), + store ) - DbScoreOption.AVS -> AvsTaskScorer(this) - DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) + DbScoreOption.AVS -> AvsTaskScorer(this, store) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this, store) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 1cc1f420b..8425eb014 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -35,13 +35,13 @@ import kotlinx.dnq.query.* class NonInteractiveEvaluation(store: TransientEntityStore, evaluation: DbEvaluation) : AbstractEvaluation(store, evaluation) { init { - require(this.evaluation.type == DbEvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.evaluation.type}. This is a programmer's error!" } + require(this.dbEvaluation.type == DbEvaluationType.NON_INTERACTIVE) { "Incompatible competition type ${this.dbEvaluation.type}. This is a programmer's error!" } require(this.template.tasks.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any tasks." } require(this.template.teams.isNotEmpty()) { "Cannot create a run from a competition that doesn't have any teams." } } /** List of [TaskRun]s registered for this [NonInteractiveEvaluation]. */ - override val tasks = this.evaluation.tasks.asSequence().map { + override val tasks = this.dbEvaluation.tasks.asSequence().map { NITaskRun(it) }.toList() @@ -71,8 +71,8 @@ class NonInteractiveEvaluation(store: TransientEntityStore, evaluation: DbEvalua override val scorer: CachingTaskScorer = store.transactional { CachingTaskScorer( when(val scoreOption = task.template.taskGroup.type.score) { DbScoreOption.KIS -> throw IllegalStateException("KIS task scorer is not applicable to non-interactive evaluations") - DbScoreOption.AVS -> AvsTaskScorer(this) - DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this) + DbScoreOption.AVS -> AvsTaskScorer(this, store) + DbScoreOption.LEGACY_AVS -> LegacyAvsTaskScorer(this, store) else -> throw IllegalStateException("The task score option $scoreOption is currently not supported.") } ) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index 8742296b8..762c3e14c 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -134,7 +134,7 @@ class DbTaskTemplate(entity: Entity) : PersistentEntity(entity), TaskTemplate { this.collection.id, this.targets.asSequence().map { it.toApi() }.toList(), this.hints.asSequence().map { it.toApi() }.toList(), - this.comment ?: "" + "debug: ${this.xdId}"//this.comment ?: "" ) /** diff --git a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index e66ea1fb8..4f0aebfb7 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -55,8 +55,7 @@ object TemplateManager { } fun getTemplate(templateId: TemplateId): ApiEvaluationTemplate? = this.store.transactional(true) { - DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq templateId and (DbEvaluationTemplate::instance eq false)) - .firstOrNull()?.toApi() + DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq templateId).firstOrNull()?.toApi() } fun getTeamLogo(teamId: TeamId) : InputStream? = this.store.transactional(true) { diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index e6e4f2156..11f1d56fe 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -226,12 +226,24 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used.") } - this.store.transactional { + val dbTask = this.store.transactional { + + val dbTaskTemplate = DbTaskTemplate.filter { it.id eq currentTaskTemplate.id!! }.first() + + /* create task, needs to be persisted before run can be created */ + val dbTask = DbTask.new { + status = DbTaskStatus.CREATED + evaluation = this@InteractiveSynchronousRunManager.evaluation.dbEvaluation + template = dbTaskTemplate + } - val dbTaskTemplate = DbTaskTemplate.query(DbTaskTemplate::templateId eq currentTaskTemplate.id!!).first() + dbTask + } + + this.store.transactional { /* Create and prepare pipeline for submission. */ - this.evaluation.ISTaskRun(dbTaskTemplate) + this.evaluation.ISTaskRun(dbTask) /* Update status. */ this.evaluation.currentTask!!.prepare() diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AbstractTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AbstractTaskScorer.kt index 322d793fa..c3d80c83a 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AbstractTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AbstractTaskScorer.kt @@ -4,6 +4,7 @@ import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.filter import kotlinx.dnq.query.mapDistinct @@ -13,14 +14,17 @@ import kotlinx.dnq.query.mapDistinct * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractTaskScorer(override val scoreable: Scoreable): TaskScorer { +abstract class AbstractTaskScorer( + override val scoreable: Scoreable, + private val store: TransientEntityStore? //nullable for unit tests + ): TaskScorer { /** * */ - override fun scoreMap(): Map { + override fun scoreMap(): Map = this.store!!.transactional (true) { val sequence = DbAnswerSet.filter { (it.task.id eq this@AbstractTaskScorer.scoreable.taskId) }.mapDistinct { it.submission }.asSequence() - return this.calculateScores(sequence) + this.calculateScores(sequence) } /** diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt index e98e3311c..5ddbce02e 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/AvsTaskScorer.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable +import jetbrains.exodus.database.TransientEntityStore import java.lang.Double.max import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -17,19 +18,20 @@ import kotlin.math.abs * @author Luca Rossetto * @version 2.0.0 */ -class AvsTaskScorer(scoreable: Scoreable, private val penaltyConstant: Double, private val maxPointsPerTask: Double) : AbstractTaskScorer(scoreable) { +class AvsTaskScorer(scoreable: Scoreable, private val penaltyConstant: Double, private val maxPointsPerTask: Double, store: TransientEntityStore?) : AbstractTaskScorer(scoreable, store) { private var lastScores: Map = emptyMap() private val lastScoresLock = ReentrantReadWriteLock() - constructor(context: Scoreable, parameters: Map) : this( + constructor(context: Scoreable, parameters: Map, store: TransientEntityStore?) : this( context, abs(parameters.getOrDefault("penalty", "$defaultPenalty").toDoubleOrNull() ?: defaultPenalty), - parameters.getOrDefault("maxPointsPerTask", "$defaultMaxPointsPerTask").toDoubleOrNull() ?: defaultMaxPointsPerTask + parameters.getOrDefault("maxPointsPerTask", "$defaultMaxPointsPerTask").toDoubleOrNull() ?: defaultMaxPointsPerTask, + store ) - constructor(context: Scoreable) : this(context, defaultPenalty, defaultMaxPointsPerTask) + constructor(context: Scoreable, store: TransientEntityStore?) : this(context, defaultPenalty, defaultMaxPointsPerTask, store) companion object { const val defaultPenalty: Double = 0.2 diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt index 2809f5e6d..2c31bcd19 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/CachingTaskScorer.kt @@ -45,7 +45,7 @@ class CachingTaskScorer(private val wrapped: TaskScorer): TaskScorer { this.latest.putAll(this.wrapped.scoreMap()) this.dirty = false } - return Collections.unmodifiableMap(this.latest) + return this.latest } finally { this.lock.unlock(stamp) } diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt index cc6fa7e33..f5bb427fa 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/InferredAveragePrecisionScorer.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.submissions.Submission import dev.dres.data.model.submissions.VerdictStatus import dev.dres.data.model.template.team.TeamId import dev.dres.run.score.Scoreable +import jetbrains.exodus.database.TransientEntityStore /** @@ -15,7 +16,7 @@ import dev.dres.run.score.Scoreable * @author Luca Rossetto * @version 1.0.0 */ -class InferredAveragePrecisionScorer( scoreable: Scoreable) : AbstractTaskScorer(scoreable) { +class InferredAveragePrecisionScorer( scoreable: Scoreable, store: TransientEntityStore?) : AbstractTaskScorer(scoreable, store) { companion object { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt index 7e82d5018..ab5bd534f 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/KisTaskScorer.kt @@ -5,20 +5,23 @@ import dev.dres.data.model.submissions.Submission import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.VerdictStatus import dev.dres.run.score.Scoreable +import jetbrains.exodus.database.TransientEntityStore import kotlin.math.max class KisTaskScorer( scoreable: Scoreable, private val maxPointsPerTask: Double = defaultmaxPointsPerTask, private val maxPointsAtTaskEnd: Double = defaultmaxPointsAtTaskEnd, - private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission -) : AbstractTaskScorer(scoreable) { + private val penaltyPerWrongSubmission: Double = defaultpenaltyPerWrongSubmission, + store: TransientEntityStore? +) : AbstractTaskScorer(scoreable, store) { - constructor(run: TaskRun, parameters: Map) : this( + constructor(run: TaskRun, parameters: Map, store: TransientEntityStore?) : this( run, parameters.getOrDefault("maxPointsPerTask", "$defaultmaxPointsPerTask").toDoubleOrNull() ?: defaultmaxPointsPerTask, parameters.getOrDefault("maxPointsAtTaskEnd", "$defaultmaxPointsAtTaskEnd").toDoubleOrNull() ?: defaultmaxPointsAtTaskEnd, - parameters.getOrDefault("penaltyPerWrongSubmission", "$defaultpenaltyPerWrongSubmission").toDoubleOrNull() ?: defaultpenaltyPerWrongSubmission + parameters.getOrDefault("penaltyPerWrongSubmission", "$defaultpenaltyPerWrongSubmission").toDoubleOrNull() ?: defaultpenaltyPerWrongSubmission, + store ) companion object { diff --git a/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt b/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt index 258d60d2a..f97776ed8 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scorer/LegacyAvsTaskScorer.kt @@ -5,6 +5,7 @@ import dev.dres.data.model.media.MediaItemType import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.* import dev.dres.run.score.Scoreable +import jetbrains.exodus.database.TransientEntityStore /** * A [TeamTaskScorer] used for AVS tasks. @@ -12,7 +13,7 @@ import dev.dres.run.score.Scoreable * @author Luca Rossetto * @version 2.0.0 */ -class LegacyAvsTaskScorer(scoreable: Scoreable) : AbstractTaskScorer(scoreable) { +class LegacyAvsTaskScorer(scoreable: Scoreable, store: TransientEntityStore?) : AbstractTaskScorer(scoreable, store) { /** * Computes and returns the scores for this [LegacyAvsTaskScorer] based on a [Sequence] of [Submission]s. diff --git a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt index 9f41b916e..5c78613a9 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/AvsTaskScorerTest.kt @@ -70,7 +70,7 @@ class AvsTaskScorerTest { @BeforeEach fun setup() { - this.scorer = AvsTaskScorer(this.scoreable, penaltyConstant = penalty, maxPointsPerTask) + this.scorer = AvsTaskScorer(this.scoreable, penaltyConstant = penalty, maxPointsPerTask, null) } @Test diff --git a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt index 0aab1bf92..9b9c8f9e8 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/KisTaskScorerTest.kt @@ -61,7 +61,7 @@ class KisTaskScorerTest { @BeforeEach fun setup() { - this.scorer = KisTaskScorer(this.scoreable, maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission) + this.scorer = KisTaskScorer(this.scoreable, maxPointsPerTask, maxPointsAtTaskEnd, penaltyPerWrongSubmission, null) } @Test diff --git a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt index e17db3041..b122e2d75 100644 --- a/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt +++ b/backend/src/test/kotlin/dres/run/score/scorer/LegacyAvsTaskScorerTest.kt @@ -85,7 +85,7 @@ class LegacyAvsTaskScorerTest { @BeforeEach fun setup() { - this.scorer = LegacyAvsTaskScorer(this.scoreable) + this.scorer = LegacyAvsTaskScorer(this.scoreable, null) } @Test From 27ecefef408cd728ad526f66503107963f43b521 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 19 Aug 2023 16:40:24 +0200 Subject: [PATCH 450/498] Fixed some issues related to websocket removal and made naming of ids more consistent --- .../admin/ListSubmissionsHandler.kt | 3 +- .../evaluation/viewer/GetTaskHintHandler.kt | 7 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 2 +- .../types/evaluation/ApiEvaluationOverview.kt | 5 +- .../types/template/tasks/ApiTaskTemplate.kt | 4 +- .../dev/dres/data/model/run/AbstractTask.kt | 5 +- .../run/InteractiveAsynchronousEvaluation.kt | 2 +- .../run/InteractiveSynchronousEvaluation.kt | 25 ++-- .../model/run/NonInteractiveEvaluation.kt | 4 +- .../model/run/interfaces/EvaluationRun.kt | 2 +- .../dres/data/model/run/interfaces/TaskRun.kt | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 12 +- .../run/InteractiveSynchronousRunManager.kt | 73 +++++------ .../dev/dres/run/NonInteractiveRunManager.kt | 9 +- .../main/kotlin/dev/dres/run/RunManager.kt | 5 +- .../scoreboard/MaxNormalizingScoreBoard.kt | 2 +- .../src/app/run/run-admin-view.component.html | 4 +- .../src/app/run/run-admin-view.component.scss | 5 +- .../src/app/viewer/run-viewer.component.ts | 123 +----------------- 19 files changed, 88 insertions(+), 206 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index 3630b35fb..bae514019 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -8,7 +8,6 @@ import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore /** * @@ -46,7 +45,7 @@ class ListSubmissionsHandler : AbstractEvaluationAdminHandler(), val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) val rac = ctx.runActionContext() - return evaluationManager.tasks(rac).filter { it.templateId == templateId }.map { + return evaluationManager.tasks(rac).filter { it.taskTemplateId == templateId }.map { ApiSubmissionInfo(evaluationId, it.taskId, it.getDbSubmissions().map { sub -> sub.toApi() }.toList()) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 7b9469b67..1a1a97f93 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -25,6 +25,7 @@ import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.eq +import kotlinx.dnq.query.filter import kotlinx.dnq.query.firstOrNull import kotlinx.dnq.query.query import java.io.FileNotFoundException @@ -105,6 +106,8 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca } catch (ioe: IOException) { throw ErrorStatusException(500, "Exception when reading query object cache file.", ctx) } + + } } @@ -230,8 +233,8 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca private fun ApiHint.toContentElement(): ApiContentElement { //TODO find a better place for this lookup - val item = this.mediaItem?.let { - DbMediaItem.query(DbMediaItem::mediaItemId eq it).firstOrNull() + val item = this.mediaItem?.let {itemId -> + DbMediaItem.filter { it.mediaItemId eq itemId }.firstOrNull() } val range = if (item?.fps != null) { this.range?.toTemporalRange(item.fps!!) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 12f593f8c..54d6ac20c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -112,7 +112,7 @@ class GetTaskTargetHandler(private val store: TransientEntityStore, private val private fun ApiTaskTemplate.toTaskTarget(): ApiTargetContent { //TODO there must be a better way to do this var cummulativeOffset = 0L - val sequence = DbTaskTemplate.query(DbTaskTemplate::templateId eq this.id).firstOrNull()?.targets?.asSequence()?.flatMap { + val sequence = DbTaskTemplate.filter{it.templateId eq this@toTaskTarget.id}.firstOrNull()?.targets?.asSequence()?.flatMap { cummulativeOffset += Math.floorDiv(it.item?.durationMs ?: 10000L, 1000L) + 1L listOf( it.toQueryContentElement(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt index b7a86d97e..cb93b299f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationOverview.kt @@ -1,20 +1,19 @@ package dev.dres.api.rest.types.evaluation import dev.dres.run.* -import kotlinx.dnq.query.asSequence data class ApiEvaluationOverview(val state: RunManagerStatus, val teamOverviews: List) { companion object { fun of(manager: InteractiveRunManager): ApiEvaluationOverview { val teamOverviews = when (manager) { is InteractiveSynchronousRunManager -> { - val overview = manager.evaluation.tasks.asSequence().map { ApiTaskOverview(it) }.toList() + val overview = manager.evaluation.taskRuns.asSequence().map { ApiTaskOverview(it) }.toList() manager.template.teams.asSequence().map { ApiTeamTaskOverview(it.teamId, overview) }.toList() } is InteractiveAsynchronousRunManager -> { - manager.evaluation.tasks.groupBy { it.teamId }.map { (teamId, tasks) -> + manager.evaluation.taskRuns.groupBy { it.teamId }.map { (teamId, tasks) -> val overview = tasks.map { ApiTaskOverview(it) } ApiTeamTaskOverview(teamId, overview) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt index 213f1efee..04855c94e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt @@ -2,7 +2,7 @@ package dev.dres.api.rest.types.template.tasks import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.CollectionId -import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.task.TaskTemplateId /** * The RESTful API equivalent for [DbTaskTemplate]. @@ -12,7 +12,7 @@ import dev.dres.data.model.template.TemplateId * @version 2.0.0 */ data class ApiTaskTemplate( - val id: TemplateId? = null, + val id: TaskTemplateId? = null, val name: String, val taskGroup: String, val taskType: String, diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index 6beb3c340..e2f7ca544 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -7,6 +7,7 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.team.TeamAggregatorImpl import dev.dres.data.model.template.team.TeamGroupId import dev.dres.data.model.template.team.TeamId @@ -47,11 +48,11 @@ abstract class AbstractTask(protected val store: TransientEntityStore, task: DbT final override val taskId: TaskId = this.store.transactional(true) { task.taskId } /** - * The [TemplateId] of this [AbstractTask]. + * The [TaskTemplateId] of this [AbstractTask]. * * Since this cannot change during the lifetime of an evaluation, it is kept in memory. */ - final override val templateId: TemplateId = this.store.transactional(true) { task.template.templateId } + final override val taskTemplateId: TaskTemplateId = this.store.transactional(true) { task.template.templateId } /** * Reference to the [ApiTaskTemplate] describing this [AbstractTask]. diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index d03fdcd0d..e38fb13ad 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -48,7 +48,7 @@ class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: } /** A [List] of all active [IATaskRun]s.*/ - override val tasks: List + override val taskRuns: List get() = this.tasksMap.values.flatten() /** A [ConcurrentHashMap] that maps a list of [IATaskRun]s to the [TeamId]s they belong to.*/ diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 6f863e7cb..110f23f3b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -47,14 +47,14 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: } /** List of [TaskRun]s registered for this [InteractiveSynchronousEvaluation]. */ - override val tasks = LinkedList() + override val taskRuns = LinkedList() - private val templates = this.template.tasks + private val apiTaskTemplates = this.template.tasks /** Returns the last [TaskRun]. */ - val currentTask: AbstractInteractiveTask? - get() = this.tasks.lastOrNull { it.templateId == this.templates[this.templateIndex].id } + val currentTaskRun: AbstractInteractiveTask? + get() = this.taskRuns.lastOrNull { it.taskTemplateId == this.apiTaskTemplates[this.templateIndex].id } /** The index of the task template this [InteractiveSynchronousEvaluation] is pointing to. */ var templateIndex: Int = 0 @@ -74,13 +74,6 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: }.toList() } - /** - * Returns the [TemplateId] this [InteractiveSynchronousEvaluation] is currently pointing to. - * - * @return [TemplateId] - */ - fun getCurrentTemplateId(): TemplateId = this.currentTask?.templateId!! - /** * Returns the [ApiTaskTemplate] this [InteractiveSynchronousEvaluation] is currently pointing to. * @@ -88,7 +81,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: * * @return [ApiTaskTemplate] */ - fun getCurrentTemplate(): ApiTaskTemplate = this.templates[this.templateIndex] + fun getCurrentTaskTemplate(): ApiTaskTemplate = this.apiTaskTemplates[this.templateIndex] /** * Moves this [InteractiveSynchronousEvaluation] to the given task index. @@ -97,7 +90,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: */ fun goTo(index: Int) { if (index < 0) throw IndexOutOfBoundsException("The template index must be greater or equal to zero.") - if (index >= this.templates.size) throw IndexOutOfBoundsException("The template index cannot exceed the number of templates.") + if (index >= this.apiTaskTemplates.size) throw IndexOutOfBoundsException("The template index cannot exceed the number of templates.") this.templateIndex = index } @@ -115,7 +108,7 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: /** The position of this [DbTask] within the [InteractiveSynchronousEvaluation]. */ override val position: Int - get() = this@InteractiveSynchronousEvaluation.tasks.indexOf(this) + get() = this@InteractiveSynchronousEvaluation.taskRuns.indexOf(this) /** The [SubmissionFilter] instance used by this [ISTaskRun]. */ override val filter: SubmissionFilter @@ -134,10 +127,10 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: init { - check(this@InteractiveSynchronousEvaluation.tasks.isEmpty() || this@InteractiveSynchronousEvaluation.tasks.last().hasEnded) { + check(this@InteractiveSynchronousEvaluation.taskRuns.isEmpty() || this@InteractiveSynchronousEvaluation.taskRuns.last().hasEnded) { "Cannot create a new task. Another task is currently running." } - (this@InteractiveSynchronousEvaluation.tasks).add(this) + (this@InteractiveSynchronousEvaluation.taskRuns).add(this) /* Initialize submission filter. */ this.filter = store.transactional { diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt index 8425eb014..60a7302c3 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/NonInteractiveEvaluation.kt @@ -41,7 +41,7 @@ class NonInteractiveEvaluation(store: TransientEntityStore, evaluation: DbEvalua } /** List of [TaskRun]s registered for this [NonInteractiveEvaluation]. */ - override val tasks = this.dbEvaluation.tasks.asSequence().map { + override val taskRuns = this.dbEvaluation.tasks.asSequence().map { NITaskRun(it) }.toList() @@ -65,7 +65,7 @@ class NonInteractiveEvaluation(store: TransientEntityStore, evaluation: DbEvalua /** The position of this [NITaskRun] within the [NonInteractiveEvaluation]. */ override val position: Int - get() = this@NonInteractiveEvaluation.tasks.indexOf(this) + get() = this@NonInteractiveEvaluation.taskRuns.indexOf(this) /** The [CachingTaskScorer] instance used by this [NITaskRun]. */ override val scorer: CachingTaskScorer = store.transactional { CachingTaskScorer( diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt index 3ac117728..15e7ef4ad 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/EvaluationRun.kt @@ -21,7 +21,7 @@ interface EvaluationRun: Run { val template: ApiEvaluationTemplate /** Collection of [TaskRun]s that make up this [EvaluationRun]. */ - val tasks: List + val taskRuns: List /** Flag indicating that participants can also use the viewer for this [EvaluationRun]. */ val participantCanView: Boolean diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index a5abb6231..064ca0b02 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -25,7 +25,7 @@ interface TaskRun: Run, Scoreable { override val teams: List /** The unique [TaskTemplateId] that identifies the task template underpinning [TaskRun]. */ - val templateId: TaskTemplateId + val taskTemplateId: TaskTemplateId /** The current [ApiTaskStatus] of this [TaskRun]. This is typically a transient property. */ val status: ApiTaskStatus diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 51d7602af..1f7baa803 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -107,7 +107,7 @@ class InteractiveAsynchronousRunManager( /** Returns list [JudgementValidator]s associated with this [InteractiveAsynchronousRunManager]. May be empty! */ override val judgementValidators: List - get() = this.evaluation.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } + get() = this.evaluation.taskRuns.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } /** The list of [Scoreboard]s maintained by this [InteractiveAsynchronousEvaluation]. */ override val scoreboards: List @@ -132,7 +132,7 @@ class InteractiveAsynchronousRunManager( } /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ - this.evaluation.tasks.forEach { task -> + this.evaluation.taskRuns.forEach { task -> task.getDbSubmissions().forEach { sub -> for (answerSet in sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }) { task.validator.validate(answerSet) @@ -410,7 +410,7 @@ class InteractiveAsynchronousRunManager( * @return [List] of [AbstractInteractiveTask]s */ override fun taskCount(context: RunActionContext): Int { - if (context.isAdmin) return this.evaluation.tasks.size + if (context.isAdmin) return this.evaluation.taskRuns.size val teamId = context.teamId() return this.evaluation.tasksForTeam(teamId).size } @@ -424,7 +424,7 @@ class InteractiveAsynchronousRunManager( * @return [List] of [AbstractInteractiveTask]s */ override fun tasks(context: RunActionContext): List { - if (context.isAdmin) return this.evaluation.tasks + if (context.isAdmin) return this.evaluation.taskRuns val teamId = context.teamId() return this.evaluation.tasksForTeam(teamId) } @@ -457,7 +457,7 @@ class InteractiveAsynchronousRunManager( * @return List of [DbSubmission]s. */ override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { - this.evaluation.tasks.flatMap { it.getDbSubmissions() } + this.evaluation.taskRuns.flatMap { it.getDbSubmissions() } } /** @@ -557,7 +557,7 @@ class InteractiveAsynchronousRunManager( } override fun reScore(taskId: TaskId) { - val task = evaluation.tasks.find { it.taskId == taskId } + val task = evaluation.taskRuns.find { it.taskId == taskId } task?.scorer?.invalidate() } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 11f1d56fe..a6cbc9b4f 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -9,12 +9,12 @@ import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.api.rest.types.template.tasks.options.ApiSubmissionOption import dev.dres.api.rest.types.template.tasks.options.ApiTaskOption -import dev.dres.data.model.admin.DbUser import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.data.model.template.task.DbTaskTemplate +import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.task.options.Defaults.VIEWER_TIMEOUT_DEFAULT @@ -81,7 +81,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch /** Returns list [JudgementValidator]s associated with this [InteractiveSynchronousRunManager]. May be empty*/ override val judgementValidators: List - get() = this.evaluation.tasks.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } + get() = this.evaluation.taskRuns.mapNotNull { if (it.hasStarted && it.validator is JudgementValidator) it.validator else null } /** List of [Scoreboard]s for this [InteractiveSynchronousRunManager]. */ override val scoreboards: List @@ -106,14 +106,14 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.registerOptionalUpdatables() /* End ongoing tasks upon initialization (in case server crashed during task execution). */ - for (task in this.evaluation.tasks) { + for (task in this.evaluation.taskRuns) { if (task.isRunning || task.status == ApiTaskStatus.RUNNING) { task.end() } } /** Trigger score updates and re-enqueue pending submissions for judgement (if any). */ - this.evaluation.tasks.forEach { task -> + this.evaluation.taskRuns.forEach { task -> task.getDbSubmissions().forEach { sub -> sub.answerSets.filter { v -> v.status eq DbVerdictStatus.INDETERMINATE }.asSequence().forEach { task.validator.validate(it) @@ -175,7 +175,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ override fun currentTaskTemplate(context: RunActionContext): ApiTaskTemplate = this.stateLock.write { checkStatus(RunManagerStatus.CREATED, RunManagerStatus.ACTIVE) - this.evaluation.getCurrentTemplate() + this.evaluation.getCurrentTaskTemplate() } override fun previous(context: RunActionContext): Boolean = this.stateLock.write { @@ -203,7 +203,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch override fun goTo(context: RunActionContext, index: Int) { checkStatus(RunManagerStatus.ACTIVE) assureNoRunningTask() - this.evaluation.tasks.any { it.status == ApiTaskStatus.RUNNING } + this.evaluation.taskRuns.any { it.status == ApiTaskStatus.RUNNING } if (index >= 0 && index < this.template.tasks.size) { /* Update active task. */ this.evaluation.goTo(index) @@ -222,7 +222,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch checkContext(context) val currentTaskTemplate = this.currentTaskTemplate(context) - if (!this.evaluation.allowRepeatedTasks && this.evaluation.tasks.any { it.template.id == currentTaskTemplate.id }) { + if (!this.evaluation.allowRepeatedTasks && this.evaluation.taskRuns.any { it.template.id == currentTaskTemplate.id }) { throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used.") } @@ -246,7 +246,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.evaluation.ISTaskRun(dbTask) /* Update status. */ - this.evaluation.currentTask!!.prepare() + this.evaluation.currentTaskRun!!.prepare() /* Reset the ReadyLatch. */ this.readyLatch.reset(VIEWER_TIMEOUT_DEFAULT) @@ -255,9 +255,9 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch // /* Enqueue WS message for sending */ // RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) - LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.getCurrentTemplateId()}.") + LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.currentTaskRun?.taskId} with task template ${this.evaluation.getCurrentTaskTemplate().id}.") - this.evaluation.currentTask!!.taskId + this.evaluation.currentTaskRun!!.taskId } override fun abortTask(context: RunActionContext) = this.stateLock.write { @@ -272,11 +272,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch // /* Enqueue WS message for sending */ // RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_END, this.currentTask(context)?.taskId)) - LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.getCurrentTemplateId()}.") + LOGGER.info("SynchronousRunManager ${this.id} aborted task ${this.evaluation.currentTaskRun?.taskId} with task template ${this.evaluation.getCurrentTaskTemplate().id}.") } /** List of [DbTask] for this [InteractiveSynchronousRunManager]. */ - override fun tasks(context: RunActionContext): List = this.evaluation.tasks + override fun tasks(context: RunActionContext): List = this.evaluation.taskRuns /** * Returns the currently active [DbTask]s or null, if no such task is active. @@ -285,11 +285,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return [DbTask] or null */ override fun currentTask(context: RunActionContext) = this.stateLock.read { - this.evaluation.currentTask - when (this.evaluation.currentTask?.status) { + when (this.evaluation.currentTaskRun?.status) { ApiTaskStatus.PREPARING, ApiTaskStatus.RUNNING, - ApiTaskStatus.ENDED -> this.evaluation.currentTask + ApiTaskStatus.ENDED -> this.evaluation.currentTaskRun else -> null } } @@ -300,7 +299,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @param taskId The [EvaluationId] of the [TaskRun]. */ override fun taskForId(context: RunActionContext, taskId: EvaluationId) = - this.evaluation.tasks.find { it.taskId == taskId } + this.evaluation.taskRuns.find { it.taskId == taskId } /** * List of all [DbSubmission]s for this [InteractiveAsynchronousRunManager], irrespective of the [DbTask] it belongs to. @@ -309,7 +308,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return List of [DbSubmission]s. */ override fun allSubmissions(context: RunActionContext): List = this.stateLock.read { - this.evaluation.tasks.flatMap { it.getDbSubmissions() } + this.evaluation.taskRuns.flatMap { it.getDbSubmissions() } } /** @@ -327,7 +326,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * * @return The number of [DbTask]s held by this [RunManager] */ - override fun taskCount(context: RunActionContext): Int = this.evaluation.tasks.size + override fun taskCount(context: RunActionContext): Int = this.evaluation.taskRuns.size /** * Adjusts the duration of the current [DbTask] by the specified amount. Amount can be either positive or negative. @@ -360,7 +359,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - return if (this.evaluation.currentTask?.status == ApiTaskStatus.RUNNING) { + return if (this.evaluation.currentTaskRun?.status == ApiTaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") max( @@ -379,7 +378,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeElapsed(context: RunActionContext): Long = this.stateLock.read { - return if (this.evaluation.currentTask?.status == ApiTaskStatus.RUNNING) { + return if (this.evaluation.currentTaskRun?.status == ApiTaskStatus.RUNNING) { val currentTaskRun = this.currentTask(context) ?: throw IllegalStateException("SynchronizedRunManager is in status ${this.status} but has no active TaskRun. This is a serious error!") System.currentTimeMillis() - (currentTaskRun.started!! + InteractiveRunManager.COUNTDOWN_DURATION) @@ -445,23 +444,23 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch // return true // } - override fun viewerPreparing(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + override fun viewerPreparing(taskTemplateId: TaskTemplateId, rac: RunActionContext, viewerInfo: ViewerInfo) { - val currentTaskId = this.currentTask(rac)?.taskId + val currentTemplateId = this.evaluation.getCurrentTaskTemplate().id - if (taskId == currentTaskId) { + if (taskTemplateId == currentTemplateId) { this.readyLatch.register(viewerInfo) } } - override fun viewerReady(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + override fun viewerReady(taskTemplateId: TaskTemplateId, rac: RunActionContext, viewerInfo: ViewerInfo) { - val currentTaskId = this.currentTask(rac)?.taskId + val currentTemplateId = this.evaluation.getCurrentTaskTemplate().id - if (taskId == currentTaskId) { + if (taskTemplateId == currentTemplateId) { this.store.transactional(true) { - if (this.evaluation.currentTask?.status == ApiTaskStatus.PREPARING) { + if (this.evaluation.currentTaskRun?.status == ApiTaskStatus.PREPARING) { this.readyLatch.setReady(viewerInfo) } } @@ -548,7 +547,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * */ override fun reScore(taskId: TaskId) { - val task = evaluation.tasks.find { it.taskId == taskId } + val task = evaluation.taskRuns.find { it.taskId == taskId } task?.scorer?.invalidate() } @@ -625,7 +624,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun invokeUpdatables() { val runStatus = this.status - val taskStatus = this.evaluation.currentTask?.status + val taskStatus = this.evaluation.currentTaskRun?.status this.updatables.forEach { if (it.shouldBeUpdated(runStatus, taskStatus)) { it.update(runStatus, taskStatus) @@ -639,10 +638,10 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch */ private fun internalStateUpdate() { /** Case 1: Facilitates internal transition from RunManagerStatus.PREPARING_TASK to RunManagerStatus.RUNNING_TASK. */ - if (this.evaluation.currentTask?.status == ApiTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { + if (this.evaluation.currentTaskRun?.status == ApiTaskStatus.PREPARING && this.readyLatch.allReadyOrTimedOut()) { this.stateLock.write { - this.evaluation.currentTask!!.start() - AuditLogger.taskStart(this.id, this.evaluation.currentTask!!.taskId, this.evaluation.getCurrentTemplate(), AuditLogSource.INTERNAL, null) + this.evaluation.currentTaskRun!!.start() + AuditLogger.taskStart(this.id, this.evaluation.currentTaskRun!!.taskId, this.evaluation.getCurrentTaskTemplate(), AuditLogSource.INTERNAL, null) } // /* Enqueue WS message for sending */ @@ -650,13 +649,13 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** Case 2: Facilitates internal transition from RunManagerStatus.RUNNING_TASK to RunManagerStatus.TASK_ENDED due to timeout. */ - if (this.evaluation.currentTask?.status == ApiTaskStatus.RUNNING) { + if (this.evaluation.currentTaskRun?.status == ApiTaskStatus.RUNNING) { this.stateLock.write { - val task = this.evaluation.currentTask!! + val task = this.evaluation.currentTaskRun!! val timeLeft = max(0L, task.duration * 1000L - (System.currentTimeMillis() - task.started!!) + InteractiveRunManager.COUNTDOWN_DURATION) if (timeLeft <= 0) { task.end() - AuditLogger.taskEnd(this.id, this.evaluation.currentTask!!.taskId, AuditLogSource.INTERNAL, null) + AuditLogger.taskEnd(this.id, this.evaluation.currentTaskRun!!.taskId, AuditLogSource.INTERNAL, null) EventStreamProcessor.event(TaskEndEvent(this.id, task.taskId)) // /* Enqueue WS message for sending */ @@ -698,7 +697,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @throws IllegalArgumentException If task is neither preparing nor running. */ private fun assertTaskPreparingOrRunning() { - val status = this.evaluation.currentTask?.status + val status = this.evaluation.currentTaskRun?.status if (status != ApiTaskStatus.RUNNING && status != ApiTaskStatus.PREPARING) throw IllegalStateException("Task is neither preparing nor running.") } @@ -708,7 +707,7 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch * @throws IllegalArgumentException If task is neither preparing nor running. */ private fun assureNoRunningTask() { - if (this.evaluation.tasks.any { it.status == ApiTaskStatus.RUNNING }) throw IllegalStateException("Task is already running!") + if (this.evaluation.taskRuns.any { it.status == ApiTaskStatus.RUNNING }) throw IllegalStateException("Task is already running!") } /** diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 70e5fca44..ef2c8cfa1 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -3,7 +3,6 @@ package dev.dres.run import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.data.model.run.* -import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskId import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator @@ -56,7 +55,7 @@ class NonInteractiveRunManager( /** */ override val judgementValidators: List - get() = this.evaluation.tasks.map { it.validator }.filterIsInstance(JudgementValidator::class.java) + get() = this.evaluation.taskRuns.map { it.validator }.filterIsInstance(JudgementValidator::class.java) override fun start(context: RunActionContext) { check(this.status == RunManagerStatus.CREATED) { "NonInteractiveRunManager is in status ${this.status} and cannot be started." } @@ -90,7 +89,7 @@ class NonInteractiveRunManager( TODO("Not yet implemented") } - override fun taskCount(context: RunActionContext): Int = this.evaluation.tasks.size + override fun taskCount(context: RunActionContext): Int = this.evaluation.taskRuns.size private val viewerMap: MutableMap = mutableMapOf() @@ -141,9 +140,9 @@ class NonInteractiveRunManager( /** * */ - override fun tasks(context: RunActionContext): List = this.evaluation.tasks + override fun tasks(context: RunActionContext): List = this.evaluation.taskRuns - private val taskMap = this.evaluation.tasks.associateBy { it.taskId } + private val taskMap = this.evaluation.taskRuns.associateBy { it.taskId } /** * diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 03b47271c..5bdefdfb6 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -11,6 +11,7 @@ import dev.dres.data.model.run.interfaces.EvaluationRun import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* +import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.run.score.scoreboard.Scoreboard import dev.dres.run.validation.interfaces.JudgementValidator import jetbrains.exodus.database.TransientEntityStore @@ -128,9 +129,9 @@ interface RunManager : Runnable { */ fun viewers(): Map - fun viewerPreparing(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) + fun viewerPreparing(taskTemplateId: TaskTemplateId, rac: RunActionContext, viewerInfo: ViewerInfo) - fun viewerReady(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) + fun viewerReady(taskTemplateId: TaskTemplateId, rac: RunActionContext, viewerInfo: ViewerInfo) /** diff --git a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt index 6bf56fd58..996bd3ca8 100644 --- a/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt +++ b/backend/src/main/kotlin/dev/dres/run/score/scoreboard/MaxNormalizingScoreBoard.kt @@ -86,7 +86,7 @@ class MaxNormalizingScoreBoard(override val name: String, override val run: Eval * Internal function; calculates the overall score per team. */ private fun recalculate() { - val scorers = this.run.tasks.filter { this.taskFilter(it.template) && (it.started != null) }.map { it.scorer } + val scorers = this.run.taskRuns.filter { this.taskFilter(it.template) && (it.started != null) }.map { it.scorer } val scores = scorers.associate { scorer -> scorer.scoreable.taskId to scorer.scores().groupBy { s -> s.first }.mapValues { it.value.maxByOrNull { s -> s.third }?.third ?: 0.0 diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index dec0cfceb..e2c929bfc 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -18,10 +18,10 @@ {{viewer.ready ? 'link' : 'link_off'}}
    - {{ viewer.viewersId }} + {{ viewer.username }}
    - User: {{ viewer.username }}, Address: {{ viewer.host }} + Address: {{ viewer.host }}
    diff --git a/frontend/src/app/run/run-admin-view.component.scss b/frontend/src/app/run/run-admin-view.component.scss index c211b38ed..8f9f62cda 100644 --- a/frontend/src/app/run/run-admin-view.component.scss +++ b/frontend/src/app/run/run-admin-view.component.scss @@ -41,7 +41,10 @@ } .outline-white { - text-shadow: 1px 1px white, 1px -1px white, -1px 1px white, -1px -1px white; + font-weight: bold; + text-shadow: #fff 0px 0px 1px; + -webkit-font-smoothing: antialiased; + } .p4 { diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index 6d4c7d28d..ffe6ea3f5 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -15,19 +15,14 @@ import { tap, withLatestFrom, } from 'rxjs/operators'; -import { webSocket, WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/webSocket'; import { AppConfig } from '../app.config'; -import { IWsMessage } from '../model/ws/ws-message.interface'; -import { IWsServerMessage } from '../model/ws/ws-server-message.interface'; -import { IWsClientMessage } from '../model/ws/ws-client-message.interface'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Position } from './model/run-viewer-position'; import { Widget } from './model/run-viewer-widgets'; import { DOCUMENT } from '@angular/common'; import {Title} from '@angular/platform-browser'; import {ApiEvaluationInfo, ApiEvaluationState, ApiTaskTemplateInfo, EvaluationService} from '../../../openapi'; -import {Overlay, OverlayRef} from "@angular/cdk/overlay"; -import {TemplatePortal} from "@angular/cdk/portal"; +import {Overlay} from "@angular/cdk/overlay"; @Component({ selector: 'app-run-viewer', @@ -35,8 +30,6 @@ import {TemplatePortal} from "@angular/cdk/portal"; styleUrls: ['./run-viewer.component.scss'] }) export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { - /** The WebSocketSubject that represent the WebSocket connection to the DRES endpoint. */ - webSocketSubject: WebSocketSubject; // /** A {@link BehaviorSubject} that reflect the WebSocket connection status. */ // webSocketConnectionSubject: BehaviorSubject = new BehaviorSubject(false); @@ -74,18 +67,6 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /** Observable of the {@link Widget} that should be displayed at the bottom. */ bottomWidget: Observable; - /** Reference to the {@link OverlayRef}. Initialized in ngAfterViewInit(). */ - private overlayRef: OverlayRef = null - - /** Reference to the overlay template. Only available once view has been loaded. */ - @ViewChild('overlayTemplate') - private overlayTemplateRef: TemplateRef; - - /** Reference to the overlay template. Only available once view has been loaded. */ - private overlaySubscription: Subscription; - - /** Internal WebSocket subscription for pinging the server. */ - private pingSubscription: Subscription; /** Cached config */ private p: any; @@ -104,24 +85,6 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { @Inject(DOCUMENT) private document: Document, private _viewContainerRef: ViewContainerRef ) { - /** Initialize basic WebSocketSubject. */ - // const wsurl = this.config.webSocketUrl; - // const connectionSubject = this.webSocketConnectionSubject - // this.webSocketSubject = webSocket({ - // url: wsurl, - // openObserver: { - // next(openEvent) { - // console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} established!`); - // connectionSubject.next(true) - // }, - // }, - // closeObserver: { - // next(closeEvent: CloseEvent) { - // console.log(`[RunViewerComponent] WebSocket connection to ${wsurl} closed: ${closeEvent.reason}.`); - // connectionSubject.next(false) - // }, - // }, - // } as WebSocketSubjectConfig); /** Observable for the current run ID. */ this.evaluationId = this.activeRoute.params.pipe( @@ -178,55 +141,6 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { shareReplay({ bufferSize: 1, refCount: true }) ); - // /* Basic observable for web socket messages received from the DRES server. */ - // this.webSocket = this.evaluationId.pipe( - // mergeMap((evaluationId) => - // this.webSocketSubject.multiplex( - // () => { - // return { evaluationId: evaluationId, type: 'REGISTER' } as IWsClientMessage; - // }, - // () => { - // return { evaluationId: evaluationId, type: 'UNREGISTER' } as IWsClientMessage; - // }, - // (message) => message.evaluationId === evaluationId || message.evaluationId === null - // ) - // .pipe( - // retryWhen((err) => - // err.pipe( - // tap((e) => - // console.error( - // '[RunViewerComponent] An error occurred with the WebSocket communication channel. Trying to reconnect in 1 second.', - // e - // ) - // ), - // delay(5000) - // ) - // ), - // map((m) => m as IWsServerMessage), - // filter((q) => q != null), - // tap((m) => console.log(`[RunViewerComponent] WebSocket message received: ${m.type}`)) - // ) - // ), - // share() - // ); - - // /* - // * Observable for run state info; this information is dynamic and is subject to change over the course of a run. - // * - // * Updates to the RunState are triggered by WebSocket messages received by the viewer. To not overwhelm the server, - // * the RunState is updated every 500ms at most. - // */ - // const wsMessages = this.webSocket.pipe( - // filter((m) => m.type !== 'PING') /* Filter out ping messages. */, - // map((b) => b.evaluationId) - // ); - - // wsMessages.subscribe({next: - // (message) => { - // //console.log("Ws Message: ", message); - // } - // }); - this.runState = interval(1000).pipe(mergeMap(() => this.evaluationId)).pipe( switchMap((id) => this.runService.getApiV2EvaluationByEvaluationIdState(id)), catchError((err, o) => { @@ -278,50 +192,21 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { * Registers this RunViewerComponent on view initialization and creates the WebSocket subscription. */ ngOnInit(): void { - /* Register WebSocket ping. */ - this.pingSubscription = interval(5000) - .pipe( - withLatestFrom(this.evaluationId), - tap(([i, evaluationId]) => this.webSocketSubject.next({ evaluationId: evaluationId, type: 'PING' } as IWsClientMessage)) - ) - .subscribe(); - + } /** * Prepare the overlay that is being displayed when WebSocket connection times out. */ ngAfterViewInit() { - this.overlayRef = this.overlay.create({ - positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(), - scrollStrategy: this.overlay.scrollStrategies.block(), - backdropClass: "overlay", - hasBackdrop: true - }); - - // /* Create subscription for WebSocket connection status and show overlay. */ - // this.overlaySubscription = this.webSocketConnectionSubject.subscribe((status) => { - // if (status == false) { - // if(!this.overlayRef.hasAttached()) { - // this.overlayRef.attach(new TemplatePortal(this.overlayTemplateRef, this._viewContainerRef)); - // } - // } else { - // this.overlayRef.detach() - // } - // }) + + } /** * Unregisters this RunViewerComponent on view destruction and cleans the WebSocket subscription. */ ngOnDestroy(): void { - /* Unregister Ping service. */ - this.pingSubscription.unsubscribe(); - this.pingSubscription = null; - this.overlaySubscription.unsubscribe() - this.overlaySubscription = null - this.overlayRef?.dispose() - this.overlayRef = null this.titleService.setTitle('DRES'); } From 054dca932d8d5715512a7b7bbd47c1a8557452eb Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 19 Aug 2023 19:19:28 +0200 Subject: [PATCH 451/498] Minor cleanup and optimization in helper threads for event stream and audit log --- backend/src/main/kotlin/dev/dres/DRES.kt | 2 + .../dev/dres/data/model/config/Config.kt | 2 - .../dev/dres/run/audit/AuditLogEntry.kt | 5 +- .../kotlin/dev/dres/run/audit/AuditLogger.kt | 72 +++++++++---------- .../run/eventstream/EventStreamProcessor.kt | 49 ++++++------- 5 files changed, 63 insertions(+), 67 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 0a59bc812..21b79de95 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -119,6 +119,8 @@ object DRES { /* Initialize Rest API. */ RestApi.init(CONFIG, store, global) + AuditLogger.startup() + println("Initialization complete!") if (args.isNotEmpty() && args.first() == "openapi") { diff --git a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt index b3f616436..91bcc550a 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/config/Config.kt @@ -32,8 +32,6 @@ data class Config( val eventsLocation: Path = DRES.APPLICATION_ROOT.resolve("events"), /** Location for Audit event log. */ val auditLocation: Path = DRES.APPLICATION_ROOT.resolve("audit"), - /** Event buffer retention time, defaults to 1 minute */ - val eventBufferRetentionTime: Int = 60_000, ) { companion object{ diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt index 5e6726bce..409036079 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogEntry.kt @@ -94,4 +94,7 @@ data class LogoutAuditLogEntry( val userId: UserId, val api: AuditLogSource, val sessionToken: SessionToken -) : AuditLogEntry() \ No newline at end of file +) : AuditLogEntry() + +class StartupAuditLogEntry : AuditLogEntry() +class ShutdownAuditLogEntry : AuditLogEntry() \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt index 52ef79deb..da0992ea5 100644 --- a/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt +++ b/backend/src/main/kotlin/dev/dres/run/audit/AuditLogger.kt @@ -10,7 +10,6 @@ import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.admin.UserId import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.run.interfaces.TaskId import dev.dres.data.model.template.TemplateId import dev.dres.run.eventstream.* @@ -18,69 +17,66 @@ import dev.dres.run.validation.interfaces.JudgementValidator import org.slf4j.LoggerFactory import org.slf4j.Marker import org.slf4j.MarkerFactory -import java.io.File import java.io.FileWriter import java.io.PrintWriter import java.nio.file.Files -import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit import kotlin.concurrent.thread object AuditLogger { - const val FLUSH_INTERVAL = 30_000 //flush every 30 seconds + private const val FLUSH_INTERVAL = 30_000 //flush every 30 seconds private val logMarker: Marker = MarkerFactory.getMarker("AUDIT") private val logger = LoggerFactory.getLogger(this.javaClass) - private val queue = ConcurrentLinkedQueue() + private val queue = LinkedBlockingQueue() private var active = true - private val writerThread = thread( - name = "AuditLogHelperThread", - isDaemon = true, - start = false - ) { - val mapper = jacksonObjectMapper() - Files.createDirectories(DRES.AUDIT_LOG_ROOT) - val auditLogFile = DRES.AUDIT_LOG_ROOT.resolve("audit.jsonl").toFile() - val writer = PrintWriter(FileWriter(auditLogFile, Charsets.UTF_8, true)) - var lastFlush = 0L - while (active || queue.isNotEmpty()) { - - try { - while (queue.isNotEmpty()) { - val logEntry = queue.poll() ?: break + init { + thread( + name = "AuditLogHelperThread", + isDaemon = true, + start = true + ) { + val mapper = jacksonObjectMapper() + Files.createDirectories(DRES.AUDIT_LOG_ROOT) + val auditLogFile = DRES.AUDIT_LOG_ROOT.resolve("audit.jsonl").toFile() + val writer = PrintWriter(FileWriter(auditLogFile, Charsets.UTF_8, true)) + var lastFlush = 0L + while (active || queue.isNotEmpty()) { + + try { + + val logEntry = queue.poll(1, TimeUnit.SECONDS) ?: continue writer.println( mapper.writeValueAsString(logEntry) ) - } - val now = System.currentTimeMillis() + val now = System.currentTimeMillis() - if (now - lastFlush > FLUSH_INTERVAL) { - writer.flush() - lastFlush = now - } + if (now - lastFlush > FLUSH_INTERVAL) { + writer.flush() + lastFlush = now + } - } catch (e: Exception) { - e.printStackTrace() + } catch (e: Exception) { + e.printStackTrace() + } } - } + writer.flush() + writer.close() - writer.flush() - writer.close() - - } - - init { - writerThread.start() + } } fun stop() { + log(ShutdownAuditLogEntry()) active = false } @@ -253,6 +249,10 @@ object AuditLogger { * @param sessionToken The [SessionToken] */ fun logout(userId: UserId, api: AuditLogSource, sessionToken: SessionToken) { + log(LogoutAuditLogEntry(userId, api, sessionToken)) + } + fun startup() { + log(StartupAuditLogEntry()) } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt b/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt index c92fbfcfc..a1d5c3a69 100644 --- a/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt +++ b/backend/src/main/kotlin/dev/dres/run/eventstream/EventStreamProcessor.kt @@ -6,7 +6,8 @@ import dev.dres.utilities.extensions.read import dev.dres.utilities.extensions.write import org.slf4j.LoggerFactory import java.io.PrintWriter -import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit import java.util.concurrent.locks.StampedLock import kotlin.concurrent.thread @@ -21,7 +22,7 @@ object EventStreamProcessor { private val mapper = jacksonObjectMapper() private val LOGGER = LoggerFactory.getLogger(this.javaClass) - private val eventQueue = ConcurrentLinkedQueue() + private val eventQueue = LinkedBlockingQueue() private val eventHandlers = mutableListOf() private val handlerLock = StampedLock() private val eventSink = PrintWriter( @@ -29,9 +30,6 @@ object EventStreamProcessor { .also { it.parentFile.mkdirs() }) - private val eventBufferRetentionTime = DRES.CONFIG.eventBufferRetentionTime - - fun event(event: StreamEvent) = eventQueue.add(event) fun register(vararg handler: StreamEventHandler) = handlerLock.write { eventHandlers.addAll(handler) } @@ -47,43 +45,38 @@ object EventStreamProcessor { while (active) { try { - while (eventQueue.isNotEmpty()) { - val event = eventQueue.poll() ?: break - - handlerLock.read { - for (handler in eventHandlers) { - try { - handler.handleStreamEvent(event) - } catch (t: Throwable) { - LOGGER.error( - "Uncaught exception while handling event $event in ${handler.javaClass.simpleName}", - t - ) - } + val event = eventQueue.poll(1, TimeUnit.SECONDS) ?: continue + + handlerLock.read { + for (handler in eventHandlers) { + try { + handler.handleStreamEvent(event) + } catch (t: Throwable) { + LOGGER.error( + "Uncaught exception while handling event $event in ${handler.javaClass.simpleName}", + t + ) } } + } - try { - eventSink.println( - mapper.writeValueAsString(event) - ) - } catch (t: Throwable) { - LOGGER.error("Error while storing event $event", t) - } + try { + eventSink.println( + mapper.writeValueAsString(event) + ) + } catch (t: Throwable) { + LOGGER.error("Error while storing event $event", t) } } catch (t: Throwable) { LOGGER.error("Uncaught exception in EventStreamProcessor", t) - } finally { - Thread.sleep(100) } if (flushTimer < System.currentTimeMillis()) { eventSink.flush() flushTimer = System.currentTimeMillis() + flushInterval } - } eventSink.flush() From 296a7afccc4569f15118320ff5edc4fec512bc15 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 20 Aug 2023 11:23:47 +0200 Subject: [PATCH 452/498] Removed external transaction contexts from judgement mechanism --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 10 +- .../judgement/AbstractJudgementHandler.kt | 2 +- .../judgement/DequeueJudgementHandler.kt | 32 +++--- .../handler/judgement/DequeueVoteHandler.kt | 36 +++---- .../judgement/JudgementStatusHandler.kt | 7 +- .../handler/judgement/PostJudgementHandler.kt | 33 ++++--- .../rest/handler/judgement/PostVoteHandler.kt | 19 ++-- .../evaluation/submission/ApiAnswerSet.kt | 2 +- .../types/template/tasks/ApiTaskTemplate.kt | 9 +- .../data/model/run/AbstractInteractiveTask.kt | 7 +- .../model/run/AbstractNonInteractiveTask.kt | 2 +- .../data/model/template/task/TaskTemplate.kt | 2 +- .../interfaces/JudgementValidator.kt | 14 ++- .../validation/interfaces/VoteValidator.kt | 12 ++- .../judged/BasicJudgementValidator.kt | 98 ++++++++++--------- .../validation/judged/BasicVoteValidator.kt | 44 +++++---- 16 files changed, 183 insertions(+), 146 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 9282fbff0..bb580174b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -184,11 +184,11 @@ object RestApi { UpdateTeamHandler(store), // Judgement - DequeueJudgementHandler(store), - DequeueVoteHandler(store), - PostJudgementHandler(store), - PostVoteHandler(store), - JudgementStatusHandler(store), + DequeueJudgementHandler(), + DequeueVoteHandler(), + PostJudgementHandler(), + PostVoteHandler(), + JudgementStatusHandler(), // Status CurrentTimeHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt index d6565d143..0170c690c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/AbstractJudgementHandler.kt @@ -19,7 +19,7 @@ import kotlinx.dnq.query.isEmpty * @author Ralph Gasser * @version 1.0 */ -abstract class AbstractJudgementHandler(protected val store: TransientEntityStore): RestHandler, AccessManagedRestHandler { +abstract class AbstractJudgementHandler: RestHandler, AccessManagedRestHandler { /** */ override val permittedRoles: Set = setOf(ApiRole.JUDGE) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt index f5d6601e0..3f122ae3e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueJudgementHandler.kt @@ -17,7 +17,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHandler(store), +class DequeueJudgementHandler : AbstractJudgementHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/judge/next" @@ -43,24 +43,24 @@ class DequeueJudgementHandler(store: TransientEntityStore) : AbstractJudgementHa methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiJudgementRequest { - /* Start transaction. */ - val request = this.store.transactional(false) { - val evaluationManager = ctx.eligibleManagerForId() - checkEligibility(ctx, evaluationManager) - val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: return@transactional null - val next = validator.next() ?: return@transactional null - val taskDescription = next.second.task.template.textualDescription() - return@transactional ApiJudgementRequest( - token = next.first, - validator = validator.id, - taskDescription = taskDescription, - answerSet = next.second.toApi(false) - ) - } - return request ?: throw ErrorStatusException( + return nextRequest(ctx) ?: throw ErrorStatusException( 202, "There is currently no submission awaiting judgement.", ctx ) } + + private fun nextRequest(ctx: Context): ApiJudgementRequest? { + val evaluationManager = ctx.eligibleManagerForId() + checkEligibility(ctx, evaluationManager) + val validator = evaluationManager.judgementValidators.find { it.hasOpen } ?: return null + val next = validator.next() ?: return null + val taskDescription = validator.taskTemplate.textualDescription() + return ApiJudgementRequest( + token = next.first, + validator = validator.id, + taskDescription = taskDescription, + answerSet = next.second + ) + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt index 23af070fb..91374baab 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/DequeueVoteHandler.kt @@ -23,7 +23,7 @@ import kotlinx.dnq.query.firstOrNull * @author Ralph Gasser * @version 1.0.0 */ -class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(store), GetRestHandler { +class DequeueVoteHandler: AbstractJudgementHandler(), GetRestHandler { override val route = "evaluation/{evaluationId}/vote/next" @OpenApi( @@ -42,25 +42,27 @@ class DequeueVoteHandler(store: TransientEntityStore): AbstractJudgementHandler( methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiJudgementRequest { - val request = this.store.transactional(false) {//TODO needs adjustment to deal with answerSets - val evaluationManager = ctx.eligibleManagerForId() - - val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: return@transactional null - val next = validator.next() - ?: /* No submission awaiting judgement */ - return@transactional null - val taskDescription = next.second.task.template.textualDescription() - return@transactional ApiJudgementRequest( - token = next.first, - validator = validator.id, - taskDescription = taskDescription, - answerSet = next.second.toApi(false) - ) - } - return request ?: throw ErrorStatusException( + return nextRequest(ctx) ?: throw ErrorStatusException( 202, "There is currently no submission awaiting judgement.", ctx ) } + + fun nextRequest(ctx: Context): ApiJudgementRequest? { //TODO needs adjustment to deal with answerSets + val evaluationManager = ctx.eligibleManagerForId() + + val validator = evaluationManager.judgementValidators.filterIsInstance().find { it.isActive } ?: return null + val next = validator.next() + ?: /* No submission awaiting judgement */ + return null + + val taskDescription = validator.taskTemplate.textualDescription() + return ApiJudgementRequest( + token = next.first, + validator = validator.id, + taskDescription = taskDescription, + answerSet = next.second + ) + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt index 2a233249e..0ea2386e6 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/JudgementStatusHandler.kt @@ -8,7 +8,6 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.run.RunManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore /** @@ -17,7 +16,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHandler(store), GetRestHandler> { +class JudgementStatusHandler: AbstractJudgementHandler(), GetRestHandler> { override val permittedRoles = setOf(ApiRole.VIEWER) override val route = "evaluation/{evaluationId}/judge/status" override val apiVersion = "v2" @@ -36,10 +35,10 @@ class JudgementStatusHandler(store: TransientEntityStore): AbstractJudgementHand ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): List = this.store.transactional(true) { + override fun doGet(ctx: Context): List { val evaluationManager = ctx.eligibleManagerForId() checkEligibility(ctx, evaluationManager) - evaluationManager.judgementValidators.map { ApiJudgementValidatorStatus(it.id, it.pending, it.open) } + return evaluationManager.judgementValidators.map { ApiJudgementValidatorStatus(it.id, it.pending, it.open) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt index 5409c3dc1..90f502574 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostJudgementHandler.kt @@ -23,7 +23,8 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandler(store), PostRestHandler { +class PostJudgementHandler : AbstractJudgementHandler(), + PostRestHandler { override val route = "evaluation/{evaluationId}/judge" @OpenApi( @@ -50,19 +51,25 @@ class PostJudgementHandler(store: TransientEntityStore): AbstractJudgementHandle throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - /* Start transaction. */ - this.store.transactional { - val evaluationManager = ctx.eligibleManagerForId() - checkEligibility(ctx, evaluationManager) - val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } - ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) - try { - validator.judge(judgement.token, judgement.verdict.toDb()) - } catch (ex: JudgementTimeoutException) { - throw ErrorStatusException(408, ex.message!!, ctx) - } - AuditLogger.judgement(evaluationManager.id, validator, judgement.token, judgement.verdict, AuditLogSource.REST, ctx.sessionToken()) + + val evaluationManager = ctx.eligibleManagerForId() + checkEligibility(ctx, evaluationManager) + val validator = evaluationManager.judgementValidators.find { it.id == judgement.validator } + ?: throw ErrorStatusException(404, "No matching task found for validator ${judgement.validator}.", ctx) + try { + validator.judge(judgement.token, judgement.verdict) + } catch (ex: JudgementTimeoutException) { + throw ErrorStatusException(408, ex.message!!, ctx) } + AuditLogger.judgement( + evaluationManager.id, + validator, + judgement.token, + judgement.verdict, + AuditLogSource.REST, + ctx.sessionToken() + ) + return SuccessStatus("Verdict ${judgement.verdict} received and accepted. Thanks!") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt index fbe77f09f..4f19a143f 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/judgement/PostVoteHandler.kt @@ -21,7 +21,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Ralph Gasser * @version 1.0.0 */ -class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(store), PostRestHandler { +class PostVoteHandler : AbstractJudgementHandler(), PostRestHandler { override val route = "evaluation/{evaluationId}/judge/vote" override val apiVersion = "v2" @@ -47,12 +47,17 @@ class PostVoteHandler(store: TransientEntityStore): AbstractJudgementHandler(sto throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } - this.store.transactional { - val evaluationManager = ctx.eligibleManagerForId() - val validator = evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } as? VoteValidator // Get first active vote validator - ?: throw ErrorStatusException(404, "There is currently no voting going on in evaluation ${evaluationManager.id}.", ctx) - validator.vote(vote.verdict.toDb()) - } + + val evaluationManager = ctx.eligibleManagerForId() + val validator = + evaluationManager.judgementValidators.find { it is VoteValidator && it.isActive } as? VoteValidator // Get first active vote validator + ?: throw ErrorStatusException( + 404, + "There is currently no voting going on in evaluation ${evaluationManager.id}.", + ctx + ) + validator.vote(vote.verdict) + return SuccessStatus("Vote received.") } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt index 33e446591..3ac0977a4 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiAnswerSet.kt @@ -6,7 +6,7 @@ import dev.dres.data.model.submissions.* import io.javalin.openapi.OpenApiIgnore /** - * The RESTful API equivalent for the type of an answer set as submitted by the DRES endpoint. + * The RESTful API equivalent for the type of answer set as submitted by the DRES endpoint. * * There is an inherent asymmetry between the answers sets received by DRES (unprocessed & validated) and those sent by DRES (processed and validated). * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt index 04855c94e..e27884578 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt @@ -2,6 +2,8 @@ package dev.dres.api.rest.types.template.tasks import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.CollectionId +import dev.dres.data.model.template.TemplateId +import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.template.task.TaskTemplateId /** @@ -21,4 +23,9 @@ data class ApiTaskTemplate( val targets: List, val hints: List, val comment: String = "" -) +) : TaskTemplate { + override fun textualDescription(): String = this.hints.filter { it.type == ApiHintType.TEXT }.maxByOrNull { it.start ?: 0 }?.description ?: name + + override val templateId: TaskTemplateId + get() = this.id ?: "N/A" +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index 0f988833c..b8526e1eb 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -2,9 +2,6 @@ package dev.dres.data.model.run import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.options.DbTargetOption -import dev.dres.data.model.template.team.TeamAggregatorImpl -import dev.dres.data.model.template.team.TeamGroupId -import dev.dres.data.model.template.team.TeamId import dev.dres.data.model.submissions.DbSubmission import dev.dres.run.validation.MediaItemsAnswerSetValidator import dev.dres.run.validation.TemporalOverlapAnswerSetValidator @@ -56,7 +53,7 @@ abstract class AbstractInteractiveTask(store: TransientEntityStore, task: DbTask .asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() - BasicJudgementValidator(knownCorrectRanges = knownRanges) + BasicJudgementValidator(template.toApi(), this.store, knownCorrectRanges = knownRanges) } DbTargetOption.VOTE -> { @@ -68,7 +65,7 @@ abstract class AbstractInteractiveTask(store: TransientEntityStore, task: DbTask val parameters = template.taskGroup.type.configurations.filter { it.key eq targetOption.description } .asSequence().associate { it.key to it.value } - BasicVoteValidator(knownCorrectRanges = knownRanges, parameters = parameters) + BasicVoteValidator(template.toApi(), this.store, knownCorrectRanges = knownRanges, parameters = parameters) } else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index cb8f0bd68..b5f507ede 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -46,7 +46,7 @@ abstract class AbstractNonInteractiveTask(store: TransientEntityStore, task: DbT .asSequence().map { ItemRange(it.item?.name!!, it.start!!, it.end!!) }.toSet() - BasicJudgementValidator(knownCorrectRanges = knownRanges) + BasicJudgementValidator(template.toApi(), this.store, knownCorrectRanges = knownRanges) } else -> throw IllegalStateException("The provided target option ${targetOption.description} is not supported by interactive tasks.") diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt index 91447d4f1..36508db3e 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/TaskTemplate.kt @@ -5,5 +5,5 @@ import dev.dres.data.model.template.TemplateId interface TaskTemplate { fun textualDescription(): String - val templateId: TemplateId + val templateId: TaskTemplateId } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt index 2b64d371c..5b7ba3852 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/JudgementValidator.kt @@ -1,5 +1,8 @@ package dev.dres.run.validation.interfaces +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.submissions.* /** @@ -25,14 +28,17 @@ interface JudgementValidator { val hasOpen: Boolean get() = this.open > 0 + /** The template of the task this validator belongs to */ + val taskTemplate: ApiTaskTemplate + /** * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]'s internal queue. * - * If such an element exists, then the [DbSubmission] is returned alongside a unique token, that can be used to update the [DbSubmission]'s [DbVerdictStatus]. + * If such an element exists, then the [ApiAnswerSet] is returned alongside a unique token, that can be used to update the [ApiAnswerSet]'s [DbVerdictStatus]. * - * @return Optional [Pair] containing a string token and the [DbSubmission] that should be judged. + * @return Optional [Pair] containing a string token and the [ApiAnswerSet] that should be judged. */ - fun next(): Pair? + fun next(): Pair? /** * Places a verdict for the [Submission] identified by the given token. @@ -40,6 +46,6 @@ interface JudgementValidator { * @param token The token used to identify the [Submission]. * @param verdict The [DbVerdictStatus] assigned by the judge. */ - fun judge(token: String, verdict: DbVerdictStatus) + fun judge(token: String, verdict: ApiVerdictStatus) } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt index 4e729492b..89e5e2a6b 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/interfaces/VoteValidator.kt @@ -1,5 +1,7 @@ package dev.dres.run.validation.interfaces +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus import dev.dres.data.model.submissions.DbAnswerSet import dev.dres.data.model.submissions.DbVerdictStatus @@ -22,14 +24,14 @@ interface VoteValidator : JudgementValidator { /** * Places a verdict for the currently active vote. * - * @param verdict The [DbVerdictStatus] of the vote. + * @param verdict The [ApiVerdictStatus] of the vote. */ - fun vote(verdict: DbVerdictStatus) + fun vote(verdict: ApiVerdictStatus) /** - * Returns the [DbAnswerSet] this [VoteValidator] is currently accepting votes for. + * Returns the [ApiAnswerSet] this [VoteValidator] is currently accepting votes for. * - * @return [DbAnswerSet] or null, if no vote is ongoing. + * @return [ApiAnswerSet] or null, if no vote is ongoing. */ - fun current() : DbAnswerSet? + fun current() : ApiAnswerSet? } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt index d729dc39b..7228f9068 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicJudgementValidator.kt @@ -1,10 +1,14 @@ package dev.dres.run.validation.judged +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.submissions.* import dev.dres.run.audit.AuditLogger import dev.dres.run.exceptions.JudgementTimeoutException import dev.dres.run.validation.interfaces.JudgementValidator import dev.dres.run.validation.interfaces.AnswerSetValidator +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.* import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -20,7 +24,9 @@ import kotlin.concurrent.write * @author Ralph Gasser * @version 2.0.0 */ -open class BasicJudgementValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList()) : AnswerSetValidator, JudgementValidator { +open class BasicJudgementValidator(override val taskTemplate: ApiTaskTemplate, protected val store: TransientEntityStore, knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), + +) : AnswerSetValidator, JudgementValidator { companion object { private val counter = AtomicInteger() @@ -77,29 +83,29 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e /** * Validates the [DbAnswerSet]. For the [BasicJudgementValidator] this means that the [DbAnswerSet] is enqueued for judgement. * - * Usually requires an ongoing transaction. - * * @param answerSet The [DbAnswerSet] to validate. */ override fun validate(answerSet: DbAnswerSet) = this.updateLock.read { - //only validate submissions which are not already validated - if (answerSet.status != DbVerdictStatus.INDETERMINATE) { - return@read - } - - //check cache first - val itemRange = ItemRange(answerSet.answers.first()) //TODO reason about semantics - val cachedStatus = this.cache[itemRange] - if (cachedStatus != null) { - answerSet.status = cachedStatus - } else if (itemRange !in this.queuedItemRanges.keys) { - this.updateLock.write { - this.queue.offer(answerSet.id to itemRange) - this.queuedItemRanges[itemRange] = mutableListOf(answerSet.id) + this.store.transactional { + //only validate submissions which are not already validated + if (answerSet.status != DbVerdictStatus.INDETERMINATE) { + return@transactional } - } else { - this.updateLock.write { - this.queuedItemRanges[itemRange]!!.add(answerSet.id) + + //check cache first + val itemRange = ItemRange(answerSet.answers.first()) //TODO reason about semantics + val cachedStatus = this.cache[itemRange] + if (cachedStatus != null) { + answerSet.status = cachedStatus + } else if (itemRange !in this.queuedItemRanges.keys) { + this.updateLock.write { + this.queue.offer(answerSet.id to itemRange) + this.queuedItemRanges[itemRange] = mutableListOf(answerSet.id) + } + } else { + this.updateLock.write { + this.queuedItemRanges[itemRange]!!.add(answerSet.id) + } } } } @@ -107,33 +113,34 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e /** * Retrieves and returns the next element that requires a verdict from this [JudgementValidator]'s internal queue. * - * If such an element exists, then the [DbAnswerSet] is returned alongside a unique token, that can be used to update - * the [DbAnswerSet]'s [DbVerdictStatus]. + * If such an element exists, then the [ApiAnswerSet] is returned alongside a unique token, that can be used to update + * the [ApiAnswerSet]'s [VerdictStatus]. * - * @return [Pair] containing a string token and the [DbSubmission] that should be judged. Can be null! + * @return [Pair] containing a string token and the [ApiAnswerSet] that should be judged. Can be null! */ - override fun next(): Pair? = this.updateLock.write { + override fun next(): Pair? = this.updateLock.write { checkTimeOuts() val next = this.queue.poll() ?: return@write null - val answerSet = DbAnswerSet.query(DbAnswerSet::id eq next.first).singleOrNull() ?: return@write null - val token = UUID.randomUUID().toString() - this.waiting[token] = next - this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) - AuditLogger.prepareJudgement(answerSet.toApi(), this, token) - token to answerSet + return@write this.store.transactional(true) { + val answerSet = DbAnswerSet.query(DbAnswerSet::id eq next.first).singleOrNull() ?: return@transactional null + val token = UUID.randomUUID().toString() + this.waiting[token] = next + this.timeouts.add((System.currentTimeMillis() + judgementTimeout) to token) + AuditLogger.prepareJudgement(answerSet.toApi(), this, token) + token to answerSet.toApi() + } } /** - * Places a verdict for the [DbSubmission] identified by the given token. - * - * Requires an ongoing transaction! + * Places a verdict for the [Submission] identified by the given token. * - * @param token The token used to identify the [DbSubmission]. + * @param token The token used to identify the [Submission]. * @param verdict The verdict of the judge. */ - override fun judge(token: String, verdict: DbVerdictStatus) = this.updateLock.write { - this.judgeInternal(token, verdict) - Unit + override fun judge(token: String, verdict: ApiVerdictStatus) { + this.updateLock.write { + this.judgeInternal(token, verdict) + } } /** @@ -142,18 +149,21 @@ open class BasicJudgementValidator(knownCorrectRanges: Collection = e * @param token The token used to identify the [DbSubmission]. * @param verdict The verdict of the judge. */ - protected fun judgeInternal(token: String, verdict: DbVerdictStatus): AnswerSetId { + protected fun judgeInternal(token: String, verdict: ApiVerdictStatus): AnswerSetId { val next = this.waiting.remove(token) ?: throw JudgementTimeoutException("This JudgementValidator does not contain a submission for the token '$token'.") //submission with token not found TODO: this should be logged /* Remove from queue set. */ val otherSubmissions = this.queuedItemRanges.remove(next.second) ?: emptyList() - for ((i, answerSetId) in (otherSubmissions + next.first).withIndex()) { - val answerSet = DbAnswerSet.query(DbAnswerSet::id eq answerSetId).singleOrNull() - if (answerSet != null) { - answerSet.status = verdict - if (i == 0) { - this.cache[ItemRange(answerSet.answers.first())] = verdict //TODO reason about semantics + this.store.transactional { + val dbVerdict = verdict.toDb() + for ((i, answerSetId) in (otherSubmissions + next.first).withIndex()) { + val answerSet = DbAnswerSet.query(DbAnswerSet::id eq answerSetId).singleOrNull() + if (answerSet != null) { + answerSet.status = dbVerdict + if (i == 0) { + this.cache[ItemRange(answerSet.answers.first())] = dbVerdict //TODO reason about semantics + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt index c8c36607b..63ed298ec 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/judged/BasicVoteValidator.kt @@ -1,9 +1,13 @@ package dev.dres.run.validation.judged +import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiVerdictStatus +import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.VoteValidator +import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.query.filter -import kotlinx.dnq.query.singleOrNull +import kotlinx.dnq.query.firstOrNull import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.locks.ReentrantReadWriteLock @@ -17,9 +21,10 @@ import kotlin.concurrent.write * @author Ralph Gasser * @version 2.0.0 */ -class BasicVoteValidator(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), private val minimumVotes: Int = defaultMinimimVotes, private val voteDifference: Int = defaultVoteDifference) : BasicJudgementValidator(knownCorrectRanges, knownWrongRanges), VoteValidator { +class BasicVoteValidator(taskTemplate: ApiTaskTemplate, store: TransientEntityStore, knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), private val minimumVotes: Int = defaultMinimimVotes, private val voteDifference: Int = defaultVoteDifference) : BasicJudgementValidator(taskTemplate, store, knownCorrectRanges, knownWrongRanges), VoteValidator { - constructor(knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), parameters: Map): this( + constructor(taskTemplate: ApiTaskTemplate, store: TransientEntityStore, knownCorrectRanges: Collection = emptyList(), knownWrongRanges: Collection = emptyList(), parameters: Map): this( + taskTemplate, store, knownCorrectRanges, knownWrongRanges, parameters.getOrDefault("minimumVotes", "$defaultMinimimVotes").toIntOrNull() ?: defaultMinimimVotes, parameters.getOrDefault("voteDifference", "$defaultVoteDifference").toIntOrNull() ?: defaultVoteDifference @@ -37,8 +42,8 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() /** Internal queue of [AnswerSetId]s that pend voting. */ private val submissionQueue = ConcurrentLinkedQueue() - /** Internal map that counts votes for [DbVerdictStatus] for the current vote. */ - private val voteCountMap = ConcurrentHashMap() + /** Internal map that counts votes for [ApiVerdictStatus] for the current vote. */ + private val voteCountMap = ConcurrentHashMap() /** Internal lock that mediates access to this [BasicVoteValidator]. */ private val updateLock = ReentrantReadWriteLock() @@ -50,14 +55,12 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() get() = this.updateLock.read { this.voteCountMap.mapKeys { it.toString() } } /** - * Places a vote for the current [DbAnswerSet]. + * Places a vote for the current [AnswerSet]. * - * Requires an ongoing transaction! - * - * @param verdict The [DbVerdictStatus] of the vote. + * @param verdict The [ApiVerdictStatus] of the vote. */ - override fun vote(verdict: DbVerdictStatus) = this.updateLock.write { - if (verdict == DbVerdictStatus.INDETERMINATE || verdict == DbVerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does + override fun vote(verdict: ApiVerdictStatus) = this.updateLock.write { + if (verdict == ApiVerdictStatus.INDETERMINATE || verdict == ApiVerdictStatus.UNDECIDABLE){ //should not happen anyway but will be ignored in case it does return@write } @@ -66,7 +69,9 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() if (enoughVotes()){ val finalVerdict = this.voteCountMap.entries.maxByOrNull { it.value }!!.key - answerSet.status = finalVerdict + this.store.transactional { + DbAnswerSet.filter { it.id eq answerSet.id }.firstOrNull()?.status = finalVerdict.toDb() + } this.submissionQueue.poll() this.voteCountMap.clear() } @@ -75,27 +80,24 @@ class BasicVoteValidator(knownCorrectRanges: Collection = emptyList() /** * Dequeues the next [DbAnswerSet] to vote for. * - * Requires an ongoing transaction. - * - * @return [DbAnswerSet] that requires voting. + * @return [ApiAnswerSet] that requires voting, if exists. */ - override fun current(): DbAnswerSet? = this.updateLock.read { + override fun current(): ApiAnswerSet? = this.updateLock.read { val answerSetId = this.submissionQueue.firstOrNull() - return DbAnswerSet.filter { it.id eq answerSetId }.singleOrNull() + return this.store.transactional (true) { DbAnswerSet.filter { it.id eq answerSetId }.firstOrNull()?.toApi() } } /** - * Places a verdict for the [DbSubmission] identified by the given token. Inherits basic logic from parent class + * Places a verdict for the Submission identified by the given token. Inherits basic logic from parent class * but siphons undecidable entries to voting subsystem. * - * Requires an ongoing transaction! * * @param token The token used to identify the [DbSubmission]. * @param verdict The verdict of the judge. */ - override fun judge(token: String, verdict: DbVerdictStatus) = this.updateLock.write { + override fun judge(token: String, verdict: ApiVerdictStatus) = this.updateLock.write { val next = this.judgeInternal(token, verdict) - if (verdict == DbVerdictStatus.UNDECIDABLE) { + if (verdict == ApiVerdictStatus.UNDECIDABLE) { this.submissionQueue.add(next) } } From 20cfe57814c512d3ec174e4cf72743b508f9a34a Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sun, 20 Aug 2023 14:23:32 +0200 Subject: [PATCH 453/498] #337: Added copy of template to CLI and frontend --- .../dres/api/cli/EvaluationTemplateCommand.kt | 36 ++++++++- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 1 + .../CloneEvaluationTemplateHandler.kt | 48 ++++++++++++ .../types/template/tasks/ApiTaskTemplate.kt | 4 + .../kotlin/dev/dres/mgmt/TemplateManager.kt | 22 +++++- doc/oas-client.json | 5 +- doc/oas.json | 75 ++++++++++++++++++- .../teams-list/teams-list.component.html | 7 +- .../template-list.component.html | 11 ++- .../template-list/template-list.component.ts | 9 +++ 10 files changed, 206 insertions(+), 12 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/template/CloneEvaluationTemplateHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt index 84c995a7d..a282c6a65 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/EvaluationTemplateCommand.kt @@ -51,8 +51,11 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private return mapOf( "ls" to listOf("list"), "remove" to listOf("delete"), + "rm" to listOf("delete"), "drop" to listOf("delete"), - "add" to listOf("create") + "add" to listOf("create"), + "cp" to listOf("copy"), + "clone" to listOf("copy") ) } @@ -121,13 +124,38 @@ class EvaluationTemplateCommand(private val store: TransientEntityStore, private return@transactional } - TemplateManager.copyTemplate(evaluationTemplate) - println("template copied") + val newId = TemplateManager.copyTemplate(evaluationTemplate) + println("Successfully copied template. New id=$newId") } - //println("Successfully copied template.") } } + /** + * [CliktCommand] to rename a [DbEvaluationTemplate]. + */ + inner class Rename : AbstractEvaluationCommand(name = "rename", help = "Renames a Template") { + + private val newName: String by option("-n", "--name", help = "New name of the Template") + .required() + .validate { require(it.isNotEmpty()) { "Template name must be non empty." } } + override fun run() { + this@EvaluationTemplateCommand.store.transactional { + val evaluationTemplate = + DbEvaluationTemplate.query((DbEvaluationTemplate::id eq this.id).or(DbEvaluationTemplate::name eq this.name)) + .firstOrNull() + if (evaluationTemplate == null) { + println("Could not find template to copy.") + return@transactional + } + + evaluationTemplate.name = newName + println("Template renamed") + } + } + } + + + /** * [CliktCommand] to list all [DbEvaluationTemplate]s. */ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index bb580174b..62d033478 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -126,6 +126,7 @@ object RestApi { ListEvaluationTemplatesHandler(), CreateEvaluationTemplateHandler(), UpdateEvaluationTemplateHandler(), + CloneEvaluationTemplateHandler(), ShowEvaluationTemplateHandler(), DeleteEvaluationTemplateHandler(), ListTeamHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CloneEvaluationTemplateHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CloneEvaluationTemplateHandler.kt new file mode 100644 index 000000000..fd626ad0b --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/CloneEvaluationTemplateHandler.kt @@ -0,0 +1,48 @@ +package dev.dres.api.rest.handler.template + +import dev.dres.api.rest.handler.PostRestHandler +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.template.ApiEvaluationTemplate +import dev.dres.mgmt.TemplateManager +import io.javalin.http.Context +import io.javalin.openapi.* + +class CloneEvaluationTemplateHandler : + AbstractEvaluationTemplateHandler(), PostRestHandler { + + override val route: String = "template/{templateId}/clone" + + @OpenApi( + summary = "Clones an existing evaluation template.", + path = "/api/v2/template/{templateId}/clone", + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [OpenApiParam( + "templateId", + String::class, + "The evaluation template ID to clone.", + required = true, + allowEmptyValue = false + )], + methods = [HttpMethod.POST], + tags = ["Template"], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiEvaluationTemplate::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("409", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doPost(ctx: Context): ApiEvaluationTemplate { + + val id = this.templateIdFromContext(ctx) + + return try { + /* Clone */ + TemplateManager.copyTemplate(id) + } catch (e: IllegalArgumentException) { + throw ErrorStatusException(404, e.message ?: "", ctx) + } + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt index e27884578..4d5495178 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt @@ -1,10 +1,12 @@ package dev.dres.api.rest.types.template.tasks +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.CollectionId import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.task.TaskTemplate import dev.dres.data.model.template.task.TaskTemplateId +import io.javalin.openapi.OpenApiIgnore /** * The RESTful API equivalent for [DbTaskTemplate]. @@ -27,5 +29,7 @@ data class ApiTaskTemplate( override fun textualDescription(): String = this.hints.filter { it.type == ApiHintType.TEXT }.maxByOrNull { it.start ?: 0 }?.description ?: name override val templateId: TaskTemplateId + @JsonIgnore + @OpenApiIgnore get() = this.id ?: "N/A" } diff --git a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index 4f0aebfb7..0c07be707 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -17,8 +17,10 @@ import jetbrains.exodus.database.TransientEntityStore import kotlinx.dnq.creator.findOrNew import kotlinx.dnq.query.* import kotlinx.dnq.util.getSafe +import org.apache.logging.log4j.core.util.IOUtils import org.joda.time.DateTime import java.io.InputStream +import java.util.Base64 object TemplateManager { @@ -58,6 +60,10 @@ object TemplateManager { DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq templateId).firstOrNull()?.toApi() } + fun getTemplateDb(templateId: TemplateId): DbEvaluationTemplate? = this.store.transactional(true) { + DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq templateId).firstOrNull() + } + fun getTeamLogo(teamId: TeamId) : InputStream? = this.store.transactional(true) { DbTeam.query(DbTeam::id eq teamId).firstOrNull()?.logo } @@ -297,6 +303,18 @@ object TemplateManager { } } + /** + * Creates a copy of an existing [DbEvaluationTemplate] + */ + @Throws(IllegalArgumentException::class) + fun copyTemplate(templateId: TemplateId): ApiEvaluationTemplate { + val existing = getTemplateDb(templateId) ?: throw IllegalArgumentException("Template not found with id $templateId") + + val copy = this.store.transactional { copyTemplate(existing) } + + return getTemplate(copy)!! + } + /** * Creates a copy of an existing [DbEvaluationTemplate] */ @@ -309,7 +327,7 @@ object TemplateManager { it.copy(id = null) }, teams = apiTemplate.teams.map { - it.copy(id = null) + it.copy(id = null, logoData = Base64.getEncoder().encodeToString(getTeamLogo(it.teamId)!!.readAllBytes())) } ) @@ -321,4 +339,4 @@ object TemplateManager { } -} \ No newline at end of file +} diff --git a/doc/oas-client.json b/doc/oas-client.json index e607275d4..29d756437 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2718,9 +2718,12 @@ }, "comment" : { "type" : "string" + }, + "templateId" : { + "type" : "string" } }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment", "templateId" ] }, "ApiTaskType" : { "type" : "object", diff --git a/doc/oas.json b/doc/oas.json index 1b9be5845..988ca13fa 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -8,8 +8,7 @@ "url" : "https://dres.dev" }, "license" : { - "name" : "MIT", - "identifier" : "MIT" + "name" : "MIT" }, "version" : "2.0.0" }, @@ -4620,6 +4619,78 @@ "security" : [ ] } }, + "/api/v2/template/{templateId}/clone" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Clones an existing evaluation template.", + "operationId" : "postApiV2TemplateByTemplateIdClone", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID to clone.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/template/{templateId}/task/list" : { "get" : { "tags" : [ "Template" ], diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html index 4e9121252..bf5fba7b9 100644 --- a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.html @@ -28,7 +28,12 @@

    Teams

    Action - diff --git a/frontend/src/app/template/template-list/template-list.component.html b/frontend/src/app/template/template-list/template-list.component.html index 3a17d43b8..7c6aee6f7 100644 --- a/frontend/src/app/template/template-list/template-list.component.html +++ b/frontend/src/app/template/template-list/template-list.component.html @@ -43,6 +43,13 @@

    Evaluation Templates

    > edit +

    {{ row.description }} - + # Tasks {{ row.taskCount }} - + # Teams {{ row.teamCount }} diff --git a/frontend/src/app/template/template-list/template-list.component.ts b/frontend/src/app/template/template-list/template-list.component.ts index 32e35e686..af1be849d 100644 --- a/frontend/src/app/template/template-list/template-list.component.ts +++ b/frontend/src/app/template/template-list/template-list.component.ts @@ -84,6 +84,15 @@ export class TemplateListComponent implements AfterViewInit{ this.router.navigate(['/template/builder', templateId]).then(s => {}); } + public copy(templateId: string){ + this.templateService.postApiV2TemplateByTemplateIdClone(templateId).subscribe(value => { + this.refresh(); + this.snackBar.open(`Successfully copied ${templateId}`, null, {duration: 5000}); + }, (error) => { + this.snackBar.open(`Error: ${error.error.description}`,null,{duration: 5000}); + }) + } + public delete(templateId: string){ const dialogRef = this.dialog.open(ConfirmationDialogComponent, { data: { From 0a4d7ef8c5e88157e7de9b3d5e87f2ed1f29b63b Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Sun, 20 Aug 2023 23:18:33 +0200 Subject: [PATCH 454/498] Improved team builder dialog --- .../team-builder-dialog.component.html | 33 +++++++++- .../team-builder-dialog.component.scss | 60 +++++++++++++++++++ .../team-builder-dialog.component.ts | 24 +++++++- .../template-builder-components.module.ts | 50 ++++++++-------- .../template-builder.service.ts | 11 +++- 5 files changed, 151 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html index 0d4f52cc8..79f749332 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html @@ -16,6 +16,7 @@

    Add team

    Name must consist of at least three characters. +


    @@ -48,7 +50,36 @@

    Add team

    [cpPosition]="'top-right'" (colorPickerChange)="onColorChange($event)" >

    - +
    +
    +

    Users in Team

    +
    + + {{user.username}} + +
    +
    +
    +

    Users Available

    +
    + + {{user.username}} + +
    +
    +
    diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss index c09fb2ba3..1d3f45db7 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss @@ -12,3 +12,63 @@ .grid-element-form { margin: 10px; } + +/* copied from https://v15.material.angular.io/cdk/drag-drop/overview */ +.member-list-container{ + width: 400px; + max-width: 100%; + margin: 0 25px 25px 0; + display: inline-block; + vertical-align: top; +} + +.member-list{ + min-height: 48px; + background: rgba(74,74,74, 0.84); + border-radius: 4px; + overflow: hidden; + display: block; +} + +.member-list-item-border{ + border-bottom: solid 1px rgba(255,255,255,0.87); +} +.member-list-item:last-child{ + border: none; +} + +.member-box{ + padding: 20px 10px; + border-bottom: solid 1px #ccc; + color: rgba(0, 0, 0, 0.87); + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + cursor: move; + background: white; + font-size: 14px; +} +.cdk-drag-preview { + box-sizing: border-box; + border-radius: 4px; + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.cdk-drag-placeholder { + opacity: 0; +} + +.cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} +.member-box:last-child{ + border: none; +} + +.member-list.cdk-drop-list-dragging .member-box:not(.cdk-drag-placeholder){ + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts index 58444c410..f5c568961 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts @@ -8,6 +8,8 @@ import { map, shareReplay, tap } from "rxjs/operators"; import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete"; import { COMMA, ENTER } from "@angular/cdk/keycodes"; import { MatChipInput, MatChipInputEvent } from "@angular/material/chips"; +import { TemplateBuilderService } from "../../template-builder.service"; +import { CdkDragDrop, moveItemInArray, transferArrayItem } from "@angular/cdk/drag-drop"; @Component({ selector: 'app-team-builder-dialog', @@ -47,6 +49,7 @@ export class TeamBuilderDialogComponent { constructor( private dialogRef: MatDialogRef, private userService: UserService, + private builder: TemplateBuilderService, private config: AppConfig, @Inject(MAT_DIALOG_DATA) private team?: ApiTeam ) { @@ -62,7 +65,7 @@ export class TeamBuilderDialogComponent { users: new FormControl(team?.users || []), userInput: new FormControl(''), }); - this.userService.getApiV2UserList().subscribe(value => this.users = value.filter(u => u.role === "PARTICIPANT" || u.role === "ADMIN").filter(user => !this.form.get('users').value.includes(user))); + this.filterUsers() this.availableUsers = this.form.get('userInput').valueChanges.pipe( startWith(''), map(value => this.filterAvailableUsers(value || '')) @@ -83,6 +86,25 @@ export class TeamBuilderDialogComponent { this.userInput.nativeElement.value = ''; } + public filterUsers() { + this.userService.getApiV2UserList().subscribe(value => { + const allUsers = this.builder.usersOfAllTeams(); + const roles = value + .filter(u => u.role === "PARTICIPANT" || u.role === "ADMIN"); + this.users = roles.filter(u => { + return !allUsers.includes(u); + }); + }); + } + + public drop(event: CdkDragDrop){ + if(event.previousContainer === event.container){ + moveItemInArray(event.container.data, event.previousIndex, event.currentIndex) + }else{ + transferArrayItem(event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex) + } + } + /** * Removes the selected user from the list of users. * diff --git a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts index 2814be9f0..0c0459cd8 100644 --- a/frontend/src/app/template/template-builder/components/template-builder-components.module.ts +++ b/frontend/src/app/template/template-builder/components/template-builder-components.module.ts @@ -47,6 +47,7 @@ import { MatChipsModule } from "@angular/material/chips"; import { MatDialogModule } from "@angular/material/dialog"; import { ColorPickerModule } from "ngx-color-picker"; import {CdkDrag, CdkDropList} from '@angular/cdk/drag-drop'; +import { MatCardModule } from "@angular/material/card"; @NgModule({ @@ -68,30 +69,31 @@ import {CdkDrag, CdkDropList} from '@angular/cdk/drag-drop'; QueryDescriptionExternalImageFormFieldComponent, TeamBuilderDialogComponent ], - imports: [ - CommonModule, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatTooltipModule, - MatIconModule, - MatMenuModule, - MatAutocompleteModule, - MatTableModule, - MatListModule, - SharedModule, - MatSelectModule, - MatButtonToggleModule, - MatGridListModule, - CompetitionBuilderModule, - NgOptimizedImage, - MatChipsModule, - MatDialogModule, - ColorPickerModule, - CdkDropList, - CdkDrag - ], + imports: [ + CommonModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatMenuModule, + MatAutocompleteModule, + MatTableModule, + MatListModule, + SharedModule, + MatSelectModule, + MatButtonToggleModule, + MatGridListModule, + CompetitionBuilderModule, + NgOptimizedImage, + MatChipsModule, + MatDialogModule, + ColorPickerModule, + CdkDropList, + CdkDrag, + MatCardModule + ], exports: [TemplateInformationComponent, JudgesListComponent, TeamsListComponent, diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index a5765a3bf..6d54e0b52 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ApiEvaluationTemplate, ApiTaskGroup, ApiTaskTemplate, ApiTaskType, ApiTeamGroup } from "../../../../openapi"; +import { ApiEvaluationTemplate, ApiTaskGroup, ApiTaskTemplate, ApiTaskType, ApiTeamGroup, ApiUser } from "../../../../openapi"; import { BehaviorSubject, Observable } from "rxjs"; import { map } from "rxjs/operators"; @@ -242,4 +242,13 @@ export class TemplateBuilderService { this.getTemplate().teamGroups.splice(this.getTemplate().teamGroups.indexOf(teamGroup), 1); this.update(this.getTemplate()); } + + /** + * Returns all the users that are in a team. + */ + usersOfAllTeams(): ApiUser[]{ + return this.getTemplate().teams.map(team => team.users).flat() // TODO cache for large templates? + } + + } From 8e3a66d6b452bad97cf001470fc28ee74bf6dfc0 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 21 Aug 2023 08:12:15 +0200 Subject: [PATCH 455/498] Fixed new team builder --- .../src/main/kotlin/dev/dres/mgmt/TemplateManager.kt | 5 ++--- .../team-builder-dialog.component.html | 12 ++++++++---- .../team-builder-dialog.component.scss | 4 ++++ .../team-builder-dialog.component.ts | 3 +-- .../template-builder/template-builder.service.ts | 11 +++++++++++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index 0c07be707..83de82998 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -73,11 +73,10 @@ object TemplateManager { * Requires a transaction context. */ fun updateDbTemplate(apiEvaluationTemplate: ApiEvaluationTemplate) { - - val dbEvaluationTemplate = + val dbEvaluationTemplate = this.store.transactional(true){ DbEvaluationTemplate.query((DbEvaluationTemplate::id) eq apiEvaluationTemplate.id and (DbEvaluationTemplate::instance eq false)) .firstOrNull() ?: throw IllegalArgumentException("No template with id '${apiEvaluationTemplate.id}'") - + } updateDbTemplate(dbEvaluationTemplate, apiEvaluationTemplate) } diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html index 79f749332..832d043bf 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html @@ -52,7 +52,9 @@

    Add team

    -

    Users in Team

    +

    Users in Team + help +

    Users in Team

    [cdkDropListConnectedTo]="[userList]" class="member-list" (cdkDropListDropped)="drop($event)"> - + {{user.username}}
    -

    Users Available

    +

    Users Available + help +

    Users Available

    [cdkDropListConnectedTo]="[memberList]" class="member-list" (cdkDropListDropped)="drop($event)"> - + {{user.username}} diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss index 1d3f45db7..53b2eadc4 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.scss @@ -30,6 +30,10 @@ display: block; } +.member-list-item { + cursor: move; +} + .member-list-item-border{ border-bottom: solid 1px rgba(255,255,255,0.87); } diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts index f5c568961..12fa9b430 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.ts @@ -88,11 +88,10 @@ export class TeamBuilderDialogComponent { public filterUsers() { this.userService.getApiV2UserList().subscribe(value => { - const allUsers = this.builder.usersOfAllTeams(); const roles = value .filter(u => u.role === "PARTICIPANT" || u.role === "ADMIN"); this.users = roles.filter(u => { - return !allUsers.includes(u); + return !this.builder.isUserInTeam(u); }); }); } diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 6d54e0b52..1befb5dae 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -250,5 +250,16 @@ export class TemplateBuilderService { return this.getTemplate().teams.map(team => team.users).flat() // TODO cache for large templates? } + isUserInTeam(user: ApiUser): boolean { + const used = this.usersOfAllTeams(); + let result = false; + used.forEach(u => { + if(user.id === u.id){ + result = true; + return + } + }) + return result; + } } From 94329bf987ee31a239c5ff7f5710a90016d1a89c Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Mon, 21 Aug 2023 17:28:49 +0200 Subject: [PATCH 456/498] Started refactoring w.r.t to accessing objects via taskId vs. taskTemplateId: - ApiEvaluationState now contains both taskId and taskTemplateId (but no more template details) - ApiEvaluationInfo contains some template information - Combination of the two must take place in viewer. - GetTaskHintHandler, GetTaskTargetHandler now strictly rely on taskId - Fixed some minor issues in existing APIs. --- .../admin/EvaluationOverviewHandler.kt | 5 +- .../client/ClientListEvaluationsHandler.kt | 9 +- .../evaluation/viewer/GetTaskHintHandler.kt | 48 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 25 +- .../evaluation/viewer/ViewerReadyHandler.kt | 18 +- .../rest/handler/preview/GetMediaHandler.kt | 16 +- .../types/evaluation/ApiEvaluationInfo.kt | 20 +- .../types/evaluation/ApiEvaluationState.kt | 19 +- .../types/evaluation/ApiTaskTemplateInfo.kt | 5 - .../types/template/tasks/ApiTaskTemplate.kt | 1 + .../dev/dres/data/model/run/RunProperties.kt | 3 + doc/oas.json | 13075 ++++++++-------- .../task-controls.component.html | 6 +- .../app/run/abstract-run-list.component.ts | 2 +- .../src/app/run/run-admin-view.component.html | 16 +- .../src/app/run/run-admin-view.component.ts | 10 +- frontend/src/app/run/run-list.component.ts | 1 - .../src/app/viewer/run-viewer.component.html | 45 +- .../src/app/viewer/run-viewer.component.ts | 57 +- .../scoreboard-viewer.component.ts | 4 +- .../src/app/viewer/task-viewer.component.html | 12 +- .../src/app/viewer/task-viewer.component.ts | 49 +- .../src/app/viewer/teams-viewer.component.ts | 16 +- 23 files changed, 6694 insertions(+), 6768 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt index ac650c821..158fd1ff8 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/EvaluationOverviewHandler.kt @@ -18,7 +18,7 @@ class EvaluationOverviewHandler : AbstractEvaluationAdminHandler(), GetRestHandl override val route = "evaluation/admin/{evaluationId}/overview" @OpenApi( - summary = "Provides a complete overview of a run.", + summary = "Provides a complete overview of an evaluation.", path = "/api/v2/evaluation/admin/{evaluationId}/overview", operationId = OpenApiOperation.AUTO_GENERATE, methods = [HttpMethod.GET], @@ -35,8 +35,7 @@ class EvaluationOverviewHandler : AbstractEvaluationAdminHandler(), GetRestHandl ) override fun doGet(ctx: Context): ApiEvaluationOverview { val evaluationId = ctx.evaluationId() - val evaluationManager = - getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) + val evaluationManager = getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found.", ctx) return ApiEvaluationOverview.of(evaluationManager) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt index 4db399114..7bf97bcc1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -9,7 +9,6 @@ import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import io.javalin.http.Context import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore /** * A [GetRestHandler] used to list all ongoing [DbEvaluation]s available to the current user. @@ -37,18 +36,18 @@ class ClientListEvaluationsHandler : AbstractEvaluationClientHandler(), GetRestH override fun doGet(ctx: Context): List { return getRelevantManagers(ctx).map { ApiEvaluationInfo( - id = it.id, + id = it.evaluation.id, name = it.name, - templateId = it.template.id, - templateDescription = it.template.description, when (it) { is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS else -> TODO() }, properties = it.runProperties, + templateId = it.template.id, + templateDescription = it.template.description, teams = emptyList(), - tasks = emptyList() + taskTemplates = emptyList() ) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index 1a1a97f93..e03296e8c 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -24,10 +24,8 @@ import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore -import kotlinx.dnq.query.eq import kotlinx.dnq.query.filter import kotlinx.dnq.query.firstOrNull -import kotlinx.dnq.query.query import java.io.FileNotFoundException import java.io.IOException import java.nio.file.Files @@ -49,11 +47,11 @@ import java.util.* class GetTaskHintHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(), GetRestHandler { - override val route = "evaluation/{evaluationId}/hint/{taskId}" + override val route = "evaluation/{evaluationId}/{taskId}/hint" @OpenApi( summary = "Returns the task hint for the specified task.", - path = "/api/v2/evaluation/{evaluationId}/hint/{taskId}", + path = "/api/v2/evaluation/{evaluationId}/{taskId}/hint", tags = ["Evaluation"], operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ @@ -78,36 +76,19 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca throw ErrorStatusException(403, "Access Denied", ctx) } - val currentTaskDescription = manager.currentTaskTemplate(rac) - val template = if (currentTaskDescription.id == taskId) { - currentTaskDescription - } else { - manager.taskForId(rac, taskId)?.template ?: throw ErrorStatusException( - 404, - "Task with specified ID $taskId does not exist.", - ctx - ) - } - + val task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) if(ctx.isParticipant() || ctx.isAdmin()) { - manager.viewerPreparing( - taskId, rac, ViewerInfo( - ctx.sessionToken()!!, - ctx.ip() - ) - ) + manager.viewerPreparing(taskId, rac, ViewerInfo(ctx.sessionToken()!!, ctx.ip())) } try { ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes - template.toTaskHint() + task.template.toTaskHint() } catch (e: FileNotFoundException) { throw ErrorStatusException(404, "Query object cache file not found!", ctx) } catch (ioe: IOException) { throw ErrorStatusException(500, "Exception when reading query object cache file.", ctx) } - - } } @@ -119,25 +100,6 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca * @throws FileNotFoundException * @throws IOException */ -// private fun DbTaskTemplate.toTaskHint(): ApiHintContent { -// val sequence = this.hints.asSequence().groupBy { it.type }.flatMap { group -> -// var index = 0 -// group.value.sortedBy { it.start ?: 0 }.flatMap { -// val ret = mutableListOf(it.toContentElement()) -// if (it.end != null) { -// if (index == (group.value.size - 1)) { -// ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) -// } else if ((group.value[index + 1].start ?: 0) > it.end!!) { -// ret.add(ApiContentElement(contentType = ret.first().contentType, offset = it.end!!)) -// } -// } -// index += 1 -// ret -// } -// } -// return ApiHintContent(this.id, sequence, false) -// } - private fun ApiTaskTemplate.toTaskHint(): ApiHintContent = store.transactional(true){ val sequence = this.hints.groupBy { it.type }.flatMap { (type, hints) -> var index = 0 diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 54d6ac20c..1ccfec492 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -39,16 +39,16 @@ import java.util.* */ class GetTaskTargetHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(), GetRestHandler { - override val route = "evaluation/{evaluationId}/target/{taskId}" + override val route = "evaluation/{evaluationId}/{taskId}/target" @OpenApi( summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", - path = "/api/v2/evaluation/{evaluationId}/target/{taskId}", + path = "/api/v2/evaluation/{evaluationId}/{taskId}/target", operationId = OpenApiOperation.AUTO_GENERATE, tags = ["Evaluation"], pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), - OpenApiParam("taskId", String::class, "The task ID.", required = true, allowEmptyValue = false) + OpenApiParam("taskId", String::class, "The task template ID.", required = true, allowEmptyValue = false) ], responses = [ OpenApiResponse("200", [OpenApiContent(ApiTargetContent::class)]), @@ -59,7 +59,7 @@ class GetTaskTargetHandler(private val store: TransientEntityStore, private val methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiTargetContent { - val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskTemplateId' not specified.", ctx) val rac = ctx.runActionContext() return this.store.transactional (true) { @@ -69,10 +69,7 @@ class GetTaskTargetHandler(private val store: TransientEntityStore, private val } /* Test for correct state. */ - var task = manager.currentTask(rac) - if (task == null) { - task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) - } + val task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) if (task.status != ApiTaskStatus.ENDED) { throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) } @@ -98,18 +95,6 @@ class GetTaskTargetHandler(private val store: TransientEntityStore, private val * @throws FileNotFoundException * @throws IOException */ -// private fun DbTaskTemplate.toTaskTarget(): ApiTargetContent { -// var cummulativeOffset = 0L -// val sequence = this.targets.asSequence().flatMap { -// cummulativeOffset += Math.floorDiv(it.item?.durationMs ?: 10000L, 1000L) + 1L -// listOf( -// it.toQueryContentElement(), -// ApiContentElement(ApiContentType.EMPTY, null, cummulativeOffset) -// ) -// }.toList() -// return ApiTargetContent(this.id, sequence) -// } - private fun ApiTaskTemplate.toTaskTarget(): ApiTargetContent { //TODO there must be a better way to do this var cummulativeOffset = 0L val sequence = DbTaskTemplate.filter{it.templateId eq this@toTaskTarget.id}.firstOrNull()?.targets?.asSequence()?.flatMap { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt index c2dc62d5b..22582c6e3 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/ViewerReadyHandler.kt @@ -17,11 +17,11 @@ import jetbrains.exodus.database.TransientEntityStore class ViewerReadyHandler : AbstractEvaluationViewerHandler(), GetRestHandler { - override val route = "evaluation/{evaluationId}/hint/{taskId}/ready" + override val route = "evaluation/{evaluationId}/{taskId}/ready" @OpenApi( summary = "Signals that a viewer is ready to show the hints for a particular task.", - path = "/api/v2/evaluation/{evaluationId}/hint/{taskId}/ready", + path = "/api/v2/evaluation/{evaluationId}/{taskId}/ready", tags = ["Evaluation"], operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ @@ -38,23 +38,15 @@ class ViewerReadyHandler : AbstractEvaluationViewerHandler(), GetRestHandler() if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access Denied", ctx) + throw ErrorStatusException(403, "Access denied!", ctx) } if (ctx.isParticipant() || ctx.isAdmin()) { - manager.viewerReady( - taskId, rac, ViewerInfo( - ctx.sessionToken()!!, - ctx.ip() - ) - ) + manager.viewerReady(taskId, rac, ViewerInfo(ctx.sessionToken()!!, ctx.ip())) } return SuccessStatus("ready received") diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt index e9615434b..b15b3fdb0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/GetMediaHandler.kt @@ -35,14 +35,14 @@ class GetMediaHandler(private val store: TransientEntityStore) : GetRestHandler< override fun doGet(ctx: Context): Any = "" @OpenApi(summary = "Returns a collection item", - path = "/api/v2/media/{itemId}", - operationId = OpenApiOperation.AUTO_GENERATE, - pathParams = [ - OpenApiParam("itemId", String::class, "The media item ID.") - ], - tags = ["Media"], - responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], - ignore = true, + path = "/api/v2/media/{itemId}", + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [ + OpenApiParam("itemId", String::class, "The media item ID.") + ], + tags = ["Media"], + responses = [OpenApiResponse("200"), OpenApiResponse("401"), OpenApiResponse("400"), OpenApiResponse("404")], + ignore = true, methods = [HttpMethod.GET] ) override fun get(ctx: Context) { diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt index 816151678..36c496f05 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -1,19 +1,15 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunProperties -import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import dev.dres.run.NonInteractiveRunManager import dev.dres.run.RunManager -import kotlinx.dnq.query.asSequence -import kotlinx.dnq.query.sortedBy /** - * Contains the basic and most importantly static information about a [InteractiveSynchronousEvaluation] and the - * associated [RunManager]. Since this information usually doesn't change in the course of a run, - * it allows for local caching and other optimizations. + * Contains the basic and most importantly static information about a [RunManager]. + * + * Since this information usually doesn't change in the course of a run, it allows for local caching and other optimizations. * * @author Ralph Gasser * @version 1.1.0 @@ -21,18 +17,16 @@ import kotlinx.dnq.query.sortedBy data class ApiEvaluationInfo( val id: String, val name: String, + val type: ApiEvaluationType, + val properties: RunProperties, // FIXME non-api type exposed via val templateId: String, val templateDescription: String?, - val type: ApiEvaluationType, - val properties: RunProperties, // FIXME non-api type exposed via api val teams: List, - val tasks: List, + val taskTemplates: List, ) { constructor(manager: RunManager): this( manager.id, manager.name, - manager.template.id, - manager.template.name, when(manager) { is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS @@ -40,6 +34,8 @@ data class ApiEvaluationInfo( else -> throw IllegalStateException("Incompatible type of run manager.") }, manager.runProperties, + manager.template.id, + manager.template.description, manager.template.teams.asSequence().map { team -> ApiTeamInfo(team) }.toList(), manager.template.tasks.map { task -> ApiTaskTemplateInfo(task) }.toList() ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt index 0da4d3215..89c9f3a7a 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationState.kt @@ -1,33 +1,30 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.run.InteractiveSynchronousEvaluation import dev.dres.data.model.run.RunActionContext import dev.dres.run.* /** - * Contains the information about the state of a [InteractiveSynchronousEvaluation] and the associated [RunManager]. + * Contains the information about the state of an [InteractiveRunManager]. * - * This is information that changes in the course of a run and therefore must be updated frequently. + * This is information that changes in the course of an evaluation and therefore must be updated frequently. * - * @version 1.2.0 + * @version 1.3.0 */ data class ApiEvaluationState( val evaluationId: String, val evaluationStatus: ApiEvaluationStatus, + val taskId: String?, val taskStatus: ApiTaskStatus, - val currentTemplate: ApiTaskTemplateInfo?, + val taskTemplateId: String?, val timeLeft: Long, val timeElapsed: Long ) { constructor(run: InteractiveRunManager, context: RunActionContext) : this( - run.id, + run.evaluation.id, run.status.toApi(), + run.currentTask(context)?.taskId, run.currentTask(context)?.status ?: ApiTaskStatus.NO_TASK, - try { - ApiTaskTemplateInfo(run.currentTaskTemplate(context)) - } catch (e: IllegalArgumentException) { - ApiTaskTemplateInfo.EMPTY_INFO - }, + run.currentTaskTemplate(context).templateId, run.timeLeft(context) / 1000, run.timeElapsed(context) / 1000 ) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt index cf2221c49..94e53348b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTaskTemplateInfo.kt @@ -13,10 +13,5 @@ import dev.dres.data.model.template.task.DbTaskTemplate * @version 1.1.0 */ data class ApiTaskTemplateInfo(val templateId: String, val name: String, val taskGroup: String, val taskType: String, val duration: Long) { - constructor(task: ApiTaskTemplate) : this(task.id!!, task.name, task.taskGroup, task.taskType, task.duration) - - companion object { - val EMPTY_INFO = ApiTaskTemplateInfo("", "N/A", "N/A", "N/A", 0) - } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt index 4d5495178..25da936ff 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskTemplate.kt @@ -1,6 +1,7 @@ package dev.dres.api.rest.types.template.tasks import com.fasterxml.jackson.annotation.JsonIgnore +import dev.dres.api.rest.types.evaluation.ApiTaskTemplateInfo import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.media.CollectionId import dev.dres.data.model.template.TemplateId diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt index f99b38fa2..24640798f 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt @@ -1,5 +1,8 @@ package dev.dres.data.model.run +/** + * + */ data class RunProperties ( // TODO shoudln't we move this to db and api ? val participantCanView: Boolean = true, val shuffleTasks: Boolean = false, //is only used for asynchronous runs diff --git a/doc/oas.json b/doc/oas.json index 988ca13fa..644ef1627 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1,6537 +1,6540 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : "DRES API", - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", - "contact" : { - "name" : "The DRES Dev Team", - "url" : "https://dres.dev" - }, - "license" : { - "name" : "MIT" - }, - "version" : "2.0.0" - }, - "paths" : { - "/api/v1/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "operationId" : "getApiV1Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "operationId" : "getApiV2CollectionList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/random" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "operationId" : "getApiV2CollectionByCollectionIdRandom", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", - "operationId" : "postApiV2CollectionByCollectionIdResolve", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", - "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", - "in" : "path", - "description" : "Name the item(s) should start with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given evaluation.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the verdict status of an AnswerSet.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "answerSetId", - "in" : "path", - "description" : "The ID of the AnswerSet.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiOverrideAnswerSetVerdictDto" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of a run.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}/ready" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Signals that a viewer is ready to show the hints for a particular task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskIdReady", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a list of available scoreboard names for the given evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", - "operationId" : "getApiV2ExternalByStartsWith", - "parameters" : [ { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a media item to the specified media collection.", - "operationId" : "postApiV2MediaItem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Tries to delete a specific media item.", - "operationId" : "deleteApiV2MediaItemByMediaId", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaItemId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "getApiV2MediaItemByMediaItemId", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Media item ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{start}/{end}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview video from a media item. ", - "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item. Must be a video.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "start", - "in" : "path", - "description" : "Start timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "end", - "in" : "path", - "description" : "End timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "video/mp4" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{timestamp}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview image from a media item.", - "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Time into the video in milliseconds (for videos only).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "image/jpeg" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the score overviews of a specific evaluation run.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/current" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeries" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns team group aggregated values of the current task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroupValue" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit/{evaluationId}" : { - "post" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions.", - "operationId" : "postApiV2SubmitByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The ID of the evaluation the submission belongs to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiClientSubmission" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "The submission was accepted by the server and there was a verdict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "The submission was accepted by the server and there has not yet been a verdict available", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "The submission was rejected by the server", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplateOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeam", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/list" : { - "get" : { - "tags" : [ "Template", "Team" ], - "summary" : "Lists all the teams across all evaluations.", - "operationId" : "getApiV2TemplateTeamList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/{teamId}" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeamByTeamId", - "parameters" : [ { - "name" : "teamId", - "in" : "path", - "description" : "The team ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/type-presets/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", - "operationId" : "getApiV2TemplateTypePresetsList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "409" : { - "description" : "Conflict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/clone" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Clones an existing evaluation template.", - "operationId" : "postApiV2TemplateByTemplateIdClone", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID to clone.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "409" : { - "description" : "Conflict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "operationId" : "getApiV2UserList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "operationId" : "getApiV2UserSessionActiveList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/UserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - } - }, - "required" : [ "name" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "mediaItemId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - }, - "metadata" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" - } - } - }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] - }, - "ApiMediaItemMetaDataEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "key" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "key", "value" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "evaluationStatus" : { - "$ref" : "#/components/schemas/ApiEvaluationStatus" - }, - "taskStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "currentTemplate" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiOverrideAnswerSetVerdictDto" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "id", "status", "taskId", "answers" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiClientAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "text" : { - "type" : "string" - }, - "mediaItemId" : { - "type" : "string" - }, - "mediaItemName" : { - "type" : "string" - }, - "mediaItemCollectionName" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - } - }, - "ApiClientAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswer" - } - } - }, - "required" : [ "answers" ] - }, - "ApiClientSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "answerSets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswerSet" - } - } - }, - "required" : [ "answerSets" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "answerSet" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "required" : [ "validator", "taskDescription", "answerSet" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "created" : { - "type" : "integer", - "format" : "int64" - }, - "modified" : { - "type" : "integer", - "format" : "int64" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiEvaluationTemplateOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskCount" : { - "type" : "integer", - "format" : "int32" - }, - "teamCount" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "id", "name", "taskCount", "teamCount" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - }, - "comment" : { - "type" : "string" - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamAggregatorType" : { - "type" : "string", - "enum" : [ "MIN", "MAX", "MEAN", "LAST" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "$ref" : "#/components/schemas/ApiTeamAggregatorType" - } - }, - "required" : [ "teams", "aggregation" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "UserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - } - }, - "securitySchemes" : { - "CookieAuth" : { - "in" : "cookie", - "name" : "SESSIONID", - "type" : "apiKey" - } - } - }, - "servers" : [ ], - "security" : [ ] +{ + "openapi" : "3.0.3", + "info" : { + "title" : "DRES API", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", + "contact" : { + "name" : "The DRES Dev Team", + "url" : "https://dres.dev" + }, + "license" : { + "name" : "MIT" + }, + "version" : "2.0.0" + }, + "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiV2CollectionList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiV2CollectionByCollectionIdRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", + "operationId" : "postApiV2CollectionByCollectionIdResolve", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name the item(s) should start with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiOverrideAnswerSetVerdictDto" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of an evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/{taskId}/hint" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task.", + "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdHint", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/{taskId}/ready" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Signals that a viewer is ready to show the hints for a particular task.", + "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdReady", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/{taskId}/target" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdTarget", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2ExternalByStartsWith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaItemId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItemByMediaItemId", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Media item ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{start}/{end}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview video from a media item. ", + "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item. Must be a video.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "start", + "in" : "path", + "description" : "Start timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "end", + "in" : "path", + "description" : "End timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "video/mp4" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{timestamp}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview image from a media item.", + "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Time into the video in milliseconds (for videos only).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "image/jpeg" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/submit/{evaluationId}" : { + "post" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions.", + "operationId" : "postApiV2SubmitByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation the submission belongs to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiClientSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "The submission was accepted by the server and there was a verdict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "The submission was accepted by the server and there has not yet been a verdict available", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "The submission was rejected by the server", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplateOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", + "parameters" : [ { + "name" : "teamId", + "in" : "path", + "description" : "The team ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/type-presets/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", + "operationId" : "getApiV2TemplateTypePresetsList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/clone" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Clones an existing evaluation template.", + "operationId" : "postApiV2TemplateByTemplateIdClone", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID to clone.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + } + }, + "required" : [ "name" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "mediaItemId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "metadata" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" + } + } + }, + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] + }, + "ApiMediaItemMetaDataEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "key" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "key", "value" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "taskTemplates" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "type", "properties", "templateId", "teams", "taskTemplates" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskId" : { + "type" : "string" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "taskTemplateId" : { + "type" : "string" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiOverrideAnswerSetVerdictDto" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + } + }, + "required" : [ "id", "status", "taskId", "answers" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "mediaItemId" : { + "type" : "string" + }, + "mediaItemName" : { + "type" : "string" + }, + "mediaItemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + } + }, + "required" : [ "answers" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "answerSet" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "required" : [ "validator", "taskDescription", "answerSet" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "created" : { + "type" : "integer", + "format" : "int64" + }, + "modified" : { + "type" : "integer", + "format" : "int64" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiEvaluationTemplateOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "id", "name", "taskCount", "teamCount" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + }, + "comment" : { + "type" : "string" + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamAggregatorType" : { + "type" : "string", + "enum" : [ "MIN", "MAX", "MEAN", "LAST" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "$ref" : "#/components/schemas/ApiTeamAggregatorType" + } + }, + "required" : [ "teams", "aggregation" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "UserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + } + }, + "securitySchemes" : { + "CookieAuth" : { + "in" : "cookie", + "name" : "SESSIONID", + "type" : "apiKey" + } + } + }, + "servers" : [ ], + "security" : [ ] } \ No newline at end of file diff --git a/frontend/src/app/evaluation/task-controls/task-controls.component.html b/frontend/src/app/evaluation/task-controls/task-controls.component.html index 3d1e16fe0..828521210 100644 --- a/frontend/src/app/evaluation/task-controls/task-controls.component.html +++ b/frontend/src/app/evaluation/task-controls/task-controls.component.html @@ -4,7 +4,7 @@ *ngIf="(runState | async)?.taskStatus !== 'PREPARING' && (runState | async)?.taskStatus !== 'RUNNING'" mat-icon-button aria-label="Start task." - matTooltip="Start task '{{ (runState | async)?.currentTemplate.name }}'." + matTooltip="Start task '{{ (runState | async)?.taskTemplateId }}'." (click)="startTask()" > play_circle_outline @@ -14,7 +14,7 @@ *ngIf="(runState | async)?.taskStatus === 'PREPARING' || (runState | async)?.taskStatus === 'RUNNING'" mat-icon-button aria-label="End running task." - matTooltip="End running task '{{ (runState | async)?.currentTemplate.name }}'." + matTooltip="End running task '{{ (runState | async)?.taskTemplateId }}'." (click)="abortTask()" > cancel @@ -48,7 +48,7 @@ *ngIf="(isAdmin | async) && (runState | async)?.taskStatus === 'RUNNING'" mat-icon-button aria-label="Adjust duration." - matTooltip="Adjust duration of task '{{ (runState | async)?.currentTemplate.name }}'." + matTooltip="Adjust duration of task '{{ (runState | async)?.taskTemplateId }}'." [matMenuTriggerFor]="menu" > alarm_add diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index 325192663..30ff3bebc 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -176,7 +176,7 @@ export class AbstractRunListComponent { teams: v.teams.length, runStatus: s.evaluationStatus, taskRunStatus: s.taskStatus, - currentTask: s.currentTemplate?.name, + currentTask: s.taskTemplateId, timeLeft: s.timeLeft > -1 ? `${Math.round(s.timeLeft)}s` : 'n/a', asynchronous: v.type === 'ASYNCHRONOUS', runProperties: v.properties, diff --git a/frontend/src/app/run/run-admin-view.component.html b/frontend/src/app/run/run-admin-view.component.html index e2c929bfc..abfd3d0f7 100644 --- a/frontend/src/app/run/run-admin-view.component.html +++ b/frontend/src/app/run/run-admin-view.component.html @@ -38,7 +38,7 @@ @@ -81,9 +81,9 @@ -
    Actions + @@ -106,8 +106,8 @@
    @@ -121,9 +121,7 @@ - + this.runAdminService.getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId(s.info.id, s.state.currentTemplate.templateId).pipe( + switchMap(s => this.runAdminService.getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId(s.info.id, s.state.taskTemplateId).pipe( catchError((err, o) => { console.log(`[RunAdminViewComponent] There was an error while submissions for the running task: ${err?.message}`); return of(null); @@ -113,9 +109,9 @@ export class RunAdminViewComponent { map((submissions: Array) => { const map = new Map(); if (submissions.length > 0) { - map.set(s.state.currentTemplate.templateId, submissions[submissions.length - 1].submissions.length); + map.set(s.state.taskTemplateId, submissions[submissions.length - 1].submissions.length); } else { - map.set(s.state.currentTemplate.templateId, 0); + map.set(s.state.taskTemplateId, 0); } return map; }) diff --git a/frontend/src/app/run/run-list.component.ts b/frontend/src/app/run/run-list.component.ts index feb1e7c07..d2e3649c4 100644 --- a/frontend/src/app/run/run-list.component.ts +++ b/frontend/src/app/run/run-list.component.ts @@ -6,7 +6,6 @@ import { AccessChecking } from '../model/access-checking.interface'; import { UserGroup } from '../model/user-group.model'; import { AccessRoleService } from '../services/session/access-role.service'; import {ApiRole} from '../../../openapi'; -import { AbstractRunListComponent } from "./abstract-run-list.component"; import { AdminRunListComponent } from "./admin-run-list.component"; import { ViewerRunListComponent } from "./viewer-run-list.component"; diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index ebfbf589c..402edf484 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -3,12 +3,12 @@ - Viewer for '{{ (runInfo | async)?.name }}' + Viewer for '{{ (info | async)?.name }}' - Description: {{ (runInfo | async)?.templateDescription }} + Description: {{ (info | async)?.name }}
    - +
    + {{node.item}} + + + + + {{node.item}} + + diff --git a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts index 78713298f..21e63d770 100644 --- a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts +++ b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts @@ -1,10 +1,240 @@ -import { Component } from '@angular/core'; +import { Component, Input } from "@angular/core"; +import { + ApiEvaluationTemplate, + ApiTaskGroup, + ApiTaskTemplate, + ApiTaskType, + ApiTeam, + ApiTeamGroup, + ApiUser +} from "../../../../../../openapi"; +import { FlatTreeControl } from "@angular/cdk/tree"; +import { MatTreeFlatDataSource, MatTreeFlattener } from "@angular/material/tree"; +import { SelectionModel } from "@angular/cdk/collections"; + +/* See https://v15.material.angular.io/components/tree/examples */ + +export enum TemplateImportTreeBranches { + NONE = 0, // 000000 + TASK_TYPES = 1 << 0, // 000001 + TASK_GROUPS = 1 << 1, // 000010 + TASK_TEMPLATES = 1 << 2, // 000100 + TEAMS = 1 << 3, // 001000 + TEAM_GROUPS = 1 << 4, // 010000 + JUDGES = 1 << 5, // 100000 + ALL = ~(~0<<6) // 111111 +} + +/** + * Represents a flat node with possible expansion and its number. + */ +export class TemplateTreeFlatNode { + level: number; + expandable: boolean; + item: T; + label: string; +} + +export class TemplateTreeNode { + children: TemplateTreeNode[] | null; + item: T; + label: string; +} @Component({ - selector: 'app-template-import-tree', - templateUrl: './template-import-tree.component.html', - styleUrls: ['./template-import-tree.component.scss'] + selector: "app-template-import-tree", + templateUrl: "./template-import-tree.component.html", + styleUrls: ["./template-import-tree.component.scss"] }) export class TemplateImportTreeComponent { + flatNodeMap = new Map, TemplateTreeNode>(); + nestedNodeMap = new Map, TemplateTreeFlatNode>(); + + selectedParent: TemplateTreeFlatNode | null = null; + + treeControl: FlatTreeControl> + treeFlattener: MatTreeFlattener, TemplateTreeFlatNode> + dataSource: MatTreeFlatDataSource, TemplateTreeFlatNode> + + selection = new SelectionModel>(true); + + @Input() + template: ApiEvaluationTemplate; + @Input() + branches: TemplateImportTreeBranches; + + constructor() { + this.treeFlattener = new MatTreeFlattener, TemplateTreeFlatNode>( + this.transformer, this.getLevel, this.isExpandable, this.getChildren + ) + this.treeControl = new FlatTreeControl>(this.getLevel, this.isExpandable); + this.dataSource = new MatTreeFlatDataSource, TemplateTreeFlatNode>(this.treeControl, this.treeFlattener); + this.dataSource.data = TemplateImportTreeComponent.buildTree(this.template, this.branches); + } + + getLevel = (node: TemplateTreeFlatNode) => node.level; + isExpandable = (node: TemplateTreeFlatNode) => node.expandable; + getChildren = (node: TemplateTreeNode) => node.children; + hasChild = (_: number, node: TemplateTreeFlatNode) => node.expandable + + transformer = (node: TemplateTreeNode, level: number) => { + const existingNode = this.nestedNodeMap.get(node); + const flatNode = existingNode && existingNode.item === node.item ? existingNode : new TemplateTreeFlatNode(); + flatNode.item = node.item; + flatNode.level = level; + flatNode.expandable = !!node.children?.length; + this.flatNodeMap.set(flatNode, node); + this.nestedNodeMap.set(node, flatNode); + return flatNode; + } + + /** Whether all the descendants of the node are selected. */ + descendantsAllSelected(node: TemplateTreeFlatNode): boolean { + const descendants = this.treeControl.getDescendants(node); + return descendants.length > 0 && + descendants.every(child => { + return this.selection.isSelected(child); + }); + } + + /** Whether part of the descendants are selected */ + descendantsPartiallySelected(node: TemplateTreeFlatNode): boolean { + const descendants = this.treeControl.getDescendants(node); + const result = descendants.some(child => this.selection.isSelected(child)); + return result && !this.descendantsAllSelected(node); + } + + /** Toggle the to-do item selection. Select/deselect all the descendants node */ + todoItemSelectionToggle(node: TemplateTreeFlatNode): void { + this.selection.toggle(node); + const descendants = this.treeControl.getDescendants(node); + this.selection.isSelected(node) + ? this.selection.select(...descendants) + : this.selection.deselect(...descendants); + + // Force update for the parent + descendants.forEach(child => this.selection.isSelected(child)); + this.checkAllParentsSelection(node); + } + + /** Toggle a leaf to-do item selection. Check all the parents to see if they changed */ + todoLeafItemSelectionToggle(node: TemplateTreeFlatNode): void { + this.selection.toggle(node); + this.checkAllParentsSelection(node); + } + + /* Checks all the parents when a leaf node is selected/unselected */ + checkAllParentsSelection(node: TemplateTreeFlatNode): void { + let parent: TemplateTreeFlatNode | null = this.getParentNode(node); + while (parent) { + this.checkRootNodeSelection(parent); + parent = this.getParentNode(parent); + } + } + + /** Check root node checked state and change it accordingly */ + checkRootNodeSelection(node: TemplateTreeFlatNode): void { + const nodeSelected = this.selection.isSelected(node); + const descendants = this.treeControl.getDescendants(node); + const descAllSelected = + descendants.length > 0 && + descendants.every(child => { + return this.selection.isSelected(child); + }); + if (nodeSelected && !descAllSelected) { + this.selection.deselect(node); + } else if (!nodeSelected && descAllSelected) { + this.selection.select(node); + } + } + + /* Get the parent node of a node */ + getParentNode(node: TemplateTreeFlatNode): TemplateTreeFlatNode | null { + const currentLevel = this.getLevel(node); + + if (currentLevel < 1) { + return null; + } + + const startIndex = this.treeControl.dataNodes.indexOf(node) - 1; + + for (let i = startIndex; i >= 0; i--) { + const currentNode = this.treeControl.dataNodes[i]; + + if (this.getLevel(currentNode) < currentLevel) { + return currentNode; + } + } + return null; + } + + public static buildTree(template: ApiEvaluationTemplate, branches: TemplateImportTreeBranches): TemplateTreeNode[] { + const root = new TemplateTreeNode(); + root.item = template; + root.label = template.name; + root.children = [] as TemplateTreeNode[]; + if(this.checkForBranch(branches, TemplateImportTreeBranches.TASK_TYPES)){ + root.children.push(this.buildTaskTypesBranch(template)); + } + if(this.checkForBranch(branches, TemplateImportTreeBranches.TASK_GROUPS)){ + root.children.push(this.buildTaskGroupsBranch(template)); + } + if(this.checkForBranch(branches, TemplateImportTreeBranches.TASK_TEMPLATES)){ + root.children.push(this.buildTaskTemplatesBranch(template)); + } + if(this.checkForBranch(branches, TemplateImportTreeBranches.TEAMS)){ + root.children.push(this.buildTeamsBranch(template)); + } + if(this.checkForBranch(branches, TemplateImportTreeBranches.TEAM_GROUPS)){ + root.children.push(this.buildTeamGroupsBranch(template)); + } + if(this.checkForBranch(branches, TemplateImportTreeBranches.TEAM_GROUPS)){ + root.children.push(this.buildJudgesBranch(template)); + } + return [root]; + } + + private static checkForBranch(branches: TemplateImportTreeBranches, test: TemplateImportTreeBranches): boolean{ + return (branches & test) === test + } + + public static buildTaskTypesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { + return this.buildBranch(template, "taskTypes", "Task Types"); + } + + public static buildTaskGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { + return this.buildBranch(template, "taskGroups", "Task Groups"); + } + + public static buildTaskTemplatesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { + return this.buildBranch(template, "tasks", "Task Templates"); + } + + public static buildTeamsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { + return this.buildBranch(template, "teams", "Teams"); + } + + public static buildTeamGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { + return this.buildBranch(template, "teamGroups", "Team Groups"); + } + + public static buildJudgesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { + return this.buildBranch(template, "judges", "Judges"); + } + + public static buildBranch(template: ApiEvaluationTemplate, key: string, rootLabel: string): TemplateTreeNode { + const root = new TemplateTreeNode(); + root.label = rootLabel; + root.item = template[key]; + root.children = template[key].map(it => { + const item = new TemplateTreeNode(); + item.label = it["name"]; + item.item = it; + item.children = null; + return item; + }); + return root; + } + } diff --git a/frontend/src/app/template/template-builder/template-builder.module.ts b/frontend/src/app/template/template-builder/template-builder.module.ts index 7e10298d9..1943f3b13 100644 --- a/frontend/src/app/template/template-builder/template-builder.module.ts +++ b/frontend/src/app/template/template-builder/template-builder.module.ts @@ -17,6 +17,8 @@ import { TeamgroupsDialogComponent } from './components/teamgroups-dialog/teamgr import { MatDialogModule } from "@angular/material/dialog"; import { MatChipsModule } from "@angular/material/chips"; import { TemplateImportTreeComponent } from './components/template-import-tree/template-import-tree.component'; +import { MatTreeModule } from "@angular/material/tree"; +import { MatCheckboxModule } from "@angular/material/checkbox"; @NgModule({ @@ -41,7 +43,9 @@ import { TemplateImportTreeComponent } from './components/template-import-tree/t TemplateBuilderComponentsModule, MatProgressSpinnerModule, MatDialogModule, - MatChipsModule + MatChipsModule, + MatTreeModule, + MatCheckboxModule ], exports: [TemplateBuilderComponent] }) From d8411bc008a877be93ad006960d9dc455f3925ba Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 17 Sep 2023 10:18:38 +0200 Subject: [PATCH 472/498] Removed exposed DbSubmission from interface --- .../admin/ListSubmissionsHandler.kt | 2 +- .../viewer/GetSubmissionHistoryInfoHandler.kt | 2 +- .../dev/dres/data/model/run/AbstractTask.kt | 7 +++++- .../dres/data/model/run/interfaces/TaskRun.kt | 9 ++++--- .../run/InteractiveAsynchronousRunManager.kt | 25 ++++--------------- .../dev/dres/run/InteractiveRunManager.kt | 7 +++--- .../dev/dres/run/NonInteractiveRunManager.kt | 4 +-- 7 files changed, 24 insertions(+), 32 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt index bae514019..8be7cf4c1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ListSubmissionsHandler.kt @@ -46,7 +46,7 @@ class ListSubmissionsHandler : AbstractEvaluationAdminHandler(), getManager(evaluationId) ?: throw ErrorStatusException(404, "Evaluation $evaluationId not found", ctx) val rac = ctx.runActionContext() return evaluationManager.tasks(rac).filter { it.taskTemplateId == templateId }.map { - ApiSubmissionInfo(evaluationId, it.taskId, it.getDbSubmissions().map { sub -> sub.toApi() }.toList()) + ApiSubmissionInfo(evaluationId, it.taskId, it.getSubmissions()) } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt index a7bd6e6b3..a00ad2f19 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionHistoryInfoHandler.kt @@ -49,7 +49,7 @@ class GetSubmissionHistoryInfoHandler: AbstractEvaluationViewerHandler(), GetRes ) == true manager.currentSubmissions(rac).map { it.toApi(hidden) } } else { - manager.taskForId(rac, taskId)?.getDbSubmissions()?.map { it.toApi() }?.toList() ?: emptyList() + manager.taskForId(rac, taskId)?.getSubmissions() ?: emptyList() } } else { emptyList() diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt index e2f7ca544..7687f3f76 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractTask.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.run import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.run.interfaces.Run import dev.dres.data.model.run.interfaces.TaskRun @@ -155,7 +156,7 @@ abstract class AbstractTask(protected val store: TransientEntityStore, task: DbT } /** Returns a [Sequence] of all [DbSubmission]s connected to this [AbstractTask]. */ - override fun getDbSubmissions() = DbAnswerSet.filter { a -> + fun getDbSubmissions() = DbAnswerSet.filter { a -> a.task.id eq this@AbstractTask.taskId }.mapDistinct { it.submission @@ -174,4 +175,8 @@ abstract class AbstractTask(protected val store: TransientEntityStore, task: DbT internal fun updateTeamAggregation(teamScores: Map) { this.teamGroupAggregators.values.forEach { it.aggregate(teamScores) } } + + override fun getSubmissions(): List = this.store.transactional(true) { + this.getDbSubmissions().map { it.toApi() }.toList() + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt index 064ca0b02..35200d100 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/interfaces/TaskRun.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.run.interfaces import dev.dres.api.rest.types.evaluation.ApiTaskStatus +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.run.InteractiveAsynchronousEvaluation.IATaskRun import dev.dres.data.model.submissions.DbSubmission @@ -47,10 +48,12 @@ interface TaskRun: Run, Scoreable { */ fun prepare() + /** - * Returns a [List] of all [DbSubmission]s that belong to this [TaskRun]. + * Returns a [List] of all [ApiSubmission]s that belong to this [TaskRun]. * - * @return [List] of [DbSubmission]s + * @return [List] of [ApiSubmission]s */ - fun getDbSubmissions(): Sequence + fun getSubmissions(): List + } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index 1f7baa803..fcc4e3b42 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -14,6 +14,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* +import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.task.options.DbSubmissionOption import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT @@ -599,32 +600,16 @@ class InteractiveAsynchronousRunManager( */ override fun viewers(): Map = Collections.unmodifiableMap(this.viewers) -// /** -// * Processes WebSocket [ClientMessage] received by the [InteractiveAsynchronousRunManager]. -// * -// * @param connection The [WebSocketConnection] through which the message was received. -// * @param message The [ClientMessage] received. -// */ -// override fun wsMessageReceived(connection: WebSocketConnection, message: ClientMessage): Boolean { -// when (message.type) { -// ClientMessageType.REGISTER -> this.viewers[connection] = true -// ClientMessageType.UNREGISTER -> this.viewers.remove(connection) -// else -> { /* No op. */ -// } -// } -// return true -// } - - override fun viewerPreparing(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + override fun viewerPreparing(taskTemplateId: TaskTemplateId, rac: RunActionContext, viewerInfo: ViewerInfo) { val currentTaskId = this.currentTask(rac)?.taskId - if (taskId == currentTaskId) { + if (taskTemplateId == currentTaskId) { this.viewers[viewerInfo] = false } } - override fun viewerReady(taskId: TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + override fun viewerReady(taskTemplateId: TaskTemplateId, rac: RunActionContext, viewerInfo: ViewerInfo) { val currentTaskId = this.currentTask(rac)?.taskId - if (taskId == currentTaskId) { + if (taskTemplateId == currentTaskId) { this.viewers[viewerInfo] = true } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt index 8d62ed09f..44bd9ff5a 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveRunManager.kt @@ -6,7 +6,6 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId -import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.DbSubmission import dev.dres.data.model.submissions.SubmissionId @@ -126,7 +125,7 @@ interface InteractiveRunManager : RunManager { * @param context The [RunActionContext] used for the invocation. * @return [List] of [TaskRun]s */ - override fun tasks(context: RunActionContext): List + override fun tasks(context: RunActionContext): List /** * Returns a reference to the currently active [TaskRun]. @@ -134,7 +133,7 @@ interface InteractiveRunManager : RunManager { * @param context The [RunActionContext] used for the invocation. * @return [TaskRun] that is currently active or null, if no such task is active. */ - fun currentTask(context: RunActionContext): TaskRun? + fun currentTask(context: RunActionContext): AbstractInteractiveTask? /** * Returns [TaskRun]s for the specified [EvaluationId]. @@ -142,7 +141,7 @@ interface InteractiveRunManager : RunManager { * @param context The [RunActionContext] used for the invocation. * @param taskId The [EvaluationId] of the desired [TaskRun]. */ - fun taskForId(context: RunActionContext, taskId: EvaluationId): TaskRun? + fun taskForId(context: RunActionContext, taskId: EvaluationId): AbstractInteractiveTask? /** * Invoked by an external caller to update an existing submission by its [SubmissionId] with a new [ApiVerdictStatus]. diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index ef2c8cfa1..3cbc200df 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -105,14 +105,14 @@ class NonInteractiveRunManager( // } override fun viewerPreparing( - taskId: dev.dres.data.model.run.TaskId, + taskTemplateId: dev.dres.data.model.run.TaskId, rac: RunActionContext, viewerInfo: ViewerInfo ) { /* nop */ } - override fun viewerReady(taskId: dev.dres.data.model.run.TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { + override fun viewerReady(taskTemplateId: dev.dres.data.model.run.TaskId, rac: RunActionContext, viewerInfo: ViewerInfo) { /* nop */ } From e92dcef46feb9a9282d7ec02a0c379b4ed4a1811 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 17 Sep 2023 10:19:19 +0200 Subject: [PATCH 473/498] Fixed inverted condition and removed unused validator --- backend/src/main/kotlin/dev/dres/DRES.kt | 2 +- .../data/model/run/AbstractInteractiveTask.kt | 7 +- .../model/run/AbstractNonInteractiveTask.kt | 7 +- .../run/InteractiveSynchronousEvaluation.kt | 4 +- .../main/kotlin/dev/dres/run/RunExecutor.kt | 13 ++-- .../TemporalContainmentAnswerSetValidator.kt | 29 ++++++--- .../TemporalOverlapAnswerSetValidator.kt | 64 ------------------- 7 files changed, 36 insertions(+), 90 deletions(-) delete mode 100644 backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 7bc9ebfa6..b0ab0b8d3 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -112,7 +112,7 @@ object DRES { TemplateManager.init(store) /* Initialize RunExecutor. */ - RunExecutor.init(CONFIG, store, global) + RunExecutor.init(store) /* Initialize EventStreamProcessor */ EventStreamProcessor.register( /* Add handlers here */) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt index b8526e1eb..a80919fd2 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractInteractiveTask.kt @@ -3,10 +3,7 @@ package dev.dres.data.model.run import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.options.DbTargetOption import dev.dres.data.model.submissions.DbSubmission -import dev.dres.run.validation.MediaItemsAnswerSetValidator -import dev.dres.run.validation.TemporalOverlapAnswerSetValidator -import dev.dres.run.validation.TextAnswerSetValidator -import dev.dres.run.validation.TransientMediaSegment +import dev.dres.run.validation.* import dev.dres.run.validation.interfaces.AnswerSetValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.BasicVoteValidator @@ -40,7 +37,7 @@ abstract class AbstractInteractiveTask(store: TransientEntityStore, task: DbTask val target = template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } .asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() - TemporalOverlapAnswerSetValidator(target) + TemporalContainmentAnswerSetValidator(target) } DbTargetOption.TEXT -> TextAnswerSetValidator( diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt index b5f507ede..8c2e89439 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/AbstractNonInteractiveTask.kt @@ -1,10 +1,7 @@ package dev.dres.data.model.run import dev.dres.data.model.template.task.options.DbTargetOption -import dev.dres.run.validation.MediaItemsAnswerSetValidator -import dev.dres.run.validation.TemporalOverlapAnswerSetValidator -import dev.dres.run.validation.TextAnswerSetValidator -import dev.dres.run.validation.TransientMediaSegment +import dev.dres.run.validation.* import dev.dres.run.validation.interfaces.AnswerSetValidator import dev.dres.run.validation.judged.BasicJudgementValidator import dev.dres.run.validation.judged.ItemRange @@ -33,7 +30,7 @@ abstract class AbstractNonInteractiveTask(store: TransientEntityStore, task: DbT val target = template.targets.filter { (it.item ne null) and (it.start ne null) and (it.end ne null) } .asSequence().map { TransientMediaSegment(it.item!!, it.range!!) }.toSet() - TemporalOverlapAnswerSetValidator(target) + TemporalContainmentAnswerSetValidator(target) } DbTargetOption.TEXT -> TextAnswerSetValidator( diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt index 6caf8b974..331670b56 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveSynchronousEvaluation.kt @@ -127,10 +127,10 @@ class InteractiveSynchronousEvaluation(store: TransientEntityStore, evaluation: init { - check(this@InteractiveSynchronousEvaluation.taskRuns.isEmpty() || this@InteractiveSynchronousEvaluation.taskRuns.last().hasEnded) { + check(this@InteractiveSynchronousEvaluation.taskRuns.isEmpty() || this@InteractiveSynchronousEvaluation.taskRuns.last().hasEnded) { //FIXME causes issues when resuming evaluation "Cannot create a new task. Another task is currently running." } - (this@InteractiveSynchronousEvaluation.taskRuns).add(this) + this@InteractiveSynchronousEvaluation.taskRuns.add(this) /* Initialize submission filter. */ this.filter = store.transactional { diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 3c96d7d60..36f319198 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -54,14 +54,17 @@ object RunExecutor { /** Initializes the [RunExecutor] singleton. * - * @param config The [Config] with which DRES was started. * @param store The [TransientEntityStore] instance used to access persistent data. - * @param cache The [CacheManager] instance used to access the media cache. */ - fun init(config: Config, store: TransientEntityStore, cache: CacheManager) { + fun init(store: TransientEntityStore) { store.transactional { - DbEvaluation.filter { (it.ended eq null) }.asSequence().forEach {e -> - this.schedule(e.toRunManager(store)) /* Re-schedule evaluations. */ + DbEvaluation.filter { (it.ended eq null) }.asSequence().forEach {evaluation -> + try { + this.schedule(evaluation.toRunManager(store)) /* Re-schedule evaluations. */ + } catch (e: IllegalStateException) { + logger.error("Could not re-schedule previous run: ${e.message}") + evaluation.ended = System.currentTimeMillis() + } } } } diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt index 31225671b..148239d58 100644 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt +++ b/backend/src/main/kotlin/dev/dres/run/validation/TemporalContainmentAnswerSetValidator.kt @@ -1,8 +1,15 @@ package dev.dres.run.validation +import dev.dres.data.model.media.MediaItem +import dev.dres.data.model.media.time.TemporalRange import dev.dres.data.model.submissions.* import dev.dres.run.validation.interfaces.AnswerSetValidator +import kotlinx.dnq.query.FilteringContext.isEmpty +import kotlinx.dnq.query.asSequence import kotlinx.dnq.query.iterator +import kotlinx.dnq.query.size + +typealias TransientMediaSegment = Pair /** * A [AnswerSetValidator] class that checks, if a submission is correct based on the target segment and the @@ -27,20 +34,21 @@ class TemporalContainmentAnswerSetValidator(private val targetSegments: Collecti */ override fun validate(answerSet: DbAnswerSet) { - /* Basically, we assume that the DBAnswerSet is wrong. */ - answerSet.status = DbVerdictStatus.WRONG + /* If there are answers, there could be a correct one */ + var correct = answerSet.answers.size() > 0 - /* Now we check all the answers. */ + /* Now we check all the answers. If an incorrect one is found, we break */ for (answer in answerSet.answers) { /* Perform sanity checks. */ val item = answer.item val start = answer.start val end = answer.end if (answer.type != DbAnswerType.TEMPORAL || item == null || start == null || end == null || start > end) { - return + correct = false + break } - if (targetSegments.any { targetSegment -> + if (!targetSegments.any { targetSegment -> /* Perform item validation. */ if (item.mediaItemId != targetSegment.first.mediaItemId) { return@any false @@ -50,11 +58,16 @@ class TemporalContainmentAnswerSetValidator(private val targetSegments: Collecti val outer = targetSegment.second.toMilliseconds() !(outer.first > start || outer.second < end) }) { - return + correct = false + break } } - /* If code reaches this point, the [DbAnswerSet] is correct. */ - answerSet.status = DbVerdictStatus.CORRECT + + answerSet.status = if (correct) { + DbVerdictStatus.CORRECT + } else { + DbVerdictStatus.WRONG + } } } \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt b/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt deleted file mode 100644 index 5115059cc..000000000 --- a/backend/src/main/kotlin/dev/dres/run/validation/TemporalOverlapAnswerSetValidator.kt +++ /dev/null @@ -1,64 +0,0 @@ -package dev.dres.run.validation - -import dev.dres.data.model.media.MediaItem -import dev.dres.data.model.media.time.TemporalRange -import dev.dres.data.model.submissions.* -import dev.dres.run.validation.interfaces.AnswerSetValidator -import kotlinx.dnq.query.iterator - -/** */ -typealias TransientMediaSegment = Pair - -/** - * A [AnswerSetValidator] class that checks, if a submission is correct based on the target segment and the - * temporal overlap of the [DbSubmission] with the provided [TransientMediaSegment]. - * - * @author Luca Rossetto - * @author Ralph Gasser - * @version 2.0.0 - */ -class TemporalOverlapAnswerSetValidator(private val targetSegments: Collection) : AnswerSetValidator { - - override val deferring: Boolean = false - - /** - * Validates the [DbAnswerSet] and updates its [DbVerdictStatus]. - * - * Usually requires an ongoing transaction. - * - * @param answerSet The [DbAnswerSet] to validate. - */ - override fun validate(answerSet: DbAnswerSet) { - /* Basically, we assume that the DBAnswerSet is wrong. */ - answerSet.status = DbVerdictStatus.WRONG - - /* Now we check all the answers. */ - for (answer in answerSet.answers) { - /* Perform sanity checks. */ - val start = answer.start - val end = answer.end - val item = answer.item - if (answer.type != DbAnswerType.TEMPORAL || item == null || start == null || end == null || start > end) { - return - } - - if (targetSegments.any { targetSegment -> - /* Perform item validation. */ - if (item.id != targetSegment.first.mediaItemId) { - return@any false - } - - /* Perform temporal validation. */ - val outer = targetSegment.second.toMilliseconds() - - !((outer.first <= start && outer.second >= start) || (outer.first <= end && outer.second >= end)) - }) { - return - } - - } - - /* If code reaches this point, the [DbAnswerSet] is correct. */ - answerSet.status = DbVerdictStatus.CORRECT - } -} \ No newline at end of file From 4b8545e42c2c20f053316263ee348a4680d2ed82 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sun, 17 Sep 2023 11:42:20 +0200 Subject: [PATCH 474/498] Returning reasonable empty results rather than 404s to viewer when task is not yet started --- .../scores/CurrentTaskScoreHandler.kt | 17 ++++++++--------- .../viewer/GetSubmissionInfoHandler.kt | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt index bdba17a67..19eb0c3fc 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/scores/CurrentTaskScoreHandler.kt @@ -52,17 +52,16 @@ class CurrentTaskScoreHandler : AbstractScoreHandler(), } val rac = ctx.runActionContext() - val scorer = manager.currentTask(rac)?.scorer ?: throw ErrorStatusException( - 404, - "No active task run in evaluation ${ctx.evaluationId()}.", - ctx - ) - val scores = scorer.scoreMap() + val template = manager.currentTaskTemplate(rac) + val scores = manager.currentTask(rac)?.scorer?.scoreMap() ?: emptyMap() + return ApiScoreOverview( - "task", - manager.currentTaskTemplate(rac).taskGroup, - manager.template.teams.asSequence().map { team -> ApiScore(team.id!!, scores[team.id] ?: 0.0) }.toList() + template.name, + template.taskGroup, + manager.template.teams.map { team -> ApiScore(team.id!!, scores[team.id] ?: 0.0) }.toList() ) + + } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt index 4e9223d72..5a3b25654 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetSubmissionInfoHandler.kt @@ -47,7 +47,8 @@ class GetSubmissionInfoHandler(private val store: TransientEntityStore): Abstrac } val limit = manager.runProperties.limitSubmissionPreviews - val currentTask = manager.currentTask(rac) ?: throw ErrorStatusException(404, "No active task.", ctx) + val currentTask = manager.currentTask(rac) ?: return emptyList() + val blind = currentTask.isRunning && manager.template.taskTypes.find { it.name == currentTask.template.taskType }?.taskOptions?.contains( ApiTaskOption.HIDDEN_RESULTS ) == true From 0240d7e524136205eca6f7ebae0f368754231b06 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 22 Sep 2023 09:43:40 +0200 Subject: [PATCH 475/498] Attempt to fix viewer synchronization mechanism, still not working --- frontend/src/app/viewer/run-viewer.component.html | 3 --- frontend/src/app/viewer/task-viewer.component.ts | 9 +++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index 402edf484..da267497a 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -177,6 +177,3 @@ - diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index 7d686ea56..b2eff2eea 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -108,10 +108,10 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { */ ngAfterViewInit(): void { /* Observable for the current query hint. */ - const currentTaskHint = this.taskStarted.pipe( - withLatestFrom(this.evaluationId), - switchMap(([task, evaluationId]) => { - return this.runService.getApiV2EvaluationByEvaluationIdByTaskIdHint(evaluationId, task.taskId).pipe( + const currentTaskHint = this.taskChanged.pipe( + mergeMap((task) => { + console.log("current task hint triggered", task) + return this.runService.getApiV2EvaluationByEvaluationIdByTaskIdHint(task.evaluationId, task.taskId).pipe( catchError((e) => { console.error("[TaskViewerComponent] Could not load current query hint due to an error.", e); return of(null); @@ -123,6 +123,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { shareReplay({ bufferSize: 1, refCount: true }) ); + /* Observable for the current query target. */ const currentTaskTarget = this.taskEnded.pipe( withLatestFrom(this.evaluationId), From fc9539a054aff9b78e992db9b7898441d6c2598e Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 22 Sep 2023 11:20:48 +0200 Subject: [PATCH 476/498] Various fixes for aync runs --- .../submission/LegacySubmissionHandler.kt | 2 +- .../run/InteractiveAsynchronousEvaluation.kt | 36 ++++---- .../dres/data/model/run/RunActionContext.kt | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 83 +++++++++++-------- .../run/InteractiveSynchronousRunManager.kt | 2 - .../run/updatables/EndOnSubmitUpdatable.kt | 8 +- 6 files changed, 75 insertions(+), 58 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt index 630205b95..784f47114 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/LegacySubmissionHandler.kt @@ -185,7 +185,7 @@ class LegacySubmissionHandler(private val store: TransientEntityStore, private v val taskType = runManager.currentTaskTemplate(rac).taskType val mapToSegment = runManager.template.taskTypes.find { it.name == taskType }?.taskOptions?.contains(ApiTaskOption.MAP_TO_SEGMENT) == true val item = DbMediaCollection.query(DbMediaCollection::id eq collection).firstOrNull()?.items?.filter { it.name eq itemParam }?.firstOrNull() - ?: throw ErrorStatusException(404, "Parameter '$PARAMETER_NAME_ITEM' is missing but required!'", ctx) + ?: throw ErrorStatusException(404, "Item '$PARAMETER_NAME_ITEM' not found'", ctx) val range: Pair? = when { map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> { val shot = map[PARAMETER_NAME_SHOT]?.first()!! diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt index e38fb13ad..0ea1eba60 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/InteractiveAsynchronousEvaluation.kt @@ -62,7 +62,13 @@ class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: init { /* Load all ongoing tasks. */ - this.dbEvaluation.tasks.asSequence().forEach { IATaskRun(it) } + this.dbEvaluation.tasks.asSequence().forEach { + try{ + IATaskRun(it) + } catch (e: Exception) { + LOGGER.error("could not load task: ${e.message}") + } + } /* Prepare the evaluation scoreboards. */ val teams = this.template.teams.asSequence().map { it.teamId }.toList() @@ -124,20 +130,20 @@ class InteractiveAsynchronousEvaluation(store: TransientEntityStore, evaluation: require(task.team != null) { "The task of an interactive asynchronous evaluation must be assigned to a single team." } } - /** - * Constructor used to generate an [IATaskRun] from a [DbTaskTemplate]. - * - * @param t [DbTaskTemplate] to generate [IATaskRun] from. - * @param teamId The [TeamId] this [IATaskRun] is created for. - */ - constructor(t: DbTaskTemplate, teamId: TeamId) : this(DbTask.new { - status = DbTaskStatus.CREATED - evaluation = this@InteractiveAsynchronousEvaluation.dbEvaluation - template = t - team = this@InteractiveAsynchronousEvaluation.dbEvaluation.template.teams.filter { it.teamId eq teamId } - .singleOrNull() - ?: throw IllegalArgumentException("Cannot start a new task run for team with ID ${teamId}. Team is not registered for competition.") - }) +// /** +// * Constructor used to generate an [IATaskRun] from a [DbTaskTemplate]. +// * +// * @param t [DbTaskTemplate] to generate [IATaskRun] from. +// * @param teamId The [TeamId] this [IATaskRun] is created for. +// */ +// constructor(t: DbTaskTemplate, teamId: TeamId) : this(DbTask.new { +// status = DbTaskStatus.CREATED +// evaluation = this@InteractiveAsynchronousEvaluation.dbEvaluation +// template = t +// team = this@InteractiveAsynchronousEvaluation.dbEvaluation.template.teams.filter { it.teamId eq teamId } +// .singleOrNull() +// ?: throw IllegalArgumentException("Cannot start a new task run for team with ID ${teamId}. Team is not registered for competition.") +// }) /** The [InteractiveAsynchronousEvaluation] this [IATaskRun] belongs to.*/ override val evaluationRun: InteractiveAsynchronousEvaluation diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt index 87f41db11..509bf1d6b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/RunActionContext.kt @@ -19,7 +19,7 @@ import io.javalin.http.Context * @author Ralph Gasser * @version 2.0.0 */ -data class RunActionContext(val userId: UserId, var evaluationId: EvaluationId?, val roles: Set) { +open class RunActionContext(val userId: UserId, var evaluationId: EvaluationId?, val roles: Set) { /** True if the user associated with this [RunActionContext] acts as [DbRole.ADMIN]*/ val isAdmin: Boolean diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index fcc4e3b42..e802432b6 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -16,6 +16,7 @@ import dev.dres.data.model.run.interfaces.TaskRun import dev.dres.data.model.submissions.* import dev.dres.data.model.template.task.TaskTemplateId import dev.dres.data.model.template.task.options.DbSubmissionOption +import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.TeamId import dev.dres.run.RunManager.Companion.MAXIMUM_RUN_LOOP_ERROR_COUNT import dev.dres.run.audit.AuditLogSource @@ -211,7 +212,12 @@ class InteractiveAsynchronousRunManager( * @return The [DbTaskTemplate] for the given team. */ override fun currentTaskTemplate(context: RunActionContext): ApiTaskTemplate { - return this.evaluation.currentTaskTemplate(context.teamId()) + val teamId = if (context is TeamRunActionContext) { + context.teamId + } else { + context.teamId() + } + return this.evaluation.currentTaskTemplate(teamId) } /** @@ -282,16 +288,6 @@ class InteractiveAsynchronousRunManager( //FIXME since task run and competition run states are separated, this is not actually a state change this.statusMap[teamId] = RunManagerStatus.ACTIVE -// /* Enqueue WS message for sending */ -// RunExecutor.broadcastWsMessage( -// teamId, -// ServerMessage( -// this.id, -// ServerMessageType.COMPETITION_UPDATE, -// this.evaluation.currentTaskForTeam(teamId)?.taskId -// ) -// ) - LOGGER.info("SynchronousRunManager ${this.id} set to task $idx") } @@ -320,20 +316,27 @@ class InteractiveAsynchronousRunManager( throw IllegalStateException("Task '${currentTaskTemplate.name}' has already been used") } + val dbTask = this.store.transactional { + val dbTaskTemplate = DbTaskTemplate.filter { it.id eq currentTaskTemplate.id!! }.first() + + /* create task, needs to be persisted before run can be created */ + val dbTask = DbTask.new { + status = DbTaskStatus.CREATED + evaluation = this@InteractiveAsynchronousRunManager.evaluation.dbEvaluation + template = dbTaskTemplate + team = DbTeam.filter { it.id eq teamId }.firstOrNull() + } + + dbTask + + } + val currentTaskRun = store.transactional { - val dbTaskTemplate = DbTaskTemplate.query(DbTaskTemplate::templateId eq currentTaskTemplate.id!!).first() - val currentTaskRun = this.evaluation.IATaskRun(dbTaskTemplate, teamId) + val currentTaskRun = this.evaluation.IATaskRun(dbTask) currentTaskRun.prepare() currentTaskRun } - -// /* Enqueue WS message for sending */ -// RunExecutor.broadcastWsMessage( -// teamId, -// ServerMessage(this.id, ServerMessageType.TASK_PREPARE, currentTaskRun.taskId) -// ) - LOGGER.info("Run manager ${this.id} started task $currentTaskTemplate.") currentTaskRun.taskId @@ -374,7 +377,7 @@ class InteractiveAsynchronousRunManager( * @return Time remaining until the task will end or -1, if no task is running. */ override fun timeLeft(context: RunActionContext): Long = this.stateLock.read { - val teamId = context.teamId() + val currentTaskRun = this.currentTask(context) return if (currentTaskRun?.isRunning == true) { @@ -447,7 +450,11 @@ class InteractiveAsynchronousRunManager( */ override fun currentTask(context: RunActionContext): InteractiveAsynchronousEvaluation.IATaskRun? = this.stateLock.read { - val teamId = context.teamId() + val teamId = if (context is TeamRunActionContext) { + context.teamId + } else { + context.teamId() + } return this.evaluation.currentTaskForTeam(teamId) } @@ -684,6 +691,8 @@ class InteractiveAsynchronousRunManager( LOGGER.info("InteractiveAsynchronousRunManager ${this.id} has reached end of run logic.") } + private data class TeamRunActionContext(val teamId: TeamId) : RunActionContext("", null, setOf(ApiRole.ADMIN)) + /** * Invokes all [Updatable]s registered with this [InteractiveSynchronousRunManager]. */ @@ -694,7 +703,7 @@ class InteractiveAsynchronousRunManager( val taskStatus = this.evaluation.currentTaskForTeam(teamId)?.status for (updatable in this.updatables) { if (updatable.shouldBeUpdated(runStatus, taskStatus)) { - updatable.update(runStatus, taskStatus, RunActionContext("SYSTEM", teamId, setOf(ApiRole.ADMIN))) + updatable.update(runStatus, taskStatus, TeamRunActionContext(teamId)) } } } @@ -745,7 +754,8 @@ class InteractiveAsynchronousRunManager( */ private fun registerOptionalUpdatables() { /* Determine if task should be ended once submission threshold per team is reached. */ - val endOnSubmit = this.template.taskTypes.flatMap { it.submissionOptions }.contains(ApiSubmissionOption.LIMIT_CORRECT_PER_TEAM) //FIXME should take into account all limits + val endOnSubmit = this.template.taskTypes.flatMap { it.submissionOptions } + .contains(ApiSubmissionOption.LIMIT_CORRECT_PER_TEAM) //FIXME should take into account all limits if (endOnSubmit) { this.updatables.add(EndOnSubmitUpdatable(this)) } @@ -793,6 +803,11 @@ class InteractiveAsynchronousRunManager( * @return [TeamId] */ private fun RunActionContext.teamId(): TeamId { + + if (this is TeamRunActionContext) { + return this.teamId + } + val userId = this.userId return this@InteractiveAsynchronousRunManager.template.teams.firstOrNull { team -> team.users.any { it.id == userId } }?.teamId ?: throw IllegalArgumentException("Could not find matching team for user, which is required for interaction with this run manager.") @@ -825,18 +840,16 @@ class InteractiveAsynchronousRunManager( val xdId = c.transientEntity.persistentEntity.id.toString() val task = DbAnswerSet.findById(xdId).task if (this.evaluation.id == task.evaluation.id) { - val t = this.taskForId(RunActionContext.INTERNAL, task.id) - if (t != null) { - t.scorer.invalidate() - this.scoreboards.forEach { it.invalidate() } -// RunExecutor.broadcastWsMessage( -// ServerMessage( -// this.id, -// ServerMessageType.TASK_UPDATED, -// task.id -// ) -// ) + val teamId = task.team?.teamId + if (teamId != null) { + val t = this.evaluation.tasksForTeam(teamId).find { it.taskId == task.id } + if (t != null) { + t.scorer.invalidate() + this.scoreboards.forEach { it.invalidate() } + + } } + } } } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index a6cbc9b4f..64a941f36 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -252,8 +252,6 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch this.readyLatch.reset(VIEWER_TIMEOUT_DEFAULT) } -// /* Enqueue WS message for sending */ -// RunExecutor.broadcastWsMessage(ServerMessage(this.id, ServerMessageType.TASK_PREPARE, this.evaluation.currentTask?.taskId)) LOGGER.info("SynchronousRunManager ${this.id} started task ${this.evaluation.currentTaskRun?.taskId} with task template ${this.evaluation.getCurrentTaskTemplate().id}.") diff --git a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt index 7b30b1c34..7d0fb8864 100644 --- a/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt +++ b/backend/src/main/kotlin/dev/dres/run/updatables/EndOnSubmitUpdatable.kt @@ -17,7 +17,7 @@ import kotlinx.dnq.query.* * @author Ralph Gasser * @version 1.2.0 */ -class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private val context: RunActionContext = RunActionContext.INTERNAL) : Updatable { +class EndOnSubmitUpdatable(private val manager: InteractiveRunManager) : Updatable { /** The [EndOnSubmitUpdatable] always belongs to the [Phase.MAIN]. */ override val phase: Phase = Phase.MAIN @@ -32,7 +32,7 @@ class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private v override fun update(runStatus: RunManagerStatus, taskStatus: ApiTaskStatus?, context: RunActionContext) { if (runStatus == RunManagerStatus.ACTIVE && taskStatus == ApiTaskStatus.RUNNING) { /* Get list of teams and list of submissions. */ - val currentTask = this.manager.currentTask(this.context) ?: return + val currentTask = this.manager.currentTask(context) ?: return /* Determine of endOnSubmit is true for given task. */ val taskType = this.manager.template.taskTypes.firstOrNull { it.name == currentTask.template.taskType }!! @@ -46,7 +46,7 @@ class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private v val limit = taskType.configuration["LIMIT_CORRECT_PER_TEAM"]?.toIntOrNull() ?: 1 /* Count number of correct submissions per team. */ - if (this.manager.timeLeft(this.context) > 0) { + if (this.manager.timeLeft(context) > 0) { for (s in submissions) { for (a in s.answerSets.toList()) { if (a.status == DbVerdictStatus.CORRECT) { @@ -57,7 +57,7 @@ class EndOnSubmitUpdatable(private val manager: InteractiveRunManager, private v /* If all teams have reached the limit, end the task. */ if (teams.all { it.value >= limit }) { - this.manager.abortTask(this.context) + this.manager.abortTask(context) } } } From 7d60b008cd2c0dd3a0589c89fe22fe2d548c0472 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 22 Sep 2023 11:58:10 +0200 Subject: [PATCH 477/498] Fixed loading of task target and task hint. --- .../evaluation/viewer/GetTaskHintHandler.kt | 128 +- .../evaluation/viewer/GetTaskTargetHandler.kt | 54 +- doc/oas.json | 13482 ++++++++-------- .../src/app/viewer/task-viewer.component.ts | 5 +- 4 files changed, 6816 insertions(+), 6853 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index e03296e8c..239432bdd 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -14,11 +14,11 @@ import dev.dres.api.rest.types.template.tasks.ApiHintType import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.run.DbTask +import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext -import dev.dres.data.model.template.task.DbHint import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.mgmt.cache.CacheManager -import dev.dres.run.InteractiveRunManager +import dev.dres.run.* import dev.dres.utilities.extensions.isAdmin import dev.dres.utilities.extensions.sessionToken import io.javalin.http.Context @@ -47,16 +47,16 @@ import java.util.* class GetTaskHintHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(), GetRestHandler { - override val route = "evaluation/{evaluationId}/{taskId}/hint" + override val route = "evaluation/{evaluationId}/template/task/{taskTemplateId}/hint" @OpenApi( - summary = "Returns the task hint for the specified task.", - path = "/api/v2/evaluation/{evaluationId}/{taskId}/hint", + summary = "Returns the task hint for the specified task template in the context of the provided evaluation.", + path = "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/hint", tags = ["Evaluation"], - operationId = OpenApiOperation.AUTO_GENERATE, + operationId = "getHintForTaskTemplateId", pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true), - OpenApiParam("taskId", String::class, "The task ID.", required = true) + OpenApiParam("evaluationId", String::class, "The ID of the evaluation.", required = true), + OpenApiParam("taskTemplateId", String::class, "The ID of the task template.", required = true) ], responses = [ OpenApiResponse("200", [OpenApiContent(ApiHintContent::class)]), @@ -67,23 +67,20 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiHintContent { - val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskId' not specified.", ctx) + val taskTemplateId = ctx.pathParamMap()["taskTemplateId"] ?: throw ErrorStatusException(400, "Parameter 'taskTemplateId' not specified.", ctx) val rac = ctx.runActionContext() return this.store.transactional(true) { - val manager = ctx.eligibleManagerForId() - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access Denied", ctx) - } + val manager = ctx.eligibleManagerForId() - val task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) - if(ctx.isParticipant() || ctx.isAdmin()) { - manager.viewerPreparing(taskId, rac, ViewerInfo(ctx.sessionToken()!!, ctx.ip())) - } + /* Performs (run manager specific) gate-keeping. */ + manager.executeGatekeeper(ctx, taskTemplateId, rac) + /* Find template and load hint. */ + val template = manager.evaluation.template.tasks.find { it.id == taskTemplateId } ?: throw ErrorStatusException(404, "Task template not found.", ctx) try { ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes - task.template.toTaskHint() + template.toTaskHint() } catch (e: FileNotFoundException) { throw ErrorStatusException(404, "Query object cache file not found!", ctx) } catch (ioe: IOException) { @@ -92,6 +89,28 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca } } + /** + * This function executes [RunManager] implementation specific checks and logic that is necessary in the context of this API call. + * + * @param ctx [Context] + * @param taskTemplateId The ID of the task template that is accessed. + * @param rac [RunActionContext] + */ + private fun RunManager.executeGatekeeper(ctx: Context, taskTemplateId: String, rac: RunActionContext) { + when(this) { + is InteractiveRunManager -> { + if (!this.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access Denied", ctx) + } + + if (ctx.isParticipant() || ctx.isAdmin()) { + this.viewerPreparing(taskTemplateId, rac, ViewerInfo(ctx.sessionToken()!!, ctx.ip())) + } + } + else -> { /* No op */ } + } + } + /** * Generates and returns a [ApiHintContent] object to be used by thi RESTful interface. Requires a valid transaction. * @@ -119,79 +138,6 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca ApiHintContent(this.id!!, sequence, false) } - /** - * Generates and returns a [ApiContentElement] object of this [DbHint] to be used by the RESTful interface. - * - * @return [ApiContentElement] - * - * @throws FileNotFoundException - * @throws IOException - */ -// private fun DbHint.toContentElement(): ApiContentElement { -// val content = when (this.type) { -// DbHintType.IMAGE -> { -// val path = if (this.item != null) { -// this@GetTaskHintHandler.cache.asyncPreviewImage(this.item!!) -// .get() /* This should return immediately, since the previews have been prepared. */ -// } else { -// this@GetTaskHintHandler.cache.asyncPreviewImage( -// DRES.EXTERNAL_ROOT.resolve( -// this.path -// ?: throw IllegalStateException("DbHint of type IMAGE is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") -// ) -// ).get() -// } -// if (Files.exists(path)) { -// if (path.toString().endsWith(".jpg", ignoreCase = true)) { -// Base64.getEncoder().encodeToString(Files.readAllBytes(path)) -// } else { //should never happen -// null -// } -// } else { -// null -// } -// } -// -// DbHintType.VIDEO -> { -// val start = this.temporalRangeStart -// ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid start timestamp but doesn't! This is a programmer's error!") -// val end = this.temporalRangeEnd -// ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid end timestamp but doesn't!! This is a programmer's error!") -// val path = if (this.item != null) { -// this@GetTaskHintHandler.cache.asyncPreviewVideo(this.item!!, start, end) -// .get() /* This should return immediately, since the previews have been prepared. */ -// } else { -// val source = DRES.EXTERNAL_ROOT.resolve( -// this.path -// ?: throw IllegalStateException("DbHint of type VIDEO is expected to hold a valid media item or external path but it doesn't! This is a programmer's error!") -// ) -// this@GetTaskHintHandler.cache.asyncPreviewVideo(source, start, end).get() -// } -// if (Files.exists(path)) { -// Base64.getEncoder().encodeToString(Files.readAllBytes(path)) -// } else { -// null -// } -// } -// -// DbHintType.TEXT -> this.text -// ?: throw IllegalStateException("A hint of type ${this.type.description} must have a valid text.") -// -// DbHintType.EMPTY -> "" -// else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") -// } -// -// val contentType = when (this.type) { -// DbHintType.IMAGE -> ApiContentType.IMAGE -// DbHintType.VIDEO -> ApiContentType.VIDEO -// DbHintType.TEXT -> ApiContentType.TEXT -// DbHintType.EMPTY -> ApiContentType.EMPTY -// else -> throw IllegalStateException("The hint type ${this.type.description} is not supported.") -// } -// -// return ApiContentElement(contentType = contentType, content = content, offset = this.start ?: 0L) -// } - private fun ApiHint.toContentElement(): ApiContentElement { //TODO find a better place for this lookup diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt index 1ccfec492..cbe7bc413 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskTargetHandler.kt @@ -11,7 +11,6 @@ import dev.dres.api.rest.types.task.ApiContentElement import dev.dres.api.rest.types.task.ApiContentType import dev.dres.api.rest.types.template.tasks.ApiTaskTemplate import dev.dres.data.model.media.DbMediaType -import dev.dres.data.model.run.DbTaskStatus import dev.dres.data.model.run.RunActionContext import dev.dres.data.model.run.RunActionContext.Companion.runActionContext import dev.dres.data.model.template.task.DbHint @@ -20,6 +19,7 @@ import dev.dres.data.model.template.task.DbTaskTemplate import dev.dres.data.model.template.task.DbTaskTemplateTarget import dev.dres.mgmt.cache.CacheManager import dev.dres.run.InteractiveRunManager +import dev.dres.run.RunManager import io.javalin.http.Context import io.javalin.openapi.* import jetbrains.exodus.database.TransientEntityStore @@ -39,16 +39,16 @@ import java.util.* */ class GetTaskTargetHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(), GetRestHandler { - override val route = "evaluation/{evaluationId}/{taskId}/target" + override val route = "evaluation/{evaluationId}/template/task/{taskTemplateId}/target" @OpenApi( - summary = "Returns the task target for the current task run (i.e. the one that is currently selected).", - path = "/api/v2/evaluation/{evaluationId}/{taskId}/target", - operationId = OpenApiOperation.AUTO_GENERATE, + summary = "Returns the task target for the specified task template in the context of the provided evaluation.", + path = "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/target", + operationId = "getTargetForTaskTemplateId", tags = ["Evaluation"], pathParams = [ - OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false), - OpenApiParam("taskId", String::class, "The task template ID.", required = true, allowEmptyValue = false) + OpenApiParam("evaluationId", String::class, "The ID of the evaluation.", required = true), + OpenApiParam("taskTemplateId", String::class, "The ID of the task template.", required = true) ], responses = [ OpenApiResponse("200", [OpenApiContent(ApiTargetContent::class)]), @@ -59,24 +59,21 @@ class GetTaskTargetHandler(private val store: TransientEntityStore, private val methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): ApiTargetContent { - val taskId = ctx.pathParamMap()["taskId"] ?: throw ErrorStatusException(400, "Parameter 'taskTemplateId' not specified.", ctx) + val taskTemplateId = ctx.pathParamMap()["taskTemplateId"] ?: throw ErrorStatusException(400, "Parameter 'taskTemplateId' not specified.", ctx) val rac = ctx.runActionContext() return this.store.transactional (true) { - val manager = ctx.eligibleManagerForId() - if (!manager.runProperties.participantCanView && ctx.isParticipant()) { - throw ErrorStatusException(403, "Access Denied", ctx) - } + val manager = ctx.eligibleManagerForId() - /* Test for correct state. */ - val task = manager.taskForId(rac, taskId) ?: throw ErrorStatusException(404, "Task with specified ID $taskId does not exist.", ctx) - if (task.status != ApiTaskStatus.ENDED) { - throw ErrorStatusException(400, "Query target can only be loaded if task has just ended.", ctx) - } + /* Performs (run manager specific) gate-keeping. */ + manager.executeGatekeeper(ctx, taskTemplateId, rac) + /* Accesses template and converts it to target. */ + val template = manager.evaluation.template.tasks.find { it.id == taskTemplateId } + ?: throw ErrorStatusException(404, "Task template not found.", ctx) try { ctx.header("Cache-Control", "public, max-age=300") //can be cached for 5 minutes - task.template.toTaskTarget() + template.toTaskTarget() } catch (e: FileNotFoundException) { throw ErrorStatusException(404, "Query object cache file not found!", ctx) } catch (ioe: IOException) { @@ -85,6 +82,27 @@ class GetTaskTargetHandler(private val store: TransientEntityStore, private val } } + /** + * This function executes [RunManager] implementation specific checks and logic that is necessary in the context of this API call. + * + * @param ctx [Context] + * @param taskTemplateId The ID of the task template that is accessed. + * @param rac [RunActionContext] + */ + private fun RunManager.executeGatekeeper(ctx: Context, taskTemplateId: String, rac: RunActionContext) { + when(this) { + is InteractiveRunManager -> { + if (!this.runProperties.participantCanView && ctx.isParticipant()) { + throw ErrorStatusException(403, "Access denied; you are not authorised to access the specified task target.", ctx) + } + if (!this.tasks(rac).any { it.taskTemplateId == taskTemplateId && it.status == ApiTaskStatus.ENDED }) { + throw ErrorStatusException(401, "Access denied; task target cannot be display if task has not been run yet.", ctx) + } + } + else -> { /* No op */ } + } + } + /** * Generates and returns a [ApiTargetContent] object to be used by the RESTful interface. * diff --git a/doc/oas.json b/doc/oas.json index 983bb6fb4..966c3cc0c 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1,6742 +1,6742 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : "DRES API", - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", - "contact" : { - "name" : "The DRES Dev Team", - "url" : "https://dres.dev" - }, - "license" : { - "name" : "MIT" - }, - "version" : "2.0.0" - }, - "paths" : { - "/api/v1/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "description" : "This has been the submission endpoint for version 1. Please refrain from using it and migrate to the v2 endpoint.", - "operationId" : "getApiV1Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : true, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "operationId" : "getApiV2CollectionList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/random" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "operationId" : "getApiV2CollectionByCollectionIdRandom", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", - "operationId" : "postApiV2CollectionByCollectionIdResolve", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", - "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", - "in" : "path", - "description" : "Name the item(s) should start with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given evaluation.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the verdict status of an AnswerSet.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "answerSetId", - "in" : "path", - "description" : "The ID of the AnswerSet.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiOverrideAnswerSetVerdictDto" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of an evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a list of available scoreboard names for the given evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/{taskId}/hint" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdHint", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/{taskId}/ready" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Signals that a viewer is ready to show the hints for a particular task.", - "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdReady", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/{taskId}/target" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdTarget", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/find/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", - "operationId" : "getApiV2ExternalFindByStartsWith", - "parameters" : [ { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection.", - "operationId" : "getApiV2ExternalList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/upload/{name}" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Receives a new external (media) item to be used in query hints.", - "operationId" : "postApiV2ExternalUploadByName", - "parameters" : [ { - "name" : "name", - "in" : "path", - "description" : "The name of the file", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "description" : "The file to upload.", - "content" : { - "image/png" : { - "schema" : { - "type" : "string", - "format" : "binary" - } - }, - "video/mp4" : { - "schema" : { - "type" : "string", - "format" : "binary" - } - } - }, - "required" : true - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/{name}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes the external item with this name, as far as it exists.", - "operationId" : "deleteApiV2ExternalByName", - "parameters" : [ { - "name" : "name", - "in" : "path", - "description" : "Filename of external item to delete", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "On success (the item is deleted)", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "For caller error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "If the caller has not the appropriate rights. Requires role admin", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "If no such external file exists", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a media item to the specified media collection.", - "operationId" : "postApiV2MediaItem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Tries to delete a specific media item.", - "operationId" : "deleteApiV2MediaItemByMediaId", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaItemId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "getApiV2MediaItemByMediaItemId", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Media item ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{start}/{end}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview video from a media item. ", - "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item. Must be a video.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "start", - "in" : "path", - "description" : "Start timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "end", - "in" : "path", - "description" : "End timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "video/mp4" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{timestamp}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview image from a media item.", - "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Time into the video in milliseconds (for videos only).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "image/jpeg" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the score overviews of a specific evaluation run.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/current" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeries" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns team group aggregated values of the current task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroupValue" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit/{evaluationId}" : { - "post" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions.", - "operationId" : "postApiV2SubmitByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The ID of the evaluation the submission belongs to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiClientSubmission" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "The submission was accepted by the server and there was a verdict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "The submission was accepted by the server and there has not yet been a verdict available", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "The submission was rejected by the server", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplateOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeam", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/list" : { - "get" : { - "tags" : [ "Template", "Team" ], - "summary" : "Lists all the teams across all evaluations.", - "operationId" : "getApiV2TemplateTeamList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/{teamId}" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeamByTeamId", - "parameters" : [ { - "name" : "teamId", - "in" : "path", - "description" : "The team ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/type-presets/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", - "operationId" : "getApiV2TemplateTypePresetsList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "409" : { - "description" : "Conflict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/clone" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Clones an existing evaluation template.", - "operationId" : "postApiV2TemplateByTemplateIdClone", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID to clone.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "409" : { - "description" : "Conflict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "operationId" : "getApiV2UserList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "operationId" : "getApiV2UserSessionActiveList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - }, - "itemCount" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "name", "itemCount" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "mediaItemId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - }, - "metadata" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" - } - } - }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] - }, - "ApiMediaItemMetaDataEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "key" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "key", "value" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "taskTemplates" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "type", "properties", "templateId", "teams", "taskTemplates" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "evaluationStatus" : { - "$ref" : "#/components/schemas/ApiEvaluationStatus" - }, - "taskId" : { - "type" : "string" - }, - "taskStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "taskTemplateId" : { - "type" : "string" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiOverrideAnswerSetVerdictDto" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "id", "status", "taskId", "answers" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiClientAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "text" : { - "type" : "string" - }, - "mediaItemId" : { - "type" : "string" - }, - "mediaItemName" : { - "type" : "string" - }, - "mediaItemCollectionName" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - } - }, - "ApiClientAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswer" - } - } - }, - "required" : [ "answers" ] - }, - "ApiClientSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "answerSets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswerSet" - } - } - }, - "required" : [ "answerSets" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "answerSet" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "required" : [ "validator", "taskDescription", "answerSet" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "created" : { - "type" : "integer", - "format" : "int64" - }, - "modified" : { - "type" : "integer", - "format" : "int64" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiEvaluationTemplateOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskCount" : { - "type" : "integer", - "format" : "int32" - }, - "teamCount" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "id", "name", "taskCount", "teamCount" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - }, - "comment" : { - "type" : "string" - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamAggregatorType" : { - "type" : "string", - "enum" : [ "MIN", "MAX", "MEAN", "LAST" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "$ref" : "#/components/schemas/ApiTeamAggregatorType" - } - }, - "required" : [ "teams", "aggregation" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "ApiUserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - } - }, - "securitySchemes" : { - "CookieAuth" : { - "in" : "cookie", - "name" : "SESSIONID", - "type" : "apiKey" - } - } - }, - "servers" : [ ], - "security" : [ ] +{ + "openapi" : "3.0.3", + "info" : { + "title" : "DRES API", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", + "contact" : { + "name" : "The DRES Dev Team", + "url" : "https://dres.dev" + }, + "license" : { + "name" : "MIT" + }, + "version" : "2.0.0" + }, + "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "description" : "This has been the submission endpoint for version 1. Please refrain from using it and migrate to the v2 endpoint.", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : true, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiV2CollectionList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiV2CollectionByCollectionIdRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", + "operationId" : "postApiV2CollectionByCollectionIdResolve", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name the item(s) should start with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiOverrideAnswerSetVerdictDto" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of an evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/hint" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task template in the context of the provided evaluation.", + "operationId" : "getHintForTaskTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskTemplateId", + "in" : "path", + "description" : "The ID of the task template.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/target" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the specified task template in the context of the provided evaluation.", + "operationId" : "getTargetForTaskTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskTemplateId", + "in" : "path", + "description" : "The ID of the task template.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/{taskId}/ready" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Signals that a viewer is ready to show the hints for a particular task.", + "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdReady", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/find/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2ExternalFindByStartsWith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection.", + "operationId" : "getApiV2ExternalList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/upload/{name}" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Receives a new external (media) item to be used in query hints.", + "operationId" : "postApiV2ExternalUploadByName", + "parameters" : [ { + "name" : "name", + "in" : "path", + "description" : "The name of the file", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "The file to upload.", + "content" : { + "image/png" : { + "schema" : { + "type" : "string", + "format" : "binary" + } + }, + "video/mp4" : { + "schema" : { + "type" : "string", + "format" : "binary" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/{name}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes the external item with this name, as far as it exists.", + "operationId" : "deleteApiV2ExternalByName", + "parameters" : [ { + "name" : "name", + "in" : "path", + "description" : "Filename of external item to delete", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "On success (the item is deleted)", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "For caller error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "If the caller has not the appropriate rights. Requires role admin", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "If no such external file exists", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaItemId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItemByMediaItemId", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Media item ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{start}/{end}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview video from a media item. ", + "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item. Must be a video.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "start", + "in" : "path", + "description" : "Start timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "end", + "in" : "path", + "description" : "End timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "video/mp4" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{timestamp}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview image from a media item.", + "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Time into the video in milliseconds (for videos only).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "image/jpeg" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/submit/{evaluationId}" : { + "post" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions.", + "operationId" : "postApiV2SubmitByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation the submission belongs to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiClientSubmission" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "The submission was accepted by the server and there was a verdict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "The submission was accepted by the server and there has not yet been a verdict available", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "The submission was rejected by the server", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplateOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", + "parameters" : [ { + "name" : "teamId", + "in" : "path", + "description" : "The team ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/type-presets/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", + "operationId" : "getApiV2TemplateTypePresetsList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/clone" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Clones an existing evaluation template.", + "operationId" : "postApiV2TemplateByTemplateIdClone", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID to clone.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + }, + "itemCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "name", "itemCount" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "mediaItemId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "metadata" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" + } + } + }, + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] + }, + "ApiMediaItemMetaDataEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "key" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "key", "value" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "taskTemplates" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "type", "properties", "templateId", "teams", "taskTemplates" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskId" : { + "type" : "string" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "taskTemplateId" : { + "type" : "string" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiOverrideAnswerSetVerdictDto" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + } + }, + "required" : [ "id", "status", "taskId", "answers" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "mediaItemId" : { + "type" : "string" + }, + "mediaItemName" : { + "type" : "string" + }, + "mediaItemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + } + }, + "required" : [ "answers" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "answerSet" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "required" : [ "validator", "taskDescription", "answerSet" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/RunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "created" : { + "type" : "integer", + "format" : "int64" + }, + "modified" : { + "type" : "integer", + "format" : "int64" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiEvaluationTemplateOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "id", "name", "taskCount", "teamCount" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + }, + "comment" : { + "type" : "string" + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamAggregatorType" : { + "type" : "string", + "enum" : [ "MIN", "MAX", "MEAN", "LAST" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "$ref" : "#/components/schemas/ApiTeamAggregatorType" + } + }, + "required" : [ "teams", "aggregation" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "ApiUserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + }, + "serverTimeStamp$backend" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "RunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + } + }, + "securitySchemes" : { + "CookieAuth" : { + "in" : "cookie", + "name" : "SESSIONID", + "type" : "apiKey" + } + } + }, + "servers" : [ ], + "security" : [ ] } \ No newline at end of file diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index b2eff2eea..c6402acfb 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -110,8 +110,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /* Observable for the current query hint. */ const currentTaskHint = this.taskChanged.pipe( mergeMap((task) => { - console.log("current task hint triggered", task) - return this.runService.getApiV2EvaluationByEvaluationIdByTaskIdHint(task.evaluationId, task.taskId).pipe( + return this.runService.getHintForTaskTemplateId(task.evaluationId, task.taskId).pipe( catchError((e) => { console.error("[TaskViewerComponent] Could not load current query hint due to an error.", e); return of(null); @@ -128,7 +127,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskTarget = this.taskEnded.pipe( withLatestFrom(this.evaluationId), switchMap(([task, evaluationId]) => - this.runService.getApiV2EvaluationByEvaluationIdByTaskIdTarget(evaluationId, task.taskId).pipe( + this.runService.getTargetForTaskTemplateId(evaluationId, task.taskId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); From bd8e493815d98727008a8876d0c7a35ec20ebc35 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Fri, 22 Sep 2023 12:56:41 +0200 Subject: [PATCH 478/498] Fixed wrong id type in request --- frontend/src/app/viewer/task-viewer.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index c6402acfb..a053a7fce 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -110,7 +110,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { /* Observable for the current query hint. */ const currentTaskHint = this.taskChanged.pipe( mergeMap((task) => { - return this.runService.getHintForTaskTemplateId(task.evaluationId, task.taskId).pipe( + return this.runService.getHintForTaskTemplateId(task.evaluationId, task.taskTemplateId).pipe( catchError((e) => { console.error("[TaskViewerComponent] Could not load current query hint due to an error.", e); return of(null); @@ -127,7 +127,7 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { const currentTaskTarget = this.taskEnded.pipe( withLatestFrom(this.evaluationId), switchMap(([task, evaluationId]) => - this.runService.getTargetForTaskTemplateId(evaluationId, task.taskId).pipe( + this.runService.getTargetForTaskTemplateId(evaluationId, task.taskTemplateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); From a5ea21a28f918b3b70e90fde11feacf268d335f6 Mon Sep 17 00:00:00 2001 From: Ralph Gasser Date: Fri, 22 Sep 2023 13:46:09 +0200 Subject: [PATCH 479/498] Fixed early loading of task hint. --- .../src/app/viewer/task-viewer.component.html | 16 ++++----- .../src/app/viewer/task-viewer.component.ts | 34 ++++++++++++------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/viewer/task-viewer.component.html b/frontend/src/app/viewer/task-viewer.component.html index be8c5e9e6..22e98e6c3 100644 --- a/frontend/src/app/viewer/task-viewer.component.html +++ b/frontend/src/app/viewer/task-viewer.component.html @@ -25,21 +25,21 @@

    {{ currentTaskName | async }} (Task is about to start!)

    -

    {{ currentTaskName | async }} ({{ toFormattedTime(timeLeft | async) }})

    +

    {{ currentHintContent | async }} ({{ toFormattedTime(timeLeft | async) }})

    - - - + + +

    {{ currentTaskName | async }} (Task complete)

    -
    - - - +
    + + +
    diff --git a/frontend/src/app/viewer/task-viewer.component.ts b/frontend/src/app/viewer/task-viewer.component.ts index c6402acfb..87c2f1142 100644 --- a/frontend/src/app/viewer/task-viewer.component.ts +++ b/frontend/src/app/viewer/task-viewer.component.ts @@ -2,7 +2,7 @@ import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } fro import {BehaviorSubject, combineLatest, from, interval, mergeMap, Observable, of, Subscription} from 'rxjs'; import { catchError, - delayWhen, + delayWhen, filter, map, repeat, shareReplay, @@ -77,10 +77,16 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { viewerState: BehaviorSubject = new BehaviorSubject(ViewerState.VIEWER_UNKNOWN); /** Reference to the current {@link ApiHintContent}. */ - currentTaskHint: Observable; + currentTaskHint = new BehaviorSubject(null); + + /** Subscription related to {@link currentTaskHint}. */ + currentTaskHintSubscription: Subscription + + /** Reference to the {@link ApiHintContent} of the current {@link ApiHintContent}. */ + currentHintContent: Observable; /** Reference to the current {@link ApiTargetContent}. */ - currentTaskTarget: Observable; + currentTargetContent: Observable; /** The subscription associated with the current viewer state. */ viewerStateSubscription: Subscription; @@ -108,9 +114,9 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { */ ngAfterViewInit(): void { /* Observable for the current query hint. */ - const currentTaskHint = this.taskChanged.pipe( + this.currentTaskHintSubscription = this.taskChanged.pipe( mergeMap((task) => { - return this.runService.getHintForTaskTemplateId(task.evaluationId, task.taskId).pipe( + return this.runService.getHintForTaskTemplateId(task.evaluationId, task.taskTemplateId).pipe( catchError((e) => { console.error("[TaskViewerComponent] Could not load current query hint due to an error.", e); return of(null); @@ -118,16 +124,17 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { ); } ), - tap((hint) => this.evaluationId.pipe(switchMap(evaluationId => this.runService.getApiV2EvaluationByEvaluationIdByTaskIdReady(evaluationId, hint.taskId))).subscribe()), - shareReplay({ bufferSize: 1, refCount: true }) - ); + tap((hint) => { + this.state.pipe(switchMap(state => this.runService.getApiV2EvaluationByEvaluationIdByTaskIdReady(state.evaluationId, state.taskTemplateId))).subscribe() + }) + ).subscribe(n => this.currentTaskHint.next(n)) /* Observable for the current query target. */ const currentTaskTarget = this.taskEnded.pipe( withLatestFrom(this.evaluationId), switchMap(([task, evaluationId]) => - this.runService.getTargetForTaskTemplateId(evaluationId, task.taskId).pipe( + this.runService.getTargetForTaskTemplateId(evaluationId, task.taskTemplateId).pipe( catchError((e) => { console.error('[TaskViewerComponent] Could not load current task target due to an error.', e); return of(null); @@ -172,8 +179,8 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { } }); - /** Map task target to representation used by viewer. */ - this.currentTaskTarget = currentTaskTarget.pipe( + /* Map task target to representation used by viewer. */ + this.currentTargetContent = currentTaskTarget.pipe( mergeMap((h: ApiHintContent) => { if (!h) { return from([]); @@ -186,7 +193,8 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { ); /* Map task hint to representation used by viewer. */ - this.currentTaskHint = currentTaskHint.pipe( + this.currentHintContent = this.currentTaskHint.pipe( + filter(h => h != null), mergeMap((hint) => { return this.timeElapsed.pipe( take(1), @@ -249,6 +257,8 @@ export class TaskViewerComponent implements AfterViewInit, OnDestroy { ngOnDestroy(): void { this.viewerStateSubscription.unsubscribe(); /* IMPORTANT! */ this.viewerStateSubscription = null; + this.currentTaskHintSubscription.unsubscribe() + this.currentTaskHintSubscription = null; } /** From 9b802a69156d7e7d1de7e903a7af2877ccb56569 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 23 Sep 2023 14:37:15 +0200 Subject: [PATCH 480/498] Added more api paths to exclude from client spec --- .../dev/dres/api/rest/ClientOpenApiPlugin.kt | 7 +- doc/oas-client.json | 758 ++---------------- 2 files changed, 76 insertions(+), 689 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt index ff34a5133..b256ba996 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/ClientOpenApiPlugin.kt @@ -35,9 +35,14 @@ class ClientOpenApiPlugin : OpenApiPlugin(OpenApiPluginConfiguration() "/evaluation/template", "/evaluation/{evaluationId}/judge", "/evaluation/{evaluationId}/vote", + "/evaluation/{evaluationId}/submission", + "/evaluation/{evaluationId}/task", + "/evaluation/{evaluationId}/{taskId}", "/download", "/mediaitem", - "/template" + "/template", + "/preview", + "/status/info" ) val relevantRoutes = diff --git a/doc/oas-client.json b/doc/oas-client.json index 29d756437..e307f0e19 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -10,6 +10,7 @@ "get" : { "tags" : [ "Submission" ], "summary" : "Endpoint to accept submissions", + "description" : "This has been the submission endpoint for version 1. Please refrain from using it and migrate to the v2 endpoint.", "operationId" : "getApiV1Submit", "parameters" : [ { "name" : "collection", @@ -146,7 +147,7 @@ } } }, - "deprecated" : false, + "deprecated" : true, "security" : [ ] } }, @@ -170,437 +171,9 @@ "200" : { "description" : "OK", "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/hint/{taskId}/ready" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Signals that a viewer is ready to show the hints for a particular task.", - "operationId" : "getApiV2EvaluationByEvaluationIdHintByTaskIdReady", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } } @@ -630,33 +203,12 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "/api/v2/client/evaluation/list" : { "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ ], "responses" : { "200" : { "description" : "OK", @@ -665,7 +217,7 @@ "schema" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiSubmission" + "$ref" : "#/components/schemas/ApiEvaluationInfo" } } } @@ -680,65 +232,28 @@ } } } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } } }, "deprecated" : false, "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/target/{taskId}" : { + "/api/v2/evaluation/info/list" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the current task run (i.e. the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTargetByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], "responses" : { "200" : { "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } } } } @@ -752,19 +267,34 @@ } } } - }, - "403" : { - "description" : "Forbidden", + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } } } } }, - "404" : { - "description" : "Not Found", + "401" : { + "description" : "Unauthorized", "content" : { "application/json" : { "schema" : { @@ -778,11 +308,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/task" : { + "/api/v2/evaluation/{evaluationId}/info" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -800,7 +330,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + "$ref" : "#/components/schemas/ApiEvaluationInfo" } } } @@ -840,11 +370,11 @@ "security" : [ ] } }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "/api/v2/evaluation/{evaluationId}/state" : { "get" : { "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", "parameters" : [ { "name" : "evaluationId", "in" : "path", @@ -855,16 +385,6 @@ "schema" : { "type" : "string" } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } } ], "responses" : { "200" : { @@ -872,10 +392,7 @@ "content" : { "application/json" : { "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } + "$ref" : "#/components/schemas/ApiEvaluationState" } } } @@ -1133,145 +650,6 @@ "security" : [ ] } }, - "/api/v2/preview/{mediaItemId}/{start}/{end}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview video from a media item. ", - "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item. Must be a video.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "start", - "in" : "path", - "description" : "Start timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "end", - "in" : "path", - "description" : "End timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "video/mp4" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{timestamp}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview image from a media item.", - "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Time into the video in milliseconds (for videos only).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "image/jpeg" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, "/api/v2/status/time" : { "get" : { "tags" : [ "Status" ], @@ -1396,7 +774,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserRequest" + "$ref" : "#/components/schemas/ApiUserRequest" } } }, @@ -1580,7 +958,7 @@ "content" : { "application/json" : { "schema" : { - "$ref" : "#/components/schemas/UserRequest" + "$ref" : "#/components/schemas/ApiUserRequest" } } }, @@ -1713,9 +1091,13 @@ }, "basePath" : { "type" : "string" + }, + "itemCount" : { + "type" : "integer", + "format" : "int32" } }, - "required" : [ "name" ] + "required" : [ "name", "itemCount" ] }, "ApiMediaItem" : { "type" : "object", @@ -1826,32 +1208,32 @@ "name" : { "type" : "string" }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, "type" : { "$ref" : "#/components/schemas/ApiEvaluationType" }, "properties" : { "$ref" : "#/components/schemas/RunProperties" }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, "teams" : { "type" : "array", "items" : { "$ref" : "#/components/schemas/ApiTeamInfo" } }, - "tasks" : { + "taskTemplates" : { "type" : "array", "items" : { "$ref" : "#/components/schemas/ApiTaskTemplateInfo" } } }, - "required" : [ "id", "name", "templateId", "type", "properties", "teams", "tasks" ] + "required" : [ "id", "name", "type", "properties", "templateId", "teams", "taskTemplates" ] }, "ApiEvaluationOverview" : { "type" : "object", @@ -1879,11 +1261,14 @@ "evaluationStatus" : { "$ref" : "#/components/schemas/ApiEvaluationStatus" }, + "taskId" : { + "type" : "string" + }, "taskStatus" : { "$ref" : "#/components/schemas/ApiTaskStatus" }, - "currentTemplate" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + "taskTemplateId" : { + "type" : "string" }, "timeLeft" : { "type" : "integer", @@ -2718,12 +2103,9 @@ }, "comment" : { "type" : "string" - }, - "templateId" : { - "type" : "string" } }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment", "templateId" ] + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] }, "ApiTaskType" : { "type" : "object", @@ -2865,7 +2247,7 @@ } } }, - "UserRequest" : { + "ApiUserRequest" : { "type" : "object", "additionalProperties" : false, "properties" : { From 4661b50ee73476d80c700c7577b5e3ed94d57b42 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 23 Sep 2023 16:31:11 +0200 Subject: [PATCH 481/498] Some API cleanup --- .../admin/AdjustPropertiesHandler.kt | 9 ++- .../client/ClientListEvaluationsHandler.kt | 18 ++---- .../client/ClientTaskInfoHandler.kt | 3 + .../handler/submission/SubmissionHandler.kt | 5 +- .../types/evaluation/ApiEvaluationInfo.kt | 6 +- .../evaluation/submission/ApiClientAnswer.kt | 6 +- .../template/ApiEvaluationStartMessage.kt | 4 +- .../dev/dres/data/model/log/ParticipantLog.kt | 3 + .../{RunProperties.kt => ApiRunProperties.kt} | 2 +- .../run/InteractiveAsynchronousRunManager.kt | 8 +-- .../run/InteractiveSynchronousRunManager.kt | 12 ++-- .../dev/dres/run/NonInteractiveRunManager.kt | 8 +-- .../main/kotlin/dev/dres/run/RunManager.kt | 9 ++- doc/oas-client.json | 57 +++++++++++++------ 14 files changed, 89 insertions(+), 61 deletions(-) rename backend/src/main/kotlin/dev/dres/data/model/run/{RunProperties.kt => ApiRunProperties.kt} (77%) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt index 79626897f..e1923238e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/AdjustPropertiesHandler.kt @@ -5,16 +5,15 @@ import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.api.rest.types.status.SuccessStatus import dev.dres.data.model.run.DbEvaluation -import dev.dres.data.model.run.RunProperties +import dev.dres.data.model.run.ApiRunProperties import dev.dres.utilities.extensions.evaluationId import io.javalin.http.BadRequestResponse import io.javalin.http.Context import io.javalin.http.bodyAsClass import io.javalin.openapi.* -import jetbrains.exodus.database.TransientEntityStore /** - * A [PatchRestHandler] handler to adjust an ongoing [DbEvaluation]'s [RunProperties]. + * A [PatchRestHandler] handler to adjust an ongoing [DbEvaluation]'s [ApiRunProperties]. * * @author Ralph Gasser * @author Luca Rossetto @@ -32,7 +31,7 @@ class AdjustPropertiesHandler : AbstractEvaluationAdminHandler(), PatchRestHandl pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), ], - requestBody = OpenApiRequestBody([OpenApiContent(RunProperties::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiRunProperties::class)]), tags = ["Evaluation Administrator"], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessStatus::class)]), @@ -43,7 +42,7 @@ class AdjustPropertiesHandler : AbstractEvaluationAdminHandler(), PatchRestHandl ) override fun doPatch(ctx: Context): SuccessStatus { val properties = try { - ctx.bodyAsClass() + ctx.bodyAsClass() } catch (e: BadRequestResponse) { throw ErrorStatusException(400, "Invalid parameters. This is a programmers error!", ctx) } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt index 7bf97bcc1..f0679b735 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientListEvaluationsHandler.kt @@ -31,24 +31,14 @@ class ClientListEvaluationsHandler : AbstractEvaluationClientHandler(), GetRestH OpenApiResponse("200", [OpenApiContent(Array::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]) ], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], methods = [HttpMethod.GET] ) override fun doGet(ctx: Context): List { return getRelevantManagers(ctx).map { - ApiEvaluationInfo( - id = it.evaluation.id, - name = it.name, - when (it) { - is InteractiveAsynchronousRunManager -> ApiEvaluationType.ASYNCHRONOUS - is InteractiveSynchronousRunManager -> ApiEvaluationType.SYNCHRONOUS - else -> TODO() - }, - properties = it.runProperties, - templateId = it.template.id, - templateDescription = it.template.description, - teams = emptyList(), - taskTemplates = emptyList() - ) + ApiEvaluationInfo(it) } } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt index 8af9f1707..66b1cee59 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/client/ClientTaskInfoHandler.kt @@ -32,6 +32,9 @@ class ClientTaskInfoHandler : AbstractEvaluationClientHandler(), pathParams = [ OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true, allowEmptyValue = false) ], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], responses = [ OpenApiResponse("200", [OpenApiContent(ApiTaskTemplateInfo::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt index f8595c892..bab979137 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/submission/SubmissionHandler.kt @@ -47,10 +47,13 @@ class SubmissionHandler(private val store: TransientEntityStore): PostRestHandle path = "/api/v2/submit/{evaluationId}", methods = [HttpMethod.POST], operationId = OpenApiOperation.AUTO_GENERATE, - requestBody = OpenApiRequestBody([OpenApiContent(ApiClientSubmission::class)]), + requestBody = OpenApiRequestBody([OpenApiContent(ApiClientSubmission::class)], required = true), pathParams = [ OpenApiParam("evaluationId", String::class, "The ID of the evaluation the submission belongs to.", required = true), ], + queryParams = [ + OpenApiParam("session", String::class, "Session Token") + ], responses = [ OpenApiResponse("200", [OpenApiContent(SuccessfulSubmissionsStatus::class)], description = "The submission was accepted by the server and there was a verdict"), OpenApiResponse("202", [OpenApiContent(SuccessfulSubmissionsStatus::class)],description = "The submission was accepted by the server and there has not yet been a verdict available"), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt index 36c496f05..a250552ba 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiEvaluationInfo.kt @@ -1,6 +1,6 @@ package dev.dres.api.rest.types.evaluation -import dev.dres.data.model.run.RunProperties +import dev.dres.data.model.run.ApiRunProperties import dev.dres.run.InteractiveAsynchronousRunManager import dev.dres.run.InteractiveSynchronousRunManager import dev.dres.run.NonInteractiveRunManager @@ -18,7 +18,8 @@ data class ApiEvaluationInfo( val id: String, val name: String, val type: ApiEvaluationType, - val properties: RunProperties, // FIXME non-api type exposed via + val status: ApiEvaluationStatus, + val properties: ApiRunProperties, val templateId: String, val templateDescription: String?, val teams: List, @@ -33,6 +34,7 @@ data class ApiEvaluationInfo( is NonInteractiveRunManager -> ApiEvaluationType.NON_INTERACTIVE else -> throw IllegalStateException("Incompatible type of run manager.") }, + manager.status.toApi(), manager.runProperties, manager.template.id, manager.template.description, diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt index 621ab378d..beddcaeb0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/submission/ApiClientAnswer.kt @@ -1,9 +1,11 @@ package dev.dres.api.rest.types.evaluation.submission +import com.fasterxml.jackson.annotation.JsonIgnore import dev.dres.data.model.media.DbMediaItem import dev.dres.data.model.media.MediaItemId import dev.dres.data.model.submissions.DbAnswer import dev.dres.data.model.submissions.DbAnswerType +import io.javalin.openapi.OpenApiIgnore import kotlinx.dnq.query.filter import kotlinx.dnq.query.singleOrNull @@ -20,6 +22,8 @@ data class ApiClientAnswer( val text: String? = null, /** The [MediaItemId] associated with the [ApiClientAnswer]. Is usually added as contextual information by the receiving endpoint. */ + @JsonIgnore + @get:OpenApiIgnore val mediaItemId: MediaItemId? = null, /** The name of the media item that is part of the answer. */ @@ -65,7 +69,7 @@ data class ApiClientAnswer( * * @return The [DbAnswerType] for this [ApiClientAnswer]. */ - fun tryDetermineType() = when { + private fun tryDetermineType() = when { this.mediaItemName != null && this.start != null && this.end != null -> DbAnswerType.TEMPORAL this.mediaItemName != null -> DbAnswerType.ITEM this.text != null -> DbAnswerType.TEXT diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationStartMessage.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationStartMessage.kt index 3dd4f0bda..6058b549d 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationStartMessage.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/ApiEvaluationStartMessage.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.types.template import dev.dres.api.rest.types.evaluation.ApiEvaluationType -import dev.dres.data.model.run.RunProperties +import dev.dres.data.model.run.ApiRunProperties import dev.dres.data.model.template.TemplateId /** @@ -10,4 +10,4 @@ import dev.dres.data.model.template.TemplateId * @author Ralph Gasser * @version 1.1.0 */ -data class ApiEvaluationStartMessage(val templateId: TemplateId, val name: String, val type: ApiEvaluationType, val properties: RunProperties = RunProperties()) +data class ApiEvaluationStartMessage(val templateId: TemplateId, val name: String, val type: ApiEvaluationType, val properties: ApiRunProperties = ApiRunProperties()) diff --git a/backend/src/main/kotlin/dev/dres/data/model/log/ParticipantLog.kt b/backend/src/main/kotlin/dev/dres/data/model/log/ParticipantLog.kt index a2756efc3..cfcbd27db 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/log/ParticipantLog.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/log/ParticipantLog.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonSetter import com.fasterxml.jackson.annotation.Nulls +import io.javalin.openapi.OpenApiIgnore enum class QueryEventCategory { TEXT, IMAGE, SKETCH, FILTER, BROWSING, COOPERATION, OTHER @@ -24,6 +25,7 @@ data class QueryEventLog internal constructor( val events: List = emptyList(), @field:JsonIgnore @get:JsonIgnore + @get:OpenApiIgnore internal val serverTimeStamp: Long = System.currentTimeMillis() ) @@ -47,5 +49,6 @@ data class QueryResultLog internal constructor( val events: List = emptyList(), @field:JsonIgnore @get:JsonIgnore + @get:OpenApiIgnore internal val serverTimeStamp: Long = System.currentTimeMillis() ) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt b/backend/src/main/kotlin/dev/dres/data/model/run/ApiRunProperties.kt similarity index 77% rename from backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt rename to backend/src/main/kotlin/dev/dres/data/model/run/ApiRunProperties.kt index 24640798f..7d0e93ae0 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/RunProperties.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/ApiRunProperties.kt @@ -3,7 +3,7 @@ package dev.dres.data.model.run /** * */ -data class RunProperties ( // TODO shoudln't we move this to db and api ? +data class ApiRunProperties ( val participantCanView: Boolean = true, val shuffleTasks: Boolean = false, //is only used for asynchronous runs val allowRepeatedTasks: Boolean = false, diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt index e802432b6..f78257b35 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveAsynchronousRunManager.kt @@ -63,9 +63,9 @@ class InteractiveAsynchronousRunManager( private val LOGGER = LoggerFactory.getLogger(InteractiveAsynchronousRunManager::class.java) } - /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ - override val runProperties: RunProperties - get() = RunProperties( + /** Generates and returns [ApiRunProperties] for this [InteractiveAsynchronousRunManager]. */ + override val runProperties: ApiRunProperties + get() = ApiRunProperties( this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, @@ -200,7 +200,7 @@ class InteractiveAsynchronousRunManager( /** * */ - override fun updateProperties(properties: RunProperties) { + override fun updateProperties(properties: ApiRunProperties) { TODO("Not yet implemented") } diff --git a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt index 64a941f36..9a8125c62 100644 --- a/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/InteractiveSynchronousRunManager.kt @@ -55,9 +55,9 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch private val LOGGER = LoggerFactory.getLogger(InteractiveSynchronousRunManager::class.java) } - /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ - override val runProperties: RunProperties - get() = RunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) + /** Generates and returns [ApiRunProperties] for this [InteractiveAsynchronousRunManager]. */ + override val runProperties: ApiRunProperties + get() = ApiRunProperties(this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, this.evaluation.limitSubmissionPreviews) /** [EvaluationId] of this [InteractiveSynchronousRunManager]. */ override val id: EvaluationId @@ -154,11 +154,11 @@ class InteractiveSynchronousRunManager(override val evaluation: InteractiveSynch } /** - * Updates the [RunProperties] for the [InteractiveSynchronousEvaluation] backing this [InteractiveSynchronousRunManager]. + * Updates the [ApiRunProperties] for the [InteractiveSynchronousEvaluation] backing this [InteractiveSynchronousRunManager]. * - * @param properties The set of new [RunProperties] + * @param properties The set of new [ApiRunProperties] */ - override fun updateProperties(properties: RunProperties) { + override fun updateProperties(properties: ApiRunProperties) { store.transactional { this.evaluation.allowRepeatedTasks = properties.allowRepeatedTasks this.evaluation.limitSubmissionPreviews = properties.limitSubmissionPreviews diff --git a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt index 3cbc200df..815238462 100644 --- a/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/NonInteractiveRunManager.kt @@ -18,9 +18,9 @@ class NonInteractiveRunManager( private val LOGGER = LoggerFactory.getLogger(this.javaClass) - /** Generates and returns [RunProperties] for this [InteractiveAsynchronousRunManager]. */ - override val runProperties: RunProperties - get() = RunProperties( + /** Generates and returns [ApiRunProperties] for this [InteractiveAsynchronousRunManager]. */ + override val runProperties: ApiRunProperties + get() = ApiRunProperties( this.evaluation.participantCanView, false, this.evaluation.allowRepeatedTasks, @@ -85,7 +85,7 @@ class NonInteractiveRunManager( LOGGER.info("SynchronousRunManager ${this.id} terminated") } - override fun updateProperties(properties: RunProperties) { + override fun updateProperties(properties: ApiRunProperties) { TODO("Not yet implemented") } diff --git a/backend/src/main/kotlin/dev/dres/run/RunManager.kt b/backend/src/main/kotlin/dev/dres/run/RunManager.kt index 5bdefdfb6..1e5ba2a24 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunManager.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunManager.kt @@ -3,7 +3,6 @@ package dev.dres.run import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.api.rest.types.evaluation.submission.ApiSubmission -import dev.dres.api.rest.types.evaluation.websocket.ClientMessage import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.run.* import dev.dres.data.model.run.interfaces.EvaluationId @@ -54,7 +53,7 @@ interface RunManager : Runnable { val judgementValidators: List /** [JudgementValidator]s for all tasks that use them */ - val runProperties: RunProperties + val runProperties: ApiRunProperties /** The [TransientEntityStore] that backs this [InteractiveRunManager]. */ val store: TransientEntityStore @@ -84,11 +83,11 @@ interface RunManager : Runnable { fun end(context: RunActionContext) /** - * Updates the [RunProperties] for this [RunManager]. + * Updates the [ApiRunProperties] for this [RunManager]. * - * @param properties The new [RunProperties] + * @param properties The new [ApiRunProperties] */ - fun updateProperties(properties: RunProperties) + fun updateProperties(properties: ApiRunProperties) /** * Returns the number of [DbTask]s held by this [RunManager]. diff --git a/doc/oas-client.json b/doc/oas-client.json index e307f0e19..beb324887 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -166,6 +166,16 @@ "schema" : { "type" : "string" } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } } ], "responses" : { "200" : { @@ -208,7 +218,17 @@ "tags" : [ "Evaluation Client" ], "summary" : "Lists an overview of all evaluation runs visible to the current client.", "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], "responses" : { "200" : { "description" : "OK", @@ -687,6 +707,16 @@ "schema" : { "type" : "string" } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } } ], "requestBody" : { "content" : { @@ -696,7 +726,7 @@ } } }, - "required" : false + "required" : true }, "responses" : { "200" : { @@ -1211,8 +1241,11 @@ "type" : { "$ref" : "#/components/schemas/ApiEvaluationType" }, + "status" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, "properties" : { - "$ref" : "#/components/schemas/RunProperties" + "$ref" : "#/components/schemas/ApiRunProperties" }, "templateId" : { "type" : "string" @@ -1233,7 +1266,7 @@ } } }, - "required" : [ "id", "name", "type", "properties", "templateId", "teams", "taskTemplates" ] + "required" : [ "id", "name", "type", "status", "properties", "templateId", "teams", "taskTemplates" ] }, "ApiEvaluationOverview" : { "type" : "object", @@ -1872,7 +1905,7 @@ "$ref" : "#/components/schemas/ApiEvaluationType" }, "properties" : { - "$ref" : "#/components/schemas/RunProperties" + "$ref" : "#/components/schemas/ApiRunProperties" } }, "required" : [ "templateId", "name", "type", "properties" ] @@ -2300,13 +2333,9 @@ "items" : { "$ref" : "#/components/schemas/QueryEvent" } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" } }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] + "required" : [ "timestamp", "events" ] }, "QueryResult" : { "type" : "object", @@ -2359,13 +2388,9 @@ "items" : { "$ref" : "#/components/schemas/QueryEvent" } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" } }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events" ] }, "TemporalPoint" : { "type" : "object", @@ -2389,7 +2414,7 @@ }, "required" : [ "start", "end", "center" ] }, - "RunProperties" : { + "ApiRunProperties" : { "type" : "object", "additionalProperties" : false, "properties" : { From 6612290d26ccb5714cc2976df2f5df561e7169f8 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Thu, 28 Sep 2023 17:04:37 +0200 Subject: [PATCH 482/498] Added tree based import UI, import NOT YET WORKING --- .../upload-json-button.component.html | 6 +- .../upload-json-button.component.ts | 2 + .../template-import-dialog.component.html | 9 ++ .../template-import-dialog.component.scss | 0 .../template-import-dialog.component.ts | 56 +++++++ .../template-import-tree.component.html | 4 +- .../template-import-tree.component.ts | 152 +++++++++++++++--- .../template-builder.component.html | 11 ++ .../template-builder.component.ts | 58 ++++++- .../template-builder.module.ts | 4 +- 10 files changed, 265 insertions(+), 37 deletions(-) create mode 100644 frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.html create mode 100644 frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.scss create mode 100644 frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.ts diff --git a/frontend/src/app/shared/upload-json-button/upload-json-button.component.html b/frontend/src/app/shared/upload-json-button/upload-json-button.component.html index ee9a97f5a..f4b3b2ce7 100644 --- a/frontend/src/app/shared/upload-json-button/upload-json-button.component.html +++ b/frontend/src/app/shared/upload-json-button/upload-json-button.component.html @@ -1,4 +1,8 @@ - + + diff --git a/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts b/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts index abb790e48..9a5b3a490 100644 --- a/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts +++ b/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts @@ -7,6 +7,8 @@ import { MatButton } from '@angular/material/button'; styleUrls: ['./upload-json-button.component.scss'], }) export class UploadJsonButtonComponent { + + @Input() inline = false; /** The display name for the button. Defaults to 'Upload' */ @Input() name = 'Upload'; /** If multi-select files are enabled. Defaults to false (only single file) */ diff --git a/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.html b/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.html new file mode 100644 index 000000000..1129c4367 --- /dev/null +++ b/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.html @@ -0,0 +1,9 @@ +

    Import {{title()}}

    +
    + + +
    +
    + + +
    diff --git a/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.scss b/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.ts b/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.ts new file mode 100644 index 000000000..1a1c43ddd --- /dev/null +++ b/frontend/src/app/template/template-builder/components/template-import-dialog/template-import-dialog.component.ts @@ -0,0 +1,56 @@ +import { Component, Inject, ViewChild } from "@angular/core"; +import { ApiEvaluationTemplate } from "../../../../../../openapi"; +import { TemplateImportTreeBranch, TemplateImportTreeComponent } from "../template-import-tree/template-import-tree.component"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; + +export interface TemplateImportDialogData{ + templates: ApiEvaluationTemplate[]; + branches: TemplateImportTreeBranch; +} + +@Component({ + selector: 'app-template-import-dialog', + templateUrl: './template-import-dialog.component.html', + styleUrls: ['./template-import-dialog.component.scss'] +}) +export class TemplateImportDialogComponent { + + @ViewChild('templateImportTree',{static: true}) importTree: TemplateImportTreeComponent + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: TemplateImportDialogData + ) { + } + + title(){ + switch(this.data.branches){ + case TemplateImportTreeBranch.NONE: + return "---NONE -- Programmer's Error---"; + case TemplateImportTreeBranch.TASK_TYPES: + return "Task Types" + case TemplateImportTreeBranch.TASK_GROUPS: + return "Task Groups" + case TemplateImportTreeBranch.TASK_TEMPLATES: + return "Task Templates" + case TemplateImportTreeBranch.TEAMS: + return "Teams" + case TemplateImportTreeBranch.TEAM_GROUPS: + return "Team Groups" + case TemplateImportTreeBranch.JUDGES: + return "Judges" + case TemplateImportTreeBranch.ALL: + return "Evaluation Templates" + } + } + + public save(){ + this.dialogRef.close(this.importTree.getImportTemplate()) + } + + public close(){ + this.dialogRef.close(); + } + + +} diff --git a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.html b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.html index 4d385d802..fed42af1d 100644 --- a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.html +++ b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.html @@ -3,7 +3,7 @@ {{node.item}} + (change)="leafItemSelectionToggle(node)">{{node.label}} @@ -15,6 +15,6 @@ {{node.item}} + (change)="itemSelectionToggle(node)">{{node.label}} diff --git a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts index 21e63d770..7fcd9cf96 100644 --- a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts +++ b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts @@ -1,6 +1,7 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input, OnInit } from "@angular/core"; import { - ApiEvaluationTemplate, + ApiEvaluationStatus, + ApiEvaluationTemplate, ApiEvaluationTemplateOverview, ApiTaskGroup, ApiTaskTemplate, ApiTaskType, @@ -14,7 +15,7 @@ import { SelectionModel } from "@angular/cdk/collections"; /* See https://v15.material.angular.io/components/tree/examples */ -export enum TemplateImportTreeBranches { +export enum TemplateImportTreeBranch { NONE = 0, // 000000 TASK_TYPES = 1 << 0, // 000001 TASK_GROUPS = 1 << 1, // 000010 @@ -33,12 +34,15 @@ export class TemplateTreeFlatNode { expandable: boolean; item: T; label: string; + branch: TemplateImportTreeBranch } export class TemplateTreeNode { children: TemplateTreeNode[] | null; item: T; label: string; + branch: TemplateImportTreeBranch; + origin: string; // TemplateID } @Component({ @@ -46,11 +50,13 @@ export class TemplateTreeNode { templateUrl: "./template-import-tree.component.html", styleUrls: ["./template-import-tree.component.scss"] }) -export class TemplateImportTreeComponent { +export class TemplateImportTreeComponent implements OnInit{ flatNodeMap = new Map, TemplateTreeNode>(); nestedNodeMap = new Map, TemplateTreeFlatNode>(); + templatesMap = new Map(); + selectedParent: TemplateTreeFlatNode | null = null; treeControl: FlatTreeControl> @@ -60,9 +66,9 @@ export class TemplateImportTreeComponent { selection = new SelectionModel>(true); @Input() - template: ApiEvaluationTemplate; + templates: ApiEvaluationTemplate[]; @Input() - branches: TemplateImportTreeBranches; + branches: TemplateImportTreeBranch; constructor() { this.treeFlattener = new MatTreeFlattener, TemplateTreeFlatNode>( @@ -70,9 +76,14 @@ export class TemplateImportTreeComponent { ) this.treeControl = new FlatTreeControl>(this.getLevel, this.isExpandable); this.dataSource = new MatTreeFlatDataSource, TemplateTreeFlatNode>(this.treeControl, this.treeFlattener); - this.dataSource.data = TemplateImportTreeComponent.buildTree(this.template, this.branches); + } + ngOnInit(): void { + this.dataSource.data = TemplateImportTreeComponent.buildTrees(this.templates, this.branches); + this.templates.forEach(it => this.templatesMap.set(it.id, it)); + } + getLevel = (node: TemplateTreeFlatNode) => node.level; isExpandable = (node: TemplateTreeFlatNode) => node.expandable; getChildren = (node: TemplateTreeNode) => node.children; @@ -84,6 +95,8 @@ export class TemplateImportTreeComponent { flatNode.item = node.item; flatNode.level = level; flatNode.expandable = !!node.children?.length; + flatNode.branch = node.branch; + flatNode.label = node.label; this.flatNodeMap.set(flatNode, node); this.nestedNodeMap.set(node, flatNode); return flatNode; @@ -106,7 +119,7 @@ export class TemplateImportTreeComponent { } /** Toggle the to-do item selection. Select/deselect all the descendants node */ - todoItemSelectionToggle(node: TemplateTreeFlatNode): void { + itemSelectionToggle(node: TemplateTreeFlatNode): void { this.selection.toggle(node); const descendants = this.treeControl.getDescendants(node); this.selection.isSelected(node) @@ -119,7 +132,7 @@ export class TemplateImportTreeComponent { } /** Toggle a leaf to-do item selection. Check all the parents to see if they changed */ - todoLeafItemSelectionToggle(node: TemplateTreeFlatNode): void { + leafItemSelectionToggle(node: TemplateTreeFlatNode): void { this.selection.toggle(node); this.checkAllParentsSelection(node); } @@ -169,61 +182,148 @@ export class TemplateImportTreeComponent { return null; } - public static buildTree(template: ApiEvaluationTemplate, branches: TemplateImportTreeBranches): TemplateTreeNode[] { + public getImportTemplate(){ + const template = { + name: "", + description: "---Automatically generated template whose elements get imported. If this is seen, there was a programmer's error somewhere---", + taskTypes: this.getAllSelectedTaskTypes(), + taskGroups: this.getAllSelectedTaskGroups(), + tasks: this.getAllSelectedTaskTemplates(), + teams: this.getAllSelectedTeams(), + teamGroups: this.getAllSelectedTeamGroups(), + judges: this.getAllSelectedJudges(), + id:"---IMPORT_TEMPLATE_NO_ID---" + } as ApiEvaluationTemplate + + /* Sanitisation: For each task, the group and type is required */ + return template; + } + + public getAllSelectedTaskTypes(){ + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TYPES) as ApiTaskType[] + } + + public getAllSelectedTaskGroups(){ + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_GROUPS) as ApiTaskGroup[]; + } + + public getAllSelectedTaskTemplates(){ + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TEMPLATES) as ApiTaskTemplate[] + } + + public getAllSelectedTeams(){ + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAMS) as ApiTeam[] + } + + public getAllSelectedTeamGroups(){ + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAM_GROUPS) as ApiTeamGroup[] + } + + public getAllSelectedJudges(){ + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.JUDGES) as ApiUser[] + } + + /** + * + * @param branch A single branch, do not use ALL or NONE here (or any combination) + * @private + */ + private getSelectedItemsForBranch(branch: TemplateImportTreeBranch){ + /* Filter appropriately */ + const items = this.selection.selected.filter(it => TemplateImportTreeComponent.checkForBranch(it.branch, branch)).map(it => this.flatNodeMap.get(it)) + switch(branch){ + case TemplateImportTreeBranch.NONE: + case TemplateImportTreeBranch.ALL: + throw new Error("Cannot type set for TemplateImportTreeBanches ALL and NONE. This is a programmer's error") + case TemplateImportTreeBranch.TASK_TYPES: + return items.map(it => it.item) + case TemplateImportTreeBranch.TASK_GROUPS: + return items.map(it => it.item) + case TemplateImportTreeBranch.TASK_TEMPLATES: + return items.map(it => { + /* Warning: collectionId remains and therefore must exist */ + const newItem = it.item as ApiTaskTemplate; + newItem.id = undefined; + return newItem + }) + case TemplateImportTreeBranch.TEAMS: + return items.map(it => { + const newItem = it.item as ApiTeam + newItem.id = undefined; + return newItem + }) + case TemplateImportTreeBranch.TEAM_GROUPS: + return items.map(it => { + const newItem = it.item as ApiTeamGroup + newItem.id = undefined + return newItem + }) + case TemplateImportTreeBranch.JUDGES: + return items.map(it => it.item) + } + } + + + + public static buildTrees(templates: ApiEvaluationTemplate[], branches: TemplateImportTreeBranch): TemplateTreeNode[]{ + return templates.map(it => this.buildTree(it, branches)); + } + + public static buildTree(template: ApiEvaluationTemplate, branches: TemplateImportTreeBranch): TemplateTreeNode { const root = new TemplateTreeNode(); root.item = template; root.label = template.name; root.children = [] as TemplateTreeNode[]; - if(this.checkForBranch(branches, TemplateImportTreeBranches.TASK_TYPES)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TYPES)){ root.children.push(this.buildTaskTypesBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranches.TASK_GROUPS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_GROUPS)){ root.children.push(this.buildTaskGroupsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranches.TASK_TEMPLATES)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TEMPLATES)){ root.children.push(this.buildTaskTemplatesBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranches.TEAMS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAMS)){ root.children.push(this.buildTeamsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranches.TEAM_GROUPS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS)){ root.children.push(this.buildTeamGroupsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranches.TEAM_GROUPS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS)){ root.children.push(this.buildJudgesBranch(template)); } - return [root]; + return root; } - private static checkForBranch(branches: TemplateImportTreeBranches, test: TemplateImportTreeBranches): boolean{ + public static checkForBranch(branches: TemplateImportTreeBranch, test: TemplateImportTreeBranch): boolean{ return (branches & test) === test } public static buildTaskTypesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "taskTypes", "Task Types"); + return this.buildBranch(template, "taskTypes", "Task Types", TemplateImportTreeBranch.TASK_TYPES); } public static buildTaskGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "taskGroups", "Task Groups"); + return this.buildBranch(template, "taskGroups", "Task Groups", TemplateImportTreeBranch.TASK_GROUPS); } public static buildTaskTemplatesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "tasks", "Task Templates"); + return this.buildBranch(template, "tasks", "Task Templates", TemplateImportTreeBranch.TASK_TEMPLATES); } public static buildTeamsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "teams", "Teams"); + return this.buildBranch(template, "teams", "Teams", TemplateImportTreeBranch.TEAMS); } public static buildTeamGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "teamGroups", "Team Groups"); + return this.buildBranch(template, "teamGroups", "Team Groups", TemplateImportTreeBranch.TEAM_GROUPS); } public static buildJudgesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "judges", "Judges"); + return this.buildBranch(template, "judges", "Judges", TemplateImportTreeBranch.JUDGES); } - public static buildBranch(template: ApiEvaluationTemplate, key: string, rootLabel: string): TemplateTreeNode { + public static buildBranch(template: ApiEvaluationTemplate, key: string, rootLabel: string, branch: TemplateImportTreeBranch): TemplateTreeNode { const root = new TemplateTreeNode(); root.label = rootLabel; root.item = template[key]; @@ -232,6 +332,8 @@ export class TemplateImportTreeComponent { item.label = it["name"]; item.item = it; item.children = null; + item.branch = branch; + item.origin = template.id return item; }); return root; diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 95590d592..6023d3cb3 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -29,6 +29,17 @@

    Edit evaluation template {{(builderService.templateAsObservable() | async)?. [inline]="true" matTooltip="Download the entire evaluation template as JSON">

    +
    + +
    +
    + +
    diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index 4dca67b58..fce4e5fd3 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -1,14 +1,28 @@ import { Component, HostListener, OnDestroy, OnInit, ViewChild } from "@angular/core"; -import {AbstractTemplateBuilderComponent} from './components/abstract-template-builder.component'; -import {DeactivationGuarded} from '../../services/can-deactivate.guard'; -import {Observable, Subscription} from 'rxjs'; -import { ApiTaskGroup, ApiTaskTemplate, ApiTaskType, DownloadService, TemplateService, UserService } from "../../../../openapi"; -import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; -import {MatSnackBar} from '@angular/material/snack-bar'; -import {TemplateBuilderService} from './template-builder.service'; -import {take} from 'rxjs/operators'; +import { AbstractTemplateBuilderComponent } from "./components/abstract-template-builder.component"; +import { DeactivationGuarded } from "../../services/can-deactivate.guard"; +import { forkJoin, Observable, Subscription } from "rxjs"; +import { + ApiEvaluationTemplate, ApiEvaluationTemplateOverview, + ApiTaskGroup, + ApiTaskTemplate, + ApiTaskType, + DownloadService, + TemplateService, + UserService +} from "../../../../openapi"; +import { ActivatedRoute, Router, RouterStateSnapshot } from "@angular/router"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { TemplateBuilderService } from "./template-builder.service"; +import { map, switchMap, take } from "rxjs/operators"; import { TaskTemplateEditorLauncher } from "./components/tasks-list/task-templates-list.component"; import { TaskTemplateEditorComponent } from "./components/task-template-editor/task-template-editor.component"; +import { MatDialog } from "@angular/material/dialog"; +import { + TemplateImportDialogComponent, + TemplateImportDialogData +} from "./components/template-import-dialog/template-import-dialog.component"; +import { TemplateImportTreeBranch } from "./components/template-import-tree/template-import-tree.component"; @Component({ selector: 'app-template-builder', @@ -32,6 +46,7 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i private downloadService: DownloadService, route: ActivatedRoute, private router: Router, + private dialg: MatDialog, snackBar: MatSnackBar, public builderService: TemplateBuilderService ) { @@ -71,6 +86,33 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i } } + public onUpload(contents: string){ + console.log("Uploaded "+contents.length+" characters") + } + + public import(){ + console.log("Import open") + let templateList : Observable; + templateList = this.templateService.getApiV2TemplateList().pipe( + map(overviews => overviews.map(o => this.templateService.getApiV2TemplateByTemplateId(o.id))), + switchMap(templateList => forkJoin(...templateList)) + ); + templateList.subscribe(templates => { + console.log("Templates ", templates) + const ownIdx = templates.indexOf(this.builderService.getTemplate()) + templates.splice(ownIdx,1) + const dialogref = this.dialg.open(TemplateImportDialogComponent, {width: '800px', data: {templates: templates, branches: TemplateImportTreeBranch.ALL} as TemplateImportDialogData}) + dialogref.afterClosed().subscribe( d => { + this.onImport(d) + }) + }) + + } + + public onImport(templateToImportFrom: ApiEvaluationTemplate){ + console.log("Importing...", templateToImportFrom) + } + public save(){ // FIXME re-enable form validation. possibly on the form-builder? this.isSaving = true; diff --git a/frontend/src/app/template/template-builder/template-builder.module.ts b/frontend/src/app/template/template-builder/template-builder.module.ts index 1943f3b13..ce27855e5 100644 --- a/frontend/src/app/template/template-builder/template-builder.module.ts +++ b/frontend/src/app/template/template-builder/template-builder.module.ts @@ -19,6 +19,7 @@ import { MatChipsModule } from "@angular/material/chips"; import { TemplateImportTreeComponent } from './components/template-import-tree/template-import-tree.component'; import { MatTreeModule } from "@angular/material/tree"; import { MatCheckboxModule } from "@angular/material/checkbox"; +import { TemplateImportDialogComponent } from './components/template-import-dialog/template-import-dialog.component'; @NgModule({ @@ -26,7 +27,8 @@ import { MatCheckboxModule } from "@angular/material/checkbox"; TemplateBuilderComponent, TeamgroupsListComponent, TeamgroupsDialogComponent, - TemplateImportTreeComponent + TemplateImportTreeComponent, + TemplateImportDialogComponent ], imports: [ From 9280dd99a49c8f7592f6c03468036f3c7b95f685 Mon Sep 17 00:00:00 2001 From: Luca Rossetto Date: Sat, 30 Sep 2023 12:27:53 +0200 Subject: [PATCH 483/498] Minor fix in media collection cli command --- .../main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt index 6aec752c9..70025e19e 100644 --- a/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt +++ b/backend/src/main/kotlin/dev/dres/api/cli/MediaCollectionCommand.kt @@ -205,9 +205,12 @@ class MediaCollectionCommand(private val config: Config) : ).flag(default = false) override fun run() { - println("Available media collections ${DbMediaCollection.all().size()}") + + val collections = MediaCollectionManager.getCollections() + + println("Available media collections ${collections.size}") if (this.plain) { - MediaCollectionManager.getCollections().forEach(::println) + collections.forEach(::println) } else { println( table { From 69422b91123943b9276ab5eacc27e82667df7c17 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 2 Oct 2023 09:32:20 +0200 Subject: [PATCH 484/498] Added logic for import from other evaluation template --- .../task-types-list.component.ts | 4 +- .../template-import-tree.component.ts | 124 ++++++++++++------ .../template-builder.component.ts | 1 + .../template-builder.service.ts | 40 ++++++ 4 files changed, 125 insertions(+), 44 deletions(-) diff --git a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts index 2899851c2..501c55116 100644 --- a/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts +++ b/frontend/src/app/template/template-builder/components/task-types-list/task-types-list.component.ts @@ -4,7 +4,7 @@ import { TemplateBuilderService } from "../../template-builder.service"; import { MatDialog } from "@angular/material/dialog"; import { ApiTaskType, TemplateService } from "../../../../../../openapi"; import { Observable } from "rxjs"; -import { filter, map, shareReplay } from "rxjs/operators"; +import { filter, map, shareReplay, tap } from "rxjs/operators"; import { CompetitionBuilderTaskTypeDialogComponent } from "../../../../competition/competition-builder/competition-builder-task-type-dialog/competition-builder-task-type-dialog.component"; @@ -74,7 +74,7 @@ export class TaskTypesListComponent extends AbstractTemplateBuilderComponent imp } else { return []; } - })); + }), tap((t) => this.table?.renderRows())); } diff --git a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts index 7fcd9cf96..43de238b0 100644 --- a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts +++ b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts @@ -76,12 +76,12 @@ export class TemplateImportTreeComponent implements OnInit{ ) this.treeControl = new FlatTreeControl>(this.getLevel, this.isExpandable); this.dataSource = new MatTreeFlatDataSource, TemplateTreeFlatNode>(this.treeControl, this.treeFlattener); - } ngOnInit(): void { this.dataSource.data = TemplateImportTreeComponent.buildTrees(this.templates, this.branches); this.templates.forEach(it => this.templatesMap.set(it.id, it)); + console.log(this.templatesMap) } getLevel = (node: TemplateTreeFlatNode) => node.level; @@ -183,44 +183,80 @@ export class TemplateImportTreeComponent implements OnInit{ } public getImportTemplate(){ - const template = { + const types: ApiTaskType[] = []; + const taskGroups: ApiTaskGroup[] = []; + const tasks: ApiTaskTemplate[] = []; + const teams: ApiTeam[] = this.getAllSelectedTeams().map(it => it[0]); + const teamGroups: ApiTeamGroup[] = this.getAllSelectedTeamGroups().map(it => it[0]); + const judges: ApiUser[] = this.getAllSelectedJudges().map(it => it[0]); + /** Sanitation */ + /* Tasks require task groups which in turn require types*/ + this.getAllSelectedTaskTemplates().forEach(it => { + it[0].id = undefined + tasks.push(it[0]); + const group = this.templatesMap.get(it[1]).taskGroups.find((g:ApiTaskGroup) => g.name === it[0].taskGroup) + if(!taskGroups.includes(group)){ + taskGroups.push(group); + } + const type = this.templatesMap.get(it[1]).taskTypes.find((t:ApiTaskType) => t.name === group.type) + if(!types.includes(type)){ + types.push(type) + } + }) + + /* TaskGroup requires TaskType */ + this.getAllSelectedTaskGroups().forEach(it => { + if(!taskGroups.includes(it[0])){ + taskGroups.push(it[0]) + const type = this.templatesMap.get(it[1]).taskTypes.find((t: ApiTaskType) => t.name === it[0].type) + if(!types.includes(type)){ + types.push(type) + } + } + }) + + this.getAllSelectedTaskTypes().forEach(it => { + if(!types.includes(it[0])){ + types.push(it[0]) + } + }) + + + return { name: "", description: "---Automatically generated template whose elements get imported. If this is seen, there was a programmer's error somewhere---", - taskTypes: this.getAllSelectedTaskTypes(), - taskGroups: this.getAllSelectedTaskGroups(), - tasks: this.getAllSelectedTaskTemplates(), - teams: this.getAllSelectedTeams(), - teamGroups: this.getAllSelectedTeamGroups(), - judges: this.getAllSelectedJudges(), + taskTypes: types, + taskGroups: taskGroups, + tasks: tasks, + teams: teams, + teamGroups: teamGroups, + judges: judges, id:"---IMPORT_TEMPLATE_NO_ID---" } as ApiEvaluationTemplate - - /* Sanitisation: For each task, the group and type is required */ - return template; } public getAllSelectedTaskTypes(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TYPES) as ApiTaskType[] + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TYPES) as [ApiTaskType, string][] } public getAllSelectedTaskGroups(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_GROUPS) as ApiTaskGroup[]; + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_GROUPS) as [ApiTaskGroup,string][]; } public getAllSelectedTaskTemplates(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TEMPLATES) as ApiTaskTemplate[] + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TEMPLATES) as [ApiTaskTemplate, string][] } public getAllSelectedTeams(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAMS) as ApiTeam[] + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAMS) as [ApiTeam, string][] } public getAllSelectedTeamGroups(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAM_GROUPS) as ApiTeamGroup[] + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAM_GROUPS) as [ApiTeamGroup,string][] } public getAllSelectedJudges(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.JUDGES) as ApiUser[] + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.JUDGES) as [ApiUser,string][] } /** @@ -236,30 +272,30 @@ export class TemplateImportTreeComponent implements OnInit{ case TemplateImportTreeBranch.ALL: throw new Error("Cannot type set for TemplateImportTreeBanches ALL and NONE. This is a programmer's error") case TemplateImportTreeBranch.TASK_TYPES: - return items.map(it => it.item) + return items.map<[ApiTaskType, string]>(it => [it.item, it.origin]) case TemplateImportTreeBranch.TASK_GROUPS: - return items.map(it => it.item) + return items.map<[ApiTaskGroup, string]>(it => [it.item, it.origin]) case TemplateImportTreeBranch.TASK_TEMPLATES: - return items.map(it => { + return items.map<[ApiTaskTemplate, string]>(it => { /* Warning: collectionId remains and therefore must exist */ const newItem = it.item as ApiTaskTemplate; - newItem.id = undefined; - return newItem + // newItem.id = undefined; // We need the id for sanitation purposes (to resolve for the parent evaluation template */ + return [newItem, it.origin] }) case TemplateImportTreeBranch.TEAMS: - return items.map(it => { + return items.map<[ApiTeam, string]>(it => { const newItem = it.item as ApiTeam newItem.id = undefined; - return newItem + return [newItem, it.origin] }) case TemplateImportTreeBranch.TEAM_GROUPS: - return items.map(it => { + return items.map<[ApiTeamGroup, string]>(it => { const newItem = it.item as ApiTeamGroup newItem.id = undefined - return newItem + return [newItem, it.origin] }) case TemplateImportTreeBranch.JUDGES: - return items.map(it => it.item) + return items.map<[ApiUser, string]>(it => [it.item,it.origin]) } } @@ -274,24 +310,25 @@ export class TemplateImportTreeComponent implements OnInit{ root.item = template; root.label = template.name; root.children = [] as TemplateTreeNode[]; - if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TYPES)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TYPES) && template.taskTypes.length > 0){ root.children.push(this.buildTaskTypesBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_GROUPS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_GROUPS) && template.taskGroups.length > 0){ root.children.push(this.buildTaskGroupsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TEMPLATES)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TEMPLATES) && template.tasks.length > 0){ root.children.push(this.buildTaskTemplatesBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAMS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAMS) && template.teams.length > 0){ root.children.push(this.buildTeamsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS)){ + if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS) && template.teamGroups.length > 0){ root.children.push(this.buildTeamGroupsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS)){ + // TODO load apiusers for judges on the fly in future version + /*if(this.checkForBranch(branches, TemplateImportTreeBranch.JUDGES) && template.judges.length > 0){ root.children.push(this.buildJudgesBranch(template)); - } + } */ return root; } @@ -300,42 +337,45 @@ export class TemplateImportTreeComponent implements OnInit{ } public static buildTaskTypesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "taskTypes", "Task Types", TemplateImportTreeBranch.TASK_TYPES); + return this.buildBranch(template, "taskTypes", "Task Types", "name",TemplateImportTreeBranch.TASK_TYPES); } public static buildTaskGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "taskGroups", "Task Groups", TemplateImportTreeBranch.TASK_GROUPS); + return this.buildBranch(template, "taskGroups", "Task Groups", "name",TemplateImportTreeBranch.TASK_GROUPS); } public static buildTaskTemplatesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "tasks", "Task Templates", TemplateImportTreeBranch.TASK_TEMPLATES); + return this.buildBranch(template, "tasks", "Task Templates","name", TemplateImportTreeBranch.TASK_TEMPLATES); } public static buildTeamsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "teams", "Teams", TemplateImportTreeBranch.TEAMS); + return this.buildBranch(template, "teams", "Teams","name", TemplateImportTreeBranch.TEAMS); } public static buildTeamGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "teamGroups", "Team Groups", TemplateImportTreeBranch.TEAM_GROUPS); + return this.buildBranch(template, "teamGroups", "Team Groups", "name", TemplateImportTreeBranch.TEAM_GROUPS); } public static buildJudgesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "judges", "Judges", TemplateImportTreeBranch.JUDGES); + return this.buildBranch(template, "judges", "Judges", "username", TemplateImportTreeBranch.JUDGES); } - public static buildBranch(template: ApiEvaluationTemplate, key: string, rootLabel: string, branch: TemplateImportTreeBranch): TemplateTreeNode { + public static buildBranch(template: ApiEvaluationTemplate, key: string, rootLabel: string, labelKey: string, branch: TemplateImportTreeBranch): TemplateTreeNode { const root = new TemplateTreeNode(); root.label = rootLabel; root.item = template[key]; root.children = template[key].map(it => { + //console.log("THE CHILD ITEM", it) const item = new TemplateTreeNode(); - item.label = it["name"]; + item.label = it[labelKey]; item.item = it; item.children = null; item.branch = branch; item.origin = template.id + //console.log("THE CHILD", item) return item; }); + //console.log("THE Branch: ", root) return root; } diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index fce4e5fd3..c79739052 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -111,6 +111,7 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i public onImport(templateToImportFrom: ApiEvaluationTemplate){ console.log("Importing...", templateToImportFrom) + this.builderService.importFrom(templateToImportFrom); } public save(){ diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index 1befb5dae..acd0e153f 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -262,4 +262,44 @@ export class TemplateBuilderService { return result; } + importFrom(from: ApiEvaluationTemplate) { + + /* types */ + from.taskTypes.forEach(it => { + if(!this.getTemplate().taskTypes.includes(it)){ + this.getTemplate().taskTypes.push(it) + } + }) + /* task groups */ + from.taskGroups.forEach(it => { + if(!this.getTemplate().taskGroups.includes(it)){ + this.getTemplate().taskGroups.push(it) + } + }) + /* tasks */ + from.tasks.forEach(it => { + if(!this.getTemplate().tasks.includes(it)){ + this.getTemplate().tasks.push(it) + } + }) + /* teams */ + from.teams.forEach(it => { + if(!this.getTemplate().teams.includes(it)){ + this.getTemplate().teams.push(it) + } + }) + /* team groups */ + from.teamGroups.forEach(it => { + if(!this.getTemplate().teamGroups.includes(it)){ + this.getTemplate().teamGroups.push(it) + } + }) + /* judges */ + from.judges.forEach(it => { + if(!this.getTemplate().judges.includes(it)){ + this.getTemplate().judges.push(it) + } + }) + this.update(this.getTemplate()); + } } From fb7be596569d436e9c9d13535c23ddddfb144023 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Mon, 2 Oct 2023 10:01:12 +0200 Subject: [PATCH 485/498] Fixed broken submission view link for async runs --- .../run/run-async-admin-view/run-async-admin-view.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts index 843c05df5..55eb69877 100644 --- a/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts +++ b/frontend/src/app/run/run-async-admin-view/run-async-admin-view.component.ts @@ -84,8 +84,9 @@ export class RunAsyncAdminViewComponent implements AfterViewInit { } public submissionsOf(task, property = 'id') { + console.log("S of ", task); this.runId.subscribe((r) => { - this.router.navigateByUrl(`run/admin/submissions/${r}/${task[property]}`); + this.router.navigateByUrl(`evaluation/admin/submissions/${r}/${task[property]}`); }); } From b00966d59ec3d26981bc877236d788f8965de719 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 3 Oct 2023 10:27:46 +0200 Subject: [PATCH 486/498] Minor cleanup, including fixing openapi validation error --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 8 +- .../handler/preview/PreviewImageHandler.kt | 8 +- .../preview/PreviewImageTimelessHandler.kt | 52 + .../template/ListTaskTypePresetsHandler.kt | 3 +- .../rest/types/template/tasks/ApiTaskGroup.kt | 2 +- .../rest/types/template/tasks/ApiTaskType.kt | 9 +- .../data/model/template/task/DbTaskGroup.kt | 2 +- .../data/model/template/task/DbTaskType.kt | 1 + doc/oas-client.json | 9 +- doc/oas.json | 13547 ++++++++-------- 10 files changed, 6883 insertions(+), 6758 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageTimelessHandler.kt diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index 2fc822bb8..aac696567 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -18,11 +18,8 @@ import dev.dres.api.rest.handler.evaluation.viewer.* import dev.dres.api.rest.handler.judgement.* import dev.dres.api.rest.handler.log.QueryLogHandler import dev.dres.api.rest.handler.log.ResultLogHandler -import dev.dres.api.rest.handler.preview.GetExternalMediaHandler +import dev.dres.api.rest.handler.preview.* import dev.dres.api.rest.handler.template.* -import dev.dres.api.rest.handler.preview.GetMediaHandler -import dev.dres.api.rest.handler.preview.PreviewImageHandler -import dev.dres.api.rest.handler.preview.PreviewVideoHandler import dev.dres.api.rest.handler.scores.ListEvaluationScoreHandler import dev.dres.api.rest.handler.submission.LegacySubmissionHandler import dev.dres.api.rest.handler.submission.SubmissionHandler @@ -104,7 +101,8 @@ object RestApi { UserDetailsHandler(), // Media - PreviewImageHandler(store, cache), + PreviewImageHandler(store, cache), /* [PreviewImageHandler] vs [PreviewImageTimelessHandler]: Optional path parameters are not allowed in OpenApi. [PreviewImageHandler] has timestamp as path parameter and must be initialised first */ + PreviewImageTimelessHandler(store,cache), /* [PreviewImageHandler] vs [PreviewImageTimelessHandler]: Optional path parameters are not allowed in OpenApi */ PreviewVideoHandler(store, cache), GetExternalMediaHandler(), // Must be registered before GetMediaHandler, as route is similar GetMediaHandler(store), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt index be3d6d0e1..085aa9046 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageHandler.kt @@ -13,8 +13,10 @@ import kotlinx.dnq.query.query /** * A general purpose, [AbstractPreviewHandler] that handles image previews for different [DbMediaItem]. * - * @author Ralph Gasser - * @version 1.0.0 + * [PreviewImageHandler] vs [PreviewImageTimelessHandler]: Optional path parameters are not allowed in OpenApi. + * + * @author Ralph Gasser and Loris Sauter + * @version 1.1.0 */ class PreviewImageHandler(store: TransientEntityStore, cache: CacheManager) : AbstractPreviewHandler(store, cache) { @@ -25,7 +27,7 @@ class PreviewImageHandler(store: TransientEntityStore, cache: CacheManager) : Ab operationId = OpenApiOperation.AUTO_GENERATE, pathParams = [ OpenApiParam("mediaItemId", String::class, "Unique ID of the media item.", required = true, allowEmptyValue = false), - OpenApiParam("timestamp", Long::class, "Time into the video in milliseconds (for videos only).", required = false, allowEmptyValue = false) + OpenApiParam("timestamp", Long::class, "Time into the video in milliseconds (for videos only).", required = true, allowEmptyValue = false) ], tags = ["Media"], responses = [ diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageTimelessHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageTimelessHandler.kt new file mode 100644 index 000000000..3bd5c4f59 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/preview/PreviewImageTimelessHandler.kt @@ -0,0 +1,52 @@ +package dev.dres.api.rest.handler.preview + +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.media.DbMediaItem +import dev.dres.mgmt.cache.CacheManager +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +/** + * A general purpose, [AbstractPreviewHandler] that handles image previews for different [DbMediaItem]. + * + * [PreviewImageHandler] vs [PreviewImageTimelessHandler]: Optional path parameters are not allowed in OpenApi. + * + * @author Ralph Gasser and Loris Sauter + * @version 1.1.0 + */ +class PreviewImageTimelessHandler(store: TransientEntityStore, cache: CacheManager) : AbstractPreviewHandler(store, cache) { + + override val route: String = "preview/{mediaItemId}" + @OpenApi( + summary = "Returns a preview image from a media item.", + path = "/api/v2/preview/{mediaItemId}", + operationId = OpenApiOperation.AUTO_GENERATE, + pathParams = [ + OpenApiParam("mediaItemId", String::class, "Unique ID of the media item.", required = true, allowEmptyValue = false), + ], + tags = ["Media"], + responses = [ + OpenApiResponse("200", [OpenApiContent(type = "image/jpeg")]), + OpenApiResponse("202"), + OpenApiResponse("400"), + OpenApiResponse("404") + ], + methods = [HttpMethod.GET] + ) + override fun get(ctx: Context) { + val params = ctx.pathParamMap() + val mediaItemId = params["mediaItemId"] ?: throw ErrorStatusException(400, "Media item ID was not specified or is invalid.", ctx) + this.store.transactional(true) { + val item = DbMediaItem.query(DbMediaItem::id eq mediaItemId).firstOrNull() + handlePreviewImageRequest(item, 0L, ctx) + } + } + + override fun doGet(ctx: Context): Any { + throw UnsupportedOperationException("PreviewImageHandler::doGet() is not supported and should not be executed!") + } +} diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt index 344cdaf4a..9470489ee 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/template/ListTaskTypePresetsHandler.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.handler.template +import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import dev.dres.DRES import dev.dres.api.rest.handler.AccessManagedRestHandler @@ -69,7 +70,7 @@ class ListTaskTypePresetsHandler : AccessManagedRestHandler, GetRestHandler ) { - constructor() : this("Default TaskType DO NOT USE!", - 300, + constructor() : this("---NO_ID---","---Default TaskType DO NOT USE!---", + 1, ApiTargetOption.TEXT, listOf(ApiHintOption.TEXT), listOf(ApiSubmissionOption.TEXTUAL_SUBMISSION), listOf(ApiTaskOption.HIDDEN_RESULTS), ApiScoreOption.KIS, mapOf() @@ -40,7 +43,7 @@ data class ApiTaskType( */ fun read(file: Path): ApiTaskType = Files.newInputStream(file, StandardOpenOption.READ).use { - ObjectMapper().readValue(it, ApiTaskType::class.java) + ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue(it, ApiTaskType::class.java) } } } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt index 323f85803..d82c35734 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskGroup.kt @@ -37,5 +37,5 @@ class DbTaskGroup(entity: Entity) : XdEntity(entity) { * * @return [ApiTargetType] */ - fun toApi(): ApiTaskGroup = ApiTaskGroup(this.name,this.type.name) + fun toApi(): ApiTaskGroup = ApiTaskGroup(this.xdId,this.name,this.type.name) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index 0090c4046..aeddf4f69 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -56,6 +56,7 @@ class DbTaskType(entity: Entity) : XdEntity(entity) { * @return [ApiTaskType] */ fun toApi(): ApiTaskType = ApiTaskType( + id=this.xdId, name = this.name, duration = this.duration, targetOption = this.target.toApi(), diff --git a/doc/oas-client.json b/doc/oas-client.json index beb324887..d5952a3d8 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1607,9 +1607,6 @@ "text" : { "type" : "string" }, - "mediaItemId" : { - "type" : "string" - }, "mediaItemName" : { "type" : "string" }, @@ -2090,6 +2087,9 @@ "type" : "object", "additionalProperties" : false, "properties" : { + "id" : { + "type" : "string" + }, "name" : { "type" : "string" }, @@ -2144,6 +2144,9 @@ "type" : "object", "additionalProperties" : false, "properties" : { + "id" : { + "type" : "string" + }, "name" : { "type" : "string" }, diff --git a/doc/oas.json b/doc/oas.json index 966c3cc0c..d130fca18 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -1,6742 +1,6807 @@ -{ - "openapi" : "3.0.3", - "info" : { - "title" : "DRES API", - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", - "contact" : { - "name" : "The DRES Dev Team", - "url" : "https://dres.dev" - }, - "license" : { - "name" : "MIT" - }, - "version" : "2.0.0" - }, - "paths" : { - "/api/v1/submit" : { - "get" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions", - "description" : "This has been the submission endpoint for version 1. Please refrain from using it and migrate to the v2 endpoint.", - "operationId" : "getApiV1Submit", - "parameters" : [ { - "name" : "collection", - "in" : "query", - "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "item", - "in" : "query", - "description" : "Identifier for the actual media object or media file.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "text", - "in" : "query", - "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "frame", - "in" : "query", - "description" : "Frame number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "shot", - "in" : "query", - "description" : "Shot number for media with temporal progression (e.g., video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "integer", - "format" : "int32" - } - }, { - "name" : "timecode", - "in" : "query", - "description" : "Timecode for media with temporal progression (e.g,. video).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "Precondition Failed", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : true, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/currentTask/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Returns an overview of the currently active task for a run.", - "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/client/evaluation/list" : { - "get" : { - "tags" : [ "Evaluation Client" ], - "summary" : "Lists an overview of all evaluation runs visible to the current client.", - "operationId" : "getApiV2ClientEvaluationList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a media collection", - "operationId" : "patchApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a new media collection.", - "operationId" : "postApiV2Collection", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists all available media collections with basic information about their content.", - "operationId" : "getApiV2CollectionList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Shows the content of the specified media collection.", - "operationId" : "getApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes a media collection identified by a collection id.", - "operationId" : "deleteApiV2CollectionByCollectionId", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/random" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a random media item from a given media collection.", - "operationId" : "getApiV2CollectionByCollectionIdRandom", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/resolve" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Resolves a list of media item names to media items", - "operationId" : "postApiV2CollectionByCollectionIdResolve", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/collection/{collectionId}/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists media items from a given media collection whose name start with the given string.", - "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", - "parameters" : [ { - "name" : "collectionId", - "in" : "path", - "description" : "Collection ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "startsWith", - "in" : "path", - "description" : "Name the item(s) should start with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation structure.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/evaluation/{evaluationId}/scores" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a CSV download with the scores for a given evaluation.", - "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/download/template/{templateId}" : { - "get" : { - "tags" : [ "Download" ], - "summary" : "Provides a JSON download of the entire evaluation template structure.", - "operationId" : "getApiV2DownloadTemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/create" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminCreate", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationStartMessage" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Adjusts the duration of a running task. This is a method for admins.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "duration", - "in" : "path", - "description" : "Duration to add. Can be negative.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Override the verdict status of an AnswerSet.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "answerSetId", - "in" : "path", - "description" : "The ID of the AnswerSet.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiOverrideAnswerSetVerdictDto" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/overview" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Provides a complete overview of an evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/properties" : { - "patch" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Changes the properties of an evaluation.", - "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/RunProperties" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts a evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all submissions for a given evaluation and task.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "templateId", - "in" : "path", - "description" : "The task template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Aborts the currently running task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/next" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all past tasks for a given evaluation.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the previous task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/start" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Starts the currently active task as a new task run. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evalation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Moves to and selects the specified task. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "idx", - "in" : "path", - "description" : "Index of the task to switch to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int32" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/terminate" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Terminates an evaluation. This is a method for administrators.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { - "get" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", - "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiViewerInfo" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { - "post" : { - "tags" : [ "Evaluation Administrator" ], - "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", - "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "viewerId", - "in" : "path", - "description" : "The viewer ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/info/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluations visible to the current user.", - "operationId" : "getApiV2EvaluationInfoList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/state/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Lists an overview of all evaluation visible to the current user.", - "operationId" : "getApiV2EvaluationStateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/info" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns basic information about a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdInfo", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Endpoint to post a judgement for a previously detached judgement request.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudge", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgement" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "408" : { - "description" : "On timeout: Judgement took too long", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be judged.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/status" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Retrieves the status of all judgement validators.", - "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/judge/vote" : { - "post" : { - "tags" : [ "Judgement" ], - "summary" : "Returns a Vote.", - "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiVote" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a list of available scoreboard names for the given evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/state" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the state of a specific evaluation.", - "operationId" : "getApiV2EvaluationByEvaluationIdState", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationState" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Timestamp that marks the lower bound for returned submissions.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", - "operationId" : "getApiV2EvaluationByEvaluationIdTask", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", - "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "Task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/hint" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task hint for the specified task template in the context of the provided evaluation.", - "operationId" : "getHintForTaskTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The ID of the evaluation.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskTemplateId", - "in" : "path", - "description" : "The ID of the task template.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiHintContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/target" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Returns the task target for the specified task template in the context of the provided evaluation.", - "operationId" : "getTargetForTaskTemplateId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The ID of the evaluation.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskTemplateId", - "in" : "path", - "description" : "The ID of the task template.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTargetContent" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/vote/next" : { - "get" : { - "tags" : [ "Judgement" ], - "summary" : "Gets the next open submission that is waiting to be voted on.", - "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiJudgementRequest" - } - } - } - }, - "202" : { - "description" : "Accepted", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/evaluation/{evaluationId}/{taskId}/ready" : { - "get" : { - "tags" : [ "Evaluation" ], - "summary" : "Signals that a viewer is ready to show the hints for a particular task.", - "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdReady", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/find/{startsWith}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection whose name start with the given string.", - "operationId" : "getApiV2ExternalFindByStartsWith", - "parameters" : [ { - "name" : "startsWith", - "in" : "path", - "description" : "Name starts with.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/list" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Lists items from the external media collection.", - "operationId" : "getApiV2ExternalList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/upload/{name}" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Receives a new external (media) item to be used in query hints.", - "operationId" : "postApiV2ExternalUploadByName", - "parameters" : [ { - "name" : "name", - "in" : "path", - "description" : "The name of the file", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "description" : "The file to upload.", - "content" : { - "image/png" : { - "schema" : { - "type" : "string", - "format" : "binary" - } - }, - "video/mp4" : { - "schema" : { - "type" : "string", - "format" : "binary" - } - } - }, - "required" : true - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/external/{name}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Deletes the external item with this name, as far as it exists.", - "operationId" : "deleteApiV2ExternalByName", - "parameters" : [ { - "name" : "name", - "in" : "path", - "description" : "Filename of external item to delete", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "On success (the item is deleted)", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "For caller error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "If the caller has not the appropriate rights. Requires role admin", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "If no such external file exists", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/query" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts query logs from participants", - "operationId" : "postApiV2LogQuery", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryEventLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/log/result" : { - "post" : { - "tags" : [ "Log" ], - "summary" : "Accepts result logs from participants.", - "operationId" : "postApiV2LogResult", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/QueryResultLog" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/login" : { - "post" : { - "tags" : [ "User" ], - "summary" : "Sets roles for session based on user account and returns a session cookie.", - "operationId" : "postApiV2Login", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/LoginRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/logout" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Clears all user roles of the current session.", - "operationId" : "getApiV2Logout", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem" : { - "post" : { - "tags" : [ "Collection" ], - "summary" : "Adds a media item to the specified media collection.", - "operationId" : "postApiV2MediaItem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaId}" : { - "delete" : { - "tags" : [ "Collection" ], - "summary" : "Tries to delete a specific media item.", - "operationId" : "deleteApiV2MediaItemByMediaId", - "parameters" : [ { - "name" : "mediaId", - "in" : "path", - "description" : "Media item ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaItem/{mediaItemId}" : { - "get" : { - "tags" : [ "Collection" ], - "summary" : "Selects and returns a specific media item.", - "operationId" : "getApiV2MediaItemByMediaItemId", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Media item ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/mediaitem" : { - "patch" : { - "tags" : [ "Collection" ], - "summary" : "Updates a Media Item to the specified Media Collection.", - "operationId" : "patchApiV2Mediaitem", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{start}/{end}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview video from a media item. ", - "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item. Must be a video.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "start", - "in" : "path", - "description" : "Start timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - }, { - "name" : "end", - "in" : "path", - "description" : "End timestamp into the video in milliseconds.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "video/mp4" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/preview/{mediaItemId}/{timestamp}" : { - "get" : { - "tags" : [ "Media" ], - "summary" : "Returns a preview image from a media item.", - "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", - "parameters" : [ { - "name" : "mediaItemId", - "in" : "path", - "description" : "Unique ID of the media item.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "timestamp", - "in" : "path", - "description" : "Time into the video in milliseconds (for videos only).", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "integer", - "format" : "int64" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "image/jpeg" : { } - } - }, - "202" : { - "description" : "Accepted" - }, - "400" : { - "description" : "Bad Request" - }, - "404" : { - "description" : "Not Found" - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the score overviews of a specific evaluation run.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/current" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns the overviews of all score boards for the specified task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "taskId", - "in" : "path", - "description" : "The task ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiScoreOverview" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns a time series for a given run and scoreboard.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - }, { - "name" : "scoreboard", - "in" : "path", - "description" : "Name of the scoreboard to return the time series for.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeries" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { - "get" : { - "tags" : [ "Evaluation Scores" ], - "summary" : "Returns team group aggregated values of the current task.", - "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The evaluation ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroupValue" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "403" : { - "description" : "Forbidden", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/info" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns an overview of the server properties.", - "operationId" : "getApiV2StatusInfo", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DresInfo" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/status/time" : { - "get" : { - "tags" : [ "Status" ], - "summary" : "Returns the current time on the server.", - "operationId" : "getApiV2StatusTime", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/CurrentTime" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/submit/{evaluationId}" : { - "post" : { - "tags" : [ "Submission" ], - "summary" : "Endpoint to accept submissions.", - "operationId" : "postApiV2SubmitByEvaluationId", - "parameters" : [ { - "name" : "evaluationId", - "in" : "path", - "description" : "The ID of the evaluation the submission belongs to.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiClientSubmission" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "The submission was accepted by the server and there was a verdict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "202" : { - "description" : "The submission was accepted by the server and there has not yet been a verdict available", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "412" : { - "description" : "The submission was rejected by the server", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Creates a new evaluation template.", - "operationId" : "postApiV2Template", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiCreateEvaluation" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", - "operationId" : "getApiV2TemplateList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplateOverview" - } - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeam", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/list" : { - "get" : { - "tags" : [ "Template", "Team" ], - "summary" : "Lists all the teams across all evaluations.", - "operationId" : "getApiV2TemplateTeamList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/team/{teamId}" : { - "post" : { - "tags" : [ "Template", "Team" ], - "summary" : "Creates a new team.", - "operationId" : "postApiV2TemplateTeamByTeamId", - "parameters" : [ { - "name" : "teamId", - "in" : "path", - "description" : "The team ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/type-presets/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", - "operationId" : "getApiV2TemplateTypePresetsList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Loads the detailed definition of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "delete" : { - "tags" : [ "Template" ], - "summary" : "Deletes the evaluation template with the given template ID.", - "operationId" : "deleteApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "Template" ], - "summary" : "Updates an existing evaluation template.", - "operationId" : "patchApiV2TemplateByTemplateId", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/SuccessStatus" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "409" : { - "description" : "Conflict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/clone" : { - "post" : { - "tags" : [ "Template" ], - "summary" : "Clones an existing evaluation template.", - "operationId" : "postApiV2TemplateByTemplateIdClone", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID to clone.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiEvaluationTemplate" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "409" : { - "description" : "Conflict", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/task/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists the task templates contained in a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTaskList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/template/{templateId}/team/list" : { - "get" : { - "tags" : [ "Template" ], - "summary" : "Lists all the teams of a specific evaluation template.", - "operationId" : "getApiV2TemplateByTemplateIdTeamList", - "parameters" : [ { - "name" : "templateId", - "in" : "path", - "description" : "The evaluation template ID.", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "401" : { - "description" : "Unauthorized", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get information about the current user.", - "operationId" : "getApiV2User", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "post" : { - "tags" : [ "User" ], - "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", - "operationId" : "postApiV2User", - "parameters" : [ ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "If the username is already taken", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Lists all available users.", - "operationId" : "getApiV2UserList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get current sessionId", - "operationId" : "getApiV2UserSession", - "parameters" : [ { - "name" : "session", - "in" : "query", - "description" : "Session Token", - "required" : false, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "text/plain" : { - "schema" : { - "type" : "string" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/session/active/list" : { - "get" : { - "tags" : [ "User" ], - "summary" : "Get details of all current user sessions", - "operationId" : "getApiV2UserSessionActiveList", - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - }, - "/api/v2/user/{userId}" : { - "delete" : { - "tags" : [ "User" ], - "summary" : "Deletes the specified user. Requires ADMIN privileges", - "operationId" : "deleteApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "get" : { - "tags" : [ "User" ], - "summary" : "Gets details of the user with the given id.", - "operationId" : "getApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User's UID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "404" : { - "description" : "If the user could not be found.", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - }, - "patch" : { - "tags" : [ "User" ], - "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", - "operationId" : "patchApiV2UserByUserId", - "parameters" : [ { - "name" : "userId", - "in" : "path", - "description" : "User ID", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "schema" : { - "type" : "string" - } - } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUserRequest" - } - } - }, - "required" : false - }, - "responses" : { - "200" : { - "description" : "OK", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ApiUser" - } - } - } - }, - "400" : { - "description" : "Bad Request", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "404" : { - "description" : "Not Found", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - }, - "500" : { - "description" : "Server Error", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/ErrorStatus" - } - } - } - } - }, - "deprecated" : false, - "security" : [ ] - } - } - }, - "components" : { - "schemas" : { - "LoginRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - } - }, - "required" : [ "username", "password" ] - }, - "ApiMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "basePath" : { - "type" : "string" - }, - "itemCount" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "name", "itemCount" ] - }, - "ApiMediaItem" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "mediaItemId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiMediaType" - }, - "collectionId" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "durationMs" : { - "type" : "integer", - "format" : "int64" - }, - "fps" : { - "type" : "number", - "format" : "float" - }, - "metadata" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" - } - } - }, - "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] - }, - "ApiMediaItemMetaDataEntry" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "key" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "key", "value" ] - }, - "ApiMediaType" : { - "type" : "string", - "enum" : [ "IMAGE", "VIDEO", "TEXT" ] - }, - "ApiPopulatedMediaCollection" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "collection" : { - "$ref" : "#/components/schemas/ApiMediaCollection" - }, - "items" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiMediaItem" - } - } - }, - "required" : [ "collection", "items" ] - }, - "ApiTemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "value" : { - "type" : "string" - }, - "unit" : { - "$ref" : "#/components/schemas/ApiTemporalUnit" - } - }, - "required" : [ "value", "unit" ] - }, - "ApiTemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/ApiTemporalPoint" - } - }, - "required" : [ "start", "end" ] - }, - "ApiTemporalUnit" : { - "type" : "string", - "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] - }, - "ApiEvaluationInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - }, - "templateId" : { - "type" : "string" - }, - "templateDescription" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamInfo" - } - }, - "taskTemplates" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplateInfo" - } - } - }, - "required" : [ "id", "name", "type", "properties", "templateId", "teams", "taskTemplates" ] - }, - "ApiEvaluationOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "state" : { - "$ref" : "#/components/schemas/RunManagerStatus" - }, - "teamOverviews" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamTaskOverview" - } - } - }, - "required" : [ "state", "teamOverviews" ] - }, - "ApiEvaluationState" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "evaluationStatus" : { - "$ref" : "#/components/schemas/ApiEvaluationStatus" - }, - "taskId" : { - "type" : "string" - }, - "taskStatus" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "taskTemplateId" : { - "type" : "string" - }, - "timeLeft" : { - "type" : "integer", - "format" : "int64" - }, - "timeElapsed" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] - }, - "ApiEvaluationStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - }, - "ApiEvaluationType" : { - "type" : "string", - "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] - }, - "ApiOverrideAnswerSetVerdictDto" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ApiSubmissionInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "evaluationId" : { - "type" : "string" - }, - "taskId" : { - "type" : "string" - }, - "submissions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmission" - } - } - }, - "required" : [ "evaluationId", "taskId", "submissions" ] - }, - "ApiTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "group" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "taskId" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiTaskStatus" - }, - "started" : { - "type" : "integer", - "format" : "int64" - }, - "ended" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] - }, - "ApiTaskStatus" : { - "type" : "string", - "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] - }, - "ApiTaskTemplateInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] - }, - "ApiTeamInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - } - }, - "required" : [ "id", "name", "color" ] - }, - "ApiTeamTaskOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOverview" - } - } - }, - "required" : [ "teamId", "tasks" ] - }, - "ApiViewerInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "viewersId" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "host" : { - "type" : "string" - }, - "ready" : { - "type" : "boolean" - } - }, - "required" : [ "viewersId", "username", "host", "ready" ] - }, - "ApiScore" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "teamId" : { - "type" : "string" - }, - "score" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "teamId", "score" ] - }, - "ApiScoreOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "scores" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScore" - } - } - }, - "required" : [ "name", "scores" ] - }, - "ApiScoreSeries" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "team" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "points" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiScoreSeriesPoint" - } - } - }, - "required" : [ "team", "name", "points" ] - }, - "ApiScoreSeriesPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "score" : { - "type" : "number", - "format" : "double" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "score", "timestamp" ] - }, - "ApiTeamGroupValue" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "value" : { - "type" : "number", - "format" : "double" - } - }, - "required" : [ "name", "value" ] - }, - "ApiAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiAnswerType" - }, - "item" : { - "$ref" : "#/components/schemas/ApiMediaItem" - }, - "text" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "temporalRange" : { - "$ref" : "#/components/schemas/TemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "status" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "taskId" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswer" - } - } - }, - "required" : [ "id", "status", "taskId", "answers" ] - }, - "ApiAnswerType" : { - "type" : "string", - "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] - }, - "ApiClientAnswer" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "text" : { - "type" : "string" - }, - "mediaItemId" : { - "type" : "string" - }, - "mediaItemName" : { - "type" : "string" - }, - "mediaItemCollectionName" : { - "type" : "string" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - } - } - }, - "ApiClientAnswerSet" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "taskName" : { - "type" : "string" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswer" - } - } - }, - "required" : [ "answers" ] - }, - "ApiClientSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "answerSets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiClientAnswerSet" - } - } - }, - "required" : [ "answerSets" ] - }, - "ApiSubmission" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "submissionId" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - }, - "memberId" : { - "type" : "string" - }, - "teamName" : { - "type" : "string" - }, - "memberName" : { - "type" : "string" - }, - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "answers" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - } - }, - "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] - }, - "ApiVerdictStatus" : { - "type" : "string", - "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] - }, - "ApiJudgement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "token", "validator", "verdict" ] - }, - "ApiJudgementRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "token" : { - "type" : "string" - }, - "validator" : { - "type" : "string" - }, - "taskDescription" : { - "type" : "string" - }, - "answerSet" : { - "$ref" : "#/components/schemas/ApiAnswerSet" - } - }, - "required" : [ "validator", "taskDescription", "answerSet" ] - }, - "ApiJudgementValidatorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "validatorName" : { - "type" : "string" - }, - "pending" : { - "type" : "integer", - "format" : "int32" - }, - "open" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "validatorName", "pending", "open" ] - }, - "ApiVote" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "verdict" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - } - }, - "required" : [ "verdict" ] - }, - "ErrorStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "description" ] - }, - "SuccessfulSubmissionsStatus" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "status" : { - "type" : "boolean" - }, - "submission" : { - "$ref" : "#/components/schemas/ApiVerdictStatus" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "status", "submission", "description" ] - }, - "CurrentTime" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timeStamp" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timeStamp" ] - }, - "DresInfo" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "version" : { - "type" : "string" - }, - "startTime" : { - "type" : "integer", - "format" : "int64" - }, - "uptime" : { - "type" : "integer", - "format" : "int64" - }, - "os" : { - "type" : "string" - }, - "jvm" : { - "type" : "string" - }, - "args" : { - "type" : "string" - }, - "cores" : { - "type" : "integer", - "format" : "int32" - }, - "freeMemory" : { - "type" : "integer", - "format" : "int64" - }, - "totalMemory" : { - "type" : "integer", - "format" : "int64" - }, - "load" : { - "type" : "number", - "format" : "double" - }, - "availableSeverThreads" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "version", "startTime", "uptime" ] - }, - "ApiContentElement" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "contentType" : { - "$ref" : "#/components/schemas/ApiContentType" - }, - "content" : { - "type" : "string" - }, - "offset" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "contentType", "offset" ] - }, - "ApiContentType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiCreateEvaluation" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - } - }, - "required" : [ "name", "description" ] - }, - "ApiEvaluationStartMessage" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "templateId" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "type" : { - "$ref" : "#/components/schemas/ApiEvaluationType" - }, - "properties" : { - "$ref" : "#/components/schemas/RunProperties" - } - }, - "required" : [ "templateId", "name", "type", "properties" ] - }, - "ApiEvaluationTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "created" : { - "type" : "integer", - "format" : "int64" - }, - "modified" : { - "type" : "integer", - "format" : "int64" - }, - "taskTypes" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskType" - } - }, - "taskGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskGroup" - } - }, - "tasks" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskTemplate" - } - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "teamGroups" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeamGroup" - } - }, - "judges" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] - }, - "ApiEvaluationTemplateOverview" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "description" : { - "type" : "string" - }, - "taskCount" : { - "type" : "integer", - "format" : "int32" - }, - "teamCount" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "id", "name", "taskCount", "teamCount" ] - }, - "ApiHint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiHintType" - }, - "start" : { - "type" : "integer", - "format" : "int64" - }, - "end" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "path" : { - "type" : "string" - }, - "dataType" : { - "type" : "string" - }, - "mediaItem" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiHintContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - }, - "loop" : { - "type" : "boolean" - } - }, - "required" : [ "taskId", "sequence", "loop" ] - }, - "ApiHintType" : { - "type" : "string", - "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] - }, - "ApiTarget" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "type" : { - "$ref" : "#/components/schemas/ApiTargetType" - }, - "target" : { - "type" : "string" - }, - "range" : { - "$ref" : "#/components/schemas/ApiTemporalRange" - } - }, - "required" : [ "type" ] - }, - "ApiTargetContent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "taskId" : { - "type" : "string" - }, - "sequence" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiContentElement" - } - } - }, - "required" : [ "taskId", "sequence" ] - }, - "ApiTargetType" : { - "type" : "string", - "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] - }, - "ApiTaskGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "type" : { - "type" : "string" - } - }, - "required" : [ "name", "type" ] - }, - "ApiTaskTemplate" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "taskGroup" : { - "type" : "string" - }, - "taskType" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "collectionId" : { - "type" : "string" - }, - "targets" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTarget" - } - }, - "hints" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHint" - } - }, - "comment" : { - "type" : "string" - } - }, - "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] - }, - "ApiTaskType" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "name" : { - "type" : "string" - }, - "duration" : { - "type" : "integer", - "format" : "int64" - }, - "targetOption" : { - "$ref" : "#/components/schemas/ApiTargetOption" - }, - "hintOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiHintOption" - } - }, - "submissionOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiSubmissionOption" - } - }, - "taskOptions" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTaskOption" - } - }, - "scoreOption" : { - "$ref" : "#/components/schemas/ApiScoreOption" - }, - "configuration" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - }, - "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] - }, - "ApiHintOption" : { - "type" : "string", - "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] - }, - "ApiScoreOption" : { - "type" : "string", - "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] - }, - "ApiSubmissionOption" : { - "type" : "string", - "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] - }, - "ApiTargetOption" : { - "type" : "string", - "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] - }, - "ApiTaskOption" : { - "type" : "string", - "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] - }, - "ApiTeam" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "color" : { - "type" : "string" - }, - "users" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiUser" - } - }, - "logoData" : { - "type" : "string" - }, - "teamId" : { - "type" : "string" - } - }, - "required" : [ "users", "teamId" ] - }, - "ApiTeamAggregatorType" : { - "type" : "string", - "enum" : [ "MIN", "MAX", "MEAN", "LAST" ] - }, - "ApiTeamGroup" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "teams" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/ApiTeam" - } - }, - "aggregation" : { - "$ref" : "#/components/schemas/ApiTeamAggregatorType" - } - }, - "required" : [ "teams", "aggregation" ] - }, - "ApiRole" : { - "type" : "string", - "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] - }, - "ApiUser" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "id" : { - "type" : "string" - }, - "username" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - }, - "sessionId" : { - "type" : "string" - } - } - }, - "ApiUserRequest" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "username" : { - "type" : "string" - }, - "password" : { - "type" : "string" - }, - "role" : { - "$ref" : "#/components/schemas/ApiRole" - } - }, - "required" : [ "username" ] - }, - "QueryEvent" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "category" : { - "$ref" : "#/components/schemas/QueryEventCategory" - }, - "type" : { - "type" : "string" - }, - "value" : { - "type" : "string" - } - }, - "required" : [ "timestamp", "category", "type", "value" ] - }, - "QueryEventCategory" : { - "type" : "string", - "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] - }, - "QueryEventLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "events", "serverTimeStamp$backend" ] - }, - "QueryResult" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "item" : { - "type" : "string" - }, - "segment" : { - "type" : "integer", - "format" : "int32" - }, - "frame" : { - "type" : "integer", - "format" : "int32" - }, - "score" : { - "type" : "number", - "format" : "double" - }, - "rank" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "item" ] - }, - "QueryResultLog" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "timestamp" : { - "type" : "integer", - "format" : "int64" - }, - "sortType" : { - "type" : "string" - }, - "resultSetAvailability" : { - "type" : "string" - }, - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryResult" - } - }, - "events" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryEvent" - } - }, - "serverTimeStamp$backend" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events", "serverTimeStamp$backend" ] - }, - "TemporalPoint" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { } - }, - "TemporalRange" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "start" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "end" : { - "$ref" : "#/components/schemas/TemporalPoint" - }, - "center" : { - "type" : "integer", - "format" : "int64" - } - }, - "required" : [ "start", "end", "center" ] - }, - "RunProperties" : { - "type" : "object", - "additionalProperties" : false, - "properties" : { - "participantCanView" : { - "type" : "boolean" - }, - "shuffleTasks" : { - "type" : "boolean" - }, - "allowRepeatedTasks" : { - "type" : "boolean" - }, - "limitSubmissionPreviews" : { - "type" : "integer", - "format" : "int32" - } - }, - "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] - }, - "RunManagerStatus" : { - "type" : "string", - "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] - } - }, - "securitySchemes" : { - "CookieAuth" : { - "in" : "cookie", - "name" : "SESSIONID", - "type" : "apiKey" - } - } - }, - "servers" : [ ], - "security" : [ ] +{ + "openapi" : "3.0.3", + "info" : { + "title" : "DRES API", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0", + "contact" : { + "name" : "The DRES Dev Team", + "url" : "https://dres.dev" + }, + "license" : { + "name" : "MIT" + }, + "version" : "2.0.0" + }, + "paths" : { + "/api/v1/submit" : { + "get" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions", + "description" : "This has been the submission endpoint for version 1. Please refrain from using it and migrate to the v2 endpoint.", + "operationId" : "getApiV1Submit", + "parameters" : [ { + "name" : "collection", + "in" : "query", + "description" : "Collection identifier. Optional, in which case the default collection for the run will be considered.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "item", + "in" : "query", + "description" : "Identifier for the actual media object or media file.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "text", + "in" : "query", + "description" : "Text to be submitted. ONLY for tasks with target type TEXT. If this parameter is provided, it superseeds all athers.", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "frame", + "in" : "query", + "description" : "Frame number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "shot", + "in" : "query", + "description" : "Shot number for media with temporal progression (e.g., video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "timecode", + "in" : "query", + "description" : "Timecode for media with temporal progression (e.g,. video).", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "Precondition Failed", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : true, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/currentTask/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Returns an overview of the currently active task for a run.", + "operationId" : "getApiV2ClientEvaluationCurrentTaskByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/client/evaluation/list" : { + "get" : { + "tags" : [ "Evaluation Client" ], + "summary" : "Lists an overview of all evaluation runs visible to the current client.", + "operationId" : "getApiV2ClientEvaluationList", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a new media collection.", + "operationId" : "postApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a media collection", + "operationId" : "patchApiV2Collection", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists all available media collections with basic information about their content.", + "operationId" : "getApiV2CollectionList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes a media collection identified by a collection id.", + "operationId" : "deleteApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Collection" ], + "summary" : "Shows the content of the specified media collection.", + "operationId" : "getApiV2CollectionByCollectionId", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiPopulatedMediaCollection" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/random" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a random media item from a given media collection.", + "operationId" : "getApiV2CollectionByCollectionIdRandom", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/resolve" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Resolves a list of media item names to media items", + "operationId" : "postApiV2CollectionByCollectionIdResolve", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/collection/{collectionId}/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists media items from a given media collection whose name start with the given string.", + "operationId" : "getApiV2CollectionByCollectionIdByStartsWith", + "parameters" : [ { + "name" : "collectionId", + "in" : "path", + "description" : "Collection ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "startsWith", + "in" : "path", + "description" : "Name the item(s) should start with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation structure.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/evaluation/{evaluationId}/scores" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a CSV download with the scores for a given evaluation.", + "operationId" : "getApiV2DownloadEvaluationByEvaluationIdScores", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/download/template/{templateId}" : { + "get" : { + "tags" : [ "Download" ], + "summary" : "Provides a JSON download of the entire evaluation template structure.", + "operationId" : "getApiV2DownloadTemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/create" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Creates a new evaluation run from an existing evaluation template. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminCreate", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationStartMessage" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Adjusts the duration of a running task. This is a method for admins.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdAdjustByDuration", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "duration", + "in" : "path", + "description" : "Duration to add. Can be negative.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/override/{answerSetId}" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Override the verdict status of an AnswerSet.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdOverrideByAnswerSetId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "answerSetId", + "in" : "path", + "description" : "The ID of the AnswerSet.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiOverrideAnswerSetVerdictDto" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/overview" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides a complete overview of an evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdOverview", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/properties" : { + "patch" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Changes the properties of an evaluation.", + "operationId" : "patchApiV2EvaluationAdminByEvaluationIdProperties", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiRunProperties" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts a evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/submission/list/{templateId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all submissions for a given evaluation and task.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdSubmissionListByTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "templateId", + "in" : "path", + "description" : "The task template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/abort" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Aborts the currently running task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskAbort", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/next" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the next task within the evaluation. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/past/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all past tasks for a given evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdTaskPastList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/previous" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the previous task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskPrevious", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/start" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Starts the currently active task as a new task run. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskStart", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evalation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/task/switch/{idx}" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Moves to and selects the specified task. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTaskSwitchByIdx", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "idx", + "in" : "path", + "description" : "Index of the task to switch to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/terminate" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Terminates an evaluation. This is a method for administrators.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdTerminate", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Lists all registered viewers for a evaluation. This is a method for admins.", + "operationId" : "getApiV2EvaluationAdminByEvaluationIdViewerList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiViewerInfo" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" : { + "post" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Forces a viewer with the given viewer ID into the READY state. This is a method for admins.", + "operationId" : "postApiV2EvaluationAdminByEvaluationIdViewerListByViewerIdForce", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "viewerId", + "in" : "path", + "description" : "The viewer ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/info/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluations visible to the current user.", + "operationId" : "getApiV2EvaluationInfoList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/state/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Lists an overview of all evaluation visible to the current user.", + "operationId" : "getApiV2EvaluationStateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/info" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns basic information about a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdInfo", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Endpoint to post a judgement for a previously detached judgement request.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudge", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgement" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "408" : { + "description" : "On timeout: Judgement took too long", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be judged.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/status" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Retrieves the status of all judgement validators.", + "operationId" : "getApiV2EvaluationByEvaluationIdJudgeStatus", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiJudgementValidatorStatus" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/judge/vote" : { + "post" : { + "tags" : [ "Judgement" ], + "summary" : "Returns a Vote.", + "operationId" : "postApiV2EvaluationByEvaluationIdJudgeVote", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiVote" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/scoreboard/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a list of available scoreboard names for the given evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdScoreboardList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/state" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the state of a specific evaluation.", + "operationId" : "getApiV2EvaluationByEvaluationIdState", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationState" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task run, if one is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/submission/list/after/{timestamp}" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns all submissions for the current task that are more recent than the provided timestamp, if a task is either running or has just ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdSubmissionListAfterByTimestamp", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Timestamp that marks the lower bound for returned submissions.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the information for the currently active task template (i.e., the one that is currently selected).", + "operationId" : "getApiV2EvaluationByEvaluationIdTask", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/task/{taskId}/submission/list" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the submissions of a specific task run, regardless of whether it is currently running or has ended.", + "operationId" : "getApiV2EvaluationByEvaluationIdTaskByTaskIdSubmissionList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "Task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/hint" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task hint for the specified task template in the context of the provided evaluation.", + "operationId" : "getHintForTaskTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskTemplateId", + "in" : "path", + "description" : "The ID of the task template.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiHintContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/template/task/{taskTemplateId}/target" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Returns the task target for the specified task template in the context of the provided evaluation.", + "operationId" : "getTargetForTaskTemplateId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskTemplateId", + "in" : "path", + "description" : "The ID of the task template.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTargetContent" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/vote/next" : { + "get" : { + "tags" : [ "Judgement" ], + "summary" : "Gets the next open submission that is waiting to be voted on.", + "operationId" : "getApiV2EvaluationByEvaluationIdVoteNext", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiJudgementRequest" + } + } + } + }, + "202" : { + "description" : "Accepted", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/evaluation/{evaluationId}/{taskId}/ready" : { + "get" : { + "tags" : [ "Evaluation" ], + "summary" : "Signals that a viewer is ready to show the hints for a particular task.", + "operationId" : "getApiV2EvaluationByEvaluationIdByTaskIdReady", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/find/{startsWith}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection whose name start with the given string.", + "operationId" : "getApiV2ExternalFindByStartsWith", + "parameters" : [ { + "name" : "startsWith", + "in" : "path", + "description" : "Name starts with.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/list" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Lists items from the external media collection.", + "operationId" : "getApiV2ExternalList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/upload/{name}" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Receives a new external (media) item to be used in query hints.", + "operationId" : "postApiV2ExternalUploadByName", + "parameters" : [ { + "name" : "name", + "in" : "path", + "description" : "The name of the file", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "The file to upload.", + "content" : { + "image/png" : { + "schema" : { + "type" : "string", + "format" : "binary" + } + }, + "video/mp4" : { + "schema" : { + "type" : "string", + "format" : "binary" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/external/{name}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Deletes the external item with this name, as far as it exists.", + "operationId" : "deleteApiV2ExternalByName", + "parameters" : [ { + "name" : "name", + "in" : "path", + "description" : "Filename of external item to delete", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "On success (the item is deleted)", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "For caller error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "If the caller has not the appropriate rights. Requires role admin", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "If no such external file exists", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/query" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts query logs from participants", + "operationId" : "postApiV2LogQuery", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryEventLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/log/result" : { + "post" : { + "tags" : [ "Log" ], + "summary" : "Accepts result logs from participants.", + "operationId" : "postApiV2LogResult", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/QueryResultLog" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/login" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Sets roles for session based on user account and returns a session cookie.", + "operationId" : "postApiV2Login", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/LoginRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/logout" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Clears all user roles of the current session.", + "operationId" : "getApiV2Logout", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem" : { + "post" : { + "tags" : [ "Collection" ], + "summary" : "Adds a media item to the specified media collection.", + "operationId" : "postApiV2MediaItem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaId}" : { + "delete" : { + "tags" : [ "Collection" ], + "summary" : "Tries to delete a specific media item.", + "operationId" : "deleteApiV2MediaItemByMediaId", + "parameters" : [ { + "name" : "mediaId", + "in" : "path", + "description" : "Media item ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaItem/{mediaItemId}" : { + "get" : { + "tags" : [ "Collection" ], + "summary" : "Selects and returns a specific media item.", + "operationId" : "getApiV2MediaItemByMediaItemId", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Media item ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/mediaitem" : { + "patch" : { + "tags" : [ "Collection" ], + "summary" : "Updates a Media Item to the specified Media Collection.", + "operationId" : "patchApiV2Mediaitem", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview image from a media item.", + "operationId" : "getApiV2PreviewByMediaItemId", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "image/jpeg" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{start}/{end}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview video from a media item. ", + "operationId" : "getApiV2PreviewByMediaItemIdByStartByEnd", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item. Must be a video.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "start", + "in" : "path", + "description" : "Start timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + }, { + "name" : "end", + "in" : "path", + "description" : "End timestamp into the video in milliseconds.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "video/mp4" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/preview/{mediaItemId}/{timestamp}" : { + "get" : { + "tags" : [ "Media" ], + "summary" : "Returns a preview image from a media item.", + "operationId" : "getApiV2PreviewByMediaItemIdByTimestamp", + "parameters" : [ { + "name" : "mediaItemId", + "in" : "path", + "description" : "Unique ID of the media item.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "timestamp", + "in" : "path", + "description" : "Time into the video in milliseconds (for videos only).", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "image/jpeg" : { } + } + }, + "202" : { + "description" : "Accepted" + }, + "400" : { + "description" : "Bad Request" + }, + "404" : { + "description" : "Not Found" + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the score overviews of a specific evaluation run.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/current" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the current task, if it is either running or has just ended.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdCurrent", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/history/{taskId}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns the overviews of all score boards for the specified task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdHistoryByTaskId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "taskId", + "in" : "path", + "description" : "The task ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiScoreOverview" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/series/{scoreboard}" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns a time series for a given run and scoreboard.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdSeriesByScoreboard", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "scoreboard", + "in" : "path", + "description" : "Name of the scoreboard to return the time series for.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeries" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/score/evaluation/{evaluationId}/teamGroup/list" : { + "get" : { + "tags" : [ "Evaluation Scores" ], + "summary" : "Returns team group aggregated values of the current task.", + "operationId" : "getApiV2ScoreEvaluationByEvaluationIdTeamGroupList", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroupValue" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "403" : { + "description" : "Forbidden", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/info" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns an overview of the server properties.", + "operationId" : "getApiV2StatusInfo", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DresInfo" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/status/time" : { + "get" : { + "tags" : [ "Status" ], + "summary" : "Returns the current time on the server.", + "operationId" : "getApiV2StatusTime", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CurrentTime" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/submit/{evaluationId}" : { + "post" : { + "tags" : [ "Submission" ], + "summary" : "Endpoint to accept submissions.", + "operationId" : "postApiV2SubmitByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The ID of the evaluation the submission belongs to.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiClientSubmission" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "The submission was accepted by the server and there was a verdict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "202" : { + "description" : "The submission was accepted by the server and there has not yet been a verdict available", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessfulSubmissionsStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "412" : { + "description" : "The submission was rejected by the server", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Creates a new evaluation template.", + "operationId" : "postApiV2Template", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiCreateEvaluation" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists an overview of all available evaluation templates with basic information about their content.", + "operationId" : "getApiV2TemplateList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplateOverview" + } + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeam", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/list" : { + "get" : { + "tags" : [ "Template", "Team" ], + "summary" : "Lists all the teams across all evaluations.", + "operationId" : "getApiV2TemplateTeamList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/team/{teamId}" : { + "post" : { + "tags" : [ "Template", "Team" ], + "summary" : "Creates a new team.", + "operationId" : "postApiV2TemplateTeamByTeamId", + "parameters" : [ { + "name" : "teamId", + "in" : "path", + "description" : "The team ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/type-presets/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task type presets available. Both, shipped with DRES and custom ones.", + "operationId" : "getApiV2TemplateTypePresetsList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}" : { + "delete" : { + "tags" : [ "Template" ], + "summary" : "Deletes the evaluation template with the given template ID.", + "operationId" : "deleteApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "Template" ], + "summary" : "Loads the detailed definition of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "Template" ], + "summary" : "Updates an existing evaluation template.", + "operationId" : "patchApiV2TemplateByTemplateId", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SuccessStatus" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/clone" : { + "post" : { + "tags" : [ "Template" ], + "summary" : "Clones an existing evaluation template.", + "operationId" : "postApiV2TemplateByTemplateIdClone", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID to clone.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "409" : { + "description" : "Conflict", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/task/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists the task templates contained in a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTaskList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/template/{templateId}/team/list" : { + "get" : { + "tags" : [ "Template" ], + "summary" : "Lists all the teams of a specific evaluation template.", + "operationId" : "getApiV2TemplateByTemplateIdTeamList", + "parameters" : [ { + "name" : "templateId", + "in" : "path", + "description" : "The evaluation template ID.", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user" : { + "post" : { + "tags" : [ "User" ], + "summary" : "Creates a new user, if the username is not already taken. Requires ADMIN privileges", + "operationId" : "postApiV2User", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "If the username is already taken", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Get information about the current user.", + "operationId" : "getApiV2User", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Lists all available users.", + "operationId" : "getApiV2UserList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get current sessionId", + "operationId" : "getApiV2UserSession", + "parameters" : [ { + "name" : "session", + "in" : "query", + "description" : "Session Token", + "required" : false, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/session/active/list" : { + "get" : { + "tags" : [ "User" ], + "summary" : "Get details of all current user sessions", + "operationId" : "getApiV2UserSessionActiveList", + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, + "/api/v2/user/{userId}" : { + "delete" : { + "tags" : [ "User" ], + "summary" : "Deletes the specified user. Requires ADMIN privileges", + "operationId" : "deleteApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "patch" : { + "tags" : [ "User" ], + "summary" : "Updates the specified user, if it exists. Anyone is allowed to update their data, however only ADMINs are allowed to update anyone.", + "operationId" : "patchApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUserRequest" + } + } + }, + "required" : false + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + }, + "get" : { + "tags" : [ "User" ], + "summary" : "Gets details of the user with the given id.", + "operationId" : "getApiV2UserByUserId", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "User's UID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiUser" + } + } + } + }, + "404" : { + "description" : "If the user could not be found.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + } + }, + "components" : { + "schemas" : { + "LoginRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + } + }, + "required" : [ "username", "password" ] + }, + "ApiMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "basePath" : { + "type" : "string" + }, + "itemCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "name", "itemCount" ] + }, + "ApiMediaItem" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "mediaItemId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiMediaType" + }, + "collectionId" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "durationMs" : { + "type" : "integer", + "format" : "int64" + }, + "fps" : { + "type" : "number", + "format" : "float" + }, + "metadata" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItemMetaDataEntry" + } + } + }, + "required" : [ "mediaItemId", "name", "type", "collectionId", "location", "metadata" ] + }, + "ApiMediaItemMetaDataEntry" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "key" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "key", "value" ] + }, + "ApiMediaType" : { + "type" : "string", + "enum" : [ "IMAGE", "VIDEO", "TEXT" ] + }, + "ApiPopulatedMediaCollection" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "collection" : { + "$ref" : "#/components/schemas/ApiMediaCollection" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiMediaItem" + } + } + }, + "required" : [ "collection", "items" ] + }, + "ApiTemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "value" : { + "type" : "string" + }, + "unit" : { + "$ref" : "#/components/schemas/ApiTemporalUnit" + } + }, + "required" : [ "value", "unit" ] + }, + "ApiTemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/ApiTemporalPoint" + } + }, + "required" : [ "start", "end" ] + }, + "ApiTemporalUnit" : { + "type" : "string", + "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] + }, + "ApiEvaluationInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "status" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "properties" : { + "$ref" : "#/components/schemas/ApiRunProperties" + }, + "templateId" : { + "type" : "string" + }, + "templateDescription" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamInfo" + } + }, + "taskTemplates" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplateInfo" + } + } + }, + "required" : [ "id", "name", "type", "status", "properties", "templateId", "teams", "taskTemplates" ] + }, + "ApiEvaluationOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "state" : { + "$ref" : "#/components/schemas/RunManagerStatus" + }, + "teamOverviews" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamTaskOverview" + } + } + }, + "required" : [ "state", "teamOverviews" ] + }, + "ApiEvaluationState" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "evaluationStatus" : { + "$ref" : "#/components/schemas/ApiEvaluationStatus" + }, + "taskId" : { + "type" : "string" + }, + "taskStatus" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "taskTemplateId" : { + "type" : "string" + }, + "timeLeft" : { + "type" : "integer", + "format" : "int64" + }, + "timeElapsed" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "evaluationId", "evaluationStatus", "taskStatus", "timeLeft", "timeElapsed" ] + }, + "ApiEvaluationStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + }, + "ApiEvaluationType" : { + "type" : "string", + "enum" : [ "SYNCHRONOUS", "ASYNCHRONOUS", "NON_INTERACTIVE" ] + }, + "ApiOverrideAnswerSetVerdictDto" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ApiSubmissionInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "taskId" : { + "type" : "string" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmission" + } + } + }, + "required" : [ "evaluationId", "taskId", "submissions" ] + }, + "ApiTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "group" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "taskId" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiTaskStatus" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "id", "name", "type", "group", "duration", "taskId", "status" ] + }, + "ApiTaskStatus" : { + "type" : "string", + "enum" : [ "NO_TASK", "CREATED", "PREPARING", "RUNNING", "ENDED", "IGNORED" ] + }, + "ApiTaskTemplateInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "templateId", "name", "taskGroup", "taskType", "duration" ] + }, + "ApiTeamInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + } + }, + "required" : [ "id", "name", "color" ] + }, + "ApiTeamTaskOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOverview" + } + } + }, + "required" : [ "teamId", "tasks" ] + }, + "ApiViewerInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "viewersId" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "host" : { + "type" : "string" + }, + "ready" : { + "type" : "boolean" + } + }, + "required" : [ "viewersId", "username", "host", "ready" ] + }, + "ApiScore" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "teamId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "teamId", "score" ] + }, + "ApiScoreOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "scores" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScore" + } + } + }, + "required" : [ "name", "scores" ] + }, + "ApiScoreSeries" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "team" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "points" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiScoreSeriesPoint" + } + } + }, + "required" : [ "team", "name", "points" ] + }, + "ApiScoreSeriesPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "score" : { + "type" : "number", + "format" : "double" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "score", "timestamp" ] + }, + "ApiTeamGroupValue" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "value" : { + "type" : "number", + "format" : "double" + } + }, + "required" : [ "name", "value" ] + }, + "ApiAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiAnswerType" + }, + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" + }, + "text" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "temporalRange" : { + "$ref" : "#/components/schemas/TemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "taskId" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswer" + } + } + }, + "required" : [ "id", "status", "taskId", "answers" ] + }, + "ApiAnswerType" : { + "type" : "string", + "enum" : [ "ITEM", "TEMPORAL", "TEXT" ] + }, + "ApiClientAnswer" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "text" : { + "type" : "string" + }, + "mediaItemName" : { + "type" : "string" + }, + "mediaItemCollectionName" : { + "type" : "string" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "ApiClientAnswerSet" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "taskName" : { + "type" : "string" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswer" + } + } + }, + "required" : [ "answers" ] + }, + "ApiClientSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "answerSets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiClientAnswerSet" + } + } + }, + "required" : [ "answerSets" ] + }, + "ApiSubmission" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "submissionId" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + }, + "memberId" : { + "type" : "string" + }, + "teamName" : { + "type" : "string" + }, + "memberName" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "answers" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "submissionId", "teamId", "memberId", "teamName", "memberName", "timestamp", "answers" ] + }, + "ApiVerdictStatus" : { + "type" : "string", + "enum" : [ "CORRECT", "WRONG", "INDETERMINATE", "UNDECIDABLE" ] + }, + "ApiJudgement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "token", "validator", "verdict" ] + }, + "ApiJudgementRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "token" : { + "type" : "string" + }, + "validator" : { + "type" : "string" + }, + "taskDescription" : { + "type" : "string" + }, + "answerSet" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + }, + "required" : [ "validator", "taskDescription", "answerSet" ] + }, + "ApiJudgementValidatorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "validatorName" : { + "type" : "string" + }, + "pending" : { + "type" : "integer", + "format" : "int32" + }, + "open" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "validatorName", "pending", "open" ] + }, + "ApiVote" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "verdict" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + } + }, + "required" : [ "verdict" ] + }, + "ErrorStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "description" ] + }, + "SuccessfulSubmissionsStatus" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "status" : { + "type" : "boolean" + }, + "submission" : { + "$ref" : "#/components/schemas/ApiVerdictStatus" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "status", "submission", "description" ] + }, + "CurrentTime" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timeStamp" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "timeStamp" ] + }, + "DresInfo" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "version" : { + "type" : "string" + }, + "startTime" : { + "type" : "integer", + "format" : "int64" + }, + "uptime" : { + "type" : "integer", + "format" : "int64" + }, + "os" : { + "type" : "string" + }, + "jvm" : { + "type" : "string" + }, + "args" : { + "type" : "string" + }, + "cores" : { + "type" : "integer", + "format" : "int32" + }, + "freeMemory" : { + "type" : "integer", + "format" : "int64" + }, + "totalMemory" : { + "type" : "integer", + "format" : "int64" + }, + "load" : { + "type" : "number", + "format" : "double" + }, + "availableSeverThreads" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "version", "startTime", "uptime" ] + }, + "ApiContentElement" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "contentType" : { + "$ref" : "#/components/schemas/ApiContentType" + }, + "content" : { + "type" : "string" + }, + "offset" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "contentType", "offset" ] + }, + "ApiContentType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiCreateEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + }, + "required" : [ "name", "description" ] + }, + "ApiEvaluationStartMessage" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "templateId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "properties" : { + "$ref" : "#/components/schemas/ApiRunProperties" + } + }, + "required" : [ "templateId", "name", "type", "properties" ] + }, + "ApiEvaluationTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "created" : { + "type" : "integer", + "format" : "int64" + }, + "modified" : { + "type" : "integer", + "format" : "int64" + }, + "taskTypes" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskType" + } + }, + "taskGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskGroup" + } + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskTemplate" + } + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "teamGroups" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeamGroup" + } + }, + "judges" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "id", "name", "taskTypes", "taskGroups", "tasks", "teams", "teamGroups", "judges" ] + }, + "ApiEvaluationTemplateOverview" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "taskCount" : { + "type" : "integer", + "format" : "int32" + }, + "teamCount" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "id", "name", "taskCount", "teamCount" ] + }, + "ApiHint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiHintType" + }, + "start" : { + "type" : "integer", + "format" : "int64" + }, + "end" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "dataType" : { + "type" : "string" + }, + "mediaItem" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiHintContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + }, + "loop" : { + "type" : "boolean" + } + }, + "required" : [ "taskId", "sequence", "loop" ] + }, + "ApiHintType" : { + "type" : "string", + "enum" : [ "EMPTY", "TEXT", "VIDEO", "IMAGE" ] + }, + "ApiTarget" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "type" : { + "$ref" : "#/components/schemas/ApiTargetType" + }, + "target" : { + "type" : "string" + }, + "range" : { + "$ref" : "#/components/schemas/ApiTemporalRange" + } + }, + "required" : [ "type" ] + }, + "ApiTargetContent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "sequence" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiContentElement" + } + } + }, + "required" : [ "taskId", "sequence" ] + }, + "ApiTargetType" : { + "type" : "string", + "enum" : [ "JUDGEMENT", "JUDGEMENT_WITH_VOTE", "MEDIA_ITEM", "MEDIA_ITEM_TEMPORAL_RANGE", "TEXT" ] + }, + "ApiTaskGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "ApiTaskTemplate" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "taskGroup" : { + "type" : "string" + }, + "taskType" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "collectionId" : { + "type" : "string" + }, + "targets" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTarget" + } + }, + "hints" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHint" + } + }, + "comment" : { + "type" : "string" + } + }, + "required" : [ "name", "taskGroup", "taskType", "duration", "collectionId", "targets", "hints", "comment" ] + }, + "ApiTaskType" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "duration" : { + "type" : "integer", + "format" : "int64" + }, + "targetOption" : { + "$ref" : "#/components/schemas/ApiTargetOption" + }, + "hintOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiHintOption" + } + }, + "submissionOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiSubmissionOption" + } + }, + "taskOptions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTaskOption" + } + }, + "scoreOption" : { + "$ref" : "#/components/schemas/ApiScoreOption" + }, + "configuration" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + } + }, + "required" : [ "name", "duration", "targetOption", "hintOptions", "submissionOptions", "taskOptions", "scoreOption", "configuration" ] + }, + "ApiHintOption" : { + "type" : "string", + "enum" : [ "IMAGE_ITEM", "VIDEO_ITEM_SEGMENT", "TEXT", "EXTERNAL_IMAGE", "EXTERNAL_VIDEO" ] + }, + "ApiScoreOption" : { + "type" : "string", + "enum" : [ "KIS", "AVS", "LEGACY_AVS" ] + }, + "ApiSubmissionOption" : { + "type" : "string", + "enum" : [ "NO_DUPLICATES", "LIMIT_CORRECT_PER_TEAM", "LIMIT_WRONG_PER_TEAM", "LIMIT_TOTAL_PER_TEAM", "LIMIT_CORRECT_PER_MEMBER", "TEMPORAL_SUBMISSION", "TEXTUAL_SUBMISSION", "ITEM_SUBMISSION", "MINIMUM_TIME_GAP" ] + }, + "ApiTargetOption" : { + "type" : "string", + "enum" : [ "SINGLE_MEDIA_ITEM", "SINGLE_MEDIA_SEGMENT", "JUDGEMENT", "VOTE", "TEXT" ] + }, + "ApiTaskOption" : { + "type" : "string", + "enum" : [ "HIDDEN_RESULTS", "MAP_TO_SEGMENT", "PROLONG_ON_SUBMISSION" ] + }, + "ApiTeam" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "color" : { + "type" : "string" + }, + "users" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiUser" + } + }, + "logoData" : { + "type" : "string" + }, + "teamId" : { + "type" : "string" + } + }, + "required" : [ "users", "teamId" ] + }, + "ApiTeamAggregatorType" : { + "type" : "string", + "enum" : [ "MIN", "MAX", "MEAN", "LAST" ] + }, + "ApiTeamGroup" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "teams" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTeam" + } + }, + "aggregation" : { + "$ref" : "#/components/schemas/ApiTeamAggregatorType" + } + }, + "required" : [ "teams", "aggregation" ] + }, + "ApiRole" : { + "type" : "string", + "enum" : [ "ANYONE", "VIEWER", "PARTICIPANT", "JUDGE", "ADMIN" ] + }, + "ApiUser" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "id" : { + "type" : "string" + }, + "username" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + }, + "sessionId" : { + "type" : "string" + } + } + }, + "ApiUserRequest" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "role" : { + "$ref" : "#/components/schemas/ApiRole" + } + }, + "required" : [ "username" ] + }, + "QueryEvent" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "$ref" : "#/components/schemas/QueryEventCategory" + }, + "type" : { + "type" : "string" + }, + "value" : { + "type" : "string" + } + }, + "required" : [ "timestamp", "category", "type", "value" ] + }, + "QueryEventCategory" : { + "type" : "string", + "enum" : [ "TEXT", "IMAGE", "SKETCH", "FILTER", "BROWSING", "COOPERATION", "OTHER" ] + }, + "QueryEventLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + } + }, + "required" : [ "timestamp", "events" ] + }, + "QueryResult" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "item" : { + "type" : "string" + }, + "segment" : { + "type" : "integer", + "format" : "int32" + }, + "frame" : { + "type" : "integer", + "format" : "int32" + }, + "score" : { + "type" : "number", + "format" : "double" + }, + "rank" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "item" ] + }, + "QueryResultLog" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "timestamp" : { + "type" : "integer", + "format" : "int64" + }, + "sortType" : { + "type" : "string" + }, + "resultSetAvailability" : { + "type" : "string" + }, + "results" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryResult" + } + }, + "events" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryEvent" + } + } + }, + "required" : [ "timestamp", "sortType", "resultSetAvailability", "results", "events" ] + }, + "TemporalPoint" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { } + }, + "TemporalRange" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "start" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "end" : { + "$ref" : "#/components/schemas/TemporalPoint" + }, + "center" : { + "type" : "integer", + "format" : "int64" + } + }, + "required" : [ "start", "end", "center" ] + }, + "ApiRunProperties" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "participantCanView" : { + "type" : "boolean" + }, + "shuffleTasks" : { + "type" : "boolean" + }, + "allowRepeatedTasks" : { + "type" : "boolean" + }, + "limitSubmissionPreviews" : { + "type" : "integer", + "format" : "int32" + } + }, + "required" : [ "participantCanView", "shuffleTasks", "allowRepeatedTasks", "limitSubmissionPreviews" ] + }, + "RunManagerStatus" : { + "type" : "string", + "enum" : [ "CREATED", "ACTIVE", "TERMINATED" ] + } + }, + "securitySchemes" : { + "CookieAuth" : { + "in" : "cookie", + "name" : "SESSIONID", + "type" : "apiKey" + } + } + }, + "servers" : [ ], + "security" : [ ] } \ No newline at end of file From 8efa8d7aef06a03b33b6de40b948c444b3e33c04 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 3 Oct 2023 16:37:09 +0200 Subject: [PATCH 487/498] Minor adjustements --- frontend/src/app/run/abstract-run-list.component.ts | 8 ++++---- .../components/judges-list/judges-list.component.ts | 5 +++-- .../teamgroups-list/teamgroups-list.component.ts | 5 +++-- .../components/teams-list/teams-list.component.ts | 5 +++-- .../app/template/template-list/template-list.component.ts | 4 ++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/run/abstract-run-list.component.ts b/frontend/src/app/run/abstract-run-list.component.ts index 30ff3bebc..ec8ce4609 100644 --- a/frontend/src/app/run/abstract-run-list.component.ts +++ b/frontend/src/app/run/abstract-run-list.component.ts @@ -3,12 +3,12 @@ import { map, take } from 'rxjs/operators'; import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { + ApiRunProperties, ApiTaskStatus, DownloadService, EvaluationAdministratorService, EvaluationScoresService, - EvaluationService, RunManagerStatus, - RunProperties -} from '../../../openapi'; + EvaluationService, RunManagerStatus +} from "../../../openapi"; export interface RunInfoWithState { id: string; @@ -20,7 +20,7 @@ export interface RunInfoWithState { currentTask?: string; timeLeft: string; asynchronous: boolean; - runProperties: RunProperties; + runProperties: ApiRunProperties; } export class AbstractRunListComponent { diff --git a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts index 8466a6804..16b63ca23 100644 --- a/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts +++ b/frontend/src/app/template/template-builder/components/judges-list/judges-list.component.ts @@ -4,7 +4,7 @@ import { ApiRole, ApiUser, TemplateService, UserService } from "../../../../../. import {combineLatest, Observable} from 'rxjs'; import {AbstractTemplateBuilderComponent} from '../abstract-template-builder.component'; import {TemplateBuilderService} from '../../template-builder.service'; -import {filter, map, shareReplay, withLatestFrom} from 'rxjs/operators'; +import { filter, map, shareReplay, tap, withLatestFrom } from "rxjs/operators"; import {MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'; import { ActivatedRoute } from "@angular/router"; import { MatSnackBar } from "@angular/material/snack-bar"; @@ -74,7 +74,8 @@ export class JudgesListComponent extends AbstractTemplateBuilderComponent implem } else { return []; } - }) + }), + tap(_ => this.judgesTable?.renderRows()) ); } diff --git a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts index b06fd1b30..f4da2c537 100644 --- a/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts +++ b/frontend/src/app/template/template-builder/components/teamgroups-list/teamgroups-list.component.ts @@ -3,7 +3,7 @@ import { AbstractTemplateBuilderComponent } from "../abstract-template-builder.c import { TemplateBuilderService } from "../../template-builder.service"; import { ApiTaskType, ApiTeam, ApiTeamGroup, TemplateService } from "../../../../../../openapi"; import { Observable } from "rxjs"; -import { filter, map } from "rxjs/operators"; +import { filter, map, tap } from "rxjs/operators"; import { MatDialog } from "@angular/material/dialog"; import { ActionableDynamicTable, @@ -60,7 +60,8 @@ export class TeamgroupsListComponent extends AbstractTemplateBuilderComponent im }else{ return []; } - }) + }), + tap((_ => this.table?.renderRows())) ); } diff --git a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts index 6910a4f52..360bfe728 100644 --- a/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts +++ b/frontend/src/app/template/template-builder/components/teams-list/teams-list.component.ts @@ -6,7 +6,7 @@ import { Observable } from "rxjs"; import { TemplateBuilderService } from "../../template-builder.service"; import { MatDialog } from "@angular/material/dialog"; import { AppConfig } from "../../../../app.config"; -import { filter, map } from "rxjs/operators"; +import { filter, map, tap } from "rxjs/operators"; import { TeamBuilderDialogComponent } from "../team-builder-dialog/team-builder-dialog.component"; import { ActivatedRoute } from "@angular/router"; import { MatSnackBar } from "@angular/material/snack-bar"; @@ -48,7 +48,8 @@ export class TeamsListComponent extends AbstractTemplateBuilderComponent impleme } else { return []; } - }) + }), + tap(_ => this.teamTable?.renderRows()) ); } diff --git a/frontend/src/app/template/template-list/template-list.component.ts b/frontend/src/app/template/template-list/template-list.component.ts index af1be849d..290398d24 100644 --- a/frontend/src/app/template/template-list/template-list.component.ts +++ b/frontend/src/app/template/template-list/template-list.component.ts @@ -3,7 +3,7 @@ import { ApiCreateEvaluation, ApiEvaluationOverview, ApiEvaluationStartMessage, ApiEvaluationTemplateOverview, DownloadService, - EvaluationAdministratorService, RunProperties, SuccessStatus, + EvaluationAdministratorService, ApiRunProperties, SuccessStatus, TemplateService } from "../../../../openapi"; import { Router } from "@angular/router"; @@ -127,7 +127,7 @@ export class TemplateListComponent implements AfterViewInit{ shuffleTasks: r.shuffleTasks, allowRepeatedTasks: r.allowRepeatedTasks, limitSubmissionPreviews: r.limit - } as RunProperties // TODO Rename in BE to ApiEvaluationProperties + } as ApiRunProperties } as ApiEvaluationStartMessage) }) ).subscribe((r: SuccessStatus) => { From d8e5a61a0fe68eb1881a7a5705657a485f662e57 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 4 Oct 2023 17:08:25 +0200 Subject: [PATCH 488/498] Resolved #434 Added 'noUi' matrix parameter for viewer --- frontend/src/app/app-routing.module.ts | 2 +- frontend/src/app/app.component.html | 2 +- frontend/src/app/app.component.ts | 31 ++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 855d26ff6..dd639db13 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -118,7 +118,7 @@ const routes: Routes = [ /* Two important 'catch-all's. */ { path: '', redirectTo: 'evaluation/list', pathMatch: 'full' }, - { path: '**', component: NotFoundComponent } + { path: '**', component: NotFoundComponent, title: "404 - Not Found!" } ]; @NgModule({ diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index c96bd789f..8b2a233df 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,4 +1,4 @@ - +
    diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 4fa7f47b0..ff862b804 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,8 +1,8 @@ import { Component, inject } from "@angular/core"; -import { Router } from '@angular/router'; +import { ActivatedRoute, ActivationEnd, Router } from "@angular/router"; import { AuthenticationService } from './services/session/authentication.sevice'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { map } from 'rxjs/operators'; +import { filter, map, shareReplay } from "rxjs/operators"; import { Observable } from 'rxjs'; import { AppConfig } from './app.config'; import {ApiRole, ApiUser} from '../../openapi'; @@ -25,6 +25,7 @@ export class AppComponent { isAdmin: Observable; loggedIn: Observable; canJudge: Observable; + noUi: Observable; constructor( private authenticationService: AuthenticationService, @@ -32,7 +33,33 @@ export class AppComponent { private snackBar: MatSnackBar, public config: AppConfig, private dialog: MatDialog, + private activeRoute: ActivatedRoute, ) { + + this.noUi = this.router.events.pipe( + filter(e => (e instanceof ActivationEnd)), + map((e) => { + return e instanceof ActivationEnd ? {path: e.snapshot.params, query: e.snapshot.queryParams} : {} + }), + map(dto => { + const p = dto?.path ?? {} + if(p["runId"]){ + const runIdPathParams= p["runId"].split(";") // manual handling of matrix params + for (const pram of runIdPathParams) { + // either as flag ;noUi or as value noUi=true. when having noUi=false this will negate it and the ui will be shown + if(pram.startsWith("noUi") && !pram.endsWith("false")){ + return true + } + } + return false + }else{ + // check query parameters + const p = dto?.query ?? {} + return p["noUi"] ?? false + } + }) + ); + this.user = this.authenticationService.user; this.loggedIn = this.authenticationService.isLoggedIn; this.isAdmin = this.authenticationService.user.pipe(map((u) => u?.role === ApiRole.ADMIN)); From a99fec17db2d14f9d895f25f702d3912151bf7a2 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 4 Oct 2023 17:11:14 +0200 Subject: [PATCH 489/498] Resolved #433 Forbidden access redirects to login page --- frontend/src/app/services/session/authentication.sevice.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/services/session/authentication.sevice.ts b/frontend/src/app/services/session/authentication.sevice.ts index bdc35e761..f6db0a467 100644 --- a/frontend/src/app/services/session/authentication.sevice.ts +++ b/frontend/src/app/services/session/authentication.sevice.ts @@ -110,7 +110,8 @@ export class AuthenticationService { if (!role) { return this.router.parseUrl(`/login?returnUrl=${state.url}`) } else if (route.data.roles && route.data.roles.indexOf(role) === -1) { - return this.router.parseUrl('/forbidden'); + //return this.router.parseUrl('/forbidden'); + return this.router.parseUrl(`/login?returnUrl=${state.url}`) } else { return true; } From 5feb9cb633fd5aeefd083f88591980c752b0dab6 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 4 Oct 2023 17:27:53 +0200 Subject: [PATCH 490/498] #434 Also removed async controls --- frontend/src/app/viewer/run-viewer.component.html | 2 +- frontend/src/app/viewer/run-viewer.component.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/viewer/run-viewer.component.html b/frontend/src/app/viewer/run-viewer.component.html index da267497a..15c9a5017 100644 --- a/frontend/src/app/viewer/run-viewer.component.html +++ b/frontend/src/app/viewer/run-viewer.component.html @@ -1,5 +1,5 @@
    -
    +
    diff --git a/frontend/src/app/viewer/run-viewer.component.ts b/frontend/src/app/viewer/run-viewer.component.ts index 4adf30367..dd8c9e569 100644 --- a/frontend/src/app/viewer/run-viewer.component.ts +++ b/frontend/src/app/viewer/run-viewer.component.ts @@ -1,5 +1,5 @@ import {AfterViewInit, Component, Inject, OnDestroy, OnInit, ViewContainerRef} from '@angular/core'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { ActivatedRoute, ActivationEnd, Params, Router } from "@angular/router"; import {interval, merge, mergeMap, Observable, of, zip} from 'rxjs'; import { catchError, @@ -55,6 +55,8 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { /** Observable of the {@link Widget} that should be displayed at the bottom. */ bottomWidget: Observable; + noUi: Observable; + /** Cached config */ private p: any; @@ -105,6 +107,13 @@ export class RunViewerComponent implements OnInit, AfterViewInit, OnDestroy { map((a) => this.resolveWidgetFromParams(a, 'bottom')), shareReplay({ bufferSize: 1, refCount: true }) ); + this.noUi = this.activeRoute.paramMap.pipe( + map((a) => { + console.log("A", a); + const map = this.parseMatrixParams(a.get('runId')) + return Object.keys(map).includes("noUi") && map['noUi'] === "true" + }) + ) /* Basic observable for general run info; this information is static and does not change over the course of a run. */ this.info = this.evaluationId.pipe( From 630084a03987ff8819cfe1f1f2b014017a622ce7 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 6 Oct 2023 14:00:21 +0200 Subject: [PATCH 491/498] #337 Preparation: Added name where missing in api types --- .../api/rest/types/template/tasks/ApiHint.kt | 9 +++++++-- .../api/rest/types/template/tasks/ApiTarget.kt | 17 ++++++++++++++--- .../dev/dres/data/model/template/task/DbHint.kt | 1 + .../model/template/task/DbTaskTemplateTarget.kt | 7 ++++++- doc/oas-client.json | 6 ++++++ doc/oas.json | 6 ++++++ 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt index 4f6cdbb77..519e2a415 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt @@ -6,8 +6,8 @@ import dev.dres.data.model.template.* /** * The RESTful API equivalent for [TaskDescriptionHint]. * - * @author Luca Rossetto & Ralph Gasser - * @version 2.0.0 + * @author Luca Rossetto & Ralph Gasser & Loris Sauter + * @version 2.1.0 */ data class ApiHint( /** @@ -65,6 +65,11 @@ data class ApiHint( */ val mediaItem: String? = null, + /** + * This is a reference to the media item's name, in case there is one. + */ + val mediaItemName: String? = null, + /** * In case [type] is * diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt index f14af2442..88c4dd478 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt @@ -6,7 +6,18 @@ import dev.dres.data.model.template.task.DbTaskTemplateTarget /** * The RESTful API equivalent for [DbTaskTemplateTarget]. * - * @author Luca Rossetto & Ralph Gasser - * @version 1.0.0 + * @author Luca Rossetto & Ralph Gasser & Loris Sauter + * @version 1.1.0 */ -data class ApiTarget(val type: ApiTargetType, val target: String? = null, val range: ApiTemporalRange? = null) +data class ApiTarget( + val type: ApiTargetType, + /** + * The actual target, the semantic of this value depends on the type + */ + val target: String? = null, + val range: ApiTemporalRange? = null, + /** + * The name of the target, which is defined for certain types (e.g. [ApiTargetType]-MEDIA_TIEM ) + */ + val name: String? = null +) diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index 69f998522..4150b3d9b 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -66,6 +66,7 @@ class DbHint(entity: Entity) : XdEntity(entity) { end = this.end, description = this.text, mediaItem = this.item?.id, + mediaItemName = this.item?.name, dataType = this.type.mimeType, path = this.path, range = this.range?.let { ApiTemporalRange(it) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt index dad772c6a..f05f6c6a8 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt @@ -56,7 +56,12 @@ class DbTaskTemplateTarget(entity: Entity) : XdEntity(entity) { DbTargetType.JUDGEMENT, DbTargetType.JUDGEMENT_WITH_VOTE -> ApiTarget(this.type.toApi(), null) DbTargetType.MEDIA_ITEM, - DbTargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget(this.type.toApi(), this.item?.id, this.range?.let { ApiTemporalRange(it) }) + DbTargetType.MEDIA_ITEM_TEMPORAL_RANGE -> ApiTarget( + type=this.type.toApi(), + target = this.item?.id, + range= this.range?.let { ApiTemporalRange(it) }, + name= this.item?.name + ) DbTargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) else -> throw IllegalStateException("Task description of type ${this.type.description} is not supported.") } diff --git a/doc/oas-client.json b/doc/oas-client.json index d5952a3d8..a0df44bb2 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2018,6 +2018,9 @@ "mediaItem" : { "type" : "string" }, + "mediaItemName" : { + "type" : "string" + }, "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" } @@ -2059,6 +2062,9 @@ }, "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" + }, + "name" : { + "type" : "string" } }, "required" : [ "type" ] diff --git a/doc/oas.json b/doc/oas.json index d130fca18..e4f2569e8 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -6370,6 +6370,9 @@ "mediaItem" : { "type" : "string" }, + "mediaItemName" : { + "type" : "string" + }, "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" } @@ -6411,6 +6414,9 @@ }, "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" + }, + "name" : { + "type" : "string" } }, "required" : [ "type" ] From 44973b8e788f8d324e5ce030b28ecdc21872e325 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Fri, 6 Oct 2023 14:36:32 +0200 Subject: [PATCH 492/498] WiP: Fixing removing of task types, yet broken --- .../rest/types/template/tasks/ApiTaskType.kt | 14 +++++-- .../model/template/task/DbTaskTemplate.kt | 2 - .../data/model/template/task/DbTaskType.kt | 9 ++++- .../kotlin/dev/dres/mgmt/TemplateManager.kt | 38 ++++++++++++++++++- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt index 081aa29bc..b98874b4e 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt @@ -1,23 +1,26 @@ package dev.dres.api.rest.types.template.tasks +import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import dev.dres.api.rest.types.template.tasks.options.* import dev.dres.data.model.template.task.DbTaskType +import io.javalin.openapi.OpenApiIgnore import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption +typealias TaskTypeId = String + /** * The RESTful API equivalent of a [DbTaskType]. * - * @author Ralph Gasser - * @author Loris Sauter + * @author Ralph Gasser & Loris Sauter * @version 1.1.0 */ data class ApiTaskType( - val id: String?, + val id: TaskTypeId? = null, val name: String, val duration: Long, val targetOption: ApiTargetOption, @@ -46,4 +49,9 @@ data class ApiTaskType( ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue(it, ApiTaskType::class.java) } } + + val taskTypeId: TaskTypeId + @JsonIgnore + @OpenApiIgnore + get() = this.id ?: "N/A" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt index b2a18da88..6bb581246 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplate.kt @@ -7,10 +7,8 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.media.DbMediaCollection import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.run.interfaces.TaskRun -import dev.dres.data.model.template.TemplateId import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* -import kotlinx.dnq.link.OnDeletePolicy import kotlinx.dnq.query.* import kotlinx.dnq.simple.min import java.lang.IllegalStateException diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index aeddf4f69..e0b973994 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -1,6 +1,8 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.template.tasks.ApiTaskType +import dev.dres.api.rest.types.template.tasks.TaskTypeId +import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.options.* import dev.dres.data.model.template.task.options.DbConfiguredOption @@ -15,7 +17,7 @@ import kotlinx.dnq.simple.min * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class DbTaskType(entity: Entity) : XdEntity(entity) { +class DbTaskType(entity: Entity) : PersistentEntity(entity) { /** Combination of [DbTaskType] name / competition must be unique. */ companion object: XdNaturalEntityType() { override val compositeIndices = listOf( @@ -23,6 +25,11 @@ class DbTaskType(entity: Entity) : XdEntity(entity) { ) } + /** The [TaskTypeId] of this [DbTaskType]. */ + var taskTypeId: TaskTypeId + get() = this.id + set(value) { this.id = value } + /** The name of this [DbTaskType]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) diff --git a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index c63b4e3e9..8b8f5193a 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -11,6 +11,9 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.task.* import dev.dres.data.model.template.task.options.DbConfiguredOption +import dev.dres.data.model.template.task.options.DbHintOption +import dev.dres.data.model.template.task.options.DbSubmissionOption +import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.template.team.TeamId @@ -92,9 +95,40 @@ object TemplateManager { dbEvaluationTemplate.modified = DateTime.now() /* Update task type information. */ - val taskTypes = apiEvaluationTemplate.taskTypes.map { it.name }.toTypedArray() + + /* Update task type information: Remove deleted types. */ + val typesIds = apiEvaluationTemplate.taskTypes.mapNotNull { it.id }.toTypedArray() + val typesToDeleteQuery = DbTaskType.query( + DbTaskType::evaluation eq dbEvaluationTemplate and not( + DbTaskType::id.containsIn(*typesIds) + ) + ) + val hintOptsToDelIds = typesToDeleteQuery.toList().map { + it.hints.toList().map { hint -> hint.entityId } + }.flatten().toTypedArray() + val submissionOptsToDelIds = typesToDeleteQuery.toList().map{ + it.submission.toList().map{target -> target.entityId} + }.flatten().toTypedArray() + val taskOptsToDelIds = typesToDeleteQuery.toList().map{ + it.options.toList().map{target -> target.entityId} + }.flatten().toTypedArray() + val configOptsToDelIds = typesToDeleteQuery.toList().map{ + it.configurations.toList().map{target -> target.entityId} + }.flatten().toTypedArray() + + /* + DbTaskTemplate has children relationships with both, DbHint and DbTaskTarget. + Despite being written in the documentation, for some reason the .removeAll above does not + delete the children, hence we have to take care of it ourselves. + https://jetbrains.github.io/xodus-dnq/properties.html + */ + DbHintOption.all().toList().filter{hintOptsToDelIds.contains(it.entityId)}.forEach { it.delete() } + DbSubmissionOption.all().toList().filter{submissionOptsToDelIds.contains(it.entityId)}.forEach{it.delete()} + DbTaskOption.all().toList().filter{taskOptsToDelIds.contains(it.entityId)}.forEach{it.delete()} + DbConfiguredOption.all().toList().filter{configOptsToDelIds.contains(it.entityId)}.forEach{it.delete()} + dbEvaluationTemplate.taskTypes.removeAll( - DbTaskType.query(DbTaskType::evaluation eq dbEvaluationTemplate and not(DbTaskType::name.containsIn(*taskTypes))) + typesToDeleteQuery ) for (apiTaskType in apiEvaluationTemplate.taskTypes) { val taskType = From 7a7e39a2f58c3d367bf8d2e8bbd4e993f4b8ecc5 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Tue, 10 Oct 2023 21:13:40 +0200 Subject: [PATCH 493/498] #337: Added support for importing from templates on the server --- .../rest/types/template/tasks/ApiTaskType.kt | 8 +- .../data/model/template/task/DbTaskType.kt | 10 +- .../kotlin/dev/dres/mgmt/TemplateManager.kt | 38 +---- .../template-import-tree.component.ts | 155 +++++++++--------- .../template-builder.component.html | 2 +- .../template-builder.component.ts | 12 +- .../template-builder.service.ts | 28 +++- 7 files changed, 116 insertions(+), 137 deletions(-) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt index b98874b4e..f7f11f417 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTaskType.kt @@ -11,7 +11,6 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption -typealias TaskTypeId = String /** * The RESTful API equivalent of a [DbTaskType]. @@ -20,7 +19,6 @@ typealias TaskTypeId = String * @version 1.1.0 */ data class ApiTaskType( - val id: TaskTypeId? = null, val name: String, val duration: Long, val targetOption: ApiTargetOption, @@ -31,7 +29,7 @@ data class ApiTaskType( val configuration: Map ) { - constructor() : this("---NO_ID---","---Default TaskType DO NOT USE!---", + constructor() : this("---Default TaskType DO NOT USE!---", 1, ApiTargetOption.TEXT, listOf(ApiHintOption.TEXT), listOf(ApiSubmissionOption.TEXTUAL_SUBMISSION), @@ -50,8 +48,4 @@ data class ApiTaskType( } } - val taskTypeId: TaskTypeId - @JsonIgnore - @OpenApiIgnore - get() = this.id ?: "N/A" } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt index e0b973994..0090c4046 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskType.kt @@ -1,8 +1,6 @@ package dev.dres.data.model.template.task import dev.dres.api.rest.types.template.tasks.ApiTaskType -import dev.dres.api.rest.types.template.tasks.TaskTypeId -import dev.dres.data.model.PersistentEntity import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.task.options.* import dev.dres.data.model.template.task.options.DbConfiguredOption @@ -17,7 +15,7 @@ import kotlinx.dnq.simple.min * @author Luca Rossetto & Ralph Gasser * @version 2.0.0 */ -class DbTaskType(entity: Entity) : PersistentEntity(entity) { +class DbTaskType(entity: Entity) : XdEntity(entity) { /** Combination of [DbTaskType] name / competition must be unique. */ companion object: XdNaturalEntityType() { override val compositeIndices = listOf( @@ -25,11 +23,6 @@ class DbTaskType(entity: Entity) : PersistentEntity(entity) { ) } - /** The [TaskTypeId] of this [DbTaskType]. */ - var taskTypeId: TaskTypeId - get() = this.id - set(value) { this.id = value } - /** The name of this [DbTaskType]. */ var name by xdRequiredStringProp(unique = false, trimmed = false) @@ -63,7 +56,6 @@ class DbTaskType(entity: Entity) : PersistentEntity(entity) { * @return [ApiTaskType] */ fun toApi(): ApiTaskType = ApiTaskType( - id=this.xdId, name = this.name, duration = this.duration, targetOption = this.target.toApi(), diff --git a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index 8b8f5193a..c63b4e3e9 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -11,9 +11,6 @@ import dev.dres.data.model.template.DbEvaluationTemplate import dev.dres.data.model.template.TemplateId import dev.dres.data.model.template.task.* import dev.dres.data.model.template.task.options.DbConfiguredOption -import dev.dres.data.model.template.task.options.DbHintOption -import dev.dres.data.model.template.task.options.DbSubmissionOption -import dev.dres.data.model.template.task.options.DbTaskOption import dev.dres.data.model.template.team.DbTeam import dev.dres.data.model.template.team.DbTeamGroup import dev.dres.data.model.template.team.TeamId @@ -95,40 +92,9 @@ object TemplateManager { dbEvaluationTemplate.modified = DateTime.now() /* Update task type information. */ - - /* Update task type information: Remove deleted types. */ - val typesIds = apiEvaluationTemplate.taskTypes.mapNotNull { it.id }.toTypedArray() - val typesToDeleteQuery = DbTaskType.query( - DbTaskType::evaluation eq dbEvaluationTemplate and not( - DbTaskType::id.containsIn(*typesIds) - ) - ) - val hintOptsToDelIds = typesToDeleteQuery.toList().map { - it.hints.toList().map { hint -> hint.entityId } - }.flatten().toTypedArray() - val submissionOptsToDelIds = typesToDeleteQuery.toList().map{ - it.submission.toList().map{target -> target.entityId} - }.flatten().toTypedArray() - val taskOptsToDelIds = typesToDeleteQuery.toList().map{ - it.options.toList().map{target -> target.entityId} - }.flatten().toTypedArray() - val configOptsToDelIds = typesToDeleteQuery.toList().map{ - it.configurations.toList().map{target -> target.entityId} - }.flatten().toTypedArray() - - /* - DbTaskTemplate has children relationships with both, DbHint and DbTaskTarget. - Despite being written in the documentation, for some reason the .removeAll above does not - delete the children, hence we have to take care of it ourselves. - https://jetbrains.github.io/xodus-dnq/properties.html - */ - DbHintOption.all().toList().filter{hintOptsToDelIds.contains(it.entityId)}.forEach { it.delete() } - DbSubmissionOption.all().toList().filter{submissionOptsToDelIds.contains(it.entityId)}.forEach{it.delete()} - DbTaskOption.all().toList().filter{taskOptsToDelIds.contains(it.entityId)}.forEach{it.delete()} - DbConfiguredOption.all().toList().filter{configOptsToDelIds.contains(it.entityId)}.forEach{it.delete()} - + val taskTypes = apiEvaluationTemplate.taskTypes.map { it.name }.toTypedArray() dbEvaluationTemplate.taskTypes.removeAll( - typesToDeleteQuery + DbTaskType.query(DbTaskType::evaluation eq dbEvaluationTemplate and not(DbTaskType::name.containsIn(*taskTypes))) ) for (apiTaskType in apiEvaluationTemplate.taskTypes) { val taskType = diff --git a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts index 43de238b0..88a4187f3 100644 --- a/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts +++ b/frontend/src/app/template/template-builder/components/template-import-tree/template-import-tree.component.ts @@ -23,7 +23,7 @@ export enum TemplateImportTreeBranch { TEAMS = 1 << 3, // 001000 TEAM_GROUPS = 1 << 4, // 010000 JUDGES = 1 << 5, // 100000 - ALL = ~(~0<<6) // 111111 + ALL = ~(~0 << 6) // 111111 } /** @@ -34,7 +34,7 @@ export class TemplateTreeFlatNode { expandable: boolean; item: T; label: string; - branch: TemplateImportTreeBranch + branch: TemplateImportTreeBranch; } export class TemplateTreeNode { @@ -50,7 +50,7 @@ export class TemplateTreeNode { templateUrl: "./template-import-tree.component.html", styleUrls: ["./template-import-tree.component.scss"] }) -export class TemplateImportTreeComponent implements OnInit{ +export class TemplateImportTreeComponent implements OnInit { flatNodeMap = new Map, TemplateTreeNode>(); nestedNodeMap = new Map, TemplateTreeFlatNode>(); @@ -59,9 +59,9 @@ export class TemplateImportTreeComponent implements OnInit{ selectedParent: TemplateTreeFlatNode | null = null; - treeControl: FlatTreeControl> - treeFlattener: MatTreeFlattener, TemplateTreeFlatNode> - dataSource: MatTreeFlatDataSource, TemplateTreeFlatNode> + treeControl: FlatTreeControl>; + treeFlattener: MatTreeFlattener, TemplateTreeFlatNode>; + dataSource: MatTreeFlatDataSource, TemplateTreeFlatNode>; selection = new SelectionModel>(true); @@ -73,21 +73,21 @@ export class TemplateImportTreeComponent implements OnInit{ constructor() { this.treeFlattener = new MatTreeFlattener, TemplateTreeFlatNode>( this.transformer, this.getLevel, this.isExpandable, this.getChildren - ) + ); this.treeControl = new FlatTreeControl>(this.getLevel, this.isExpandable); this.dataSource = new MatTreeFlatDataSource, TemplateTreeFlatNode>(this.treeControl, this.treeFlattener); } ngOnInit(): void { - this.dataSource.data = TemplateImportTreeComponent.buildTrees(this.templates, this.branches); - this.templates.forEach(it => this.templatesMap.set(it.id, it)); - console.log(this.templatesMap) - } + this.dataSource.data = TemplateImportTreeComponent.buildTrees(this.templates, this.branches); + this.templates.forEach(it => this.templatesMap.set(it.id, it)); + console.log(this.templatesMap); + } getLevel = (node: TemplateTreeFlatNode) => node.level; isExpandable = (node: TemplateTreeFlatNode) => node.expandable; getChildren = (node: TemplateTreeNode) => node.children; - hasChild = (_: number, node: TemplateTreeFlatNode) => node.expandable + hasChild = (_: number, node: TemplateTreeFlatNode) => node.expandable; transformer = (node: TemplateTreeNode, level: number) => { const existingNode = this.nestedNodeMap.get(node); @@ -100,7 +100,7 @@ export class TemplateImportTreeComponent implements OnInit{ this.flatNodeMap.set(flatNode, node); this.nestedNodeMap.set(node, flatNode); return flatNode; - } + }; /** Whether all the descendants of the node are selected. */ descendantsAllSelected(node: TemplateTreeFlatNode): boolean { @@ -182,7 +182,7 @@ export class TemplateImportTreeComponent implements OnInit{ return null; } - public getImportTemplate(){ + public getImportTemplate(): ApiEvaluationTemplate | null { const types: ApiTaskType[] = []; const taskGroups: ApiTaskGroup[] = []; const tasks: ApiTaskTemplate[] = []; @@ -192,34 +192,42 @@ export class TemplateImportTreeComponent implements OnInit{ /** Sanitation */ /* Tasks require task groups which in turn require types*/ this.getAllSelectedTaskTemplates().forEach(it => { - it[0].id = undefined + it[0].id = undefined; tasks.push(it[0]); - const group = this.templatesMap.get(it[1]).taskGroups.find((g:ApiTaskGroup) => g.name === it[0].taskGroup) - if(!taskGroups.includes(group)){ + const group = this.templatesMap.get(it[1]).taskGroups.find((g: ApiTaskGroup) => g.name === it[0].taskGroup); + if (!taskGroups.includes(group)) { taskGroups.push(group); } - const type = this.templatesMap.get(it[1]).taskTypes.find((t:ApiTaskType) => t.name === group.type) - if(!types.includes(type)){ - types.push(type) + const type = this.templatesMap.get(it[1]).taskTypes.find((t: ApiTaskType) => t.name === group.type); + if (!types.includes(type)) { + types.push(type); } - }) + }); /* TaskGroup requires TaskType */ this.getAllSelectedTaskGroups().forEach(it => { - if(!taskGroups.includes(it[0])){ - taskGroups.push(it[0]) - const type = this.templatesMap.get(it[1]).taskTypes.find((t: ApiTaskType) => t.name === it[0].type) - if(!types.includes(type)){ - types.push(type) + if (!taskGroups.includes(it[0])) { + taskGroups.push(it[0]); + const type = this.templatesMap.get(it[1]).taskTypes.find((t: ApiTaskType) => t.name === it[0].type); + if (!types.includes(type)) { + types.push(type); } } - }) + }); this.getAllSelectedTaskTypes().forEach(it => { - if(!types.includes(it[0])){ - types.push(it[0]) + if (!types.includes(it[0])) { + types.push(it[0]); } - }) + }); + + /* Catch no selection */ + + if ( + this.selection.isEmpty() + ) { + return null; + } return { @@ -231,32 +239,32 @@ export class TemplateImportTreeComponent implements OnInit{ teams: teams, teamGroups: teamGroups, judges: judges, - id:"---IMPORT_TEMPLATE_NO_ID---" - } as ApiEvaluationTemplate + id: "---IMPORT_TEMPLATE_NO_ID---" + } as ApiEvaluationTemplate; } - public getAllSelectedTaskTypes(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TYPES) as [ApiTaskType, string][] + public getAllSelectedTaskTypes() { + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TYPES) as [ApiTaskType, string][]; } - public getAllSelectedTaskGroups(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_GROUPS) as [ApiTaskGroup,string][]; + public getAllSelectedTaskGroups() { + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_GROUPS) as [ApiTaskGroup, string][]; } - public getAllSelectedTaskTemplates(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TEMPLATES) as [ApiTaskTemplate, string][] + public getAllSelectedTaskTemplates() { + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TASK_TEMPLATES) as [ApiTaskTemplate, string][]; } - public getAllSelectedTeams(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAMS) as [ApiTeam, string][] + public getAllSelectedTeams() { + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAMS) as [ApiTeam, string][]; } - public getAllSelectedTeamGroups(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAM_GROUPS) as [ApiTeamGroup,string][] + public getAllSelectedTeamGroups() { + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.TEAM_GROUPS) as [ApiTeamGroup, string][]; } - public getAllSelectedJudges(){ - return this.getSelectedItemsForBranch(TemplateImportTreeBranch.JUDGES) as [ApiUser,string][] + public getAllSelectedJudges() { + return this.getSelectedItemsForBranch(TemplateImportTreeBranch.JUDGES) as [ApiUser, string][]; } /** @@ -264,44 +272,43 @@ export class TemplateImportTreeComponent implements OnInit{ * @param branch A single branch, do not use ALL or NONE here (or any combination) * @private */ - private getSelectedItemsForBranch(branch: TemplateImportTreeBranch){ + private getSelectedItemsForBranch(branch: TemplateImportTreeBranch) { /* Filter appropriately */ - const items = this.selection.selected.filter(it => TemplateImportTreeComponent.checkForBranch(it.branch, branch)).map(it => this.flatNodeMap.get(it)) - switch(branch){ + const items = this.selection.selected.filter(it => TemplateImportTreeComponent.checkForBranch(it.branch, branch)).map(it => this.flatNodeMap.get(it)); + switch (branch) { case TemplateImportTreeBranch.NONE: case TemplateImportTreeBranch.ALL: - throw new Error("Cannot type set for TemplateImportTreeBanches ALL and NONE. This is a programmer's error") + throw new Error("Cannot type set for TemplateImportTreeBanches ALL and NONE. This is a programmer's error"); case TemplateImportTreeBranch.TASK_TYPES: - return items.map<[ApiTaskType, string]>(it => [it.item, it.origin]) + return items.map<[ApiTaskType, string]>(it => [it.item, it.origin]); case TemplateImportTreeBranch.TASK_GROUPS: - return items.map<[ApiTaskGroup, string]>(it => [it.item, it.origin]) + return items.map<[ApiTaskGroup, string]>(it => [it.item, it.origin]); case TemplateImportTreeBranch.TASK_TEMPLATES: return items.map<[ApiTaskTemplate, string]>(it => { /* Warning: collectionId remains and therefore must exist */ const newItem = it.item as ApiTaskTemplate; // newItem.id = undefined; // We need the id for sanitation purposes (to resolve for the parent evaluation template */ - return [newItem, it.origin] - }) + return [newItem, it.origin]; + }); case TemplateImportTreeBranch.TEAMS: return items.map<[ApiTeam, string]>(it => { - const newItem = it.item as ApiTeam + const newItem = it.item as ApiTeam; newItem.id = undefined; - return [newItem, it.origin] - }) + return [newItem, it.origin]; + }); case TemplateImportTreeBranch.TEAM_GROUPS: return items.map<[ApiTeamGroup, string]>(it => { - const newItem = it.item as ApiTeamGroup - newItem.id = undefined - return [newItem, it.origin] - }) + const newItem = it.item as ApiTeamGroup; + newItem.id = undefined; + return [newItem, it.origin]; + }); case TemplateImportTreeBranch.JUDGES: - return items.map<[ApiUser, string]>(it => [it.item,it.origin]) + return items.map<[ApiUser, string]>(it => [it.item, it.origin]); } } - - public static buildTrees(templates: ApiEvaluationTemplate[], branches: TemplateImportTreeBranch): TemplateTreeNode[]{ + public static buildTrees(templates: ApiEvaluationTemplate[], branches: TemplateImportTreeBranch): TemplateTreeNode[] { return templates.map(it => this.buildTree(it, branches)); } @@ -310,19 +317,19 @@ export class TemplateImportTreeComponent implements OnInit{ root.item = template; root.label = template.name; root.children = [] as TemplateTreeNode[]; - if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TYPES) && template.taskTypes.length > 0){ + if (this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TYPES) && template.taskTypes.length > 0) { root.children.push(this.buildTaskTypesBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_GROUPS) && template.taskGroups.length > 0){ + if (this.checkForBranch(branches, TemplateImportTreeBranch.TASK_GROUPS) && template.taskGroups.length > 0) { root.children.push(this.buildTaskGroupsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TEMPLATES) && template.tasks.length > 0){ + if (this.checkForBranch(branches, TemplateImportTreeBranch.TASK_TEMPLATES) && template.tasks.length > 0) { root.children.push(this.buildTaskTemplatesBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAMS) && template.teams.length > 0){ + if (this.checkForBranch(branches, TemplateImportTreeBranch.TEAMS) && template.teams.length > 0) { root.children.push(this.buildTeamsBranch(template)); } - if(this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS) && template.teamGroups.length > 0){ + if (this.checkForBranch(branches, TemplateImportTreeBranch.TEAM_GROUPS) && template.teamGroups.length > 0) { root.children.push(this.buildTeamGroupsBranch(template)); } // TODO load apiusers for judges on the fly in future version @@ -332,24 +339,24 @@ export class TemplateImportTreeComponent implements OnInit{ return root; } - public static checkForBranch(branches: TemplateImportTreeBranch, test: TemplateImportTreeBranch): boolean{ - return (branches & test) === test + public static checkForBranch(branches: TemplateImportTreeBranch, test: TemplateImportTreeBranch): boolean { + return (branches & test) === test; } public static buildTaskTypesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "taskTypes", "Task Types", "name",TemplateImportTreeBranch.TASK_TYPES); + return this.buildBranch(template, "taskTypes", "Task Types", "name", TemplateImportTreeBranch.TASK_TYPES); } public static buildTaskGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "taskGroups", "Task Groups", "name",TemplateImportTreeBranch.TASK_GROUPS); + return this.buildBranch(template, "taskGroups", "Task Groups", "name", TemplateImportTreeBranch.TASK_GROUPS); } public static buildTaskTemplatesBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "tasks", "Task Templates","name", TemplateImportTreeBranch.TASK_TEMPLATES); + return this.buildBranch(template, "tasks", "Task Templates", "name", TemplateImportTreeBranch.TASK_TEMPLATES); } public static buildTeamsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { - return this.buildBranch(template, "teams", "Teams","name", TemplateImportTreeBranch.TEAMS); + return this.buildBranch(template, "teams", "Teams", "name", TemplateImportTreeBranch.TEAMS); } public static buildTeamGroupsBranch(template: ApiEvaluationTemplate): TemplateTreeNode { @@ -371,7 +378,7 @@ export class TemplateImportTreeComponent implements OnInit{ item.item = it; item.children = null; item.branch = branch; - item.origin = template.id + item.origin = template.id; //console.log("THE CHILD", item) return item; }); diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index 6023d3cb3..a83f8ac58 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -37,7 +37,7 @@

    Edit evaluation template {{(builderService.templateAsObservable() | async)?. aria-label="Import from other evaluation templates" matTooltip="Import from other evaluation templates" (click)="import()"> - file_upload + merge

    diff --git a/frontend/src/app/template/template-builder/template-builder.component.ts b/frontend/src/app/template/template-builder/template-builder.component.ts index c79739052..9932110a5 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.ts +++ b/frontend/src/app/template/template-builder/template-builder.component.ts @@ -98,9 +98,11 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i switchMap(templateList => forkJoin(...templateList)) ); templateList.subscribe(templates => { - console.log("Templates ", templates) - const ownIdx = templates.indexOf(this.builderService.getTemplate()) - templates.splice(ownIdx,1) + const ownIdx = templates.map(it => it.id).indexOf(this.builderService.getTemplate().id) + if(ownIdx !== -1){ + /* In case something went wrong, ownIdx results in -1, which with the splice would result in splicing the last element, which in this case we dont want */ + templates.splice(ownIdx,1) + } const dialogref = this.dialg.open(TemplateImportDialogComponent, {width: '800px', data: {templates: templates, branches: TemplateImportTreeBranch.ALL} as TemplateImportDialogData}) dialogref.afterClosed().subscribe( d => { this.onImport(d) @@ -111,7 +113,9 @@ export class TemplateBuilderComponent extends AbstractTemplateBuilderComponent i public onImport(templateToImportFrom: ApiEvaluationTemplate){ console.log("Importing...", templateToImportFrom) - this.builderService.importFrom(templateToImportFrom); + if(templateToImportFrom){ + this.builderService.importFrom(templateToImportFrom); + } } public save(){ diff --git a/frontend/src/app/template/template-builder/template-builder.service.ts b/frontend/src/app/template/template-builder/template-builder.service.ts index acd0e153f..77798c321 100644 --- a/frontend/src/app/template/template-builder/template-builder.service.ts +++ b/frontend/src/app/template/template-builder/template-builder.service.ts @@ -263,38 +263,54 @@ export class TemplateBuilderService { } importFrom(from: ApiEvaluationTemplate) { + /* Import check is currently on name, may switch to completely UUID based matching */ /* types */ from.taskTypes.forEach(it => { - if(!this.getTemplate().taskTypes.includes(it)){ + if(!this.getTemplate().taskTypes.map(that => that.name).includes(it.name)){ + this.getTemplate().taskTypes.push(it) + }else{ + it.name = `${it.name} (Imported)` this.getTemplate().taskTypes.push(it) } }) /* task groups */ from.taskGroups.forEach(it => { - if(!this.getTemplate().taskGroups.includes(it)){ + if(!this.getTemplate().taskGroups.map(that => that.name).includes(it.name)){ + this.getTemplate().taskGroups.push(it) + }else{ + it.name = `${it.name} (Imported)` this.getTemplate().taskGroups.push(it) } }) /* tasks */ from.tasks.forEach(it => { - if(!this.getTemplate().tasks.includes(it)){ + if(!this.getTemplate().tasks.map(that => that.name).includes(it.name)){ + this.getTemplate().tasks.push(it) + }else{ + it.name = `${it.name} (Imported)` this.getTemplate().tasks.push(it) } }) /* teams */ from.teams.forEach(it => { - if(!this.getTemplate().teams.includes(it)){ + if(!this.getTemplate().teams.map(that => that.name).includes(it.name)){ + this.getTemplate().teams.push(it) + }else{ + it.name = `${it.name} (Imported)` this.getTemplate().teams.push(it) } }) /* team groups */ from.teamGroups.forEach(it => { - if(!this.getTemplate().teamGroups.includes(it)){ + if(!this.getTemplate().teamGroups.map(that => that.name).includes(it.name)){ + this.getTemplate().teamGroups.push(it) + }else{ + it.name = `${it.name} (Imported)` this.getTemplate().teamGroups.push(it) } }) - /* judges */ + /* judges are userids, hence no renaming */ from.judges.forEach(it => { if(!this.getTemplate().judges.includes(it)){ this.getTemplate().judges.push(it) From 062cece2a60f8f514fe76c96d12dd70b961cc9a1 Mon Sep 17 00:00:00 2001 From: Loris Sauter Date: Wed, 11 Oct 2023 11:30:17 +0200 Subject: [PATCH 494/498] #337: Added support for import of templates, runs --- .../main/kotlin/dev/dres/api/rest/RestApi.kt | 1 + .../evaluation/admin/ForceViewerHandler.kt | 2 +- .../evaluation/admin/GetEvaluationHandler.kt | 46 +++++++ doc/oas-client.json | 67 ++++++++- doc/oas.json | 129 +++++++++++++++++- .../upload-json-button.component.html | 2 +- .../upload-json-button.component.ts | 4 +- .../template-builder.component.html | 5 +- .../template-builder.component.ts | 48 +++++-- .../template-builder.service.ts | 16 +++ frontend/src/app/utilities/api.utilities.ts | 18 +++ 11 files changed, 312 insertions(+), 26 deletions(-) create mode 100644 backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/GetEvaluationHandler.kt create mode 100644 frontend/src/app/utilities/api.utilities.ts diff --git a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt index aac696567..f73a3c354 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/RestApi.kt @@ -195,6 +195,7 @@ object RestApi { ListAllTeamsHandler(store), CreateTeamHandler(store), UpdateTeamHandler(store), + GetEvaluationHandler(store), // Judgement DequeueJudgementHandler(), diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt index e60880ebf..6178fff65 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/ForceViewerHandler.kt @@ -19,7 +19,7 @@ import jetbrains.exodus.database.TransientEntityStore * @author Loris Sauter * @version 2.0.0 */ -class ForceViewerHandler : AbstractEvaluationAdminHandler(), PostRestHandler { +class ForceViewerHandler() : AbstractEvaluationAdminHandler(), PostRestHandler { override val route: String = "evaluation/admin/{evaluationId}/viewer/list/{viewerId}/force" @OpenApi( diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/GetEvaluationHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/GetEvaluationHandler.kt new file mode 100644 index 000000000..b6b9ff9d5 --- /dev/null +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/admin/GetEvaluationHandler.kt @@ -0,0 +1,46 @@ +package dev.dres.api.rest.handler.evaluation.admin + +import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiEvaluation +import dev.dres.api.rest.types.evaluation.ApiEvaluationOverview +import dev.dres.api.rest.types.status.ErrorStatus +import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.data.model.run.DbEvaluation +import dev.dres.utilities.extensions.evaluationId +import io.javalin.http.Context +import io.javalin.openapi.* +import jetbrains.exodus.database.TransientEntityStore +import kotlinx.dnq.query.eq +import kotlinx.dnq.query.firstOrNull +import kotlinx.dnq.query.query + +class GetEvaluationHandler(private val store: TransientEntityStore): AbstractEvaluationAdminHandler(), + GetRestHandler { + override val route = "evaluation/admin/{evaluationId}" + + @OpenApi( + summary = "Provides the evaluation.", + path = "/api/v2/evaluation/admin/{evaluationId}", + operationId = OpenApiOperation.AUTO_GENERATE, + methods = [HttpMethod.GET], + pathParams = [ + OpenApiParam("evaluationId", String::class, "The evaluation ID", required = true, allowEmptyValue = false), + ], + tags = ["Evaluation Administrator"], + responses = [ + OpenApiResponse("200", [OpenApiContent(ApiEvaluation::class)]), + OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), + OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) + ] + ) + override fun doGet(ctx: Context): ApiEvaluation { + /* Obtain run id and run. */ + val evaluationId = ctx.pathParamMap().getOrElse("evaluationId") { throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", ctx) } + return this.store.transactional(true) { + DbEvaluation.query(DbEvaluation::id eq evaluationId).firstOrNull()?.toApi() + ?: throw ErrorStatusException(404, "Run $evaluationId not found", ctx) + } + + } +} diff --git a/doc/oas-client.json b/doc/oas-client.json index a0df44bb2..250c87086 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -1228,6 +1228,43 @@ "type" : "string", "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] }, + "ApiEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "template" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + }, + "created" : { + "type" : "integer", + "format" : "int64" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTask" + } + } + }, + "required" : [ "evaluationId", "name", "type", "template", "created", "tasks" ] + }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -1351,6 +1388,33 @@ }, "required" : [ "evaluationId", "taskId", "submissions" ] }, + "ApiTask" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "taskId", "templateId", "submissions" ] + }, "ApiTaskOverview" : { "type" : "object", "additionalProperties" : false, @@ -2150,9 +2214,6 @@ "type" : "object", "additionalProperties" : false, "properties" : { - "id" : { - "type" : "string" - }, "name" : { "type" : "string" }, diff --git a/doc/oas.json b/doc/oas.json index e4f2569e8..353647bf0 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -957,6 +957,68 @@ "security" : [ ] } }, + "/api/v2/evaluation/admin/{evaluationId}" : { + "get" : { + "tags" : [ "Evaluation Administrator" ], + "summary" : "Provides the evaluation.", + "operationId" : "getApiV2EvaluationAdminByEvaluationId", + "parameters" : [ { + "name" : "evaluationId", + "in" : "path", + "description" : "The evaluation ID", + "required" : true, + "deprecated" : false, + "allowEmptyValue" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ApiEvaluation" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "401" : { + "description" : "Unauthorized", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + }, + "404" : { + "description" : "Not Found", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ErrorStatus" + } + } + } + } + }, + "deprecated" : false, + "security" : [ ] + } + }, "/api/v2/evaluation/admin/{evaluationId}/adjust/{duration}" : { "patch" : { "tags" : [ "Evaluation Administrator" ], @@ -5580,6 +5642,43 @@ "type" : "string", "enum" : [ "FRAME_NUMBER", "SECONDS", "MILLISECONDS", "TIMECODE" ] }, + "ApiEvaluation" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "evaluationId" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "type" : { + "$ref" : "#/components/schemas/ApiEvaluationType" + }, + "template" : { + "$ref" : "#/components/schemas/ApiEvaluationTemplate" + }, + "created" : { + "type" : "integer", + "format" : "int64" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + }, + "tasks" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiTask" + } + } + }, + "required" : [ "evaluationId", "name", "type", "template", "created", "tasks" ] + }, "ApiEvaluationInfo" : { "type" : "object", "additionalProperties" : false, @@ -5703,6 +5802,33 @@ }, "required" : [ "evaluationId", "taskId", "submissions" ] }, + "ApiTask" : { + "type" : "object", + "additionalProperties" : false, + "properties" : { + "taskId" : { + "type" : "string" + }, + "templateId" : { + "type" : "string" + }, + "started" : { + "type" : "integer", + "format" : "int64" + }, + "ended" : { + "type" : "integer", + "format" : "int64" + }, + "submissions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/ApiAnswerSet" + } + } + }, + "required" : [ "taskId", "templateId", "submissions" ] + }, "ApiTaskOverview" : { "type" : "object", "additionalProperties" : false, @@ -6502,9 +6628,6 @@ "type" : "object", "additionalProperties" : false, "properties" : { - "id" : { - "type" : "string" - }, "name" : { "type" : "string" }, diff --git a/frontend/src/app/shared/upload-json-button/upload-json-button.component.html b/frontend/src/app/shared/upload-json-button/upload-json-button.component.html index f4b3b2ce7..82cac937a 100644 --- a/frontend/src/app/shared/upload-json-button/upload-json-button.component.html +++ b/frontend/src/app/shared/upload-json-button/upload-json-button.component.html @@ -2,7 +2,7 @@ cloud_upload {{ name }} - diff --git a/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts b/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts index 9a5b3a490..a222fa31d 100644 --- a/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts +++ b/frontend/src/app/shared/upload-json-button/upload-json-button.component.ts @@ -14,7 +14,7 @@ export class UploadJsonButtonComponent { /** If multi-select files are enabled. Defaults to false (only single file) */ @Input() multi = false; // Currently only single file upload handled /** The handler to process the uploaded thing */ - @Input() handler: (contents: string) => void; + @Input() handler: (contents: any) => void; @ViewChild('fileInput', { static: false }) fileUpload: HTMLInputElement; @@ -34,7 +34,7 @@ export class UploadJsonButtonComponent { const reader = new FileReader(); reader.onload = (e: any) => { - this.handler(e.target.result); + this.handler(JSON.parse(e.target.result)); }; if (this.multi) { // TODO multi file upload diff --git a/frontend/src/app/template/template-builder/template-builder.component.html b/frontend/src/app/template/template-builder/template-builder.component.html index a83f8ac58..030f85af3 100644 --- a/frontend/src/app/template/template-builder/template-builder.component.html +++ b/frontend/src/app/template/template-builder/template-builder.component.html @@ -30,7 +30,10 @@

    Edit evaluation template {{(builderService.templateAsObservable() | async)?. matTooltip="Download the entire evaluation template as JSON">

    - +